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.