summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--paredit.el871
1 files changed, 604 insertions, 267 deletions
diff --git a/paredit.el b/paredit.el
index bb44ea8..d0d4eb1 100644
--- a/paredit.el
+++ b/paredit.el
@@ -1,105 +1,193 @@
;;; -*- Mode: Emacs-Lisp; outline-regexp: " \n;;;;+" -*-
;;;;;; Paredit: Parenthesis-Editing Minor Mode
-;;;;;; Version 20
+;;;;;; Version 21
-;;; This code is written by Taylor R. 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/:
+;;; Copyright (c) 2008, Taylor R. Campbell
+;;;
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+;;;
+;;; * Redistributions of source code must retain the above copyright
+;;; notice, this list of conditions and the following disclaimer.
+;;;
+;;; * Redistributions in binary form must reproduce the above copyright
+;;; notice, this list of conditions and the following disclaimer in
+;;; the documentation and/or other materials provided with the
+;;; distribution.
+;;;
+;;; * Neither the names of the authors nor the names of contributors
+;;; may be used to endorse or promote products derived from this
+;;; software without specific prior written permission.
+;;;
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+;;; This file is permanently stored at
+;;; <http://mumble.net/~campbell/emacs/paredit-21.el>.
;;;
-;;; (add-to-list 'load-path "/path/to/elisp/")
+;;; The currently released version of paredit is available at
+;;; <http://mumble.net/~campbell/emacs/paredit.el>.
+;;;
+;;; The latest beta version of paredit is available at
+;;; <http://mumble.net/~campbell/emacs/paredit-beta.el>.
+;;;
+;;; Release notes are available at
+;;; <http://mumble.net/~campbell/emacs/paredit.release>.
+
+;;; Install paredit by placing `paredit.el' in `/path/to/elisp', a
+;;; directory of your choice, and adding to your .emacs file:
+;;;
+;;; (add-to-list 'load-path "/path/to/elisp")
;;; (autoload 'paredit-mode "paredit"
;;; "Minor mode for pseudo-structurally editing Lisp code."
;;; t)
-;;; (add-hook '...-mode-hook (lambda () (paredit-mode +1)))
;;;
-;;; Usually the ... will be lisp or scheme or both. Alternatively, you
-;;; can manually toggle this mode with M-x paredit-mode. Customization
-;;; of paredit can be accomplished with `eval-after-load':
+;;; Toggle Paredit Mode with `M-x paredit-mode RET', or enable it
+;;; always in a major mode `M' (e.g., `lisp' or `scheme') with:
+;;;
+;;; (add-hook M-mode-hook (lambda () (paredit-mode +1)))
+;;;
+;;; Customize paredit using `eval-after-load':
;;;
;;; (eval-after-load 'paredit
;;; '(progn ...redefine keys, &c....))
;;;
-;;; This should run in GNU Emacs 21 or later and XEmacs 21.5 or later.
-;;; It is highly unlikely to work in earlier versions of GNU Emacs, and
-;;; it may have obscure problems in earlier versions of XEmacs due to
-;;; the way its syntax parser reports conditions, as a result of which
-;;; the code that uses the syntax parser must mask *all* error
-;;; conditions, not just those generated by the syntax parser.
+;;; Paredit should run in GNU Emacs 21 or later and XEmacs 21.5 or
+;;; later. Paredit is highly unlikely to work in earlier versions of
+;;; GNU Emacs, and it may have obscure problems in earlier versions of
+;;; XEmacs due to the way its syntax parser reports conditions, as a
+;;; result of which the code that uses the syntax parser must mask all
+;;; error conditions, not just those generated by the syntax parser.
+;;;
+;;; Questions, bug reports, comments, feature suggestions, &c., may be
+;;; addressed via email to the author's surname at mumble.net or via
+;;; IRC to the user named Riastradh on irc.freenode.net in the #paredit
+;;; channel.
+;;;
+;;; Please contact the author rather than forking your own versions, to
+;;; prevent the dissemination of random variants floating about the
+;;; internet unbeknownst to the author. Laziness is not an excuse:
+;;; your laziness costs me confusion and time trying to support
+;;; paredit, so if you fork paredit, you make the world a worse place.
+;;;
+;;; *** WARNING *** IMPORTANT *** DO NOT SUBMIT BUGS BEFORE READING ***
+;;;
+;;; If you plan to submit a bug report, where some sequence of keys in
+;;; Paredit Mode, or some sequence of paredit commands, doesn't do what
+;;; you wanted, then it is helpful to isolate an example in a very
+;;; small buffer, and it is **ABSOLUTELY**ESSENTIAL** that you supply,
+;;; along with the sequence of keys or commands,
+;;;
+;;; (1) the version of Emacs,
+;;; (2) the version of paredit.el[*], and
+;;; (3) the **COMPLETE** state of the buffer used to reproduce the
+;;; problem, including major mode, minor modes, local key
+;;; bindings, entire contents of the buffer, leading line breaks
+;;; or spaces, &c.
+;;;
+;;; It is often extremely difficult to reproduce problems, especially
+;;; with commands like `paredit-kill'. If you do not supply **ALL** of
+;;; this information, then it is highly probable that I cannot
+;;; reproduce your problem no matter how hard I try, and the effect of
+;;; submitting a bug without this information is only to waste your
+;;; time and mine. So, please, include all of the above information.
+;;;
+;;; [*] If you are using a beta version of paredit, be sure that you
+;;; are using the *latest* edition of the beta version, available
+;;; at <http://mumble.net/~campbell/emacs/paredit-beta.el>. If you
+;;; are not using a beta version, then upgrade either to that or to
+;;; the latest release version; I cannot support older versions,
+;;; and I can't fathom any reason why you might be using them. So
+;;; the answer to item (2) should be either `release' or `beta'.
-;;; This mode changes the keybindings for a number of simple keys,
-;;; notably (, ), ", \, and ;. The bracket keys (round or square) are
-;;; defined to insert parenthesis pairs and move past the close,
+;;; The paredit minor mode, Paredit Mode, binds a number of simple
+;;; keys, notably `(', `)', `"', and `\', to commands that more
+;;; carefully insert S-expression structures in the buffer. The
+;;; parenthesis delimiter keys (round or square) are defined to insert
+;;; parenthesis pairs and move past the closing delimiter,
;;; respectively; the double-quote key is multiplexed to do both, and
-;;; also insert an escape if within a string; backslashes prompt the
-;;; user for the next character to input, because a lone backslash can
-;;; break structure inadvertently; and semicolons ensure that they do
-;;; not accidentally comment valid structure. (Use M-; to comment an
-;;; expression.) These all have their ordinary behaviour when inside
-;;; comments, and, outside comments, if truly necessary, you can insert
-;;; them literally with C-q.
+;;; also to insert an escape if within a string; and backslashes prompt
+;;; the user for the next character to input, because a lone backslash
+;;; can break structure inadvertently. These all have their ordinary
+;;; behaviour when inside comments, and, outside comments, if truly
+;;; necessary, you can insert them literally with `C-q'.
;;;
-;;; These keybindings are set up for my preference. One particular
-;;; preference which I've seen vary greatly from person to person is
-;;; whether the command to move past a closing delimiter ought to
-;;; insert a newline. Since I find this behaviour to be more common
-;;; than that which inserts no newline, I have ) bound to it, and the
-;;; more involved M-) to perform the less common action. This bothers
-;;; some users, though, and they prefer the other way around. This
-;;; code, which you can use `eval-after-load' to put in your .emacs,
-;;; will exchange the bindings:
+;;; The key bindings are designed so that when typing new code in
+;;; Paredit Mode, you can generally use exactly the same keystrokes as
+;;; you would have used without Paredit Mode. Earlier versions of
+;;; paredit.el did not conform to this, because Paredit Mode bound `)'
+;;; to a command that would insert a newline. Now `)' is bound to a
+;;; command that does not insert a newline, and `M-)' is bound to the
+;;; command that inserts a newline. To revert to the former behaviour,
+;;; add the following forms to an `eval-after-load' form for paredit.el
+;;; in your .emacs file:
;;;
;;; (define-key paredit-mode-map (kbd ")")
-;;; 'paredit-close-parenthesis)
+;;; 'paredit-close-round-and-newline)
;;; (define-key paredit-mode-map (kbd "M-)")
-;;; 'paredit-close-parenthesis-and-newline)
+;;; 'paredit-close-round)
;;;
-;;; Paredit also changes the bindings of keys for deleting and killing,
-;;; so that they will not destroy any S-expression structure by killing
-;;; or deleting only one side of a bracket or quote pair. If the point
-;;; is on a closing bracket, DEL will move left over it; if it is on an
-;;; opening bracket, C-d will move right over it. Only if the point is
-;;; between a pair of brackets will C-d or DEL delete them, and in that
-;;; case it will delete both simultaneously. M-d and M-DEL kill words,
-;;; but skip over any S-expression structure. C-k kills from the start
-;;; of the line, either to the line's end, if it contains only balanced
-;;; expressions; to the first closing bracket, if the point is within a
-;;; form that ends on the line; or up to the end of the last expression
-;;; that starts on the line after the point.
+;;; Paredit Mode also binds the usual keys for deleting and killing, so
+;;; that they will not destroy any S-expression structure by killing or
+;;; deleting only one side of a parenthesis or quote pair. If the
+;;; point is on a closing delimiter, `DEL' will move left over it; if
+;;; it is on an opening delimiter, `C-d' will move right over it. Only
+;;; if the point is between a pair of delimiters will `C-d' or `DEL'
+;;; delete them, and in that case it will delete both simultaneously.
+;;; `M-d' and `M-DEL' kill words, but skip over any S-expression
+;;; structure. `C-k' kills from the start of the line, either to the
+;;; line's end, if it contains only balanced expressions; to the first
+;;; closing delimiter, if the point is within a form that ends on the
+;;; line; or up to the end of the last expression that starts on the
+;;; line after the point.
;;;
-;;; 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).
+;;; The behaviour of the commands for deleting and killing can be
+;;; overridden by passing a `C-u' prefix argument: `C-u DEL' will
+;;; delete a character backward, `C-u C-d' will delete a character
+;;; forward, and `C-u C-k' will kill text from the point to the end of
+;;; the line, irrespective of the S-expression structure in the buffer.
+;;; This can be used to fix mistakes in a buffer, but should generally
+;;; be avoided.
;;;
-;;; 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.
+;;; Paredit performs automatic reindentation as locally as possible, to
+;;; avoid interfering with custom indentation used elsewhere in some
+;;; S-expression. Only the advanced S-expression manipulation commands
+;;; automatically reindent, and only the forms that were immediately
+;;; operated upon (and their subforms).
;;;
-;;; Questions, bug reports, comments, feature suggestions, &c., can be
-;;; addressed to the author via mail on the host mumble.net to campbell
-;;; or via IRC on irc.freenode.net in the #paredit channel under the
-;;; nickname Riastradh.
+;;; This code is written for clarity, not efficiency. It frequently
+;;; walks over S-expressions redundantly. If you have problems with
+;;; the time it takes to execute some of the commands, let me know, but
+;;; first be sure that what you're doing is reasonable: it is
+;;; preferable to avoid immense S-expressions in code anyway.
;;; This assumes Unix-style LF line endings.
-(defconst paredit-version 20)
+(defconst paredit-version 21)
+(defconst paredit-beta-p nil)
(eval-and-compile
(defun paredit-xemacs-p ()
- ;; No idea I got this definition from. Edward O'Connor (hober on
- ;; IRC) suggested the current definition.
+ ;; No idea where I got this definition from. Edward O'Connor
+ ;; (hober in #emacs) suggested the current definition.
;; (and (boundp 'running-xemacs)
;; running-xemacs)
(featurep 'xemacs))
(defun paredit-gnu-emacs-p ()
+ ;++ This could probably be improved.
(not (paredit-xemacs-p)))
(defmacro xcond (&rest clauses)
@@ -116,7 +204,7 @@ Signal an error if no clause matches."
(condition-case condition
(backward-sexp)
(error (if (eq (car condition) 'error)
- (paredit-warn "%s%s%s%s"
+ (paredit-warn "%s%s%s%s%s"
"Paredit is unable to discriminate"
" S-expression parse errors from"
" other errors. "
@@ -206,26 +294,26 @@ Deprecated: use `paredit-mode' instead."
(progn (setq paredit-commands
`(
"Basic Insertion Commands"
- ("(" paredit-open-parenthesis
+ ("(" paredit-open-round
("(a b |c d)"
"(a b (|) c d)")
("(foo \"bar |baz\" quux)"
"(foo \"bar (|baz\" quux)"))
- (")" paredit-close-parenthesis-and-newline
+ (")" paredit-close-round
+ ("(a b |c )" "(a b c)|")
+ ("; Hello,| world!"
+ "; Hello,)| world!"))
+ ("M-)" paredit-close-round-and-newline
("(defun f (x| ))"
"(defun f (x)\n |)")
("; (Foo.|"
"; (Foo.)|"))
- ("M-)" paredit-close-parenthesis
- ("(a b |c )" "(a b c)|")
- ("; Hello,| world!"
- "; Hello,)| world!"))
- ("[" paredit-open-bracket
+ ("[" paredit-open-square
("(a b |c d)"
"(a b [|] c d)")
("(foo \"bar |baz\" quux)"
"(foo \"bar [baz\" quux)"))
- ("]" paredit-close-bracket
+ ("]" paredit-close-square
("(define-key keymap [frob| ] 'frobnicate)"
"(define-key keymap [frob]| 'frobnicate)")
("; [Bar.|"
@@ -246,11 +334,6 @@ Deprecated: use `paredit-mode' instead."
"(string #\\x|)")
("\"foo|bar\"\n ; Escaping character... (\")"
"\"foo\\\"|bar\""))
- (";" paredit-semicolon
- ("|(frob grovel)"
- ";|\n(frob grovel)")
- ("(frob grovel) |"
- "(frob grovel) ;|"))
("M-;" paredit-comment-dwim
("(foo |bar) ; baz"
"(foo bar) ; |baz")
@@ -329,7 +412,7 @@ Deprecated: use `paredit-mode' instead."
; no need given C-M-f & C-M-b.
"Depth-Changing Commands"
- ("M-(" paredit-wrap-sexp
+ ("M-(" paredit-wrap-round
("(foo |bar baz)"
"(foo (|bar) baz)"))
("M-s" paredit-splice-sexp
@@ -384,6 +467,7 @@ Deprecated: use `paredit-mode' instead."
("hello-\n| world"
"hello-|world"))
("C-c C-M-l" paredit-recentre-on-sexp)
+ ("M-q" paredit-reindent-defun)
))
nil) ; end of PROGN
@@ -469,11 +553,13 @@ Deprecated: use `paredit-mode' instead."
(defun paredit-insert-html-examples ()
"Insert HTML for a paredit quick reference table."
(interactive)
- (let ((insert-lines (lambda (&rest lines)
- (mapc (lambda (line) (insert line) (newline))
- lines)))
- (html-keys (lambda (keys)
- (mapconcat 'paredit-html-quote keys ", ")))
+ (let ((insert-lines
+ (lambda (&rest lines)
+ (mapc (lambda (line) (insert line) (newline))
+ lines)))
+ (html-keys
+ (lambda (keys)
+ (mapconcat 'paredit-html-quote keys ", ")))
(html-example
(lambda (example)
(concat "<table><tr><td><pre>"
@@ -563,68 +649,93 @@ If in a character literal, do nothing. This prevents changing what was
If there was a margin comment after the closing delimiter, preserve it
on the same line.")
(interactive)
- (paredit-move-past-close-and-newline ,close)))))
-
-(define-paredit-pair ?\( ?\) "parenthesis")
-(define-paredit-pair ?\[ ?\] "bracket")
-(define-paredit-pair ?\{ ?\} "brace")
-(define-paredit-pair ?\< ?\> "brocket")
+ (paredit-move-past-close-and-newline ,close))
+ (defun ,(paredit-conc-name "paredit-wrap-" name)
+ (&optional argument)
+ ,(concat "Wrap the following S-expression.
+See `paredit-wrap-sexp' for more details.")
+ (interactive "P")
+ (paredit-wrap-sexp argument ,open ,close))
+ (add-to-list 'paredit-wrap-commands
+ ',(paredit-conc-name "paredit-wrap-" name)))))
+
+(defvar paredit-wrap-commands '(paredit-wrap-sexp)
+ "List of paredit commands that wrap S-expressions.
+Used by `paredit-yank-pop'; for internal paredit use only.")
+
+(define-paredit-pair ?\( ?\) "round")
+(define-paredit-pair ?\[ ?\] "square")
+(define-paredit-pair ?\{ ?\} "curly")
+(define-paredit-pair ?\< ?\> "angled")
+
+;;; Aliases for the old names.
+
+(defalias 'paredit-open-parenthesis 'paredit-open-round)
+(defalias 'paredit-close-parenthesis 'paredit-close-round)
+(defalias 'paredit-close-parenthesis-and-newline
+ 'paredit-close-round-and-newline)
+
+(defalias 'paredit-open-bracket 'paredit-open-square)
+(defalias 'paredit-close-bracket 'paredit-close-square)
+(defalias 'paredit-close-bracket-and-newline
+ 'paredit-close-square-and-newline)
(defun paredit-move-past-close (close)
(cond ((or (paredit-in-string-p)
(paredit-in-comment-p))
(insert close))
((not (paredit-in-char-p))
- (paredit-move-past-close-and-reindent)
+ (paredit-move-past-close-and-reindent close)
(paredit-blink-paren-match nil))))
(defun paredit-move-past-close-and-newline (close)
- (cond ((or (paredit-in-string-p)
- (paredit-in-comment-p))
- (insert close))
- (t (if (paredit-in-char-p) (forward-char))
- (paredit-move-past-close-and-reindent)
- (let ((comment.point (paredit-find-comment-on-line)))
- (newline)
- (if comment.point
- (save-excursion
- (forward-line -1)
- (end-of-line)
- (indent-to (cdr comment.point))
- (insert (car comment.point)))))
- (lisp-indent-line)
- (paredit-ignore-sexp-errors (indent-sexp))
- (paredit-blink-paren-match t))))
+ (if (or (paredit-in-string-p)
+ (paredit-in-comment-p))
+ (insert close)
+ (if (paredit-in-char-p) (forward-char))
+ (paredit-move-past-close-and-reindent close)
+ (let ((comment.point (paredit-find-comment-on-line)))
+ (newline)
+ (if comment.point
+ (save-excursion
+ (forward-line -1)
+ (end-of-line)
+ (indent-to (cdr comment.point))
+ (insert (car comment.point)))))
+ (lisp-indent-line)
+ (paredit-ignore-sexp-errors (indent-sexp))
+ (paredit-blink-paren-match t)))
(defun paredit-find-comment-on-line ()
"Find a margin comment on the current line.
+Return nil if there is no such comment or if there is anything but
+ whitespace until such a comment.
If such a comment exists, delete the comment (including all leading
whitespace) and return a cons whose car is the comment as a string
and whose cdr is the point of the comment's initial semicolon,
relative to the start of the line."
(save-excursion
- (catch 'return
- (while t
- (if (search-forward ";" (point-at-eol) t)
- (if (not (or (paredit-in-string-p)
- (paredit-in-char-p)))
- (let* ((start (progn (backward-char) ;before semicolon
- (point)))
- (comment (buffer-substring start
- (point-at-eol))))
- (paredit-skip-whitespace nil (point-at-bol))
- (delete-region (point) (point-at-eol))
- (throw 'return
- (cons comment (- start (point-at-bol))))))
- (throw 'return nil))))))
+ (paredit-skip-whitespace t (point-at-eol))
+ (and (eq ?\; (char-after))
+ (not (eq ?\; (char-after (1+ (point)))))
+ (not (or (paredit-in-string-p)
+ (paredit-in-char-p)))
+ (let* ((start ;Move to before the semicolon.
+ (progn (backward-char) (point)))
+ (comment
+ (buffer-substring start (point-at-eol))))
+ (paredit-skip-whitespace nil (point-at-bol))
+ (delete-region (point) (point-at-eol))
+ (cons comment (- start (point-at-bol)))))))
(defun paredit-insert-pair (n open close forward)
- (let* ((regionp (and (paredit-region-active-p)
- (paredit-region-safe-for-insert-p)))
- (end (and regionp
- (not n)
- (prog1 (region-end)
- (goto-char (region-beginning))))))
+ (let* ((regionp
+ (and (paredit-region-active-p)
+ (paredit-region-safe-for-insert-p)))
+ (end
+ (and regionp
+ (not n)
+ (prog1 (region-end) (goto-char (region-beginning))))))
(let ((spacep (paredit-space-for-delimiter-p nil open)))
(if spacep (insert " "))
(insert open)
@@ -645,8 +756,8 @@ If such a comment exists, delete the comment (including all leading
(end (region-end)))
(goto-char beginning)
(let* ((beginning-state (paredit-current-parse-state))
- (end-state (parse-partial-sexp beginning end
- nil nil beginning-state)))
+ (end-state
+ (parse-partial-sexp beginning end nil nil beginning-state)))
(and (= (nth 0 beginning-state) ; 0. depth in parens
(nth 0 end-state))
(eq (nth 3 beginning-state) ; 3. non-nil if inside a
@@ -662,14 +773,20 @@ If such a comment exists, delete the comment (including all leading
;; close when want an open the string or an open when we want to
;; close the string), do insert a space.
(and (not (if endp (eobp) (bobp)))
- (memq (char-syntax (if endp
- (char-after)
- (char-before)))
+ (memq (char-syntax (if endp (char-after) (char-before)))
(list ?w ?_ ?\"
(let ((matching (matching-paren delimiter)))
(and matching (char-syntax matching)))))))
-(defun paredit-move-past-close-and-reindent ()
+(defun paredit-move-past-close-and-reindent (close)
+ (let ((open (paredit-missing-close)))
+ (if open
+ (if (eq close (matching-paren open))
+ (save-excursion
+ (message "Missing closing delimiter: %c" close)
+ (insert close))
+ (error "Mismatched missing closing delimiter: %c ... %c"
+ open close))))
(let ((orig (point)))
(up-list)
(if (catch 'return ; This CATCH returns T if it
@@ -685,18 +802,26 @@ If such a comment exists, delete the comment (including all leading
((save-excursion (forward-line -1)
(end-of-line)
(paredit-in-comment-p))
- ;; Moving the closing parenthesis any further
+ ;; Moving the closing delimiter any further
;; would put it into a comment, so we just
- ;; indent the closing parenthesis where it is
- ;; and abort the loop, telling its continuation
- ;; that no leading whitespace should be deleted.
+ ;; indent the closing delimiter where it is and
+ ;; abort the loop, telling its continuation that
+ ;; no leading whitespace should be deleted.
(lisp-indent-line)
(throw 'return nil))
(t (delete-indentation)))))))
(paredit-delete-leading-whitespace))))
+(defun paredit-missing-close ()
+ (save-excursion
+ (paredit-handle-sexp-errors (backward-up-list)
+ (error "Not inside a list."))
+ (let ((open (char-after)))
+ (paredit-handle-sexp-errors (progn (forward-sexp) nil)
+ open))))
+
(defun paredit-delete-leading-whitespace ()
- ;; This assumes that we're on the closing parenthesis already.
+ ;; This assumes that we're on the closing delimiter already.
(save-excursion
(backward-char)
(while (let ((syn (char-syntax (char-before))))
@@ -826,7 +951,7 @@ If not in a string, act as `paredit-doublequote'; if no prefix argument
nil)) ; express the rubout char?
; ?\^? works, but ugh...)
-;;; The placement of this function in this file is totally random.
+;;; The placement of these functions in this file is totally random.
(defun paredit-newline ()
"Insert a newline and indent it.
@@ -842,37 +967,24 @@ If in a string, just insert a literal newline."
(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.
+ ;; there's only a closing delimiter after the point.
(paredit-ignore-sexp-errors (indent-sexp))))
+
+(defun paredit-reindent-defun (&optional argument)
+ "Reindent the definition that the point is on.
+If the point is in a string or a comment, fill the paragraph instead,
+ and with a prefix argument, justify as well."
+ (interactive "P")
+ (if (or (paredit-in-string-p)
+ (paredit-in-comment-p))
+ (fill-paragraph argument)
+ (save-excursion
+ (beginning-of-defun)
+ (indent-sexp))))
;;;; Comment Insertion
-(defun paredit-semicolon (&optional n)
- "Insert a semicolon, moving any code after the point to a new line.
-If in a string, comment, or character literal, insert just a literal
- semicolon, and do not move anything to the next line.
-With a prefix argument N, insert N semicolons."
- (interactive "P")
- (if (not (or (paredit-in-string-p)
- (paredit-in-comment-p)
- (paredit-in-char-p)
- ;; No more code on the line after the point.
- (save-excursion
- (paredit-skip-whitespace t (point-at-eol))
- (or (eolp)
- ;; Let the user prefix semicolons to existing
- ;; comments.
- (eq (char-after) ?\;)))))
- ;; Don't use NEWLINE-AND-INDENT, because that will delete all of
- ;; the horizontal whitespace first, but we just want to move the
- ;; code following the point onto the next line while preserving
- ;; the point on this line.
- ;++ Why indent only the line?
- (save-excursion (newline) (lisp-indent-line)))
- (insert (make-string (if n (prefix-numeric-value n) 1)
- ?\; )))
-
-(defun paredit-comment-dwim (&optional arg)
+(defun paredit-comment-dwim (&optional argument)
"Call the Lisp comment command you want (Do What I Mean).
This is like `comment-dwim', but it is specialized for Lisp editing.
If transient mark mode is enabled and the mark is active, comment or
@@ -890,17 +1002,34 @@ At the top level, where indentation is calculated to be at column 0,
and if the point is after all code on the line, insert a single-
semicolon margin comment at `comment-column'."
(interactive "*P")
- (require 'newcomment)
- (comment-normalize-vars)
+ (paredit-initialize-comment-dwim)
(cond ((paredit-region-active-p)
(comment-or-uncomment-region (region-beginning)
(region-end)
- arg))
+ argument))
((paredit-comment-on-line-p)
- (if arg
- (comment-kill (if (integerp arg) arg nil))
+ (if argument
+ (comment-kill (if (integerp argument) argument nil))
(comment-indent)))
(t (paredit-insert-comment))))
+
+;;; This is all a horrible, horrible hack, primarily for GNU Emacs 21,
+;;; in which there is no `comment-or-uncomment-region'.
+
+(defun paredit-initialize-comment-dwim ()
+ (require 'newcomment)
+ (if (not (fboundp 'comment-or-uncomment-region))
+ (defalias 'comment-or-uncomment-region
+ (lambda (beginning end &optional argument)
+ (interactive "*r\nP")
+ (funcall (if (save-excursion (goto-char beginning)
+ (comment-forward (point-max))
+ (<= end (point)))
+ 'uncomment-region
+ 'comment-region)
+ beginning end argument))))
+ (defalias 'paredit-initialize-comment-dwim 'comment-normalize-vars)
+ (comment-normalize-vars))
(defun paredit-comment-on-line-p ()
(save-excursion
@@ -957,17 +1086,24 @@ At the top level, where indentation is calculated to be at column 0,
;;;; Character Deletion
-(defun paredit-forward-delete (&optional arg)
+(defun paredit-forward-delete (&optional argument)
"Delete a character forward or move forward over a delimiter.
If on an opening S-expression delimiter, move forward into the
S-expression.
If on a closing S-expression delimiter, refuse to delete unless the
S-expression is empty, in which case delete the whole S-expression.
-With a prefix argument, simply delete a character forward, without
- regard for delimiter balancing."
+With a numeric prefix argument N, delete N characters forward.
+With a `C-u' prefix argument, simply delete a character forward,
+ without regard for delimiter balancing."
(interactive "P")
- (cond ((or arg (eobp))
+ (cond ((or (consp argument) (eobp))
(delete-char 1))
+ ((integerp argument)
+ (if (< argument 0)
+ (paredit-backward-delete argument)
+ (while (> argument 0)
+ (paredit-forward-delete)
+ (setq argument (- argument 1)))))
((paredit-in-string-p)
(paredit-forward-delete-in-string))
((paredit-in-comment-p)
@@ -983,15 +1119,20 @@ With a prefix argument, simply delete a character forward, without
((let ((syn (char-syntax (char-after))))
(or (eq syn ?\( )
(eq syn ?\" )))
- (forward-char))
+ (if (save-excursion
+ (paredit-handle-sexp-errors (progn (forward-sexp) t)
+ nil))
+ (forward-char)
+ (message "Deleting spurious opening delimiter.")
+ (delete-char 1)))
((and (not (paredit-in-char-p (1- (point))))
(eq (char-syntax (char-after)) ?\) )
(eq (char-before) (matching-paren (char-after))))
(backward-delete-char 1) ; Empty list -- delete both
(delete-char 1)) ; delimiters.
;; Just delete a single character, if it's not a closing
- ;; parenthesis. (The character literal case is already
- ;; handled by now.)
+ ;; delimiter. (The character literal case is already handled
+ ;; by now.)
((not (eq (char-syntax (char-after)) ?\) ))
(delete-char 1))))
@@ -1018,17 +1159,25 @@ With a prefix argument, simply delete a character forward, without
(backward-delete-char 1)
(delete-char 1)))))
-(defun paredit-backward-delete (&optional arg)
+(defun paredit-backward-delete (&optional argument)
"Delete a character backward or move backward over a delimiter.
If on a closing S-expression delimiter, move backward into the
S-expression.
If on an opening S-expression delimiter, refuse to delete unless the
S-expression is empty, in which case delete the whole S-expression.
-With a prefix argument, simply delete a character backward, without
- regard for delimiter balancing."
+With a numeric prefix argument N, delete N characters backward.
+With a `C-u' prefix argument, simply delete a character backward,
+ without regard for delimiter balancing."
(interactive "P")
- (cond ((or arg (bobp))
- (backward-delete-char 1)) ;++ should this untabify?
+ (cond ((or (consp argument) (bobp))
+ ;++ Should this untabify?
+ (backward-delete-char 1))
+ ((integerp argument)
+ (if (< argument 0)
+ (paredit-forward-delete (- 0 argument))
+ (while (> argument 0)
+ (paredit-backward-delete)
+ (setq argument (- argument 1)))))
((paredit-in-string-p)
(paredit-backward-delete-in-string))
((paredit-in-comment-p)
@@ -1041,13 +1190,18 @@ With a prefix argument, simply delete a character backward, without
((let ((syn (char-syntax (char-before))))
(or (eq syn ?\) )
(eq syn ?\" )))
- (backward-char))
+ (if (save-excursion
+ (paredit-handle-sexp-errors (progn (backward-sexp) t)
+ nil))
+ (backward-char)
+ (message "Deleting spurious closing delimiter.")
+ (backward-delete-char 1)))
((and (eq (char-syntax (char-before)) ?\( )
(eq (char-after) (matching-paren (char-before))))
(backward-delete-char 1) ; Empty list -- delete both
(delete-char 1)) ; delimiters.
- ;; Delete it, unless it's an opening parenthesis. The case
- ;; of character literals is already handled by now.
+ ;; Delete it, unless it's an opening delimiter. The case of
+ ;; character literals is already handled by now.
((not (eq (char-syntax (char-before)) ?\( ))
(backward-delete-char-untabify 1))))
@@ -1076,15 +1230,18 @@ With a prefix argument, simply delete a character backward, without
;;;; Killing
-(defun paredit-kill (&optional arg)
+(defun paredit-kill (&optional argument)
"Kill a line as if with `kill-line', but respecting delimiters.
In a string, act exactly as `kill-line' but do not kill past the
closing string delimiter.
On a line with no S-expressions on it starting after the point or
within a comment, act exactly as `kill-line'.
-Otherwise, kill all S-expressions that start after the point."
+Otherwise, kill all S-expressions that start after the point.
+With a `C-u' prefix argument, just do the standard `kill-line'.
+With a numeric prefix argument N, do `kill-line' that many times."
(interactive "P")
- (cond (arg (kill-line))
+ (cond (argument
+ (kill-line (if (integerp argument) argument 1)))
((paredit-in-string-p)
(paredit-kill-line-in-string))
((or (paredit-in-comment-p)
@@ -1263,7 +1420,7 @@ Otherwise, kill all S-expressions that start after the point."
(backward-char 1)))))
(backward-kill-word 1))
-;;; Word-Killing Auxiliaries
+;;;;;; Word-Killing Auxiliaries
(defun paredit-kill-word-state (parse-state adjacent-char-fn)
(cond ((paredit-in-comment-p parse-state) 'comment)
@@ -1355,34 +1512,86 @@ With a prefix argument N, encompass all N S-expressions forward."
(lines-on-sexps (count-lines start-point end-point)))
(goto-line (+ start-line (/ lines-on-sexps 2)))
(recenter)))))
+
+(defun paredit-focus-on-defun ()
+ "Moves display to the top of the definition at point."
+ (interactive)
+ (beginning-of-defun)
+ (recenter 0))
;;;; Depth-Changing Commands: Wrapping, Splicing, & Raising
-(defun paredit-wrap-sexp (&optional n)
- "Wrap the following S-expression in a list.
-If a prefix argument N is given, wrap N S-expressions.
+(defun paredit-wrap-sexp (&optional argument open close)
+ "Wrap the following S-expression.
+If a `C-u' prefix argument is given, wrap all S-expressions following
+ the point until the end of the buffer or of the enclosing list.
+If a numeric prefix argument N is given, wrap N S-expressions.
Automatically indent the newly wrapped S-expression.
As a special case, if the point is at the end of a list, simply insert
- a pair of parentheses, rather than insert a lone opening parenthesis
- and then signal an error, in the interest of preserving structure."
+ a parenthesis pair, rather than inserting a lone opening delimiter
+ and then signalling an error, in the interest of preserving
+ structure.
+By default OPEN and CLOSE are round delimiters."
(interactive "P")
- (paredit-handle-sexp-errors
- (paredit-insert-pair (or n
- (and (not (paredit-region-active-p))
- 1))
- ?\( ?\)
- 'goto-char)
- (insert ?\) )
- (backward-char))
+ (paredit-lose-if-not-in-sexp 'paredit-wrap-sexp)
+ (let ((open (or open ?\( ))
+ (close (or close ?\) )))
+ (paredit-handle-sexp-errors
+ ((lambda (n) (paredit-insert-pair n open close 'goto-char))
+ (cond ((integerp argument) argument)
+ ((consp argument) (paredit-count-sexps-forward))
+ ((paredit-region-active-p) nil)
+ (t 1)))
+ (insert close)
+ (backward-char)))
(save-excursion (backward-up-list) (indent-sexp)))
+(defun paredit-count-sexps-forward ()
+ (save-excursion
+ (let ((n 0))
+ (paredit-ignore-sexp-errors
+ (while (not (eobp))
+ (forward-sexp)
+ (setq n (+ n 1))))
+ n)))
+
+(defun paredit-yank-pop (&optional argument)
+ "Replace just-yanked text with the next item in the kill ring.
+If this command follows a `yank', just run `yank-pop'.
+If this command follows a `paredit-wrap-sexp', or any other paredit
+ wrapping command (see `paredit-wrap-commands'), run `yank' and
+ reindent the enclosing S-expression.
+If this command is repeated, run `yank-pop' and reindent the enclosing
+ S-expression.
+
+The argument is passed on to `yank' or `yank-pop'; see their
+ documentation for details."
+ (interactive "*p")
+ (cond ((eq last-command 'yank)
+ (yank-pop argument))
+ ((memq last-command paredit-wrap-commands)
+ (yank argument)
+ ;; `yank' futzes with `this-command'.
+ (setq this-command 'paredit-yank-pop)
+ (save-excursion (backward-up-list) (indent-sexp)))
+ ((eq last-command 'paredit-yank-pop)
+ ;; Pretend we just did a `yank', so that we can use
+ ;; `yank-pop' without duplicating its definition.
+ (setq last-command 'yank)
+ (yank-pop argument)
+ ;; Return to our original state.
+ (setq last-command 'paredit-yank-pop)
+ (setq this-command 'paredit-yank-pop)
+ (save-excursion (backward-up-list) (indent-sexp)))
+ (t (error "Last command was not a yank or a wrap: %s" last-command))))
+
;;; Thanks to Marco Baringer for the suggestion of a prefix argument
;;; for PAREDIT-SPLICE-SEXP. (I, Taylor R. 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)
+(defun paredit-splice-sexp (&optional argument)
"Splice the list that the point is on by removing its delimiters.
With a prefix argument as in `C-u', kill all S-expressions backward in
the current list before splicing all S-expressions forward into the
@@ -1393,47 +1602,53 @@ With two prefix arguments as in `C-u C-u', kill all S-expressions
With a numerical prefix argument N, kill N S-expressions backward in
the current list before splicing the remaining S-expressions into the
enclosing list. If N is negative, kill forward.
-This always creates a new entry on the kill ring."
+Inside a string, unescape all backslashes, or signal an error if doing
+ so would invalidate the buffer's structure."
(interactive "P")
- (save-excursion
- (paredit-kill-surrounding-sexps-for-splice arg)
- (backward-up-list) ; Go up to the beginning...
+ (if (paredit-in-string-p)
+ (paredit-splice-string argument)
(save-excursion
- (forward-sexp) ; Go forward an expression, to
- (backward-delete-char 1)) ; delete the end delimiter.
- (delete-char 1) ; ...to delete the open char.
- (paredit-ignore-sexp-errors
- (backward-up-list) ; Reindent, now that the
- (indent-sexp)))) ; structure has changed.
-
-(defun paredit-kill-surrounding-sexps-for-splice (arg)
- (cond ((paredit-in-string-p) (error "Splicing illegal in strings."))
- ((or (not arg) (eq arg 0)) nil)
- ((or (numberp arg) (eq arg '-))
- ;; Kill ARG S-expressions before/after the point by saving
- ;; the point, moving across them, and killing the region.
- (let* ((arg (if (eq arg '-) -1 arg))
- (saved (paredit-point-at-sexp-boundary (- arg))))
- (paredit-ignore-sexp-errors (backward-sexp arg))
- (kill-region-new saved (point))))
- ((consp arg)
- (let ((v (car arg)))
- (if (= v 4) ; one prefix argument
+ (paredit-kill-surrounding-sexps-for-splice argument)
+ (backward-up-list) ; Go up to the beginning...
+ (save-excursion
+ (forward-sexp) ; Go forward an expression, to
+ (backward-delete-char 1)) ; delete the end delimiter.
+ (delete-char 1) ; ...to delete the open char.
+ (paredit-ignore-sexp-errors
+ (backward-up-list) ; Reindent, now that the
+ (indent-sexp))))) ; structure has changed.
+
+(defun paredit-kill-surrounding-sexps-for-splice (argument)
+ (cond ((or (paredit-in-string-p)
+ (paredit-in-comment-p))
+ (error "Invalid context for splicing S-expressions."))
+ ((or (not argument) (eq argument 0)) nil)
+ ((or (numberp argument) (eq argument '-))
+ ;; Kill S-expressions before/after the point by saving the
+ ;; point, moving across them, and killing the region.
+ (let* ((argument (if (eq argument '-) -1 argument))
+ (saved (paredit-point-at-sexp-boundary (- argument))))
+ (goto-char saved)
+ (paredit-ignore-sexp-errors (backward-sexp argument))
+ (paredit-hack-kill-region saved (point))))
+ ((consp argument)
+ (let ((v (car argument)))
+ (if (= v 4) ;One `C-u'.
;; Move backward until we hit the open paren; then
;; kill that selected region.
- (let ((end (paredit-point-at-sexp-start)))
+ (let ((end (point)))
(paredit-ignore-sexp-errors
(while (not (bobp))
(backward-sexp)))
- (kill-region-new (point) end))
+ (paredit-hack-kill-region (point) end))
;; Move forward until we hit the close paren; then
;; kill that selected region.
- (let ((beginning (paredit-point-at-sexp-end)))
+ (let ((beginning (point)))
(paredit-ignore-sexp-errors
(while (not (eobp))
(forward-sexp)))
- (kill-region-new beginning (point))))))
- (t (error "Bizarre prefix argument: %s" arg))))
+ (paredit-hack-kill-region beginning (point))))))
+ (t (error "Bizarre prefix argument `%s'." argument))))
(defun paredit-splice-sexp-killing-backward (&optional n)
"Splice the list the point is on by removing its delimiters, and
@@ -1458,14 +1673,14 @@ With a prefix argument N, kill only the following N S-expressions."
With a prefix argument N, raise the following N S-expressions. If N
is negative, raise the preceding N S-expressions."
(interactive "p")
+ (paredit-lose-if-not-in-sexp 'paredit-raise-sexp)
;; Select the S-expressions we want to raise in a buffer substring.
(let* ((bound (save-excursion (forward-sexp n) (point)))
- (sexps (save-excursion ;++ Is this necessary?
- (if (and n (< n 0))
- (buffer-substring bound
- (paredit-point-at-sexp-end))
- (buffer-substring (paredit-point-at-sexp-start)
- bound)))))
+ (sexps (if (and n (< n 0))
+ (buffer-substring bound
+ (paredit-point-at-sexp-end))
+ (buffer-substring (paredit-point-at-sexp-start)
+ bound))))
;; Move up to the list we're raising those S-expressions out of and
;; delete it.
(backward-up-list)
@@ -1475,6 +1690,73 @@ With a prefix argument N, raise the following N S-expressions. If N
(while (> n 0)
(paredit-forward-and-indent)
(setq n (1- n)))))))
+
+(defun paredit-convolute-sexp (&optional n)
+ "Convolute S-expressions.
+Save the S-expressions preceding point and delete them.
+Splice the S-expressions following point.
+Wrap the enclosing list in a new list prefixed by the saved text.
+With a prefix argument N, move up N lists before wrapping."
+ (interactive "p")
+ (paredit-lose-if-not-in-sexp 'paredit-convolute-sexp)
+ (let (open close) ;++ Is this a good idea?
+ (let ((prefix
+ (let ((end (point)))
+ (paredit-ignore-sexp-errors
+ (while (not (bobp)) (backward-sexp)))
+ (prog1 (buffer-substring (point) end)
+ (backward-up-list)
+ (save-excursion (forward-sexp)
+ (setq close (char-before))
+ (backward-delete-char 1))
+ (setq open (char-after))
+ (delete-region (point) end)))))
+ (backward-up-list n)
+ (paredit-insert-pair 1 open close 'goto-char)
+ (insert prefix)
+ (backward-up-list)
+ (paredit-ignore-sexp-errors (indent-sexp)))))
+
+(defun paredit-splice-string (argument)
+ (let ((original-point (point))
+ (start+end (paredit-string-start+end-points)))
+ (let ((start (car start+end))
+ (end (cdr start+end)))
+ ;; START and END both lie before the respective quote
+ ;; characters, which we want to delete; thus we increment START
+ ;; by one to extract the string, and we increment END by one to
+ ;; delete the string.
+ (let* ((escaped-string
+ (cond ((not (consp argument))
+ (buffer-substring (1+ start) end))
+ ((= 4 (car argument))
+ (buffer-substring original-point end))
+ (t
+ (buffer-substring (1+ start) original-point))))
+ (unescaped-string
+ (paredit-unescape-string escaped-string)))
+ (if (not unescaped-string)
+ (error "Unspliceable string.")
+ (save-excursion
+ (goto-char start)
+ (delete-region start (1+ end))
+ (insert unescaped-string))
+ (if (not (and (consp argument)
+ (= 4 (car argument))))
+ (goto-char (- original-point 1))))))))
+
+(defun paredit-unescape-string (string)
+ (with-temp-buffer
+ (insert string)
+ (goto-char (point-min))
+ (while (and (not (eobp))
+ ;; nil -> no bound; t -> no errors.
+ (search-forward "\\" nil t))
+ (delete-char -1)
+ (forward-char))
+ (condition-case condition
+ (progn (check-parens) (buffer-string))
+ (error nil))))
;;;; Slurpage & Barfage
@@ -1490,7 +1772,7 @@ If in a string, move the opening double-quote forward by one
(save-excursion
(cond ((or (paredit-in-comment-p)
(paredit-in-char-p))
- (error "Invalid context for slurpage"))
+ (error "Invalid context for slurping S-expressions."))
((paredit-in-string-p)
(paredit-forward-slurp-into-string))
(t
@@ -1505,7 +1787,11 @@ If in a string, move the opening double-quote forward by one
(paredit-handle-sexp-errors ; list if it's not in this,
(progn (paredit-forward-and-indent)
(throw 'return nil))
- (up-list))))
+ (up-list)
+ (setq close ; adjusting for mixed
+ (prog1 (char-before) ; delimiters as necessary,
+ (backward-delete-char 1)
+ (insert close))))))
(insert close))) ; to insert that delimiter.
(defun paredit-forward-slurp-into-string ()
@@ -1524,6 +1810,7 @@ If in a string, move the opening double-quote forward by one
Automatically reindent the newly barfed S-expression with respect to
its new enclosing form."
(interactive)
+ (paredit-lose-if-not-in-sexp 'paredit-forward-slurp-sexp)
(save-excursion
(up-list) ; Up to the end of the list to
(let ((close (char-before))) ; save and delete the closing
@@ -1551,7 +1838,7 @@ If in a string, move the opening double-quote backward by one
(save-excursion
(cond ((or (paredit-in-comment-p)
(paredit-in-char-p))
- (error "Invalid context for slurpage"))
+ (error "Invalid context for slurping S-expressions."))
((paredit-in-string-p)
(paredit-backward-slurp-into-string))
(t
@@ -1564,12 +1851,14 @@ If in a string, move the opening double-quote backward by one
(catch 'return
(while t
(paredit-handle-sexp-errors
- (progn (backward-sexp)
- (throw 'return nil))
- (backward-up-list))))
+ (progn (backward-sexp) (throw 'return nil))
+ (backward-up-list)
+ (setq open
+ (prog1 (char-after)
+ (save-excursion (insert open) (delete-char 1)))))))
(insert open))
;; Reindent the line at the beginning of wherever we inserted the
- ;; opening parenthesis, and then indent the whole S-expression.
+ ;; opening delimiter, and then indent the whole S-expression.
(backward-up-list)
(lisp-indent-line)
(indent-sexp))
@@ -1593,6 +1882,7 @@ If in a string, move the opening double-quote backward by one
Automatically reindent the barfed S-expression and the form from which
it was barfed."
(interactive)
+ (paredit-lose-if-not-in-sexp 'paredit-forward-slurp-sexp)
(save-excursion
(backward-up-list)
(let ((open (char-after)))
@@ -1603,8 +1893,7 @@ Automatically reindent the barfed S-expression and the form from which
(eq (char-after) ?\; ))
(forward-line 1))
(if (eobp)
- (error
- "Barfing all subexpressions with no close-paren?"))
+ (error "Barfing all subexpressions with no close-paren?"))
;** Don't use `insert' here. Consider, e.g., barfing from
;** (foo|)
;** and how `save-excursion' works.
@@ -1623,7 +1912,7 @@ Automatically reindent the barfed S-expression and the form from which
(save-excursion (insert " \"")))
((or (paredit-in-comment-p)
(paredit-in-char-p))
- (error "Invalid context for `paredit-split-sexp'"))
+ (error "Invalid context for splitting S-expression."))
(t (let ((open (save-excursion (backward-up-list)
(char-after)))
(close (save-excursion (up-list)
@@ -1644,10 +1933,9 @@ Both must be lists, strings, or atoms; error if there is a mismatch."
(if (or (paredit-in-comment-p)
(paredit-in-string-p)
(paredit-in-char-p))
- (error "Invalid context in which to join S-expressions.")
- (let ((left-point (save-excursion (paredit-point-at-sexp-end)))
- (right-point (save-excursion
- (paredit-point-at-sexp-start))))
+ (error "Invalid context for joining S-expressions.")
+ (let ((left-point (paredit-point-at-sexp-end))
+ (right-point (paredit-point-at-sexp-start)))
(let ((left-char (char-before left-point))
(right-char (char-after right-point)))
(let ((left-syntax (char-syntax left-char))
@@ -1676,6 +1964,47 @@ Both must be lists, strings, or atoms; error if there is a mismatch."
(t
(error "Mismatched S-expressions to join.")))))))))
+;;;; Variations on the Lurid Theme
+
+;;; I haven't the imagination to concoct clever names for these.
+
+(defun paredit-add-to-previous-list ()
+ "Add the S-expression following point to the list preceding point."
+ (interactive)
+ (paredit-lose-if-not-in-sexp 'paredit-add-to-previous-list)
+ (save-excursion
+ (backward-down-list)
+ (paredit-forward-slurp-sexp)))
+
+(defun paredit-add-to-next-list ()
+ "Add the S-expression preceding point to the list following point.
+If no S-expression precedes point, move up the tree until one does."
+ (interactive)
+ (paredit-lose-if-not-in-sexp 'paredit-add-to-next-list)
+ (save-excursion
+ (down-list)
+ (paredit-backward-slurp-sexp)))
+
+(defun paredit-join-with-previous-list ()
+ "Join the list the point is on with the previous list in the buffer."
+ (interactive)
+ (paredit-lose-if-not-in-sexp 'paredit-join-with-previous-list)
+ (save-excursion
+ (while (paredit-handle-sexp-errors (save-excursion (backward-sexp) nil)
+ (backward-up-list)
+ t))
+ (paredit-join-sexps)))
+
+(defun paredit-join-with-next-list ()
+ "Join the list the point is on with the next list in the buffer."
+ (interactive)
+ (paredit-lose-if-not-in-sexp 'paredit-join-with-next-list)
+ (save-excursion
+ (while (paredit-handle-sexp-errors (save-excursion (forward-sexp) nil)
+ (up-list)
+ t))
+ (paredit-join-sexps)))
+
;;;; Utilities
(defun paredit-in-string-escape-p ()
@@ -1690,7 +2019,7 @@ This assumes that `paredit-in-string-p' has already returned true."
(backward-char)))
oddp))
-(defun paredit-in-char-p (&optional arg)
+(defun paredit-in-char-p (&optional argument)
"True if the point is immediately after a character literal.
A preceding escape character, not preceded by another escape character,
is considered a character literal prefix. (This works for elisp,
@@ -1699,9 +2028,9 @@ 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)) ?\\ )))))
+ (let ((argument (or argument (point))))
+ (and (eq (char-before argument) ?\\ )
+ (not (eq (char-before (1- argument)) ?\\ )))))
(defun paredit-forward-and-indent ()
"Move forward an S-expression, indenting it fully.
@@ -1726,7 +2055,7 @@ If TRAILING-P is nil, skip leading whitespace; otherwise, skip trailing
(lambda ()
(and mark-active transient-mark-mode)))))
-(defun kill-region-new (start end)
+(defun paredit-hack-kill-region (start end)
"Kill the region between START and END.
Do not append to any current kill, and
do not let the next kill append to this one."
@@ -1794,14 +2123,22 @@ If no parse state is supplied, compute one from the beginning of the
((> n 0) (paredit-point-at-sexp-end))))
(defun paredit-point-at-sexp-start ()
- (forward-sexp)
- (backward-sexp)
- (point))
+ (save-excursion
+ (forward-sexp)
+ (backward-sexp)
+ (point)))
(defun paredit-point-at-sexp-end ()
- (backward-sexp)
- (forward-sexp)
- (point))
+ (save-excursion
+ (backward-sexp)
+ (forward-sexp)
+ (point)))
+
+(defun paredit-lose-if-not-in-sexp (command)
+ (if (or (paredit-in-string-p)
+ (paredit-in-comment-p)
+ (paredit-in-char-p))
+ (error "Invalid context for command `%s'." command)))
;;;; Initialization