diff options
-rw-r--r-- | paredit.el | 554 |
1 files changed, 356 insertions, 198 deletions
@@ -1,7 +1,7 @@ ;;; -*- mode: emacs-lisp -*- ;;;;;; paredit: Parenthesis editing minor mode -;;;;;; Version 7 +;;;;;; Version 8 ;;; Taylor Campbell wrote this code; he places it in the public domain. @@ -14,6 +14,12 @@ ;;; Usually the ... will be lisp or scheme or both. Alternatively, you ;;; can manually toggle this mode with M-x 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 +;;; earlier versions as well. An alternative set of keybindings is +;;; available in PAREDIT-TERMINAL-MODE that works in Emacs under Unix +;;; terminals with the -nw option (implied or otherwise). +;;; ;;; This mode changes the keybindings for (, ), and ", most notably; ;;; if you really, really want a literal one of those, use C-q. ;;; @@ -54,46 +60,106 @@ ;;; This assumes Unix-style LF line endings. -(defconst paredit-version 7) +(defconst paredit-version 8) + +(define-minor-mode paredit-mode + "Minor mode for pseudo-structurally editing Lisp code. +Uses keybindings that will not work under a Unix terminal; see +`paredit-terminal-mode' for an alternative set of keybindings that will +work in `emacs -nw' running under a Unix terminal." + :lighter " Paredit" + (if (and paredit-mode paredit-terminal-mode) + (paredit-terminal-mode -1))) + +(define-minor-mode paredit-terminal-mode + "Minor mode for pseudo-structurally editing Lisp code. +Uses alternative keybindings that work in `emacs -nw' running under +Unix terminals." + :lighter " Paredit(nw)" + (if (and paredit-mode paredit-terminal-mode) + (paredit-mode -1))) (defvar paredit-mode-map (let ((keymap (make-sparse-keymap))) - (define-key keymap "(" 'paredit-open-list) - (define-key keymap ")" 'paredit-close-list) - (define-key keymap "\"" 'paredit-doublequote) - (define-key keymap "\\" 'paredit-backslash) - (define-key keymap (kbd "C-j") 'paredit-newline) + (define-key keymap "(" 'paredit-open-list) + (define-key keymap ")" 'paredit-close-list-and-newline) + (define-key keymap (kbd "M-)") 'paredit-close-list) + (define-key keymap (kbd "M-\"") 'paredit-close-string-and-newline) + (define-key keymap "\"" 'paredit-doublequote) + (define-key keymap "\\" 'paredit-backslash) + + ;; This defies ordinary conventions, but I believe it is justified + ;; and more convenient this way, to have RET be fancy and C-j + ;; insert a vanilla newline. You can always change this in your + ;; .emacs if you want the conventional configuration, however. + (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 "DEL") 'paredit-backward-delete) (define-key keymap (kbd "C-k") 'paredit-kill) - ;; C-up & C-down are by default useless paragraph commands, while - ;; C-M-up & C-M-down are BACKWARD-UP-LIST & BACKWARD-DOWN-LIST. - ;; C-left & C-right are by default word movement commands, but as - ;; are M-left & M-right, so I think it's OK to override them. - (define-key keymap (kbd "<C-up>") 'up-list) - (define-key keymap (kbd "<C-down>") 'down-list) - (define-key keymap (kbd "<C-right>") 'forward-sexp) - (define-key keymap (kbd "<C-left>") 'backward-sexp) - - (define-key keymap (kbd "M-(") 'forward-wrap-sexp) - (define-key keymap (kbd "M-)") 'backward-wrap-sexp) - (define-key keymap (kbd "M-/") 'splice-sexp) - (define-key keymap (kbd "M-\\") 'join-sexps) - - (define-key keymap (kbd "C-)") 'forward-slurp-sexp) - (define-key keymap (kbd "C-}") 'forward-barf-sexp) - (define-key keymap (kbd "C-(") 'backward-slurp-sexp) - (define-key keymap (kbd "C-{") 'backward-barf-sexp) + ;; The default keybindings in this area are: + ;; C-up forward-paragraph + ;; C-down backward-paragraph + ;; C-M-up backward-up-list + ;; C-M-down down-list + ;; C-right M-right forward-word + ;; C-left M-left backward-word + ;; This all seems rather inconsistent to me. I'm not worried about + ;; overriding C-up & C-down here, because paragraph commands are + ;; not very useful in Lisp code, and C-left & C-right, because they + ;; already have aliases with meta instead of control. + ;; + ;; Chosen here is for C-up to go up a list backward (because that + ;; will usually mean going up a line, as one might logically expect + ;; with the up key), C-down to go down a list (forward, for a + ;; similar reason), an added meta to go in the other direction. + (define-key keymap (kbd "<C-up>") 'backward-up-list) + (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 "M-(") 'paredit-wrap-sexp) + (define-key keymap (kbd "M-/") 'paredit-splice-sexp) + (define-key keymap (kbd "M-\\") 'paredit-join-sexps) + + (define-key keymap (kbd "C-)") 'paredit-forward-slurp-sexp) + (define-key keymap (kbd "C-}") 'paredit-forward-barf-sexp) + (define-key keymap (kbd "C-(") 'paredit-backward-slurp-sexp) + (define-key keymap (kbd "C-{") 'paredit-backward-barf-sexp) keymap) - "Keymap for the paredit minor mode.") + "Keymap for the paredit minor mode. +Does not work in `emacs -nw' running under Unix terminals, only in +Emacs with a window system.") -(define-minor-mode paredit-mode - "Minor mode for pseudo-structurally editing Lisp code." - :lighter " Paredit") +(defvar paredit-terminal-mode-map + (let ((keymap (make-sparse-keymap))) + (set-keymap-parent keymap paredit-mode-map) + + ;; Bizarre terminal sequences for M-right, M-left, C-M-right, & + ;; C-M-left, respectively. + (define-key keymap (kbd "ESC <right>") 'paredit-forward-slurp-sexp) + (define-key keymap (kbd "ESC <left>") 'paredit-forward-barf-sexp) + (define-key keymap (kbd "ESC M-O d") 'paredit-backward-slurp-sexp) + (define-key keymap (kbd "ESC M-O c") 'paredit-backward-barf-sexp) + + ;; 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 "<C-up>") &c. return. + (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) + (define-key keymap (kbd "ESC M-O b") 'backward-down-list) + + keymap) + "Keymap for the paredit minor mode. +Works in `emacs -nw' running under Unix terminals.") @@ -125,13 +191,34 @@ unintentionally." (insert ")")) ((not (paredit-in-char-p)) (paredit-move-past-close-and-reindent) - (if blink-matching-paren - (condition-case nil - (save-excursion - (backward-sexp) - (forward-sexp) - (blink-matching-open)) - (scan-error nil)))))) + (paredit-blink-paren-match nil)))) + +(defun paredit-close-list-and-newline () + "Moves past one closing delimiter, adds a newline, and reindents." + (interactive) + (cond ((or (paredit-in-string-p) + (paredit-in-comment-p)) + (insert ")")) + (t (if (paredit-in-char-p) (forward-char)) + (paredit-move-past-close-and-reindent) + (insert ?\n ) + (lisp-indent-line) + (condition-case () (indent-sexp) + (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. @@ -142,7 +229,7 @@ line after the comment and indented appropriately." (interactive) (let ((orig (point))) (up-list) - (if (catch 'exit ; This CATCH returns T if it + (if (catch 'return ; This CATCH returns T if it (while t ; should delete leading spaces (save-excursion ; and NIL if not. (let ((before-paren (1- (point)))) @@ -151,7 +238,7 @@ line after the comment and indented appropriately." ;; Can't call PAREDIT-DELETE-LEADING-WHITESPACE ;; here -- we must return from SAVE-EXCURSION ;; first. - (throw 'exit t)) + (throw 'return t)) ((save-excursion (previous-line) (end-of-line) (paredit-in-comment-p)) @@ -161,18 +248,25 @@ line after the comment and indented appropriately." ;; and abort the loop, telling its continuation ;; that no leading whitespace should be deleted. (lisp-indent-line) - (throw 'exit nil)) + (throw 'return nil)) (t (delete-indentation))))))) - (paredit-delete-leading-whitespace))) - (condition-case nil (indent-sexp) - (scan-error nil))) + (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) + (blink-matching-open)) + (scan-error nil)))) (defun paredit-delete-leading-whitespace () ;; This assumes that we're on the closing parenthesis already. (save-excursion (backward-char) (while (let ((syn (char-syntax (char-before)))) - (and (or (eq syn ?\ ) (eq syn ?-)) ; whitespace syntax + (and (or (eq syn ?\ ) (eq syn ?-)) ; whitespace syntax ;; The above line is a perfect example of why the ;; following test is necessary. (not (paredit-in-char-p (1- (point)))))) @@ -254,7 +348,7 @@ Moves forward one character first if on an escaped character." ;; Indent the following S-expression, but don't signal an error if ;; there's only a closing parenthesis after the point, not a full ;; S-expression. - (condition-case nil (indent-sexp) + (condition-case () (indent-sexp) (scan-error nil))) (defun paredit-forward-delete (&optional arg) @@ -266,33 +360,32 @@ S-expression is empty, in which case the whole S-expression is deleted. With a prefix argument, simply deletes a character forward, without regard for delimiter balancing." (interactive "P") - (if arg ; I'd pass the argument to DELETE-CHAR, - (delete-char 1) ; but I don't know how to do it right. - (cond ((paredit-in-string-p) - (paredit-forward-delete-in-string)) - ((paredit-in-comment-p) - ;++ What to do here? This could move a partial S-expression - ;++ into a comment and thereby invalidate the file's form, - ;++ or move random text out of a comment. - (delete-char 1)) - ((paredit-in-char-p) ; Escape -- delete both chars. - (backward-delete-char 1) - (delete-char 1)) - ((eq (char-after) ?\\ ) ; ditto - (delete-char 2)) - ((or (eq (char-after) ?\( ) - (eq (char-after) ?\" )) - (forward-char)) - ((and (eq (char-before) ?\( ) - (not (paredit-in-char-p (1- (point)))) - (eq (char-after) ?\) )) - (backward-delete-char 1) - (delete-char 1)) - ;; Just delete a single character, if it's not a closing - ;; parenthesis. (The character literal case is already - ;; handled by now.) - ((not (eq (char-after) ?\) )) - (delete-char 1))))) + (cond (arg (delete-char 1)) ; I'd pass the arg if I knew how. + ((paredit-in-string-p) + (paredit-forward-delete-in-string)) + ((paredit-in-comment-p) + ;++ What to do here? This could move a partial S-expression + ;++ into a comment and thereby invalidate the file's form, + ;++ or move random text out of a comment. + (delete-char 1)) + ((paredit-in-char-p) ; Escape -- delete both chars. + (backward-delete-char 1) + (delete-char 1)) + ((eq (char-after) ?\\ ) ; ditto + (delete-char 2)) + ((or (eq (char-after) ?\( ) + (eq (char-after) ?\" )) + (forward-char)) + ((and (eq (char-before) ?\( ) + (not (paredit-in-char-p (1- (point)))) + (eq (char-after) ?\) )) + (backward-delete-char 1) + (delete-char 1)) + ;; Just delete a single character, if it's not a closing + ;; parenthesis. (The character literal case is already + ;; handled by now.) + ((not (eq (char-after) ?\) )) + (delete-char 1)))) (defun paredit-forward-delete-in-string () (let ((start+end (paredit-string-start+end-points))) @@ -326,31 +419,30 @@ S-expression is empty, in which case the whole S-expression is deleted. With a prefix argument, simply deletes a character backward, without regard for delimiter balancing." (interactive "P") - (if arg - (backward-delete-char 1) ;++ should this untabify? - (cond ((paredit-in-string-p) - (paredit-backward-delete-in-string)) - ((paredit-in-comment-p) - (backward-delete-char 1)) - ((paredit-in-char-p) ; Escape -- delete both chars. - (backward-delete-char 1) - (delete-char 1)) - ((paredit-in-char-p (1- (point))) - (backward-delete-char 2)) ; ditto - ((and (or (eq (char-before) ?\) ) - (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) ?\) )) - (backward-delete-char 1) - (delete-char 1)) - ;; Delete it, unless it's an opening parenthesis. The case - ;; of character literals is already handled by now. - ((not (eq (char-before) ?\( )) - (backward-delete-char-untabify 1))))) + (cond (arg (backward-delete-char 1)) ;++ should this untabify? + ((paredit-in-string-p) + (paredit-backward-delete-in-string)) + ((paredit-in-comment-p) + (backward-delete-char 1)) + ((paredit-in-char-p) ; Escape -- delete both chars. + (backward-delete-char 1) + (delete-char 1)) + ((paredit-in-char-p (1- (point))) + (backward-delete-char 2)) ; ditto + ((and (or (eq (char-before) ?\) ) + (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) ?\) )) + (backward-delete-char 1) + (delete-char 1)) + ;; Delete it, unless it's an opening parenthesis. The case + ;; of character literals is already handled by now. + ((not (eq (char-before) ?\( )) + (backward-delete-char-untabify 1)))) (defun paredit-backward-delete-in-string () (let ((start+end (paredit-string-start+end-points))) @@ -375,57 +467,114 @@ regard for delimiter balancing." (backward-delete-char 1) (delete-char 1))))) -(defun paredit-kill () - "Kills a line or S-expression. -If an S-expression starts on the same line as the point, kills that -S-expression; otherwise, behaves as `kill-line', except won't kill a -closing string delimiter." - (interactive) - (cond ((paredit-in-string-p) - (paredit-kill-in-string)) - ((or (eq (char-after) ?\n ) - (paredit-in-comment-p) +(defun paredit-kill (&optional arg) + "Kills a line as if with `kill-line', but respecting delimiters. +In a string, acts exactly as `kill-line' but will not kill past the +closing string delimiter. +On a line with no S-expressions on it starting after the point or +within a comment, acts exactly as `kill-line'. +Otherwise, kills all S-expressions that start after the point." + (interactive "P") + (cond (arg (kill-line)) + ((paredit-in-string-p) + (paredit-kill-line-in-string)) + ((or (paredit-in-comment-p) (save-excursion (skip-chars-forward " \t\n" (point-at-eol)) - (or (eq (point) (point-at-eol)) - (eq (char-after) ?\; )))) - (if (eq (char-before (point-at-eol)) - ?\\ ) - ;++ This is a crock: we don't want to kill an incomplete - ;++ escape sequence, so we include the newline. This - ;++ won't work on the last line of the buffer, however, if - ;++ it is not followed by one empty line. - (progn (kill-region (point) (1+ (point-at-eol))) - (insert ?\n )) - (kill-line))) - (t (kill-sexp)))) - -(defun paredit-kill-in-string () - (if (eq (char-after) ?\n ) - ;; Delete the newline only if we're at the end of the line. (The - ;; ordinary Emacs behaviour is to do this also if there's only - ;; whitespace following, but I hate that behaviour.) - (kill-region (point) (1+ (point))) - ;; Skip ahead to the end of the line or the double-quote. Kill - ;; that region. + (or (eq (char-after) ?\; ) + (eolp)))) + ;** Be careful about trailing backslashes. + (kill-line)) + (t (paredit-kill-sexps-on-line)))) + +(defun paredit-kill-line-in-string () + (if (save-excursion (skip-chars-forward " \t\n" (point-at-eol)) + (eolp)) + (kill-line) (save-excursion - ;; Make sure not to split an escaped character sequence. + ;; Be careful not to split an escape sequence. (if (paredit-in-string-escape-p) (backward-char)) (let ((beg (point))) - (while (not (memq (char-after) '(?\n ?\" ))) + (while (not (or (eolp) + (eq (char-after) ?\" ))) (forward-char) ;; Skip past escaped characters. (if (eq (char-before) ?\\ ) (forward-char))) (kill-region beg (point)))))) +(defun paredit-kill-sexps-on-line () + (if (paredit-in-char-p) ; Move past the \ and prefix. + (backward-char 2)) ; (# in Scheme/CL, ? in elisp) + (let ((beg (point)) + (eol (point-at-eol)) + (end-of-list-p nil)) + ;; Move to the end of the last S-expression that started on this + ;; line, or to the closing delimiter if the last S-expression in + ;; this list is on the line. + (catch 'return + (while (save-excursion + (condition-case () + (forward-sexp) + ;++ I wrote here: + ;++ ;++ THIS IS BROKEN -- FIX + ;++ But now I don't remember what was broken and needs + ;++ fixing. This whole thing, notably END-OF-LIST-P, + ;++ was a crock to fix a corner case that I also don't + ;++ remember now... + (scan-error + (up-list) + (setq end-of-list-p (eq (point-at-eol) eol)) + (throw 'return nil))) + (and (not (eobp)) + (progn (backward-sexp) + (eq (point-at-eol) eol)))) + (forward-sexp))) + (if (not kill-whole-line) + (kill-region beg + ;; If all of the S-expressions were on one line, + ;; i.e. we're still on that line after moving past + ;; the last one, kill the whole line, including + ;; any comments; otherwise just kill to the end of + ;; the last S-expression we found. Be sure, + ;; though, not to kill any closing parentheses. + (if (and (not end-of-list-p) + (eq (point-at-eol) eol)) + eol + (point))) + (kill-region beg + (or (save-excursion ; Delete indentation forward... + (skip-chars-forward " \n\t") + (and (not (eq (char-after) ?\; )) + (point))) + ;; ...or just use the point past the newline, if + ;; we encounter a comment. + (point-at-eol))) + (cond ((save-excursion (skip-chars-backward " \n\t" + (point-at-bol)) + (bolp)) + ;; Nothing but indentation before the point, so indent it. + (lisp-indent-line)) + ;; If there is something before the point, make sure we + ;; don't join things that shouldn't be joined. + ((let ((syn-before (char-syntax (char-before))) + (syn-after (char-syntax (char-after)))) + (or (and (eq syn-before ?\) ) ; Separate opposing + (eq syn-after ?\( )) ; parentheses, + (and (eq syn-before ?\" ) ; string delimiter + (eq syn-after ?\" )) ; pairs, + (and (memq syn-before '(?_ ?w)) ; or word or symbol + (memq syn-after '(?_ ?w))) ; constituents. + )) + (insert " ")))))) + ;;; ---------------- -;;; Wrappage, splicage, & joinage +;;; Wrappage, splicage, joinage, & inversion -(defun forward-wrap-sexp (&optional n) +(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. @@ -434,16 +583,7 @@ Automatically indents the newly wrapped S-expression." (insert-parentheses (or n 1)) (save-excursion (backward-up-list) (indent-sexp))) -(defun backward-wrap-sexp (&optional n) - "Wraps the preceding 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." - (interactive "p") - (insert-parentheses (- (or n 1))) - (save-excursion (backward-up-list) (indent-sexp))) - -(defun splice-sexp () +(defun paredit-splice-sexp () "Splices the list the point is on by removing its delimiters." (interactive) (save-excursion @@ -455,7 +595,7 @@ Automatically indents the newly wrapped S-expression." (backward-up-list) ; Reindent, now that the (indent-sexp))) ; structure has changed. -(defun join-sexps () +(defun paredit-join-sexps () "Joins two adjacent S-expressions into one S-expression." (interactive) (save-excursion @@ -473,59 +613,63 @@ Automatically indents the newly wrapped S-expression." ;;; ---------------- ;;; Slurpage & barfage -(defun forward-slurp-sexp (&optional n) +(defun paredit-forward-slurp-sexp () "Adds the S-expression following the current list into that list by moving the closing delimiter. -If a prefix argument N is given, N S-expressions are slurped into the -current list. Automatically reindents the newly slurped S-expressions with respect to their new enclosing form." - (interactive "p") + (interactive) (save-excursion (up-list) ; Up to the end of the list to (let ((close (char-before))) ; save and delete the closing (backward-delete-char 1) ; delimiter. - (condition-case nil ; Go to the end of the last - (paredit-forward-and-indent n); S-expression, - (scan-error nil)) ; (ignoring going too far) + (catch 'return ; Go to the end of the desired + (while t ; S-expression, going up a + (condition-case () ; list if it's not in this, + (progn (paredit-forward-and-indent) + (throw 'return nil)) + (scan-error (up-list))))) (insert close)))) ; to insert that delimiter. -(defun forward-barf-sexp (&optional n) +(defun paredit-forward-barf-sexp () "Removes the last S-expression in the current list from that list by moving the closing delimiter. -If a prefix argument N is given, the last N S-expressions are barfed -out of the current list. Automatically reindents all of the newly barfed S-expressions with respect to their new enclosing form." - (interactive "p") + (interactive) (save-excursion (up-list) ; Up to the end of the list to (let ((close (char-before))) ; save and delete the closing (backward-delete-char 1) ; delimiter. - (condition-case nil ; Go back to where we want to - (backward-sexp n) ; insert the delimiter. + (condition-case () ; Go back to where we want to + (backward-sexp) ; insert the delimiter. (scan-error nil)) ; Ignore scan errors, and (skip-chars-backward " \t\n") ; skip leading whitespace. - (if (bobp) - (message "Barfing all subexpressions with no open-paren?")) + (cond ((bobp) + (message + "Barfing all subexpressions with no open-paren?")) + ((paredit-in-comment-p) ; Don't put the close-paren in + (newline-and-indent))) ; a comment. (insert close)) ;; Reindent all of the newly barfed S-expressions. - (paredit-forward-and-indent n))) + (paredit-forward-and-indent))) -(defun backward-slurp-sexp (&optional n) +(defun paredit-backward-slurp-sexp () "Adds the S-expression preceding the current list into that list by moving the closing delimiter. -If a prefix argument N is given, N S-expressions are slurped into the -current list. -Automatically reindents the whole form into which new S-expressions -were slurped." - (interactive "p") +Automatically reindents the whole form into which new S-expression was +slurped." + (interactive) (save-excursion (backward-up-list) (let ((open (char-after))) (delete-char 1) - (condition-case nil (backward-sexp n) - (scan-error nil)) + (catch 'return + (while t + (condition-case () + (progn (backward-sexp) + (throw 'return nil)) + (scan-error (backward-up-list))))) (insert open)) ;; Reindent the line at the beginning of wherever we inserted the ;; opening parenthesis, and then indent the whole S-expression. @@ -533,27 +677,36 @@ were slurped." (lisp-indent-line) (indent-sexp))) -(defun backward-barf-sexp (&optional n) +(defun paredit-backward-barf-sexp () "Removes the first S-expression in the current list from that list by moving the closing delimiter. -If a prefix argument N is given, the first N S-expressions are barfed -out of the current list. -Automatically reindents all of the barfed S-expressions and the form -from which they were barfed." - (interactive "p") - (save-excursion - (backward-up-list) - (let ((open (char-after))) - (delete-char 1) - (condition-case nil (paredit-forward-and-indent n) - (scan-error nil)) - (skip-chars-forward " \t\n") ;++ should handle comments - (if (eobp) - (message "Barfing all subexpressions with no close-paren?")) - (insert open)) - (backward-up-list) - (lisp-indent-line) - (indent-sexp))) +Automatically reindents the barfed S-expression and the form from which +it was barfed." + (interactive) + ;; SAVE-EXCURSION here does the wrong thing, but manually saving and + ;; restoring the point does the right thing. Here's an example of + ;; how SAVE-EXCURSION breaks: + ;; (foo|) C-{ + ;; foo|() + ;; It should be: + ;; foo(|) + (let ((beg (point))) + (unwind-protect + (progn + (backward-up-list) + (let ((open (char-after))) + (delete-char 1) + (condition-case () (paredit-forward-and-indent) + (scan-error nil)) + (skip-chars-forward " \t\n") ;++ should handle comments + (if (eobp) + (message + "Barfing all subexpressions with no close-paren?")) + (insert open)) + (backward-up-list) + (lisp-indent-line) + (indent-sexp)) + (goto-char beg)))) @@ -565,11 +718,9 @@ from which they were barfed." (save-excursion (let ((orig (point))) (beginning-of-defun) - ;; Item 3 of the list PARSE-PARTIAL-SEXP returns is the string - ;; delimiter if the point at the second argument is in a string; - ;; otherwise it's nil. - (eq (nth 3 (parse-partial-sexp (point) orig)) - ?\" )))) + ;; Item 3 of the list PARSE-PARTIAL-SEXP returns is true if the + ;; point at the second argument is in a string, otherwise false. + (nth 3 (parse-partial-sexp (point) orig))))) (defun paredit-string-start+end-points () "Returns a cons of the points of the open and quotes of this string. @@ -626,16 +777,23 @@ and Common Lisp.)" (not (eq (char-before (1- arg)) ?\\ ))))) -(defun paredit-forward-and-indent (n) - "Move forward N S-expressions, indenting them all fully with +(defun paredit-forward-and-indent () + "Move forward an S-expression, indenting it fully with both `lisp-indent-line' and then `indent-sexp'." - (while (< 0 n) - (forward-sexp) ; Find the beginning of this - (backward-sexp) ; next S-expression. + (forward-sexp) ; Go forward, and then find the + (save-excursion ; beginning of this next + (backward-sexp) ; S-expression. (lisp-indent-line) ; Indent its opening line, and - (indent-sexp) ; the rest of it. - (forward-sexp) ; Advance past it. - (setq n (1- n)))) + (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)))) |