Tuesday, August 26, 2008

emacs deficiencies

I want to like emacs more than I do. But it's hard. It's missing all kinds of features that I've gotten used to from more modern text editors. There are many basic operations that, although common in other editing programs and easy enough to implement in emacs-lisp, have never found their way into standard emacs. For instance, why aren't there built-in functions for:

  • count-words-[buffer|region]
  • unfill-paragraph
  • kill-entire-line (this function is standard in xemacs)
  • kill-to-beginning-of-line (equivalent to M-0 C-k)

I mean honestly, it's the feakin' 21st century here. It's not like counting the number of words in your buffer is a fringe use-case.

Also, how hard would it be to support soft-wrapped text? And why can't I bind commands to the capital letter key combos (e.g. C-N in addition to C-n)? longlines-mode will get you a close-enough approximation of soft-wrapped text and it should be possible to bind commands with the shift modifier. I just can't get it working on my Mac but I think it's a Terminal.app configuration issue.

The biggest issue I have with emacs however is that it doesn't support multiple major modes at once. This is a big problem for anyone programming with embedded languages – particularly web programmers – as it means that when you're editing HTML with Javascript or CSS, emacs can only be in one (major) mode at a time. So even if it has support for all the different languages in a given buffer, emacs can't switch modes as you move around in the buffer from one language section to another.

There is a project called MMM (Multiple Major Modes) that's trying (or tried, it hasn't been updated since 2004) to remedy that problem but it's an add-on, has limited language support, and requires customization for every language combination that you want to use.

Okay, enough with the complaining. Let's try something a little more constructive. Here are some useful bits of emacs-lisp to rectify some of my complaints. I found all of them with Google so I can't take credit for anything below. Apologies for the line truncation that's happening in the <pre> tags. Something lame with blogspot templates going on there.

Kill Away

(defun unfill-paragraph ()
  (interactive)
  (let ((fill-column (point-max)))
  (fill-paragraph nil)))

;; No longer neccessary. See update below
(defun kill-entire-line ()
  "Kill this entire line (including newline), 
regardless of where point is within the line."
  (setq kill-whole-line t)
  (interactive)
  (beginning-of-line)
  (kill-line)
  (setq kill-whole-line nil))

;; If you care to bind it
(global-set-key [(control ,)] 'kill-entire-line)

Counting words

;; This one is nice and simple but has a 
;; slightly awkward output. E.g.
;; Word count: 325 occurances.
(defun wc ()
  (interactive)
  (message "Word count: %s" 
           (how-many "\\w+" 
                     (point-min) (point-max))))

;; Nice version from Robert J. Chassell's 
;; /Programming in Emacs Lisp/
;; Available at http://www.gnu.org/software/emacs/emacs-lisp-intro/
(defun count-words-region (beginning end)
  "Print number of words in the region."
  (interactive "r")
  (message "Counting words in region ... ")

;;; 1. Set up appropriate conditions.
  (save-excursion
    (let ((count 0))
      (goto-char beginning)

;;; 2. Run the while loop.
      (while (and (< (point) end)
                  (re-search-forward "\\w+\\W*" end t))
        (setq count (1+ count)))

;;; 3. Send a message to the user.
      (cond ((zerop count)
             (message
              "The region does NOT have any words."))
            ((= 1 count)
             (message
              "The region has 1 word."))
            (t
             (message
              "The region has %d words." count))))))

;; Count the words in the entire document
(defun count-words-buffer ()
  "Count all the words in the buffer"
  (interactive)
  (count-words-region (point-min) (point-max) )
)

Longlines.el (aka soft-wrap)

Most excellent elisp code to simulate soft-wrapping in emacs. Basically, hard-returns are inserted when text is loaded or typed into a buffer but all such returns are removed when the buffer is saved to disk.

Shell Mode Improvements

I have my shell set to use a colorized prompt. With default setting, emacs shell mode renders a colorized prompt pretty poorly. Amit Patel's blogI has some useful settings

(add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on)
(setq comint-prompt-read-only t)

Update: (2008-09-09)

Well eat me and get big: emacs22 addresses some of my complaints. First, kill-entire-line is a standard command. It's supposed to be bound to C-S-<backspace> but I can't get it to work on my Mac in Terminal.app. I suspect Terminal.app is doing something non-kosher to the shift-ctrl combo but I'm not sure. In any case, it should be possible to bind commands to capital letters.

Second, longlines support is standard. M-x longlines-mode and you're set.

Haven't checked it out yet but Flymake seems like it should be awfully cool. It's supposed to do on the fly compile/syntax checking.

You can cycle through open buffers with C-x left and C-x right.