Climacs Search

Climacs Search: Multiple Query Replace From Buffer

Climacs has a bunch of search commands, but it needs more!

What it has at the moment are many of the standard ones:

  • Incremental Search (forward and back) – C-s and C-r
  • String Search (and Reverse String Search)
  • Word Search (and Reverse Word Search)
  • Regex Search Forward and Regex Search Backward
  • How Many (lines after point match some regex)
  • Replace String (unconditionally)
  • Query Replace – M-%

and a few less common:

  • Multiple Query Replace
  • Query Exchange
  • Multiple Query Replace From Buffer

Incremental Search and Query Replace have a fair subset of the GNU emacs functionality in their loops. While in an incremental search you can switch direction, add a newline to the search string (C-j), snarf the next word into the search string (C-w), or the remainder of the line (C-y), or the top of the kill ring (M-y) etc. While in a query replace loop you can replace, skip, exit, replace and exit, or just replace the rest of the matches.

So far so good for the normal stuff. You’ll see that some functionality isn’t there yet. Partially this is just that no-one has gotten around to it (and if your favourite functionality is missing patches are appreciated!). But partially this is because climacs isn’t emacs, and we’re still feeling our way towards what is the best way to use the command-loop, in which a lot of CLIMy magic happens.

As an example of the issues, take the emacsly-traditional way of invoking a string search – C-s immediately followed by the command to exit the search (RET in GNU emacs, Altmode in TWENEX EMACS, etc). The Incremental Search command works by setting up a special command table (a CLIM object imaginatively named isearch-climacs-table) that maps gestures to commands (so that, for instance, C-j is mapped to com-isearch-append-newline). The gesture #\Newline invokes com-isearch-exit. But when the search string is empty, #\Newline should run the command String Search (or Reverse String Search, if we’re isearching backward). String Search prompts the user for a string to search for (!), and one of the beauties of CLIM is that the system handles this automatically. But not here, where we are inside another command (and, indeed, inside another command loop – but that’s another story), so for the moment I have to reach behind the curtain and pull a few levers myself:

(execute-frame-command *application-frame*
                        (frame-command-table *application-frame*)
                        (frame-standard-input *application-frame*)
                        (if search-forward-p
                            `(com-string-search ,*unsupplied-argument-marker*)
                            `(com-reverse-string-search ,*unsupplied-argument-marker*))

Obviously this is uglyrific, but the moral of the story isn’t that CLIM makes normal things hard (although there are some things about command tables that the CLIM 2 spec leaves rather underspecified), but that I don’t yet know the best way to get this (admittedly rather special) behaviour out of the existing command loop. (One possibility: have a pending-commands slot that the command-loop checks, analogous to the remaining-keys slot used by the keyboard macro machinery.)

Another thing I don’t understand is why GNU emacs doesn’t have a command like Multiple Query Replace. This prompts for pairs of strings and then goes through the buffer asking if you want to replace any of the first strings of the pairs with the corresponding second string. Since this happens essentially in parallel, you can replace FOO with BAR, BAR with BAZ, BAZ with QUUX and QUUX with FOO in one invocation, without any messing around with temporary strings or abbrevs (as suggested as workarounds in the GNU emacs documentation). (The command Query Exchange is specialised for the case when you want to swap each FOO with a BAR and vice versa.) If you think entering the search and replacement strings in the minibuffer is prone to mistake, you can put them in a buffer and use Multiple Query Replace From Buffer. An essential refactoring tool, I would have thought.

Well, essential once climacs gets Tag Files (the Zmacs name for a collection of buffers or files, whether named in a TAG file, or all the open buffers, or all the files in a system etc.). Then various searching commands can be run over all the files in a Tag File. Which means the ability to suspend and resume a given search, and to visualise pending operations, becomes important.

And then there are the syntax specific searches that climacs should really have – things that allow you to ask for all lists of length 3 with CL:IF as the first element…

But all that is for another day.

Leave a Reply