Writing my new plugin in Emacs Lisp, I learned a few things. Here are some of them, in no particular order.

xml-rpc.el has some problems

I don't mean to be unkind here. xml-rpc.el seems pretty good if you are doing exactly one kind of thing: calling a method on a server that can be reached by http. For anything else, it is very hard to use. My plugin has the following requirements:

  • It must be called by another program through xml-rpc.
  • It must read from and write to files (and not http).

Neither of these things is made easy with xml-rpc. The first I can understand – xml-rpc would have to somehow insinuate itself into the emacs event loop and watch for calls all the time. The second is less easy to understand. Parsing xml-rpc is not really related to reading from http. So why are the two things tied together? In my opinion, xml-rpc.el would be a much nicer library if parsing the xml-rpc was separated completely from the http stuff. There could be convenience functions, but they would be a layer of abstraction on top of the other two layers.

xml.el has some problems

There isn't a function to get a list of all nodes with a given name? Are you kidding me? Here is my implementation, but I bet there is a better one somewhere:

(defun xml-find-nodes-matching (node name)
  "Returns all children of `node' that have an `xml-node-name' equal to `name'."
  (if (or (eq node '()) (not (listp node)))
      '()
    (if (equal (xml-node-name node) name)
        (cons node (delq nil (mapcar (lambda (nd) (xml-find-nodes-matching nd name)) (xml-node-children node))))
      (delq nil (apply 'append (mapcar (lambda (nd) (xml-find-nodes-matching nd name)) (xml-node-children node)))))))

In general, both xml-rpc.el and xml.el use a distressing number of calls to car, cdr, and cdaddr (and all the versions in between).

Perl has some problems

Ikiwiki sends named parameters as an even-lengthed xml-rpc array rather than as an xml-rpc struct. This is because not all of the functions that can be called via xml-rpc take named parameters and I guess Perl isn't smart enough to tell a hash from an even-lengthed array. This isn't a huge problem, but it does mean that I need to convert the input to each of the functions that I write into a hash before I use it.

Ikiwiki has some problems

I would like to be able to ignore files that don't have a particular extension. For htmlify, this is the way it works by default. It seems like most of the functions that plugins can implement are not this way by default, though, and that is a shame. If the plugin is not external – that is, it is written in Perl – there is really no problem. The function is called, checks the extension of the source file, and returns without doing anything. However, when the plugin is external and the call must happen through xml-rpc, ikiwiki must transmit the data via xml-rpc and receive the returned data back via xml-rpc. Unnecessary calls take a lot longer in that context.

So I would like for most calls to hook to take an optional extension parameter that takes an extension (or even better, a regexp), and only call the function if the file name has the same extension (or matches the regexp).

Working with emacs buffers is pretty nice

Does a function that you're trying to write in emacs lisp give you a string? It's pretty easy to throw it in a temporary buffer and then tell emacs to do the things that you would normally do while you were editing in order to get the proper information out of the string. The with-temporary-buffer macro makes it especially easy to do just that.

Getting info from a structured list is easier to do as a recursive function

One of the things that took me the longest was to figure out what this couple of lines of code was doing:

(setq valtype (car (caddar xml-list))
      valvalue (caddr (caddar xml-list)))  

What should the value of xml-list look like in order to get the correct thing out of it? It turned out that I needed to take the cdr of the cdaddr of the caddar of the parsed xml in order to get the correct value. That only worked when ikiwiki was responding to a method call, though. I had a much easier time getting the right values out when I simply started using the xml-find-nodes-matching function that I showed above.

When you see yourself writing more than a few car or cdr calls in a row (or first, rest, or nth calls in Clojure), stop and try to write a function that finds what you are looking for. The function doesn't have to be recursive, but that might be the easiest way to do it.

It's nice when all calls and responses are dumped to a file

In a sense, this is just saying that code can be easier to debug if you're tracing it. But since the calls between ikiwiki and the plugin must go through files anyway, we get the program traced automatically.

blog comments powered by Disqus