diff options
Diffstat (limited to 'paredit.el')
| -rw-r--r-- | paredit.el | 245 | 
1 files changed, 182 insertions, 63 deletions
@@ -1,18 +1,23 @@  ;;; -*- mode: emacs-lisp -*-  ;;;;;; paredit: Parenthesis editing minor mode -;;;;;; Version 9 +;;;;;; Version 10 -;;; Taylor Campbell wrote this code; he places it in the public domain. +;;; This code is written by Taylor Campbell (except where explicitly +;;; noted) and placed in the Public Domain.  All warranties are +;;; disclaimed.  ;;; Add this to your .emacs after adding paredit.el to /path/to/elisp/:  ;;;  ;;;   (add-to-list 'load-path "/path/to/elisp/") -;;;   (require 'paredit) -;;;   (add-hook '...-mode-hook (lambda () (paredit-mode 1))) +;;;   (autoload 'enable-paredit-mode "paredit" +;;;     "Turns on pseudo-structural editing of Lisp code." +;;;     t) +;;;   (add-hook '...-mode-hook 'enable-paredit-mode)  ;;;  ;;; Usually the ... will be lisp or scheme or both.  Alternatively, you -;;; can manually toggle this mode with M-x paredit-mode. +;;; can manually turn on this mode with M-x enable-paredit-mode and +;;; turn it off with M-x disable-paredit-mode.  ;;;  ;;; This is written for GNU Emacs.  It is known not to work in XEmacs.  ;;; The author wrote it with GNU Emacs 22.0.50, but it should work in @@ -26,7 +31,8 @@  ;;; This is only lightly tested; some of it may not work as well as one  ;;; might expect.  Comments, in particular, are not handled with as  ;;; much grace as I'd like, but I'm not sure quite yet how to handle -;;; them as gracefully as I'd like. +;;; them as gracefully as I'd like.  (Block comments are not handled at +;;; all, only line comments.)  ;;;  ;;; There is one small but deeply fundamental problem in this model of  ;;; pretending to be a structure editor on top of what is really a text @@ -60,7 +66,7 @@  ;;; This assumes Unix-style LF line endings. -(defconst paredit-version 9) +(defconst paredit-version 10) @@ -87,10 +93,14 @@      (define-key keymap (kbd "RET") 'paredit-newline)      (define-key keymap (kbd "C-j") 'newline)      (define-key keymap (kbd "C-d") 'paredit-forward-delete) +    (define-key keymap (kbd "<deletechar>") 'paredit-forward-delete)      (define-key keymap (kbd "DEL") 'paredit-backward-delete) -          (define-key keymap (kbd "C-k") 'paredit-kill) +    (define-key keymap (kbd "C-M-f") 'paredit-forward) +    (define-key keymap (kbd "C-M-b") 'paredit-backward) +    (define-key keymap (kbd "C-c C-M-l") 'paredit-recentre-on-sexp) +      ;; The default keybindings in this area are:      ;;   C-up             forward-paragraph      ;;   C-down           backward-paragraph @@ -111,8 +121,8 @@      (define-key keymap (kbd "<C-down>")   'down-list)      (define-key keymap (kbd "<C-M-up>")   'up-list)      (define-key keymap (kbd "<C-M-down>") 'backward-down-list) -    (define-key keymap (kbd "<C-right>")  'forward-sexp) -    (define-key keymap (kbd "<C-left>")   'backward-sexp) +    (define-key keymap (kbd "<C-right>")  'paredit-forward) +    (define-key keymap (kbd "<C-left>")   'paredit-backward)      (define-key keymap (kbd "M-(")  'paredit-wrap-sexp)      (define-key keymap (kbd "M-/")  'paredit-splice-sexp) @@ -142,7 +152,7 @@ Emacs with a window system.")      ;; Terminal sequences for C-up, C-down, C-M-left, & C-M-down,      ;; respectively.  (These are the same as in the regular mode map,      ;; except that Emacs doesn't recognize the correlation between what -    ;; the terminal sends it and what KBD gives for "<C-up>") &c.) +    ;; the terminal sends it and what KBD gives for "<C-up>" &c.)      (define-key keymap (kbd "ESC O a")     'backward-up-list)      (define-key keymap (kbd "ESC O b")     'down-list)      (define-key keymap (kbd "ESC M-O a")   'up-list) @@ -152,6 +162,10 @@ Emacs with a window system.")    "Keymap for the paredit minor mode.  Works in `emacs -nw' running under Unix terminals.") +;++ Two separate minor modes here is a bit of a kludge.  It would be +;++ nice if DEFINE-MINOR-MODE had an option for dynamically choosing a +;++ keymap when the mode is enabled. +  (define-minor-mode paredit-mode    "Minor mode for pseudo-structurally editing Lisp code.  Uses keybindings that will not work under a Unix terminal; see @@ -170,15 +184,17 @@ Unix terminals.    :lighter " Paredit(nw)")  (defun enable-paredit-mode () -  "Turns on paredit mode, or paredit terminal mode if `window-system' -is nil." +  "Turns on pseudo-structural editing of Lisp code. +Uses `paredit-terminal-mode' if `window-system' is nil and +`paredit-mode' if not."    (interactive)    (if window-system        (paredit-mode 1)        (paredit-terminal-mode 1)))  (defun disable-paredit-mode () -  "Turns off paredit mode or paredit terminal mode." +  "Turns off pseudo-structural editing of Lisp code. +Disables both `paredit-mode' and `paredit-terminal-mode'."    (interactive)    (paredit-mode -1)    (paredit-terminal-mode -1)) @@ -188,18 +204,20 @@ is nil."  ;;; ----------------  ;;; Basic editing commands -(defun paredit-open-list () +(defun paredit-open-list (&optional n)    "Inserts a balanced parenthesis pair. +With a prefix argument N, puts the closing parentheses after N +S-expressions forward.  If in string or comment, inserts a single opening parenthesis.  If in a character literal, does nothing.  This prevents accidentally  changing what was in the character literal to a meaningful delimiter  unintentionally." -  (interactive) +  (interactive "P")    (cond ((or (paredit-in-string-p)               (paredit-in-comment-p))           (insert "("))          ((not (paredit-in-char-p)) -         (insert-parentheses 0)))) +         (insert-parentheses (or n 0)))))  (defun paredit-close-list ()    "Moves past one closing parenthesis and reindents. @@ -229,19 +247,6 @@ unintentionally."               (scan-error nil))             (paredit-blink-paren-match t)))) -(defun paredit-close-string-and-newline () -  "Moves to the end of the string, inserts a newline, and indents. -If not in a string, acts as `paredit-doublequote'." -  (interactive) -  (if (not (paredit-in-string-p)) -      (paredit-doublequote) -    (let ((start+end (paredit-string-start+end-points))) -      (goto-char (1+ (cdr start+end))) -      (insert ?\n ) -      (lisp-indent-line) -      (condition-case () (indent-sexp) -        (scan-error nil))))) -  (defun paredit-move-past-close-and-reindent ()    "Moves one character past the next closing parenthesis.  Deletes extraneous whitespace before the closing parenthesis.  Comments @@ -261,7 +266,7 @@ line after the comment and indented appropriately."                         ;; here -- we must return from SAVE-EXCURSION                         ;; first.                         (throw 'return t)) -                      ((save-excursion (previous-line) +                      ((save-excursion (previous-line 1)                                         (end-of-line)                                         (paredit-in-comment-p))                         ;; Moving the closing parenthesis any further @@ -274,16 +279,6 @@ line after the comment and indented appropriately."                        (t (delete-indentation)))))))          (paredit-delete-leading-whitespace)))) -(defun paredit-blink-paren-match (absolutely-p) -  (if (or absolutely-p blink-matching-paren) -      (condition-case () -          (save-excursion -            (backward-sexp) -            (forward-sexp) -            (let ((blink-matching-paren-on-screen t)) -              (blink-matching-open))) -        (scan-error nil)))) -  (defun paredit-delete-leading-whitespace ()    ;; This assumes that we're on the closing parenthesis already.    (save-excursion @@ -295,6 +290,29 @@ line after the comment and indented appropriately."                    (not (paredit-in-char-p (1- (point))))))        (backward-delete-char 1)))) +(defun paredit-blink-paren-match (absolutely-p) +  (if (or absolutely-p blink-matching-paren) +      (condition-case () +          (save-excursion +            (backward-sexp) +            (forward-sexp) +            (let ((blink-matching-paren-on-screen t)) +              (blink-matching-open))) +        (scan-error nil)))) + +(defun paredit-close-string-and-newline () +  "Moves to the end of the string, inserts a newline, and indents. +If not in a string, acts as `paredit-doublequote'." +  (interactive) +  (if (not (paredit-in-string-p)) +      (paredit-doublequote) +    (let ((start+end (paredit-string-start+end-points))) +      (goto-char (1+ (cdr start+end))) +      (insert ?\n ) +      (lisp-indent-line) +      (condition-case () (indent-sexp) +        (scan-error nil))))) +  (defun paredit-doublequote ()    "Inserts a pair of double-quotes.  Inside a comment, inserts a literal double-quote. @@ -353,11 +371,11 @@ unintentionally."    ;; I'm too lazy to figure out how to do this without a separate    ;; interactive function.    (interactive "cEscaping character...") -  (if (eq char 127)                     ; The luser made a typo and hit -      t                                 ; DEL to delete the backslash. -    (insert char) -    nil)) - +  (if (eq char 127)                     ; The backslash was a typo, so +      t                                 ; the luser wants to delete it. +    (insert char)                       ; (Is there a better way to +    nil))                               ; express the rubout char? +                                        ; ?\^? works, but ugh...)  (defun paredit-newline ()    "Inserts a newline and indents it.  This is like `newline-and-indent', but it not only indents the line @@ -456,7 +474,6 @@ regard for delimiter balancing."                    (eq (char-before) ?\" ))                (not (paredit-in-char-p (1- (point)))))           (backward-char)) -        ;++ This should test more thoroughly, e.g. for (   ).          ((and (eq (char-before) ?\( )                (not (paredit-in-char-p (1- (point))))                (eq (char-after)  ?\) )) @@ -554,6 +571,10 @@ Otherwise, kills all S-expressions that start after the point."                      (progn (backward-sexp)                             (eq (point-at-eol) eol))))          (forward-sexp))) +    ;; If we got to the end of the list and it's on the same line, +    ;; move backward past the closing delimiter before killing.  (This +    ;; allows something like killing the whitespace in (    ).) +    (if end-of-list-p (progn (up-list) (backward-char)))      (if (not kill-whole-line)          (kill-region beg                       ;; If all of the S-expressions were on one line, @@ -595,21 +616,126 @@ Otherwise, kills all S-expressions that start after the point."  ;;; ---------------- -;;; Wrappage, splicage, joinage, & inversion +;;; Cursor and screen movement + +(defun paredit-forward () +  "Moves forward an S-expression. +If there are any closing delimiters impeding such movement, first moves +forward up lists until there are no more." +  (interactive) +  (catch 'return +    (while t +      (condition-case () +          (progn (forward-sexp) +                 (throw 'return nil)) +        (scan-error (up-list)))))) + +(defun paredit-backward () +  "Moves backward an S-expression. +If there are any opening delimiters impeding such movement, first moves +backward up lists until there are no more." +  (interactive) +  (catch 'return +    (while t +      (condition-case () +          (progn (backward-sexp) +                 (throw 'return nil)) +        (scan-error (backward-up-list)))))) + +;;; Why is this not in lisp.el? + +(defun backward-down-list (&optional arg) +  "Move backward and descend into one level of parentheses. +With ARG, do this that many times. +A negative argument means move forward but still descend a level." +  (interactive "p") +  (down-list (- (or arg 1)))) + +;;; Thanks to Marco Baringer for suggesting & writing this function. + +(defun paredit-recentre-on-sexp (&optional n) +  "Recentres the screen on the S-expression following the point. +With a prefix argument N, encompasses all N S-expressions forward." +  (interactive "P") +  (save-excursion +    (forward-sexp n) +    (let ((end-point (point))) +      (backward-sexp n) +      (let* ((start-point (point)) +             (start-line (count-lines (point-min) (point))) +             (lines-on-sexps (count-lines start-point end-point))) +        (goto-line (+ start-line (/ lines-on-sexps 2))) +        (recenter))))) + + + +;;; ---------------- +;;; Wrappage, splicage, & joinage  (defun paredit-wrap-sexp (&optional n)    "Wraps the following S-expression in a list.  If a prefix argument N is given, N S-expressions are contained in the  list. -Automatically indents the newly wrapped S-expression." +Automatically indents the newly wrapped S-expression. +As a special case, if at the end of a list, will simply insert a pair +of parentheses, rather than insert a lone opening parenthesis and then +signal an error."    (interactive "p") -  (insert-parentheses (or n 1)) +  (condition-case () +      (insert-parentheses (or n 1)) +    (scan-error (insert ?\) ) +                (backward-char)))    (save-excursion (backward-up-list) (indent-sexp))) -(defun paredit-splice-sexp () -  "Splices the list the point is on by removing its delimiters." -  (interactive) +;;; Thanks to Marco Baringer for the suggestion of a prefix argument +;;; for PAREDIT-SPLICE-SEXP.  (I, Taylor Campbell, however, still +;;; implemented it, in case any of you lawyer-folk get confused by the +;;; remark in the top of the file about explicitly noting code written +;;; by other people.) + +(defun paredit-splice-sexp (&optional arg) +  "Splices the list the point is on by removing its delimiters. +With a prefix argument as in `C-u', deletes all S-expressions backward +in the current list before splicing all S-expressions forward into the +enclosing list. +With two prefix arguments as in `C-u C-u', deletes all S-expressions +forward in the current list before splicing all S-expressions backward +into the enclosing list. +With a numerical prefix argument N, deletes N S-expressions backward in +the current list before splicing the remaining S-expressions into the +enclosing list." +  (interactive "P")    (save-excursion +    (if (and arg (not (eq arg 0))) +        (cond ((numberp arg) +               ;; Delete ARG S-expressions before/after the point by +               ;; saving the point, moving across them, and deleting +               ;; the region. +               (let ((saved (point))) +                 (condition-case () +                     (backward-sexp arg) +                   (scan-error nil)) +                 (if (< arg 0) +                     (delete-region saved (point)) +                     (delete-region (point) saved)))) +              ((consp arg) +               (let ((v (car arg))) +                 (if (= v 4) +                     ;; Move backward until we hit the open paren; then +                     ;; delete that selected region. +                     (let ((end (point))) +                       (condition-case () +                           (while (not (bobp)) (backward-sexp)) +                         (scan-error nil)) +                       (delete-region (point) end)) +                     ;; Move forward until we hit the close paren; then +                     ;; delete that selected region. +                     (let ((beg (point))) +                       (condition-case () +                           (while (not (eobp)) (forward-sexp)) +                         (scan-error nil)) +                       (delete-region beg (point)))))) +              (t (error "Bizarre prefix argument: %s" arg))))      (backward-up-list)                  ; Go up to the beginning...      (save-excursion        (forward-sexp)                    ; Go forward an expression, to @@ -721,7 +847,9 @@ it was barfed."              (delete-char 1)              (condition-case () (paredit-forward-and-indent)                (scan-error nil)) -            (skip-chars-forward " \t\n") ;++ should handle comments +            (while (progn (skip-chars-forward " \t\n") +                          (eq (char-after) ?\; )) +              (goto-char (1+ (point-at-eol))))              (if (eobp)                  (message                   "Barfing all subexpressions with no close-paren?")) @@ -809,15 +937,6 @@ and Common Lisp.)"      (lisp-indent-line)                  ; Indent its opening line, and      (indent-sexp)))                     ;   the rest of it. -;;; Why is this not in lisp.el? - -(defun backward-down-list (&optional arg) -  "Move backward and descend into one level of parentheses. -With ARG, do this that many times. -A negative argument means move forward but still descend a level." -  (interactive "p") -  (down-list (- (or arg 1)))) -  (provide 'paredit)  | 
