Useful: insert-pair

August 26th, 2008

One of the more useful Emacs features I’ve found is insert-pair. As the name implies, it inserts a pair of characters. You define pairs in insert-pair-alist, which contains an open/close pair to use when inserting. The true utility may not be clear until you realize that adding a prefix determines the number of expressions to wrap in the pair. For example, if you have this text (assume point is at the “T”):

The quick brown fox.

And you hit M-4 M-(, you will end up with this:

(The quick brown fox)

If you then move point to the opening paren and hit C-c p (delete-pair), it will remove the parens. Note that delete-pair doesn’t use insert-pair-alist, but merely calls forward-sexp. Depending on the characters you want to remove, it may not work. Parens are skipped over by forward-sexp, but quotes are not. This can be fixed, but I haven’t gotten to that just yet.

In any case, here’s the code. The stock distribution has pairs for parens, brackets, braces, single and double quotes, and some other stuff (C-h v insert-pair-alist RET for the full list). I add pairs for single and double directional quotes, and plusses (useful when concatenating strings in JavaScript). Mimicing the default M-( binding, we add M- bindings for most pairs.

(add-to-list 'insert-pair-alist '(?“ ?”))
(add-to-list 'insert-pair-alist '(?‘ ?’))
(add-to-list 'insert-pair-alist '(?+ ?+))
(define-key osx-key-mode-map "\M-'" 'insert-pair)
(define-key osx-key-mode-map "\M-\"" 'insert-pair)
(define-key osx-key-mode-map "\M-\“" 'insert-pair)
(define-key osx-key-mode-map "\M-\‘" 'insert-pair)
(define-key osx-key-mode-map "\M-[" 'insert-pair)
(define-key osx-key-mode-map "\M-+" 'insert-pair)
(define-key osx-key-mode-map "\C-c`" 'insert-pair)
(define-key osx-key-mode-map "\C-cp" 'delete-pair)

Comments on “How to edit multiple files on several directories in less than a minute”

August 21st, 2008

Gabriel Saldaña wrote about using search & replace across files in emacs.

Type: M-x dired-do-query-replace-regexp to run the find and replace command. It will prompt you first for the text you want to find, then will prompt you with the text you want to replace it with.

Then Emacs will start the find and replace operation, and will prompt you on every find if you want to replace the text or skip it. To replace, type “y”, to skip to the next find type “n”. To replace all occurrences without asking, type “!”.

I would note that “!” only replaces matches within the same file; every time you start the query-replace in a new file, you must guide Emacs again. This seems like it’s still quite a bit of work, since you have to hit that bang key 239 times.

Now that you’ve made all these changes, you need to save the files. To avoid saving manually all files, you can open ibuffer M-x ibuffer

Which will list all you opened files (called buffers). Now, like in dired, you need to mark the buffers you want to work with. To mark all unsaved files, type “* u” and then type “S” (that’s shift+s, for the capital letter) to save them.

While I recommend ibuffer, there’s no need to bring it in just to save files. Just hit C-x s (save-some-buffers) and hit “!”. Emacs will save every modified buffer for you.

While I understand that Gabriel’s situation is due to less-than-ideal programmers, I would be remiss if I didn’t say don’t do this. Not the query-replace, which is fine, but the structure of the project in which it is being used. It’s copy and paste code reuse, it doesn’t scale, and it’s a bad idea. Avoid.

Shell dirtracking again

August 19th, 2008

Trey Jackson posted this code to handle directory tracking in a shell.

Trey, I don’t know if you know, but you’ve basically reinvented dirtrack.el. It should be included with your Emacs distribution. See my recent posts on the topic.

Smarter trailing whitespace deletion

August 15th, 2008

I used to use delete-trailing-whitespace in my local-file-write-hooks for any source file I edited, but I had to disable it a while back. Many files containing legacy source were extremely messy in this regard, and it made making simple fixes harder to track, since there was a large amount of noise in the diff from the whitespace changes.

But I just solved this. Here at Digg, we document all new code with phpDocumentor. Leveraging this, I wrote a new function to examine the header of the source file and strip whitespace if I have an @author tag.

(defun maybe-delete-trailing-whitespace ()
  "Delete trailing whitespace if I am the author of this file."
  (interactive)
  (and (delete-trailing-whitespacep) (delete-trailing-whitespace)))

(defun delete-trailing-whitespacep ()
  "Should we delete trailing whitespace when saving this file?"
  (save-excursion
    (goto-char (point-min))
    (next-line 25)
    (let ((pos (point)))
      (goto-char (point-min))
      (re-search-forward (concat "@author +" user-full-name) pos t))))

(defun progmodes-write-hooks ()
  "Hooks which run on file write for programming modes"
  (prog1 nil
    (set-buffer-file-coding-system 'utf-8-unix)
    (untabify-buffer)
    (copyright-update)
    (maybe-delete-trailing-whitespace)))

(defun progmodes-hooks ()
  "Hooks for all programming modes"
  (add-hook 'local-write-file-hooks 'progmodes-write-hooks))

(add-hook 'php-mode-hook 'progmodes-hooks)
(add-hook 'js2-mode-hook 'progmodes-hooks)
;; Repeat for any other mode for editing source

It works a treat, happily stripping trailing whitespace if I’m listed as an author in the header, and leaving it unmolested otherwise. I’m not entirely satisfied with delete-trailing-whitespacep, which feels a little graceless. Maybe someone knows a better way to do it?

Response to Aquamacs gripes

August 15th, 2008

David Reitter replied to my post griping about Aquamacs.

Re 1.3: these are Emacs limitations; we’re working on them.

Perhaps it wasn’t such a good idea to put out a press release for a major feature if there are serious known limitations. It’s a cool feature, but it’s not release quality.

Re 1.4: Other modes do NOT use Command-{/} because Command (mapped to A- in Aquamacs) is not a traditional Emacs modifier key. If you do something unwise like mapping it manually to Meta, then of course you’re on your own…

I take issue with the characterization that using command as meta is “unwise.” I am, after all, using the mechanism you yourself put in place (M-x customize-variable RET mac-command-modifier RET), which contains no mention of it being “unwise” or otherwise problematic. But I grant that this is more of a problem with my setup. I learned early on that Meta is the key to the left of space, whatever the label on it happens to be, and I find that much more palatable then stretching for the smaller Option/Alt key.

Re 3: You need a Lisp interpreter installed if you want to use common lisp. The correct way to run Emacs Lisp stuff is M-x lisp-interaction-mode or M-x emacs-lisp-mode.

Emacs is a lisp interpreter. I don’t want to use CL, I want to write, tweak, and eval Emacs lisp in a buffer. This may be my inner curmudgeon at work; lisp-mode used to do this, but perhaps that has changed to emacs-lisp-mode.

Re 4: Have you reported the bug? It won’t get fixed if it isn’t reported, and this is a problem in GNU Emacs (and not just Aquamacs specifically).

I sent a near-identical list of issues directly to David. I’m unable to participate in the Aquamacs mailing lists (including aquamacs-bugs) because any email is bounced due to my email domain lacking a “postmaster” address. I have no control over how my email is run, so this isn’t something I can fix. David said that “bugs should be non-restrictive… I’m looking into it.” Thus far, it has not been changed.