summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaylor R Campbell <campbell@mumble.net>2008-09-28 13:42:28 +0000
committerTaylor R Campbell <campbell@mumble.net>2008-09-28 13:42:28 +0000
commitb033174024d8f004e2baf43b0b69fc254fdb5d1e (patch)
tree4974b011b2711ed358a227498d89fc5169b0cd79
parent7f89e54961795d8b8b3daa9bbb0254973bfa4191 (diff)
Changes for version 19.v19
This version introduces support for delimiters other than round brackets. Previously, if the major mode's syntax table considered square brackets (and curly braces, &c.) to be delimiters, since no paredit commands would insert them balanced, deleting them would be tricky: paredit's DEL & C-d bindings would refuse to delete them because they would be imbalanced unless you manually type the closing delimiter. Now commands are defined for the opening and closing of parentheses (round), brackets (square), braces (curly), and brockets (angled), named `paredit-open-<type>', `paredit-close-<type>-and-newline', and `paredit-close-<type>'; paredit-mode binds the opening and closing square bracket keys to be `paredit-open-bracket' and `paredit-close- bracket', respectively. The rest you can bind yourself; this minimal pair of bindings will, I think, account for accidental insertion, elisp vectors, and (bletch) the equation of square and round brackets as parentheses in some non-standard Scheme extensions. Also now supported in this version is insertion of delimiter pairs around active regions in transient-mark-mode. If you mark a region with transient-mark-mode enabled, you can use any of the delimiter pair insertion keys (like opening round bracket, double-quote, &c.) to insert a pair of delimiters around the region. There are now two ways to parenthesize lists of expressions with visual feedback: using M-( (paredit-wrap-sexp) followed by C-) (paredit-forward-slurp-sexp) until satisfied, and now C-M-SPC (mark-sexp) until you have marked the desired expressions and then any of the delimiter pair insertion keys to insert the delimiters. darcs-hash:20080928134228-00fcc-3ec209344dbaea07e93f2e02cddead13f7e39c6a
-rw-r--r--paredit.el348
1 files changed, 212 insertions, 136 deletions
diff --git a/paredit.el b/paredit.el
index e95ea6b..b8da727 100644
--- a/paredit.el
+++ b/paredit.el
@@ -1,9 +1,9 @@
;;; -*- Mode: Emacs-Lisp; outline-regexp: " \n;;;;+" -*-
;;;;;; Paredit: Parenthesis-Editing Minor Mode
-;;;;;; Version 18
+;;;;;; Version 19
-;;; This code is written by Taylor Campbell (except where explicitly
+;;; This code is written by Taylor R. Campbell (except where explicitly
;;; noted) and placed in the Public Domain. All warranties are
;;; disclaimed.
@@ -40,18 +40,33 @@
;;; than 21 or so.
;;; This mode changes the keybindings for a number of simple keys,
-;;; notably (, ), ", \, and ;. The round bracket keys are defined to
-;;; insert parenthesis pairs and move past the close, 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
+;;; notably (, ), ", \, and ;. The bracket keys (round or square) are
+;;; defined to insert parenthesis pairs and move past the close,
+;;; 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.
;;;
-;;; It also changes several standard editing keybindings including
+;;; 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:
+;;;
+;;; (define-key paredit-mode-map (kbd ")")
+;;; 'paredit-close-parenthesis)
+;;; (define-key paredit-mode-map (kbd "M-)")
+;;; 'paredit-close-parenthesis-and-newline)
+;;;
+;;; Paredit also changes several standard editing keybindings including
;;; RET, C-j, C-d, DEL, & C-k. RET & C-j are transposed from their
;;; usual paired meaning, where RET inserts a newline and C-j fancily
;;; adds a new line with indentation &c., but I find the transposition
@@ -80,7 +95,7 @@
;;; This assumes Unix-style LF line endings.
-(defconst paredit-version 18)
+(defconst paredit-version 19)
;;;; Minor Mode Definition
@@ -136,18 +151,30 @@ Deprecated: use `paredit-mode' instead."
(progn (setq paredit-commands
`(
"Basic Insertion Commands"
- ("(" paredit-open-list
+ ("(" paredit-open-parenthesis
("(a b |c d)"
"(a b (|) c d)")
("(foo \"bar |baz\" quux)"
"(foo \"bar (|baz\" quux)"))
- (")" paredit-close-list-and-newline
+ (")" paredit-close-parenthesis-and-newline
("(defun f (x| ))"
- "(defun f (x)\n |)"))
- ("M-)" paredit-close-list
+ "(defun f (x)\n |)")
+ ("; (Foo.|"
+ "; (Foo.)|"))
+ ("M-)" paredit-close-parenthesis
("(a b |c )" "(a b c)|")
("; Hello,| world!"
"; Hello,)| world!"))
+ ("[" paredit-open-bracket
+ ("(a b |c d)"
+ "(a b [|] c d)")
+ ("(foo \"bar |baz\" quux)"
+ "(foo \"bar [baz\" quux)"))
+ ("]" paredit-close-bracket
+ ("(define-key keymap [frob| ] 'frobnicate)"
+ "(define-key keymap [frob]| 'frobnicate)")
+ ("; [Bar.|"
+ "; [Bar.]|"))
("\"" paredit-doublequote
("(frob grovel |full lexical)"
"(frob grovel \"|\" full lexical)")
@@ -443,45 +470,62 @@ Deprecated: use `paredit-mode' instead."
" </tr>")))))
(insert "</table>\n"))
-;;;; Basic Editing Commands
+;;;; Delimiter Insertion
-(defun paredit-open-list (&optional n)
- "Insert a balanced parenthesis pair.
-With a prefix argument N, put the closing parentheses after N
+(eval-when-compile
+ (defun paredit-name (&rest strings)
+ (intern (apply 'concat strings)))
+ (defmacro define-paredit-pair (open close name)
+ `(progn
+ (defun ,(paredit-name "paredit-open-" name) (&optional n)
+ ,(concat "Insert a balanced " name " pair.
+With a prefix argument N, put the closing " name " after N
S-expressions forward.
-If in string or comment, insert a single opening parenthesis.
-If in a character literal, do nothing. This prevents accidentally
- changing what was in the character literal to a meaningful delimiter
- unintentionally."
- (interactive "P")
- (cond ((or (paredit-in-string-p)
- (paredit-in-comment-p))
- (insert "("))
- ((not (paredit-in-char-p))
- (insert-parentheses (or n 0)))))
-
-(defun paredit-close-list ()
- "Move past one closing parenthesis and reindent.
-If in a string or comment, insert a single closing parenthesis.
-If in a character literal, do nothing. This prevents accidentally
- changing what was in the character literal to a meaningful delimiter
- unintentionally."
- (interactive)
+If the region is active, `transient-mark-mode' is enabled, and the
+ region's start and end fall in the same parenthesis depth, insert a
+ " name " pair around the region.
+If in a string or a comment, insert a single " name ".
+If in a character literal, do nothing. This prevents changing what was
+ in the character literal to a meaningful delimiter unintentionally.")
+ (interactive "P")
+ (cond ((or (paredit-in-string-p)
+ (paredit-in-comment-p))
+ (insert ,open))
+ ((not (paredit-in-char-p))
+ (paredit-insert-pair n ,open ,close 'goto-char))))
+ (defun ,(paredit-name "paredit-close-" name) ()
+ ,(concat "Move past one closing delimiter and reindent.
+\(Agnostic to the specific closing delimiter.)
+If in a string or comment, insert a single closing " name ".
+If in a character literal, do nothing. This prevents changing what was
+ in the character literal to a meaningful delimiter unintentionally.")
+ (interactive)
+ (paredit-move-past-close ,close))
+ (defun ,(paredit-name "paredit-close-" name "-and-newline") ()
+ ,(concat "Move past one closing delimiter, add a newline,"
+ " and reindent.
+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")
+
+(defun paredit-move-past-close (close)
(cond ((or (paredit-in-string-p)
(paredit-in-comment-p))
- (insert ")"))
+ (insert close))
((not (paredit-in-char-p))
(paredit-move-past-close-and-reindent)
(paredit-blink-paren-match nil))))
-(defun paredit-close-list-and-newline ()
- "Move past one closing delimiter, add a newline, and reindent.
-If there was a margin comment after the closing delimiter, preserve
- the margin comment on the same line."
- (interactive)
+(defun paredit-move-past-close-and-newline (close)
(cond ((or (paredit-in-string-p)
(paredit-in-comment-p))
- (insert ")"))
+ (insert close))
(t (if (paredit-in-char-p) (forward-char))
(paredit-move-past-close-and-reindent)
(let ((comment.point (paredit-find-comment-on-line)))
@@ -519,13 +563,57 @@ If such a comment exists, delete the comment (including all leading
(cons comment (- start (point-at-bol))))))
(throw 'return nil))))))
+(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))))))
+ (if (or n regionp) (paredit-skip-whitespace t))
+ (let ((spacep (paredit-space-for-delimiter-p nil open)))
+ (if spacep (insert " "))
+ (insert open)
+ (save-excursion
+ ;; Move past the desired region.
+ (cond (n (funcall forward (save-excursion (forward-sexp n)
+ (point))))
+ (regionp (funcall forward (+ end (if spacep 2 1)))))
+ (insert close)
+ (if (paredit-space-for-delimiter-p t close)
+ (insert " "))))))
+
+(defun paredit-region-safe-for-insert-p ()
+ (save-excursion
+ (let ((beginning (region-beginning))
+ (end (region-end)))
+ (goto-char beginning)
+ (let* ((beginning-state (paredit-current-parse-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
+ (nth 3 end-state)) ; string
+ (eq (nth 4 beginning-state) ; 4. comment status, yada
+ (nth 4 end-state))
+ (eq (nth 5 beginning-state) ; 5. t if following char
+ (nth 5 end-state))))))) ; quote
+
+(defun paredit-space-for-delimiter-p (endp delimiter)
+ ;; If at the buffer limit, don't insert a space. If there is a word,
+ ;; symbol, other quote, or non-matching parenthesis delimiter (i.e. a
+ ;; 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)))
+ (list ?w ?_ ?\"
+ (let ((matching (matching-paren delimiter)))
+ (and matching (char-syntax matching)))))))
+
(defun paredit-move-past-close-and-reindent ()
- "Move one character past the next closing parenthesis.
-Delete extraneous whitespace before the closing parenthesis. Do not
- delete comments, however; if there is a comment between the point and
- the next closing parenthesis, move the closing parenthesis to the
- line after the comment and indent appropriately."
- (interactive)
(let ((orig (point)))
(up-list)
(if (catch 'return ; This CATCH returns T if it
@@ -577,6 +665,10 @@ Delete extraneous whitespace before the closing parenthesis. Do not
"Insert a pair of double-quotes.
With a prefix argument N, wrap the following N S-expressions in
double-quotes, escaping intermediate characters if necessary.
+If the region is active, `transient-mark-mode' is enabled, and the
+ region's start and end fall in the same parenthesis depth, insert a
+ pair of double-quotes around the region, again escaping intermediate
+ characters if necessary.
Inside a comment, insert a literal double-quote.
At the end of a string, move past the closing double-quote.
In the middle of a string, insert a backslash-escaped double-quote.
@@ -592,26 +684,13 @@ If in a character literal, do nothing. This prevents accidentally
((paredit-in-comment-p)
(insert ?\" ))
((not (paredit-in-char-p))
- (if n (paredit-skip-whitespace t))
- (let* ((end (and n (save-excursion (paredit-forward-for-quote
- (prefix-numeric-value n))
- (point))))
- (spacep (paredit-space-for-quote-p nil ?\) )))
- (if spacep (insert " "))
- (insert ?\" )
- (save-excursion
- ;; Move past the S-expressions we counted, if we were to
- ;; count them. Account for the quote and optionally the
- ;; space, which we just inserted.
- (if n (goto-char (+ end 1 (if spacep 1 0))))
- (insert ?\" )
- (if (paredit-space-for-quote-p t ?\( )
- (insert " ")))))))
+ (paredit-insert-pair n ?\" ?\" 'paredit-forward-for-quote))))
(defun paredit-meta-doublequote (&optional n)
"Move to the end of the string, insert a newline, and indent.
If not in a string, act as `paredit-doublequote'; if no prefix argument
- is specified, the default is to wrap one S-expression, however, not
+ is specified and the region is not active or `transient-mark-mode' is
+ disabled, the default is to wrap one S-expression, however, not
zero."
(interactive "p")
(if (not (paredit-in-string-p))
@@ -622,25 +701,13 @@ If not in a string, act as `paredit-doublequote'; if no prefix argument
(lisp-indent-line)
(condition-case () (indent-sexp)
(scan-error nil)))))
-
-(defun paredit-space-for-quote-p (endp delim-syn)
- ;; If at the buffer limit, don't insert a space. If there is a word,
- ;; symbol, other quote, or non-matching parenthesis delimiter (i.e. a
- ;; 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)))
- (list ?w ?_ ?\" delim-syn))))
-(defun paredit-forward-for-quote (n)
- (let ((end (save-excursion (forward-sexp n) (point)))
- (state (paredit-current-parse-state)))
+(defun paredit-forward-for-quote (end)
+ (let ((state (paredit-current-parse-state)))
(while (< (point) end)
(let ((new-state (parse-partial-sexp (point) (1+ (point))
nil nil state)))
- (if (not (paredit-in-string-p new-state))
+ (if (paredit-in-string-p new-state)
(if (not (paredit-in-string-escape-p))
(setq state new-state)
;; Escape character: turn it into an escaped escape
@@ -666,6 +733,8 @@ If not in a string, act as `paredit-doublequote'; if no prefix argument
;; Advance the end point for the same reason as above.
(setq end (1+ end)))))))
+;;;; Escape Insertion
+
(defun paredit-backslash ()
"Insert a backslash followed by a character to escape."
(interactive)
@@ -698,7 +767,29 @@ If not in a string, act as `paredit-doublequote'; if no prefix argument
(insert char) ; (Is there a better way to
nil)) ; express the rubout char?
; ?\^? works, but ugh...)
+
+;;; The placement of this function in this file is totally random.
+
+(defun paredit-newline ()
+ "Insert a newline and indent it.
+This is like `newline-and-indent', but it not only indents the line
+ that the point is on but also the S-expression following the point,
+ if there is one.
+Move forward one character first if on an escaped character.
+If in a string, just insert a literal newline."
+ (interactive)
+ (if (paredit-in-string-p)
+ (newline)
+ (if (and (not (paredit-in-comment-p)) (paredit-in-char-p))
+ (forward-char))
+ (newline-and-indent)
+ ;; Indent the following S-expression, but don't signal an error if
+ ;; there's only a closing parenthesis after the point.
+ (condition-case () (indent-sexp)
+ (scan-error nil))))
+;;;; 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
@@ -743,7 +834,7 @@ At the top level, where indentation is calculated to be at column 0,
semicolon margin comment at `comment-column'."
(interactive "*P")
(comment-normalize-vars)
- (cond ((and mark-active transient-mark-mode)
+ (cond ((paredit-region-active-p)
(comment-or-uncomment-region (region-beginning)
(region-end)
arg))
@@ -805,27 +896,9 @@ At the top level, where indentation is calculated to be at column 0,
(progn (indent-to comment-column
1) ; 1 -> force one leading space
(insert ?\; ))))))
-
-;;; The placement of this function in this file is totally random.
-
-(defun paredit-newline ()
- "Insert a newline and indent it.
-This is like `newline-and-indent', but it not only indents the line
- that the point is on but also the S-expression following the point,
- if there is one.
-Move forward one character first if on an escaped character.
-If in a string, just insert a literal newline."
- (interactive)
- (if (paredit-in-string-p)
- (newline)
- (if (and (not (paredit-in-comment-p)) (paredit-in-char-p))
- (forward-char))
- (newline-and-indent)
- ;; Indent the following S-expression, but don't signal an error if
- ;; there's only a closing parenthesis after the point.
- (condition-case () (indent-sexp)
- (scan-error nil))))
+;;;; Character Deletion
+
(defun paredit-forward-delete (&optional arg)
"Delete a character forward or move forward over a delimiter.
If on an opening S-expression delimiter, move forward into the
@@ -943,6 +1016,8 @@ With a prefix argument, simply delete a character backward, without
(backward-delete-char 1)
(delete-char 1)))))
+;;;; Killing
+
(defun paredit-kill (&optional arg)
"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
@@ -984,14 +1059,14 @@ Otherwise, kill all S-expressions that start after the point."
(if (paredit-in-char-p) ; Move past the \ and prefix.
(backward-char 2)) ; (# in Scheme/CL, ? in elisp)
(let ((beginning (point))
- (eol (point-at-eol))
- (end-of-list-p (paredit-forward-sexps-to-kill)))
- ;; If we got to the end of the list and it's on the same line,
- ;; move backward past the closing delimiter before killing. (This
- ;; allows something like killing the whitespace in ( ).)
- (if end-of-list-p (progn (up-list) (backward-char)))
- (if kill-whole-line
- (paredit-kill-sexps-on-whole-line beginning)
+ (eol (point-at-eol)))
+ (let ((end-of-list-p (paredit-forward-sexps-to-kill beginning eol)))
+ ;; If we got to the end of the list and it's on the same line,
+ ;; move backward past the closing delimiter before killing. (This
+ ;; allows something like killing the whitespace in ( ).)
+ (if end-of-list-p (progn (up-list) (backward-char)))
+ (if kill-whole-line
+ (paredit-kill-sexps-on-whole-line beginning)
(kill-region beginning
;; If all of the S-expressions were on one line,
;; i.e. we're still on that line after moving past
@@ -1002,30 +1077,28 @@ Otherwise, kill all S-expressions that start after the point."
(if (and (not end-of-list-p)
(eq (point-at-eol) eol))
eol
- (point))))))
+ (point)))))))
-(defun paredit-forward-sexps-to-kill ()
- (let ((beginning (point))
- (eol (point-at-eol))
- (end-of-list-p nil))
+(defun paredit-forward-sexps-to-kill (beginning eol)
+ (let ((end-of-list-p nil))
;; Move to the end of the last S-expression that started on this
;; line, or to the closing delimiter if the last S-expression in
;; this list is on the line.
(catch 'return
- (while (and (not (eobp))
- (save-excursion
- (condition-case ()
- (forward-sexp)
- (scan-error
- (up-list)
- (setq end-of-list-p (eq (point-at-eol) eol))
- (throw 'return nil)))
- ;; We have to deal with a weird special case here
- ;; of kill
- (and (condition-case ()
- (progn (backward-sexp) t)
- (scan-error nil))
- (eq (point-at-eol) eol))))
+ (while t
+ (save-excursion
+ (condition-case ()
+ (forward-sexp)
+ (scan-error
+ (up-list)
+ (setq end-of-list-p (eq (point-at-eol) eol))
+ (throw 'return nil)))
+ (if (or (eobp)
+ (not (condition-case ()
+ (progn (backward-sexp) t)
+ (scan-error nil)))
+ (not (eq (point-at-eol) eol)))
+ (throw 'return nil)))
(forward-sexp)))
end-of-list-p))
@@ -1180,7 +1253,7 @@ With a prefix argument N, encompass all N S-expressions forward."
(goto-line (+ start-line (/ lines-on-sexps 2)))
(recenter)))))
-;;;; Wrappage, Splicage, & Raisage
+;;;; Depth-Changing Commands: Wrapping, Splicing, & Raising
(defun paredit-wrap-sexp (&optional n)
"Wrap the following S-expression in a list.
@@ -1188,17 +1261,16 @@ If a 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 structural
- validity."
+ and then signal an error, in the interest of preserving structure."
(interactive "p")
(condition-case ()
- (insert-parentheses (or n 1))
+ (paredit-insert-pair (or n 1) ?\( ?\) 'goto-char)
(scan-error (insert ?\) )
(backward-char)))
(save-excursion (backward-up-list) (indent-sexp)))
;;; Thanks to Marco Baringer for the suggestion of a prefix argument
-;;; for PAREDIT-SPLICE-SEXP. (I, Taylor Campbell, however, still
+;;; 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.)
@@ -1226,7 +1298,7 @@ With a numerical prefix argument N, kill N S-expressions backward in
(progn (backward-up-list) ; Reindent, now that the
(indent-sexp)) ; structure has changed.
(scan-error nil))))
-
+
(defun paredit-kill-surrounding-sexps-for-splice (arg)
(if (and arg (not (eq arg 0)))
(cond ((numberp arg)
@@ -1257,7 +1329,7 @@ With a numerical prefix argument N, kill N S-expressions backward in
(scan-error nil))
(kill-region beginning (point))))))
(t (error "Bizarre prefix argument: %s" arg)))))
-
+
(defun paredit-splice-sexp-killing-backward (&optional n)
"Splice the list the point is on by removing its delimiters, and
also kill all S-expressions before the point in the current list.
@@ -1501,6 +1573,10 @@ If TRAILING-P is nil, skip leading whitespace; otherwise, skip trailing
(funcall (if trailing-p #'skip-chars-forward #'skip-chars-backward)
" \t\n " ; This should skip using the syntax table, but LF
limit)) ; is a comment end, not newline, in Lisp mode.
+
+(defun paredit-region-active-p ()
+ "Return T if the region is active and NIL if not."
+ (and mark-active transient-mark-mode))
;;;;; S-expression Parsing Utilities