emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/paredit 1bb9831 002/224: Changes for versions 2--6.


From: ELPA Syncer
Subject: [nongnu] elpa/paredit 1bb9831 002/224: Changes for versions 2--6.
Date: Sat, 7 Aug 2021 09:22:05 -0400 (EDT)

branch: elpa/paredit
commit 1bb98310d97e206655e2196746abbe9f352ba6d5
Author: Taylor R Campbell <campbell@mumble.net>
Commit: Taylor R Campbell <campbell@mumble.net>

    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(-)

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 "<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)
@@ -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)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]