M-g vs. goto-line

Just as many of you, I always missed the goto-line command in Emacs having no key assigned by default despite its usefulness, and one day (when I moved from XEmacs) I bound it to M-g. However, starting with Emacs 22, they decided to have it bound to M-g M-g, along with previous-error which was bound to M-g M-p and next-error to M-g M-n. The last part looked nice especially when I wasn’t happy with C-x ` at all, but it was just far from acceptable to me that I had to type M-g twice for jumping to a specific line number, so I insisted on my binding of M-g.

Time has passed, and now I have a bit of skills in elisp, and here it goes:

(defun goto-line-number ()
  (interactive)
  (goto-line (string-to-number
              (read-from-minibuffer
               "Goto line: "
               (char-to-string last-command-event)))))
(loop for n from 1 to 9 do
      (global-set-key (format "\M-g%d" n) 'goto-line-number))
(global-set-key "\M-g?" 'describe-prefix-bindings)

This snippet makes it as if M-g were a key for goto-line by tweaking the key bindings of M-g 1..9. For example, typing M-g 2 lets you enter the minibuffer with the first figure (2) input, and you are ready to type the rest of the figures of a line number, just as below:

-UUU:@----F13  init.el        Top (1,0)     [1]  (Emacs-Lisp FlyC:1/5 hs AC yas)
Goto line: 2 

This means you can just say M-g 2 0 0 RET to get to the line 200 while you can say M-g M-n to jump to the next error line. Cool.

For those who may wonder, the last line of the snippet helps you remember other key combinations of the M-g family by typing ? after M-g. Happy hacking!

knu-z v2.0 released – a fork of “z”, a better “cd”

I am happy to announce that my fork of “z”, which was originally developed by rupa deadwyler, has reached the functionality level I aimed, and here I present you the very first release, knu-z v2.0, available for both zsh and bash. (The original is now at v1.5, hence v2.0)

Here is a list of user-side improvements I’ve made over the original version:

  • Search method enhancements
    • Adopted simple substring match instead of regular expression match, for those who often go under directories with dots in the name.
    • While it’s basically substring match, there is special syntax for searching by prefix and/or suffix.
    • $HOME part is ignored in matching.  Otherwise, it often gets in your way if you like to take your project names from your handle, or just by annoying coincidence.
  • Shell completion enhancements
    • Rewrote bash completion that didn’t seem working.
    • Rewrote zsh completion that was old (compctl) with the new completion system (“compsys”) for far better UI.
    • Subdirectories of your home directory show up like ~/src/project1 instead of /home/user/src/project1. [bash] [zsh]
    • Completion candidates are sorted by the score. [zsh]
    • Glob patterns can be used to narrow down candidates. [bash] [zsh]
    • Enhances cd by powering its completion function to include matching directories in the z database.  This almost eliminates the need for learning and “using” z.  Once you install “z”, the normal cd simply gets smart, and it automatically learns as you use it normally.
  • Other enhancements
    • Added a way to manually remove a directory from the database: z --del directory
    • Nonexistent directories are not immediately eliminated from the database.  It is worth keeping a directory on a certain removable medium or a remote disk that you often but don’t always mount.

All you need is put “. /path/to/z.sh” in your ~/.zshrc or ~/.bashrc. Don’t forget to check out the README file.

Compatibility?  No worries.  The data file format and core algorithms are compatible with those of the original.  Enjoy!

Toggling “hairy” features in cperl-mode of Emacs

Emacs’s cperl-mode has many “electric” features, but how would you turn them all at once? Here’s my implementation of cperl-toggle-hairy.

(eval-after-load "cperl-mode"
  '(progn
     (defun cperl-toggle-hairy ()
       (interactive)
       (unless (boundp 'cperl-electric-prev-alist)
         (set (make-local-variable 'cperl-electric-prev-alist) nil))
       (cond (cperl-electric-prev-alist
              (dolist (pair cperl-electric-prev-alist)
                (set (car pair) (cdr pair)))
              (abbrev-mode (if abbrev-mode 1 0))
              (setq cperl-electric-prev-alist nil)
              (message "cperl-toggle-hairy: on.")
              t)
             (t
              (dolist (var '(abbrev-mode
                             cperl-auto-newline
                             cperl-electric-keywords
                             cperl-electric-lbrace-space
                             cperl-electric-linefeed
                             cperl-electric-parens
                             cperl-hairy
                             cperl-indent-region-fix-constructs
                             cperl-info-on-command-no-prompt
                             cperl-lazy-help-time))
                (setq cperl-electric-prev-alist
                      (cons (cons var (symbol-value var)) cperl-electric-prev-alist))
                (set var nil))
              (message "cperl-toggle-hairy: off.")
              nil)))
 
     (define-key cperl-mode-map "\C-c\C-l" 'cperl-toggle-hairy)
 
     (defun cperl-electric-quote (arg)
       (interactive "P")
       (self-insert-command (prefix-numeric-value arg))
       (and
        (cperl-val 'cperl-electric-parens)
        (let ((properties (text-properties-at (point))))
          (and (null (memq 'font-lock-string-face properties))
               (null (memq 'font-lock-comment-face properties))
               (save-excursion (insert last-command-event))))))
     (define-key cperl-mode-map "\"" 'cperl-electric-quote)
     (define-key cperl-mode-map "'"  'cperl-electric-quote)))

As you see, I put cperl-electric-quote as a New Year’s present.

Pasting a text literally to Emacs under terminal

A text editor is distinguished from "cat > file" where it greatly assists the user in writing a structured text by offering such features as auto-indentation, auto-correction, completion for programming, etc., not to mention basic editing features like search, mass replace, cut, copy and paste. These advanced features are quite nice especially in programming, but sometimes they bite.
Suppose you are under terminal editing a ruby program in Emacs with ruby-electric-mode turned on, feeling like pasting a text from outside the editor like clipboard, GNU screen or tmux. You could easily imagine what would happen. The pasted text would be messed up with superfluous closing parenthesis and quotations that are automatically inserted during the character-by-character pasting process. Of course ruby-electric-mode is a minor mode that can be easily turned off, but for example, in cperl-mode there are many individual flags for a variety of its “electric” features and they cannot be turned off all at once (see the following post for cperl-toggle-hairy). Anyway, changing the editing state back and forth is definitely not something you want just for a copy & paste. It is not supposed to be such a pain in the first place.

Here’s my quick answer.

(defun ins ()
  (interactive)
  (let* ((target (current-buffer))
         (buffer (generate-new-buffer (format "*Paste Buffer for <%s>*"
                                              (buffer-name target)))))
    (with-current-buffer buffer
      (make-local-variable 'ins-target-buffer)
      (setq ins-target-buffer target)
      (local-set-key "\C-c\C-c"
                     '(lambda ()
                        (interactive)
                        (let ((buffer (current-buffer)))
                          (switch-to-buffer ins-target-buffer)
                          (insert-buffer-substring buffer)
                          (kill-buffer buffer))))
      (local-set-key "\C-c\C-k"
                     '(lambda ()
                        (interactive)
                        (let ((buffer (current-buffer)))
                          (switch-to-buffer ins-target-buffer)
                          (kill-buffer buffer)))))
    (switch-to-buffer buffer)
    (message (format "Enter a text to paste.  Type C-c C-c when done. (C-c C-k to dismiss)"
                     (buffer-name target)))))

Whenever you feel like pasting a text from clipboard at point, type M-x ins, paste, type C-c C-c and it’s done. Simple.