summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaylor R Campbell <campbell@mumble.net>2008-09-28 13:44:00 +0000
committerTaylor R Campbell <campbell@mumble.net>2008-09-28 13:44:00 +0000
commit342fee5dfeeff59f94875639b3daa9383ba856a0 (patch)
tree21f2d592a40f6d38ab00d02fff6241c8807dd9c2
parent94645884ce0ee1e797dbf54f3fedcd72ed9ce7a7 (diff)
Changes for version 21.v21
Paredit 21 no longer has a message releasing it into the public domain. Instead it is copyright (C) 2008, Taylor R. Campbell, and available under the terms of the 3-clause BSD licence. See the paredit.el file for details. *** Style and Bugs - Use of the terms `parenthesis', `bracket', and `delimiter' has been clarified throughout the file: . /Parentheses/ are nestable objects indicated by matching delimiter pairs. For example, the text `(foo (bar baz) quux)' has two parentheses, `(foo ... quux)' and `(bar baz)'; each is delimited by matching round delimiters. . /Delimiters/ are the actual atomic objects that delimit parentheses. They may be shaped differently; for example, `[' is an opening square delimiter, and `{' is an opening curly delimiter. . The terms `bracket', `brace', and `brocket' (the latter a misspelling of `broket', for `broken bracket') are eschewed. The names `paredit-open-parenthesis' &c. have been changed to `paredit-open-round' &c., and likewise with `bracket' -> `square', `brace' -> `curly', and `brocket' -> `angled'. The old names with the morphemes `parenthesis' and `bracket' have been preserved so that users need not update initialization files immediately, but will instead be confused when a subsequent minor update breaks all their initialization files for no good reason. - Some commands now check their context more strictly, so that they won't try to do anything in comments or strings. Whether this is the right thing, I don't know. - Several small elements of style were adjusted. This should make no functional difference on the code. (If it does, then I have made a mistake.) - paredit.el no longer defines `kill-region-new'; the function is instead called `paredit-hack-kill-region', to reflect its hackish nature and to avoid name clashes. I believe that the only name remaining defined by paredit.el without `paredit-' prefixed is `backward-down-list', whose definition is so obvious that it would be silly to define it any other way, so that any name conflict does not worry me. (I suppose `kill-region-new' is obvious, too -- it is to `kill-region' as `kill-new' is to `kill'. But the omission from a pattern isn't as clear as with `backward-down-list'.) - `paredit-comment-dwim' should work in GNU Emacs 21 now, whose newcomment.el defines no `comment-or-uncomment-region'. More and more ugly compatibility hacks are accruing in paredit.el as a consequence, to my dismay. Oh well. - The release notes are now formatted more legibly, paginated nicely, and organized with Outline Mode. The gross discrepancy in writing style over the years has not changed. - The introductory comments in the file are more clearly written now. - Fixed a bug in S-expression slurpage with mixed delimiters. *** Altered Behaviour - The bindings for `)' and `M-)' have been exchanged: `)' is bound to `paredit-close-round' and `M-)' to `paredit-close-round-and-newline', so that you can now without any glaring exceptions type new code using the same keystrokes with and without Paredit Mode. You can also now paste into Emacs through a terminal without leaving spurious blank lines in the buffer. You are, of course, free to revert to the old behaviour in your .emacs file. - `paredit-semicolon' is no more. Now you may insert semicolons as you please without the infuriating escape of the following text on the line. - `paredit-move-past-close-and-newline' will now leave comments on the same line only if they are margin comments and no S-expressions are intervening between the point and the comment; that is, only if it looks like the margin comment really pertains to the expression being closed will paredit leave it on the same line. - `paredit-backward-delete', `paredit-forward-delete', and `paredit-kill' formerly accepted prefix arguments to run the basic `backward-delete-char', `delete-char', and `kill-line' commands instead, without regard for the value of the prefix argument. Now `C-u' will run the basic commands, but . `paredit-kill' will pass a numeric argument on to `kill-line', and . `paredit-backward-delete' and `paredit-forward-delete' will both delete N characters if given a numeric prefix argument N. (`paredit-kill' should probably do the same, but repeating the command N times is probably not what you want -- what you probably want is to kill at most N *lines*, but `paredit-kill' N times might kill many more lines than that. I don't know what the right thing is here, but I welcome feedback from users who want to do something like this.) - With a `C-u' prefix argument, `paredit-wrap-sexp' now wraps all S-expressions following the point until the end of the buffer or a closing delimiter. - `paredit-splice-sexp' with a `C-u' prefix argument (also known as `paredit-splice-sexp-killing-backward') will now kill from the point, rather than from the beginning of the next S-expression (or, with `C-u C-u', from the end of the previous S-expression). This means that it is more likely to do what you mean with (let ((a b) (c d)) |;; I want to preserve this comment after `C-u M-s'. (foo bar baz)) - `paredit-splice-sexp' now splices strings, by removing backslash escapes, or signals an error if doing so would destroy the structure of the buffer. - I have finally introduced the first bit of code to try to deal sensibly with broken buffers. It will probably go only downhill from here, and continue in an interminable series of kludges to handle every possible way in which the buffer can go *wrong* (it's bad enough how many ways it can be *right*). If you try type a closing delimiter from within a partial S-expression that has an opening delimiter but no closing delimiter, then it will honk at you and insert the closing delimiter -- or if what you typed doesn't match the opening delimiter, it will just honk and refuse to do anything. Also, `DEL' and `C-d' will delete spurious (but not [yet] mismatched) opening and closing delimiters, respectively. (Thanks to John Wiegley for inspiring me to do these dreary deeds.) *** New Commands - New command `paredit-yank-pop' cooperates with `paredit-wrap-sexp' by behaving either like `yank' or like `yank-pop' if the previous command was `paredit-wrap-sexp' or `paredit-yank-pop', and with the added bonus of reindenting the newly wrapped form. It is in need of a key to be bound to it; since it is different from both `yank' and `yank-pop', I decided not to override `C-y' or `M-y', and I considered `C-c C-y', but I imagine that many major modes want to take that. - New command `paredit-focus-on-defun' moves display to the top of the definition at the point. - New command `paredit-reindent-defun', which `M-q' is bound to in Paredit Mode, indents the definition the point is on, or, if the point is in a string or comment, fills the paragraph instead. (Thanks to John Wiegley for the idea.) - New variations on slurpage, barfage, and joinage. I'm still looking for keys to bind to these commands. Find them with the strings `add-to-{previous,next}-list' and `join-with-{previous,next}-list' in their names. (Again, thanks to John Wiegley for the idea.) - New command `paredit-convolute-sexp' performs the combined function of `paredit-splice-sexp-killing-backward', `paredit-wrap-sexp', and `yank'. Example: (let ((foo bar)) (let ((baz quux)) |(zot mumble) (frotz)) (gargle mumph)) -> (let ((baz quux)) (let ((foo bar)) (zot mumble) (frotz) (gargle mumph))) This would otherwise have been done with the key sequence `C-u M-s C-M-u M-( C-y C-M-u C-M-q'. `C-u M-s' could be `M-up', and `C-y C-M-u C-M-q' could be `C-c C-y' if that key is chosen for `paredit-yank-pop', making the sequence `M-up C-M-u M-( C-c C-y'. If there is a good key for `paredit-convolute-sexp', it could be a nice improvement over even that terser sequence. (Once again, this was inspired by John Wiegley's idea (and name).) [Observe, though, that the form (FROTZ) stuck with (ZOT MUMBLE) the whole time, and was not carried along as the `end' of the (LET ((BAZ QUUX)) ...) form. Hence this is *not* the idea mentioned below by the name `paredit-interchange-sexps', but a simpler approximation of the idea.] - `define-paredit-pair' now defines commands `paredit-wrap-...' for wrapping S-expressions with different delimiters, like `paredit-wrap-sexp'. The function `paredit-wrap-sexp' now accepts optional arguments for the delimiters to insert; the new commands are defined in terms of the modified `paredit-wrap-sexp'. `M-[' is now bound to `paredit-wrap-square'. darcs-hash:20080928134400-00fcc-618d1b2da41f6a67aaef69d72d89ce15379372cc
-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