emacs - snippets

Useful elisp snippets

Lists

Next biggest / next smallest elements of a sorted list

(defun next-biggest (l i)
  "Return the element of the sorted list L that is after I"
  (while (and l (>= i (car l)))
    (setq l (cdr l)))
  (car l))

(defun next-smallest (l i)
  "Return the element of the sorted list L that is before I"
  (setq l (reverse l))
  (while (and l (<= i (car l)))
    (setq l (cdr l)))
  (car l))

alist manipulation

Similar to add-to-list, but replaces element if car is equal.
(defun add-to-alist (alist-var elt-cons &optional no-replace)
  "Add to the value of ALIST-VAR an element ELT-CONS if it isn't there yet.
If an element with the same car as the car of ELT-CONS is already present,
replace it with ELT-CONS unless NO-REPLACE is non-nil; if a matching
element is not already present, add ELT-CONS to the front of the alist.
The test for presence of the car of ELT-CONS is done with `equal'."
  (let ((existing-element (assoc (car elt-cons) (symbol-value alist-var))))
    (if existing-element
        (or no-replace
            (rplacd existing-element (cdr elt-cons)))
      (set alist-var (cons elt-cons (symbol-value alist-var))))))

Regular expression assoc lists

(defun regexp-assoc (key list)
  "Return first item of LIST with a car that matches KEY"
  (let ((l list)
        res)
    (while (and l (not res))
      (if (string-match (caar l) key)
          (setq res (car l)))
      (setq l (cdr l)))
    res))

(defun regexp-assoc-best (key list)
  "Return the item of LIST with a car that produces the longest match for KEY"
  (let ((l list)
        (best 0)
        res)
    (while l
      (when (and (string-match (caar l) key)
                 (eq (match-beginning 0) 0)
                 (> (match-end 0) best))
          (setq res (car l)
                best (match-end 0)))
      (setq l (cdr l)))
    res))

Best matching path

(defun path-alist (path alist)
  "Return the element of ALIST that best matches PATH (longest match)"
  (regexp-assoc-best (expand-file-name path)
                     (mapcar
                      '(lambda (x)
                         (cons (file-name-as-directory (expand-file-name (car x)))
                               (cdr x)))
                      alist)))
Moving

Expression navigation

Functions to move across or to the start or end of expressions. Bind to e.g. M-left and M-right for navigation through code.
(defun forward-or-end-sexp (&optional arg)
  "Move forward across or to end of one balanced expression (sexp).
With argument, do it that many times.
Negative arg -N means move backward across N balanced expressions."
  (interactive "p")
  (if (and arg (< arg 0))
      (backward-or-beginning-sexp (- arg))
    (if (looking-at "\\s(")
        (forward-sexp arg)
      (up-list)
      (if (and arg (> arg 1))
          (forward-sexp (- arg 1))))))

(defun backward-or-beginning-sexp (&optional arg)
  "Move backward across or to start of one balanced expression (sexp).
With argument, do it that many times.
Negative arg -N means move forward across N balanced expressions."
  (interactive "p")
  (if (and arg (< arg 0))
      (forward-or-end-sexp (- arg))
    (if (save-excursion
          (backward-char 1)
          (looking-at "\\s)"))
        (backward-sexp arg)
      (backward-up-list)
      (if (and arg (> arg 1))
          (backward-sexp (- arg 1))))))

pc-select compatible functions.
Bind with:
(global-set-key [(meta left)] 'backward-or-beginning-sexp-nomark)
(global-set-key [(meta shift left)] 'backward-or-beginning-sexp-mark)
(global-set-key [(meta right)] 'forward-or-end-sexp-nomark)
(global-set-key [(meta shift right)] 'forward-or-end-sexp-mark)
(defun forward-or-end-sexp-mark (&optional arg)
  "Ensure mark is active; move forward across or to end of one balanced
expression (sexp).  With argument, do it that many times.  Negative arg -N
means move backward across N balanced expressions."
  (interactive "p")
  (ensure-mark)
  (forward-or-end-sexp arg))

(defun forward-or-end-sexp-nomark (&optional arg)
  "Deactivate mark; move forward across or to end of one balanced
expression (sexp).  With argument, do it that many times.  Negative arg -N
means move backward across N balanced expressions."
  (interactive "p")
  (setq mark-active nil)
  (forward-or-end-sexp arg))

(defun backward-or-beginning-sexp-mark (&optional arg)
  "Ensure mark is active; move backward across or to start of one balanced
expression (sexp).  With argument, do it that many times.  Negative arg -N
means move forward across N balanced expressions."
  (interactive "p")
  (ensure-mark)
  (backward-or-beginning-sexp arg))

(defun backward-or-beginning-sexp-nomark (&optional arg)
  "Deactivate mark; move backward across or to start of one balanced
expression (sexp).  With argument, do it that many times.  Negative arg -N
means move forward across N balanced expressions."
  (interactive "p")
  (setq mark-active nil)
  (backward-or-beginning-sexp arg))
Editing

Deleting words

(defun delete-word (arg)
  "Kill characters forward until encountering the end of a word.
With argument, do this that many times."
  (interactive "p")
  (delete-region (point) (progn (forward-word arg) (point))))

(defun backward-delete-word (arg)
  "Kill characters backward until encountering the beginning of a word.
With argument, do this that many times."
  (interactive "p")
  (delete-word (- arg)))

Deleting expressions

see Expression navigation above
(defun delete-sexp (arg)
  "Delete the sexp (balanced expression) following or containing the cursor.
With ARG, delete that many sexps after the cursor.
Negative arg -N means delete N sexps before the cursor."
  (interactive "p")
  (let ((opoint (point)))
    (forward-or-end-sexp arg)
    (delete-region opoint (point))))

(defun backward-delete-sexp (arg)
  "Delete the sexp (balanced expression) containing or before the cursor.
With ARG, delete that many sexps before the cursor.
Negative arg -N means delete N sexps after the cursor."
  (interactive "p")
  (let ((opoint (point)))
    (backward-or-beginning-sexp arg)
    (delete-region opoint (point))))

Deleting whitespace

(defun delete-whitespace ()
  "Delete characters from point up to next non-whitespace char"
  (interactive)
  (let ((here (point)))
    (skip-syntax-forward "-")
    (if (/= (point) here)
	(delete-region (point) here))))

Underline

(defun underline ()
  "Underline text with dashes.
Inserts a new line and inserts dashes under the text to be underlined.
If the mark is active, underline the selected text, otherwise underline the
whole line."
  (interactive)
  (save-excursion
    (let* ((beg (if mark-active (point) (point-at-bol)))
           (end (if mark-active (mark)  (point-at-eol)))
           (pad (if mark-active (- (min beg end) (point-at-bol)) 0))
           (len (abs (- beg end)))
           )
      (goto-char (point-at-eol))
      (insert "\n" (make-string pad ? ) (make-string len ?-)))))

Shuffle lines

(defun shuffle-lines (beg end)
  "Scramble all the lines in region defined by BEG END
If region contains less than 2 lines, lines are left untouched."
  (interactive "*r")
  (catch 'cancel
    (save-restriction
      (narrow-to-region beg end)
      ;;   Exit when there is not enough lines in region
      (if (< (- (point-max) (point-min)) 3)
      (throw 'cancel t))

      ;;    Prefix lines with a random number and a space
      (goto-char (point-min))
      (while (not (eobp))
        (insert (int-to-string (random 32000)) " ")
        (forward-line 1))

      ;;  Sort lines according to first field (random number)
      (sort-numeric-fields 1 (point-min) (point-max))

      (goto-char (point-min))  ;Remove the prefix fields
      (while (not (eobp))
        (delete-region (point) (progn (forward-word 1) (+ (point) 1)))
        (forward-line 1))
      )))

Duplicating text

(defun duplicate-region (beg end &optional sep)
  "Duplicate the region"
  (interactive "*r")
  (let ((p (point)))
    (copy-region-as-kill beg end)
    (message "%d" (point))
    (goto-char end)
    (if (stringp sep) (insert sep))
    (yank)
    (goto-char p)))

(defun duplicate-line-or-region ()
  "duplicate the region if active otherwise the current line"
  (interactive)
  (if mark-active
      (duplicate-region (point) (mark))
    (duplicate-region (point-at-bol) (point-at-eol) "\n")))

Commenting

(defvar comment-insert-block-on-empty-line nil
  "Whether to insert block comments on empty lines in comment-insert")

(defun comment-insert ()
  "Insert a new comment on current line"
  (interactive)
  (let ((empty-line (save-excursion (beginning-of-line) (looking-at "\\s-*$"))))
    (if (and (not (equal (point) (point-at-bol)))
             (save-excursion (backward-char) (looking-at "\\S-")))
        ;; insert space if immediately preceding char not whitespace
        (insert " "))
    (insert comment-start)
    (if (and empty-line comment-insert-block-on-empty-line)
        (comment-indent-new-line))
    (save-excursion
      (if (and empty-line comment-insert-block-on-empty-line)
          (insert-and-inherit ?\n))
      (insert comment-end)
      (indent-for-tab-command))
    (indent-for-tab-command)))

(defun comment-line-or-region (&optional comment-eol)
  "comment the region if active otherwise comment the current line"
  (interactive "P")
  (if mark-active
      ;; comment selection
      (comment-region (point) (mark))
    (if (or comment-eol
            (save-excursion (beginning-of-line) (looking-at "\\s-*$")))
        ;; insert comment at end of line
        (comment-insert)
      ;; comment whole line
      (comment-region (point-at-bol) (point-at-eol)))))

(defun uncomment-line-or-region ()
  "uncomment the region if active otherwise comment the current line"
  (interactive)
  (if mark-active
      (uncomment-region (point) (mark))
    (uncomment-region (point-at-bol) (point-at-eol))))

Duplicating and commenting

(defun copy-and-comment-region (beg end &optional sep)
  "Duplicate the region and comment out the original"
  (interactive "*r")
  (duplicate-region beg end sep)
  (comment-region beg end))

(defun copy-and-comment-line-or-region ()
  "duplicate & comment the region if active otherwise the current line"
  (interactive)
  (if mark-active
      (copy-and-comment-region (point) (mark))
    (copy-and-comment-region (point-at-bol) (point-at-eol) "\n")))

Quick text replacement

(defun qreplace (expr)
  "Interactive replace using sed style expression EXPR"
  (interactive (progn
                 (barf-if-buffer-read-only)
                 (list (read-from-minibuffer "Enter expression: " '(",,," . 2)))))
  (if (< (length expr) 3)
      (message "Invalid expression %s" expr)
    (let* ((d (substring expr 0 1))
           (e (concat d "\\([^" d "]*\\)" d "\\([^" d "]*\\)" d)))
      (if (not (string-match e expr))
          (message "Invalid expression %s" expr)
        (if mark-active
            (query-replace-regexp (match-string 1 expr) (match-string 2 expr)
                                  nil (point) (mark))
          (query-replace-regexp (match-string 1 expr) (match-string 2 expr))))
      )))

Copyright ©Rob Walker 2004

Email:rob@tenfoot.org.uk

Last updated on 2006-03-30

Valid XHTML 1.0!Valid CSS!