diff --git a/elpa-to-submit/paredit.el b/elpa-to-submit/paredit.el index b59754aec2..2794f9026b 100644 --- a/elpa-to-submit/paredit.el +++ b/elpa-to-submit/paredit.el @@ -1,41 +1,24 @@ -;;; -*- Mode: Emacs-Lisp; outline-regexp: " \n;;;;+" -*- - -;;;;;; Paredit: Parenthesis-Editing Minor Mode -;;;;;; Version 22 (beta) - -;;; NOTE: THIS IS A BETA VERSION OF PAREDIT. USE AT YOUR OWN RISK. -;;; THIS FILE IS SUBJECT TO CHANGE, AND NOT SUITABLE FOR DISTRIBUTION -;;; BY PACKAGE MANAGERS SUCH AS APT, PKGSRC, MACPORTS, &C. - -;;; Copyright (c) 2008, 2009, 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. +;;; paredit.el --- minor mode for editing parentheses -*- Mode: Emacs-Lisp -*- + +;; Copyright (C) 2005--2010 Taylor R. Campbell + +;; Author: Taylor R. Campbell +;; Version: 22 +;; Created: 2005-07-31 +;; Keywords: lisp + +;; Paredit is free software: you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Paredit is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with paredit. If not, see . ;;; This file is permanently stored at ;;; . @@ -53,14 +36,14 @@ ;;; 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." +;;; (autoload 'enable-paredit-mode "paredit" +;;; "Turn on pseudo-structural editing of Lisp code." ;;; t) ;;; -;;; Toggle Paredit Mode with `M-x paredit-mode RET', or enable it -;;; always in a major mode `M' (e.g., `lisp' or `scheme') with: +;;; Enable Paredit Mode on the fly with `M-x enable-paredit-mode RET', +;;; or always enable it in a major mode `M' (e.g., `lisp') with: ;;; -;;; (add-hook M-mode-hook (lambda () (paredit-mode +1))) +;;; (add-hook M-mode-hook 'enable-paredit-mode) ;;; ;;; Customize paredit using `eval-after-load': ;;; @@ -101,8 +84,8 @@ ;;; 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 +;;; with commands such as `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. @@ -179,7 +162,7 @@ ;;; This assumes Unix-style LF line endings. (defconst paredit-version 22) -(defconst paredit-beta-p t) +(defconst paredit-beta-p nil) (eval-and-compile @@ -262,19 +245,13 @@ Paredit behaves badly if parentheses are imbalanced, so exercise (error (setq paredit-mode nil) (signal (car condition) (cdr condition))))))) -;;; Old functions from when there was a different mode for emacs -nw. - (defun enable-paredit-mode () - "Turn on pseudo-structural editing of Lisp code. - -Deprecated: use `paredit-mode' instead." + "Turn on pseudo-structural editing of Lisp code." (interactive) (paredit-mode +1)) (defun disable-paredit-mode () - "Turn off pseudo-structural editing of Lisp code. - -Deprecated: use `paredit-mode' instead." + "Turn off pseudo-structural editing of Lisp code." (interactive) (paredit-mode -1)) @@ -344,6 +321,15 @@ Deprecated: use `paredit-mode' instead." "(string #\\x|)") ("\"foo|bar\"\n ; Escaping character... (\")" "\"foo\\\"|bar\"")) + (";" paredit-semicolon + ("|(frob grovel)" + ";|(frob grovel)") + ("(frob |grovel)" + "(frob ;grovel\n)") + ("(frob |grovel (bloit\n zargh))" + "(frob ;|grovel\n (bloit\n zargh))") + ("(frob grovel) |" + "(frob grovel) ;|")) ("M-;" paredit-comment-dwim ("(foo |bar) ; baz" "(foo bar) ; |baz") @@ -415,10 +401,10 @@ Deprecated: use `paredit-mode' instead." "(foo |(bar baz) quux)") ("(|(foo) bar)" "|((foo) bar)")) -;;;("C-M-u" backward-up-list) ; These two are built-in. -;;;("C-M-d" down-list) - ("C-M-p" backward-down-list) ; Built-in, these are FORWARD- - ("C-M-n" up-list) ; & BACKWARD-LIST, which have + ("C-M-u" paredit-backward-up) + ("C-M-d" paredit-forward-down) + ("C-M-p" paredit-backward-down) ; Built-in, these are FORWARD- + ("C-M-n" paredit-forward-up) ; & BACKWARD-LIST, which have ; no need given C-M-f & C-M-b. "Depth-Changing Commands" @@ -623,11 +609,6 @@ Deprecated: use `paredit-mode' instead." ;;;; Delimiter Insertion -(defvar paredit-space-delimiter-chars (list ?w ?_ ?\") - "Characters for which a space should be inserted between them -and a delimiter. Set this to (list ?\\\") as a buffer-local value -for non-lisp languages.") - (eval-and-compile (defun paredit-conc-name (&rest strings) (intern (apply 'concat strings))) @@ -782,6 +763,22 @@ If such a comment exists, delete the comment (including all leading (eq (nth 5 beginning-state) ; 5. t if following char (nth 5 end-state))))))) ; quote +(defvar paredit-space-for-delimiter-predicates nil + "List of predicates for whether to put space by delimiter at point. +Each predicate is a function that is is applied to two arguments, ENDP + and DELIMITER, and that returns a boolean saying whether to put a + space next to the delimiter -- before the delimiter if ENDP is false, + after the delimiter if ENDP is true. +If any predicate returns false, no space is inserted: every predicate + has veto power. +Each predicate may assume that the point is not at the beginning of the + buffer, if ENDP is false, or at the end of the buffer, if ENDP is + true; and that the point is not preceded, if ENDP is false, or + followed, if ENDP is true, by a word or symbol constituent, a quote, + or the delimiter matching DELIMITER. +Each predicate should examine only text before the point, if ENDP is + false, or only text after the point, if ENDP is true.") + (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 @@ -789,9 +786,17 @@ If such a comment exists, delete the comment (including all leading ;; close the string), do insert a space. (and (not (if endp (eobp) (bobp))) (memq (char-syntax (if endp (char-after) (char-before))) - (append paredit-space-delimiter-chars - (list (let ((matching (matching-paren delimiter))) - (and matching (char-syntax matching)))))))) + (list ?w ?_ ?\" + (let ((matching (matching-paren delimiter))) + (and matching (char-syntax matching))) + (and (not endp) + (eq ?\" (char-syntax delimiter)) + ?\) ))) + (catch 'exit + (dolist (predicate paredit-space-for-delimiter-predicates) + (if (not (funcall predicate endp delimiter)) + (throw 'exit nil))) + t))) (defun paredit-move-past-close-and-reindent (close) (let ((open (paredit-missing-close))) @@ -802,30 +807,29 @@ If such a comment exists, delete the comment (including all leading (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 - (while t ; should delete leading spaces - (save-excursion ; and NIL if not. - (let ((before-paren (1- (point)))) - (back-to-indentation) - (cond ((not (eq (point) before-paren)) - ;; Can't call PAREDIT-DELETE-LEADING-WHITESPACE - ;; here -- we must return from SAVE-EXCURSION - ;; first. - (throw 'return t)) - ((save-excursion (forward-line -1) - (end-of-line) - (paredit-in-comment-p)) - ;; Moving the closing delimiter any further - ;; would put it into a comment, so we just - ;; 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)))) + (up-list) + (if (catch 'return ; This CATCH returns T if it + (while t ; should delete leading spaces + (save-excursion ; and NIL if not. + (let ((before-paren (1- (point)))) + (back-to-indentation) + (cond ((not (eq (point) before-paren)) + ;; Can't call PAREDIT-DELETE-LEADING-WHITESPACE + ;; here -- we must return from SAVE-EXCURSION + ;; first. + (throw 'return t)) + ((save-excursion (forward-line -1) + (end-of-line) + (paredit-in-comment-p)) + ;; Moving the closing delimiter any further + ;; would put it into a comment, so we just + ;; 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 @@ -965,25 +969,31 @@ 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 these functions 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." +If in a string, just insert a literal newline. +If in a comment and if followed by invalid structure, call + `indent-new-comment-line' to keep the invalid structure in a + comment." (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 delimiter after the point. - (paredit-ignore-sexp-errors (indent-sexp)))) + (cond ((paredit-in-string-p) + (newline)) + ((paredit-in-comment-p) + (if (paredit-region-ok-p (point) (point-at-eol)) + (progn (newline-and-indent) (indent-sexp)) + (indent-new-comment-line))) + (t + (if (paredit-in-char-p) + (forward-char)) + (newline-and-indent) + ;; Indent the following S-expression, but don't signal an + ;; error if there's only a closing delimiter after the point. + (paredit-ignore-sexp-errors (indent-sexp))))) (defun paredit-reindent-defun (&optional argument) "Reindent the definition that the point is on. @@ -1000,6 +1010,64 @@ If the point is in a string or a comment, fill the paragraph instead, ;;;; Comment Insertion +(defun paredit-semicolon (&optional n) + "Insert a semicolon. +With a prefix argument N, insert N semicolons. +If in a string, do just that and nothing else. +If in a character literal, move to the beginning of the character + literal before inserting the semicolon. +If the enclosing list ends on the line after the point, break the line + after the last S-expression following the point. +If a list begins on the line after the point but ends on a different + line, break the line after the last S-expression following the point + before the list." + (interactive "p") + (if (or (paredit-in-string-p) (paredit-in-comment-p)) + (insert (make-string (or n 1) ?\; )) + (if (paredit-in-char-p) + (backward-char 2)) + (let ((line-break-point (paredit-semicolon-find-line-break-point))) + (if line-break-point + (paredit-semicolon-with-line-break line-break-point (or n 1)) + (insert (make-string (or n 1) ?\; )))))) + +(defun paredit-semicolon-find-line-break-point () + (let ((line-break-point nil) + (eol (point-at-eol))) + (and (save-excursion + (paredit-handle-sexp-errors + (progn + (while + (progn + (setq line-break-point (point)) + (forward-sexp) + (and (eq eol (point-at-eol)) + (not (eobp))))) + (backward-sexp) + (eq eol (point-at-eol))) + ;; If we hit the end of an expression, but the closing + ;; delimiter is on another line, don't break the line. + (save-excursion + (paredit-skip-whitespace t (point-at-eol)) + (not (or (eolp) (eq (char-after) ?\; )))))) + line-break-point))) + +(defun paredit-semicolon-with-line-break (line-break-point n) + (let ((line-break-marker (make-marker))) + (set-marker line-break-marker line-break-point) + (set-marker-insertion-type line-break-marker t) + (insert (make-string (or n 1) ?\; )) + (save-excursion + (goto-char line-break-marker) + (set-marker line-break-marker nil) + (newline) + (lisp-indent-line) + ;; This step is redundant if we are inside a list, but even if we + ;; are at the top level, we want at least to indent whatever we + ;; bumped off the line. + (paredit-ignore-sexp-errors (indent-sexp)) + (paredit-indent-sexps)))) + ;;; This is all a horrible, horrible hack, primarily for GNU Emacs 21, ;;; in which there is no `comment-or-uncomment-region'. @@ -1094,7 +1162,7 @@ This is expected to be called only in `paredit-comment-dwim'; do not (save-excursion (newline) (lisp-indent-line) - (indent-sexp)))) + (paredit-indent-sexps)))) (t ;; Margin comment (indent-to comment-column 1) ; 1 -> force one leading space @@ -1260,12 +1328,13 @@ With a numeric prefix argument N, do `kill-line' that many times." (kill-line (if (integerp argument) argument 1))) ((paredit-in-string-p) (paredit-kill-line-in-string)) - ((or (paredit-in-comment-p) - (save-excursion - (paredit-skip-whitespace t (point-at-eol)) - (or (eq (char-after) ?\; ) - (eolp)))) + ((paredit-in-comment-p) + (kill-line)) + ((save-excursion (paredit-skip-whitespace t (point-at-eol)) + (or (eolp) (eq (char-after) ?\; ))) ;** Be careful about trailing backslashes. + (if (paredit-in-char-p) + (backward-char)) (kill-line)) (t (paredit-kill-sexps-on-line)))) @@ -1277,14 +1346,9 @@ With a numeric prefix argument N, do `kill-line' that many times." ;; Be careful not to split an escape sequence. (if (paredit-in-string-escape-p) (backward-char)) - (let ((beginning (point))) - (while (not (or (eolp) - (eq (char-after) ?\" ))) - (forward-char) - ;; Skip past escaped characters. - (if (eq (char-before) ?\\ ) - (forward-char))) - (kill-region beginning (point)))))) + (kill-region (point) + (min (point-at-eol) + (cdr (paredit-string-start+end-points))))))) (defun paredit-kill-sexps-on-line () (if (paredit-in-char-p) ; Move past the \ and prefix. @@ -1476,6 +1540,28 @@ With a numeric prefix argument N, do `kill-line' that many times." (defun paredit-copy-as-kill () "Save in the kill ring the region that `paredit-kill' would kill." (interactive) + (cond ((paredit-in-string-p) + (paredit-copy-as-kill-in-string)) + ((paredit-in-comment-p) + (copy-region-as-kill (point) (point-at-eol))) + ((save-excursion (paredit-skip-whitespace t (point-at-eol)) + (or (eolp) (eq (char-after) ?\; ))) + ;** Be careful about trailing backslashes. + (save-excursion + (if (paredit-in-char-p) + (backward-char)) + (copy-region-as-kill (point) (point-at-eol)))) + (t (paredit-copy-sexps-as-kill)))) + +(defun paredit-copy-as-kill-in-string () + (save-excursion + (if (paredit-in-string-escape-p) + (backward-char)) + (copy-region-as-kill (point) + (min (point-at-eol) + (cdr (paredit-string-start+end-points)))))) + +(defun paredit-copy-sexps-as-kill () (save-excursion (if (paredit-in-char-p) (backward-char 2)) @@ -1496,6 +1582,105 @@ With a numeric prefix argument N, do `kill-line' that many times." (t (point)))))))) +;;;; Safe Region Killing/Copying + +;;; This is an experiment. It's not enough: `paredit-kill-ring-save' +;;; is always safe; it's `yank' that's not safe, but even trickier to +;;; implement than `paredit-kill-region'. Also, the heuristics for +;;; `paredit-kill-region' are slightly too conservative -- they will +;;; sometimes reject killing regions that would be safe to kill. +;;; (Consider, e,g., a region that starts in a comment and ends in the +;;; middle of a symbol at the end of a line: that's safe to kill, but +;;; `paredit-kill-region' won't allow it.) I don't know whether they +;;; are too liberal: I haven't constructed a region that is unsafe to +;;; kill but which `paredit-kill-region' will kill, but I haven't ruled +;;; out the possibility either. + +(defun paredit-kill-ring-save (beginning end) + "Save the balanced region, but don't kill it, like `kill-ring-save'. +If the text of the region is imbalanced, signal an error instead. +With a prefix argument, disregard any imbalance." + (interactive "r") + (if (not current-prefix-arg) + (paredit-check-region beginning end)) + (setq this-command 'kill-ring-save) + (kill-ring-save beginning end)) + +(defun paredit-kill-region (beginning end &optional yank-handler) + "Kill balanced text between point and mark, like `kill-region'. +If that text is imbalanced, signal an error instead." + (interactive "r") + (if (and beginning end) + ;; Check that region begins and ends in a sufficiently similar + ;; state, so that deleting it will leave the buffer balanced. + (save-excursion + (goto-char beginning) + (let* ((state (paredit-current-parse-state)) + (state* (parse-partial-sexp beginning end nil nil state))) + (paredit-check-region-state state state*)))) + (setq this-command 'kill-region) + (kill-region beginning end yank-handler)) + +(defun paredit-check-region-state (beginning-state end-state) + (paredit-check-region-state-depth beginning-state end-state) + (paredit-check-region-state-string beginning-state end-state) + (paredit-check-region-state-comment beginning-state end-state) + (paredit-check-region-state-char-quote beginning-state end-state)) + +(defun paredit-check-region-state-depth (beginning-state end-state) + (let ((beginning-depth (nth 0 beginning-state)) + (end-depth (nth 0 end-state))) + (if (not (= beginning-depth end-depth)) + (error "Mismatched parenthesis depth: %S at start, %S at end." + beginning-depth + end-depth)))) + +(defun paredit-check-region-state-string (beginning-state end-state) + (let ((beginning-string-p (nth 3 beginning-state)) + (end-string-p (nth 3 end-state))) + (if (not (eq beginning-string-p end-string-p)) + (error "Mismatched string state: start %sin string, end %sin string." + (if beginning-string-p "" "not ") + (if end-string-p "" "not "))))) + +(defun paredit-check-region-state-comment (beginning-state end-state) + (let ((beginning-comment-state (nth 4 beginning-state)) + (end-comment-state (nth 4 end-state))) + (if (not (or (eq beginning-comment-state end-comment-state) + (and (eq beginning-comment-state nil) + (eq end-comment-state t) + (eolp)))) + (error "Mismatched comment state: %s" + (cond ((and (integerp beginning-comment-state) + (integerp end-comment-state)) + (format "depth %S at start, depth %S at end." + beginning-comment-state + end-comment-state)) + ((integerp beginning-comment-state) + "start in nested comment, end otherwise.") + ((integerp end-comment-state) + "end in nested comment, start otherwise.") + (beginning-comment-state + "start in comment, end not in comment.") + (end-comment-state + "end in comment, start not in comment.") + (t + (format "start %S, end %S." + beginning-comment-state + end-comment-state))))))) + +(defun paredit-check-region-state-char-quote (beginning-state end-state) + (let ((beginning-char-quote (nth 5 beginning-state)) + (end-char-quote (nth 5 end-state))) + (if (not (eq beginning-char-quote end-char-quote)) + (let ((phrase "character quotation")) + (error "Mismatched %s: start %sin %s, end %sin %s." + phrase + (if beginning-char-quote "" "not ") + phrase + (if end-char-quote "" "not ") + phrase))))) + ;;;; Cursor and Screen Movement (eval-and-compile @@ -1558,6 +1743,103 @@ With a prefix argument N, encompass all N S-expressions forward." (beginning-of-defun) (recenter 0)) +;;;; Generalized Upward/Downward Motion + +(defun paredit-up/down (n vertical-direction) + (let ((horizontal-direction (if (< 0 n) +1 -1))) + (while (/= n 0) + (goto-char + (paredit-next-up/down-point horizontal-direction vertical-direction)) + (setq n (- n horizontal-direction))))) + +(defun paredit-next-up/down-point (horizontal-direction vertical-direction) + (let ((state (paredit-current-parse-state)) + (scan-lists + (lambda () + (scan-lists (point) horizontal-direction vertical-direction)))) + (cond ((paredit-in-string-p state) + (let ((start+end (paredit-string-start+end-points state))) + (if (< 0 vertical-direction) + (if (< 0 horizontal-direction) + (+ 1 (cdr start+end)) + (car start+end)) + ;; We could let the user try to descend into lists + ;; within the string, but that would be asymmetric + ;; with the up case, which rises out of the whole + ;; string and not just out of a list within the + ;; string, so this case will just be an error. + (error "Can't descend further into string.")))) + ((< 0 vertical-direction) + ;; When moving up, just try to rise up out of the list. + (or (funcall scan-lists) + (buffer-end horizontal-direction))) + ((< vertical-direction 0) + ;; When moving down, look for a string closer than a list, + ;; and use that if we find it. + (let* ((list-start + (paredit-handle-sexp-errors (funcall scan-lists) nil)) + (string-start + (paredit-find-next-string-start horizontal-direction + list-start))) + (if (and string-start list-start) + (if (< 0 horizontal-direction) + (min string-start list-start) + (max string-start list-start)) + (or string-start + ;; Scan again: this is a kludgey way to report the + ;; error if there really was one. + (funcall scan-lists) + (buffer-end horizontal-direction))))) + (t + (error "Vertical direction must be nonzero in `%s'." + 'paredit-up/down))))) + +(defun paredit-find-next-string-start (horizontal-direction limit) + (let ((next-char (if (< 0 horizontal-direction) 'char-after 'char-before)) + (pastp (if (< 0 horizontal-direction) '< '>))) + (paredit-handle-sexp-errors + (save-excursion + (catch 'exit + (while t + (if (and limit (funcall pastp (point) limit)) + (throw 'exit nil)) + (forward-sexp horizontal-direction) + (save-excursion + (backward-sexp horizontal-direction) + (if (eq ?\" (char-syntax (funcall next-char))) + (throw 'exit (+ (point) horizontal-direction))))))) + nil))) + +(defun paredit-forward-down (&optional argument) + "Move forward down into a list. +With a positive argument, move forward down that many levels. +With a negative argument, move backward down that many levels." + (interactive "p") + (paredit-up/down (or argument +1) -1)) + +(defun paredit-backward-up (&optional argument) + "Move backward up out of the enclosing list. +With a positive argument, move backward up that many levels. +With a negative argument, move forward up that many levels. +If in a string initially, that counts as one level." + (interactive "p") + (paredit-up/down (- 0 (or argument +1)) +1)) + +(defun paredit-forward-up (&optional argument) + "Move forward up out of the enclosing list. +With a positive argument, move forward up that many levels. +With a negative argument, move backward up that many levels. +If in a string initially, that counts as one level." + (interactive "p") + (paredit-up/down (or argument +1) +1)) + +(defun paredit-backward-down (&optional argument) + "Move backward down into a list. +With a positive argument, move backward down that many levels. +With a negative argument, move forward down that many levels." + (interactive "p") + (paredit-up/down (- 0 (or argument +1)) -1)) + ;;;; Depth-Changing Commands: Wrapping, Splicing, & Raising (defun paredit-wrap-sexp (&optional argument open close) @@ -1646,16 +1928,23 @@ Inside a string, unescape all backslashes, or signal an error if doing (interactive "P") (if (paredit-in-string-p) (paredit-splice-string argument) - (save-excursion - (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. + (paredit-kill-surrounding-sexps-for-splice argument) + (let ((end (point))) + (backward-up-list) ; Go up to the beginning... + (save-excursion + (forward-char 1) ; (Skip over leading whitespace + (paredit-skip-whitespace t end) + (setq end (point))) ; for the `delete-region'.) + (let ((indent-start nil) (indent-end nil)) + (save-excursion + (setq indent-start (point)) + (forward-sexp) ; Go forward an expression, to + (backward-delete-char 1) ; delete the end delimiter. + (setq indent-end (point))) + (delete-region (point) end) ; ...to delete the open char. + ;; Reindent only the region we preserved. + (indent-region indent-start indent-end)))))) (defun paredit-kill-surrounding-sexps-for-splice (argument) (cond ((or (paredit-in-string-p) @@ -1707,13 +1996,13 @@ With a prefix argument N, kill only the following N S-expressions." (- (prefix-numeric-value n)) '(16)))) -(defun paredit-raise-sexp (&optional n) +(defun paredit-raise-sexp (&optional argument) "Raise the following S-expression in a tree, deleting its siblings. With a prefix argument N, raise the following N S-expressions. If N is negative, raise the preceding N S-expressions. If the point is on an S-expression, such as a string or a symbol, not between them, that S-expression is considered to follow the point." - (interactive "p") + (interactive "P") (save-excursion (cond ((paredit-in-string-p) (goto-char (car (paredit-string-start+end-points)))) @@ -1722,21 +2011,19 @@ If the point is on an S-expression, such as a string or a symbol, not ((paredit-in-comment-p) (error "No S-expression to raise in comment."))) ;; Select the S-expressions we want to raise in a buffer substring. - (let* ((bound (save-excursion (forward-sexp n) (point))) - (sexps (if (and n (< n 0)) - (buffer-substring bound - (paredit-point-at-sexp-end)) - (buffer-substring (paredit-point-at-sexp-start) - bound)))) + (let* ((n (prefix-numeric-value argument)) + (bound (scan-sexps (point) n)) + (sexps + (if (< 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) - (delete-region (point) (save-excursion (forward-sexp) (point))) - (save-excursion (insert sexps)) ; Insert & reindent the sexps. - (save-excursion (let ((n (abs (or n 1)))) - (while (> n 0) - (paredit-forward-and-indent) - (setq n (1- n)))))))) + (delete-region (point) (scan-sexps (point) 1)) + (let* ((indent-start (point)) + (indent-end (save-excursion (insert sexps) (point)))) + (indent-region indent-start indent-end))))) (defun paredit-convolute-sexp (&optional n) "Convolute S-expressions. @@ -1917,7 +2204,6 @@ If in a string, move the opening double-quote backward by one (save-excursion (backward-sexp)) (let ((open (char-after)) (target (point))) - (message "open = %S" open) (delete-char 1) (backward-sexp) (insert open) @@ -2079,14 +2365,18 @@ Assumes that `paredit-in-string-p' is false, so that it need not handle (and (eq (char-before argument) ?\\ ) (not (eq (char-before (1- argument)) ?\\ ))))) +(defun paredit-indent-sexps () + "If in a list, indent all following S-expressions in the list." + (let ((start (point)) + (end (paredit-handle-sexp-errors (progn (up-list) (point)) nil))) + (if end + (indent-region start end)))) + (defun paredit-forward-and-indent () - "Move forward an S-expression, indenting it fully. -Indent with `lisp-indent-line' and then `indent-sexp'." - (forward-sexp) ; Go forward, and then find the - (save-excursion ; beginning of this next - (backward-sexp) ; S-expression. - (lisp-indent-line) ; Indent its opening line, and - (indent-sexp))) ; the rest of it. + "Move forward an S-expression, indenting it with `indent-region'." + (let ((start (point))) + (forward-sexp) + (indent-region start (point)))) (defun paredit-skip-whitespace (trailing-p &optional limit) "Skip past any whitespace, or until the point LIMIT is reached. @@ -2163,7 +2453,7 @@ If no parse state is supplied, compute one from the beginning of the ;; else an integer (the current comment nesting) (and (nth 4 (or state (paredit-current-parse-state))) t)) - + (defun paredit-point-at-sexp-boundary (n) (cond ((< n 0) (paredit-point-at-sexp-start)) ((= n 0) (point)) @@ -2186,6 +2476,30 @@ If no parse state is supplied, compute one from the beginning of the (paredit-in-comment-p) (paredit-in-char-p)) (error "Invalid context for command `%s'." command))) + +(defun paredit-check-region (start end) + (save-restriction + (narrow-to-region start end) + (if (fboundp 'check-parens) + (check-parens) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (forward-sexp)))))) + +(defun paredit-region-ok-p (start end) + (paredit-handle-sexp-errors + (progn + (save-restriction + (narrow-to-region start end) + ;; Can't use `check-parens' here -- it signals the wrong kind + ;; of errors. + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (forward-sexp)))) + t) + nil)) ;;;; Initialization @@ -2194,3 +2508,9 @@ If no parse state is supplied, compute one from the beginning of the (paredit-annotate-functions-with-examples) (provide 'paredit) + +;;; Local Variables: +;;; outline-regexp: " \n;;;;+" +;;; End: + +;;; paredit.el ends here diff --git a/starter-kit-defuns.el b/starter-kit-defuns.el index 06f7a0c791..85b0986cc6 100644 --- a/starter-kit-defuns.el +++ b/starter-kit-defuns.el @@ -214,6 +214,13 @@ Symbols matching the text at point are put first in the completion list." (list ?\")) (paredit-mode 1)) +(defun esk-space-for-delimiter? (endp delimiter) + (not (member major-mode '(ruby-mode espresso-mode js2-mode)))) + +(eval-after-load 'paredit + '(add-to-list 'paredit-space-for-delimiter-predicates + 'esk-space-for-delimiter?)) + (defun message-point () (interactive) (message "%s" (point)))