From 1bb98310d97e206655e2196746abbe9f352ba6d5 Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Sun, 28 Sep 2008 13:30:10 +0000 Subject: Changes for versions 2--6. - Flushed M-" (paredit-insert-doublequote), which was rather useless and which " (paredit-doublequote) now subsumes the functionality of. - Added instrumented forward deletion as well as backward deletion, which now behave well inside strings. - Flushed unnecessary individual round bracket insertion keys; use C-q instead. - Added C-left & C-right: backward-sexp & forward-sexp, respectively. - Fixed the test of whether the point is in a character literal. - Modified " (paredit-doublequote) to insert escaped double-quote if in the middle of the string, rather than to jump past the string's closing delimiter. - Introduced bogus backslash escaping mechanism. - Introduced new command for breaking the line and indenting, and bound C-j, rather than RET, to it, according to convention. - Improved C-k (paredit-kill), particularly in strings where it will no longer kill the closing delimiter of the string. - Changed the splicage, joinage, slurpage, and barfage commands so that they will reindent only the modified list, not the whole definition. darcs-hash:20080928133010-00fcc-90a9f6931a49b99f633def9bc51157b3bc40a6bf --- paredit.el | 546 +++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 390 insertions(+), 156 deletions(-) (limited to 'paredit.el') diff --git a/paredit.el b/paredit.el index abc29e9..4e0f45f 100644 --- a/paredit.el +++ b/paredit.el @@ -1,7 +1,7 @@ ;;; -*- mode: emacs-lisp -*- ;;;;;; paredit: Parenthesis editing minor mode -;;;;;; Version 1 +;;;;;; Version 6 ;;; Taylor Campbell wrote this code; he places it in the public domain. @@ -11,35 +11,62 @@ ;;; (require 'paredit) ;;; (add-hook '...-mode-hook (lambda () (paredit-mode 1))) ;;; -;;; Usually the ... will be lisp or scheme or both. +;;; Usually the ... will be lisp or scheme or both. Alternatively, you +;;; can manually toggle this mode with M-x paredit-mode. +;;; +;;; This mode changes the keybindings for (, ), and ", most notably; +;;; if you really, really want a literal one of those, use C-q. +;;; +;;; 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. 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 editor, though: escapes, in +;;; character or string literals, which can throw off the parsing of +;;; balanced delimiters. The only way I've come up to deal with this +;;; with any semblance of grace is to insert only completed escape +;;; characters, by rebinding backslash to query for the character to +;;; escape, and for the rest of the code to assume only completed +;;; escapes. This is a crock, but an unfortunately necessary one. +;;; +;;; Automatic reindentation is performed as locally as possible, to +;;; ensure that Emacs does not interfere with custom indentation used +;;; elsewhere in some S-expression. It is performed only by the +;;; advanced S-expression frobnication commands, and only on the forms +;;; that were immediately operated upon (& their subforms). +;;; +;;; This code is written for clarity, not efficiency. S-expressions +;;; are frequently walked over redundantly. If you have problems with +;;; some of the commands taking too long to execute, tell me, but first +;;; make sure that what you're doing is reasonable: it is stylistically +;;; bad to have huge, long, hideously nested code anyway. ;;; This assumes Unix-style LF line endings. -(defconst paredit-version 1) +(defconst paredit-version 6) (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 (kbd "M-\"") 'paredit-insert-doublequote) + (define-key keymap "\\" 'paredit-backslash) - (define-key keymap (kbd "RET") 'newline-and-indent) - (define-key keymap (kbd "DEL") 'paredit-backspace) + (define-key keymap (kbd "C-j") 'paredit-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) - ;; In case something broke and you really, really need to insert a - ;; literal parenthesis. Don't use these often. - (define-key keymap (kbd "C-M-(") - (lambda () (interactive) (insert "("))) - (define-key keymap (kbd "C-M-)") - (lambda () (interactive) (insert ")"))) - ;; 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 "") 'up-list) (define-key keymap (kbd "") 'down-list) + (define-key keymap (kbd "") 'forward-sexp) + (define-key keymap (kbd "") 'backward-sexp) (define-key keymap (kbd "M-(") 'forward-wrap-sexp) (define-key keymap (kbd "M-)") 'backward-wrap-sexp) @@ -68,9 +95,9 @@ If in string, comment, or character literal, inserts a single opening parenthesis." (interactive) - (if (or (paredit-in-comment-p) - (paredit-in-string-p) - (eq (char-before) ?\\ )) + (if (or (paredit-in-string-p) + (paredit-in-comment-p) + (paredit-in-char-p)) (insert "(") (insert-parentheses 0))) @@ -79,120 +106,256 @@ parenthesis." If in a string, comment, or character literal, inserts a single closing parenthesis." (interactive) - (if (or (paredit-in-comment-p) - (paredit-in-string-p) - (eq (char-before) ?\\ )) + (if (or (paredit-in-string-p) + (paredit-in-comment-p) + (paredit-in-char-p)) (insert ")") - (move-past-close-and-reindent))) + (move-past-close-and-reindent) + ;; Reindent not only the current line but, if there is a valid + ;; S-expression following the point, that too. + (condition-case nil (indent-sexp) + (scan-error nil))) + (if blink-matching-paren + (condition-case nil + (save-excursion + (backward-sexp) + (forward-sexp) + (blink-matching-open)) + (error nil)))) (defun paredit-doublequote () "Inserts a pair of double-quotes. -Inside a comment or character literal, inserts a literal double-quote. -Inside a string, moves to the end of the string." +Inside a comment, inserts a literal double-quote. +At the end of a string, moves past the closing double-quote. +In the middle of a string, inserts a backslash-escaped double-quote." (interactive) - (cond ((or (paredit-in-comment-p) - (eq (char-before) ?\\ )) - ;; Special case for when we're on character literals, just to - ;; be convenient. - (insert "\"")) - ((paredit-in-string-p) - (while (not (eq (char-after) ?\" )) - (forward-char) - (if (eq (char-after) ?\\ ) ; Skip escaped characters. - (forward-char))) - (forward-char)) - (t (insert-pair 0 ?\" ?\")))) - -(defun paredit-insert-doublequote (&optional n) - "Inserts a single literal double-quote. -Inside a string, inserts an escaped double-quote: \\\" -Outside of a string, comment, or character literal, displays a message -to the user and inserts a single literal double-quote nevertheless." - (interactive "p") - (let ((string - (cond ((or (paredit-in-comment-p) - (eq (char-before) ?\\ )) - "\"") ; plain doublequote - ((paredit-in-string-p) - "\\\"") ; escaped doublequote - (t (message "Inserting naked doublequote%s..." - (if (> n 1) "s" "")) - "\"")))) ; plain doublequote - (while (< 0 n) - (insert string) - (setq n (1- n))))) - -(defun paredit-backspace () - "Deletes a character backward or moves backward over a delimiter. -If at the start of an empty string literal, deletes the whole string, -including both delimiters. If at the start of a non-empty string -literal, moves back outside of the string literal. If anywhere else in -a string literal, deletes a single character. -If on a closing parenthesis, moves backward one character. -If on an opening parenthesis followed by a closing parenthesis, deletes -both parentheses. -If on any other opening parenthesis, does nothing. -Anywhere else, deletes a character backward." + (cond ((paredit-in-string-p) + (if (eq (cdr (paredit-string-start+end-points)) + (point)) + (forward-char) + (insert ?\\ ?\" ))) + ((paredit-in-comment-p) + (insert ?\" )) + ;; I'm not sure what to do about the character literal case. + ((not (paredit-in-char-p)) + (let ((insert-space + (lambda (endp) + (if (and (not (if endp (eobp) (bobp))) + (memq (char-syntax + (if endp (char-after) (char-before))) + (list ?w ?_ + (char-syntax ?\" ) + (char-syntax ?\( ) + (if endp ;++ sloppy + nil + (char-syntax ?\) ))))) + (insert " "))))) + (funcall insert-space nil) + (insert ?\" ) + (save-excursion + (insert ?\" ) + (funcall insert-space t)))))) + +(defun paredit-backslash () + "Inserts a backslash followed by a character to escape." + (interactive) + ;; This funny conditional is necessary because PAREDIT-IN-COMMENT-P + ;; assumes that PAREDIT-IN-STRING-P already returned false; otherwise + ;; it may break. + (insert ?\\ ) + (if (or (paredit-in-string-p) + (not (paredit-in-comment-p))) + (let ((delp t)) + (unwind-protect (setq delp + (call-interactively #'paredit-escape)) + (if delp (backward-delete-char 1)))))) + +;;; This auxiliary interactive function returns true if the backslash +;;; should be deleted and false if not. + +(defun paredit-escape (char) + ;; I'm too lazy to figure out how to do this without a separate + ;; interactive function. + (interactive "cCharacter to escape: ") + (if (eq char 127) ; luser made a typo and deleted + t + (insert char) + nil)) + +(defun paredit-newline () + "Inserts a newline and indents it. +This is like `newline-and-indent', but it not only indents the line +that the point is on but also the S-expression following the point, if +there is one." (interactive) - (cond ((paredit-in-comment-p) - (backward-delete-char 1)) - ((paredit-in-string-p) - (if (and (eq (char-after) ?\" ) - (eq (char-before) ?\" ) - (not (eq (char-before (1- (point))) - ?\\ ))) - (progn (backward-char) - (kill-sexp)) - (backward-delete-char 1))) - ((and (or (eq (char-before) ?\) ) - (eq (char-before) ?\" )) - (not (eq (char-before (1- (point))) - ?\\ ))) - (backward-char)) - ;++ This should test more thoroughly, e.g. for ( ). - ((and (eq (char-before) ?\( ) - (eq (char-after) ?\) )) - (backward-char) - (kill-sexp)) - ;; Delete it, unless it's an opening parenthesis not preceded - ;; by a backslash (i.e. not a character literal). - ((or (not (eq (char-before) ?\( )) - (eq (char-before (1- (point))) - ?\\ )) - (backward-delete-char-untabify 1)))) + (if (paredit-in-char-p) + (forward-char)) + (newline-and-indent) + ;; Indent the following S-expression, but don't signal an error if + ;; there's only a closing parenthesis after the point. + (condition-case nil (indent-sexp) + (scan-error nil))) + +(defun paredit-forward-delete (&optional arg) + "Deletes a character forward or moves forward over a delimiter. +If on an opening S-expression delimiter, moves forward into the +S-expression. +If on a closing S-expression delimiter, refuses to delete unless the +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)) + ((eq (char-after) ?\\ ) ; Escape -- delete both chars. + (delete-char 2)) + ((paredit-in-char-p) ; ditto + (backward-delete-char 1) + (delete-char 1)) + ((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 () + (cond ((paredit-in-string-escape-p) + (backward-delete-char 1) + (delete-char 1)) + ((eq (char-after) ?\\ ) + (delete-char 2)) + (t + (let ((start+end (paredit-string-start+end-points))) + (cond ((not (eq (point) (cdr start+end))) + ;; Delete the char if it's not the close-quote. + (delete-char 1)) + ((eq (1- (point)) (car start+end)) + ;; If it is the close-quote, delete only if we're also + ;; right past the open-quote (i.e. it's empty), and + ;; then delete both quotes. Otherwise we refuse to + ;; delete it. + (backward-delete-char 1) + (delete-char 1))))))) + +(defun paredit-backward-delete (&optional arg) + "Deletes a character backward or moves backward over a delimiter. +If on a closing S-expression delimiter, moves backward into the +S-expression. +If on an opening S-expression delimiter, refuses to delete unless the +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) + (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))))) + +(defun paredit-backward-delete-in-string () + (cond ((paredit-in-string-escape-p) + (backward-delete-char 1) + (delete-char 1)) + ((and (not (eq (char-before) ?\")) + ;; Delete a whole string escape -- but first make sure we + ;; don't run backwards out the front end of the string. + (save-excursion (backward-char) + (paredit-in-string-escape-p))) + (backward-delete-char 2)) + (t + (let ((start+end (paredit-string-start+end-points))) + (cond ((not (eq (1- (point)) (car start+end))) + ;; Delete the char if it's not the open-quote. + ;; Delete twice if it's an escaped character. + (backward-delete-char 1) + (if (paredit-in-string-escape-p) + (backward-delete-char 1))) + ((eq (point) (cdr start+end)) + ;; If it is the open-quote, delete only if we're also + ;; right past the close-quote (i.e. it's empty), and + ;; then delete both quotes. Otherwise we refuse to + ;; delete it. + (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'." +S-expression; otherwise, behaves as `kill-line', except won't kill a +closing string delimiter." (interactive) - (cond ((or (eq (char-after) ?\n ) + (cond ((paredit-in-string-p) + (paredit-kill-in-string)) + ((or (eq (char-after) ?\n ) (paredit-in-comment-p) (save-excursion (skip-chars-forward " \t\n" (point-at-eol)) (or (eq (point) (point-at-eol)) (eq (char-after) ?\; )))) - (kill-line)) - ((paredit-in-string-p) - (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.) - (delete-char) - (while (not (or (eq (char-after) ?\n ) - (eq (char-after) ?\" ))) - (cond ((eq (char-after) ?\\ ) - (delete-char) - ;; The one after the backslash is escaped, so eat - ;; it (most importantly if it's a doublequote), - ;; unless it's a newline. - (if (not (eq (char-after (1+ point)) - ?\n )) - (delete-char))) - (t (delete-char)))))) + (if (eq (char-before (point-at-eol)) + ?\\ ) + ;++ This is a crock: we don't want to catch an incomplete + ;++ escape sequence. + (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. + (save-excursion + ;; Make sure not to split an escaped character sequence. + (if (paredit-in-string-escape-p) + (backward-char)) + (let ((beg (point))) + (while (not (memq (char-after) '(?\n ?\" ))) + (forward-char) + ;; Skip past escaped characters. + (if (eq (char-before) ?\\ ) + (forward-char))) + (kill-region beg (point)))))) + ;;; ---------------- @@ -201,20 +364,20 @@ S-expression; otherwise, behaves as `kill-line'." (defun forward-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." +list. +Automatically indents the newly wrapped S-expression." (interactive "p") (insert-parentheses (or n 1)) - (save-excursion (beginning-of-defun) - (indent-sexp))) + (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." +list. +Automatically indents the newly wrapped S-expression." (interactive "p") (insert-parentheses (- (or n 1))) - (save-excursion (beginning-of-defun) - (indent-sexp))) + (save-excursion (backward-up-list) (indent-sexp))) (defun splice-sexp () "Splices the list the point is on by removing its delimiters." @@ -225,7 +388,7 @@ list." (forward-sexp) ; Go forward an expression, to (backward-delete-char 1)) ; delete the end delimiter. (delete-char 1) ; ...to delete the open char. - (beginning-of-defun) ; Reindent, now that the + (backward-up-list) ; Reindent, now that the (indent-sexp))) ; structure has changed. (defun join-sexps () @@ -238,8 +401,8 @@ list." (forward-sexp) ; Go to the start of the (backward-sexp) ; following expression. (delete-char 1) ; Delete the opening delimiter. - (beginning-of-defun) ; Reindent the whole defun, now - (indent-sexp))) ; that its structure changed. + (backward-up-list) ; Reindent the list, now that + (indent-sexp))) ; its structure has changed. @@ -256,22 +419,25 @@ list." "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." +current list. +Automatically reindents the newly slurped S-expressions with respect to +their new enclosing form." (interactive "p") (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. - (ignore-errors (forward-sexp n)) ; Go to the end of the last exp - (insert close)) ; to insert that delimiter. - (beginning-of-defun) ; Reindent the form, now that - (indent-sexp))) ; the structure has changed. + (ignore-errors ; Go to the end of the last + (paredit-forward-and-indent n)) ; S-expression, + (insert close)))) ; to insert that delimiter. (defun forward-barf-sexp (&optional n) "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." +out of the current list. +Automatically reindents all of the newly barfed S-expressions with +respect to their new enclosing form." (interactive "p") (save-excursion (up-list) ; Up to the end of the list to @@ -279,17 +445,19 @@ out of the current list." (backward-delete-char 1) ; delimiter. (ignore-errors (backward-sexp n)) ; Go back to where we want to (skip-chars-backward " \t\n") ; insert the delimiter. - (if (eq (point) (point-min)) - (error "Barfing all subexpressions with no open-paren?") - (insert close))) - (beginning-of-defun) ; Reindent: structure has - (indent-sexp))) ; changed. + (if (bobp) + (message "Barfing all subexpressions with no open-paren?")) + (insert close)) + ;; Reindent all of the newly barfed S-expressions. + (paredit-forward-and-indent n))) (defun backward-slurp-sexp (&optional n) "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." +current list. +Automatically reindents the whole form into which new S-expressions +were slurped." (interactive "p") (save-excursion (backward-up-list) @@ -297,52 +465,118 @@ current list." (delete-char 1) (ignore-errors (backward-sexp n)) (insert open)) - (beginning-of-defun) + ;; Reindent the line at the beginning of wherever we inserted the + ;; opening parenthesis, and then indent the whole S-expression. + (backward-up-list) + (lisp-indent-line) (indent-sexp))) (defun backward-barf-sexp (&optional n) "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." +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) - (ignore-errors (forward-sexp n)) - (skip-chars-forward " \t\n") - (if (eq (point) (point-max)) - (error "Barfing all subexpressions with no close-paren?") - (insert open))) - (beginning-of-defun) + (ignore-errors (paredit-forward-and-indent n)) + (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))) ;;; ---------------- -;;; Two utility functions - -(defun paredit-in-comment-p () - "True if the point is within a Lisp line comment." - ;++ Make this work on block comments? - (save-excursion - ;; The third T argument to SEARCH-BACKWARD says to return NIL, - ;; not to signal an error, if no match is found. - (and (search-backward ";" (point-at-bol) t) - (not (eq (char-before) ?\\ )) - t))) - -;;; Taken roughly from thingatpt.el. +;;; Several utility functions (defun paredit-in-string-p () "True if the point is within a double-quote-delimited string." - (let ((orig (point))) - (save-excursion + (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)) ?\" )))) +(defun paredit-string-start+end-points () + "Returns a cons of the points of the open and quotes of this string. +This assumes that `paredit-in-string-p' has already returned true, i.e. +that the point is already within a string." + (save-excursion + (let ((orig (point))) + (beginning-of-defun) + (let* ((state (parse-partial-sexp (point) orig)) + (start (nth 8 state))) + (goto-char start) + (forward-sexp) + (cons start (1- (point))))))) + +(defun paredit-in-string-escape-p () + "True if the point is on a character escaped by a backslash. +This is true only if the character is preceded by an odd number of +backslashes. +This assumes that `paredit-in-string-p' has already returned true." + (let ((oddp nil)) + (save-excursion + (while (eq (char-before) ?\\ ) + (setq oddp (not oddp)) + (backward-char))) + oddp)) + +(defun paredit-in-comment-p () + "True if the point is within a Lisp line comment. +This assumes that `paredit-in-string-p' has already returned false." + ;++ Make this work on block comments? + (save-excursion + (let ((orig (point)) (res nil)) + (goto-char (point-at-bol)) + ;; The third T argument to SEARCH-FORWARD says to return NIL, + ;; not to signal an error, if no match is found. + (setq res (search-forward ";" orig t)) + (while (and res + (or (paredit-in-string-p) + (prog2 (backward-char) + (paredit-in-char-p) + (forward-char)))) + (forward-char) + (setq res (search-forward ";" orig t))) + (and res (<= res orig))))) + +(defun paredit-in-char-p (&optional arg) + "True if the point is immediately after a character literal. +A preceding backslash, not preceded by another backslash, is considered +a character literal prefix. (This works for elisp, Common Lisp, and +Scheme.) +Assumes that `paredit-in-string-p' is false, so that it need not handle +long sequences of preceding backslashes in string escapes. (This +assumes some other leading character token -- ? in elisp, # in Scheme +and Common Lisp.)" + (let ((arg (or arg (point)))) + (and (eq (char-before arg) ?\\ ) + (not (eq (char-before (1- arg)) + ?\\ ))))) + +(defun paredit-forward-and-indent (n) + "Move forward N S-expressions, indenting them all fully with +`lisp-indent-line' and then `indent-sexp'." + (while (< 0 n) + (forward-sexp) ; Find the beginning of this + (backward-sexp) ; next 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)))) + (provide 'paredit) -- cgit v1.2.1