diff options
| author | Daniel Colascione | 2014-04-09 09:58:08 -0700 |
|---|---|---|
| committer | Daniel Colascione | 2014-04-09 09:58:08 -0700 |
| commit | 3f63a9f7de4f252a0309c2143e6d916d734ffe22 (patch) | |
| tree | 01a53bee941983edfc24944f9774741eafa87c81 | |
| parent | 6c971fb0f44169d7f77a9575301a4935106c0360 (diff) | |
| download | emacs-3f63a9f7de4f252a0309c2143e6d916d734ffe22.tar.gz emacs-3f63a9f7de4f252a0309c2143e6d916d734ffe22.zip | |
Make up-list and backward-up-list get out of more spots
| -rw-r--r-- | doc/lispref/ChangeLog | 8 | ||||
| -rw-r--r-- | doc/lispref/errors.texi | 5 | ||||
| -rw-r--r-- | doc/lispref/positions.texi | 16 | ||||
| -rw-r--r-- | lisp/ChangeLog | 8 | ||||
| -rw-r--r-- | lisp/emacs-lisp/lisp.el | 116 | ||||
| -rw-r--r-- | test/ChangeLog | 4 | ||||
| -rw-r--r-- | test/automated/syntax-tests.el | 97 |
7 files changed, 221 insertions, 33 deletions
diff --git a/doc/lispref/ChangeLog b/doc/lispref/ChangeLog index 2ae1faffc4e..25fa8ca4946 100644 --- a/doc/lispref/ChangeLog +++ b/doc/lispref/ChangeLog | |||
| @@ -1,3 +1,11 @@ | |||
| 1 | 2014-04-09 Daniel Colascione <dancol@dancol.org> | ||
| 2 | |||
| 3 | * errors.texi (Standard Errors): Document required error | ||
| 4 | parameters for `scan-error'. | ||
| 5 | |||
| 6 | * positions.texi (List Motion): Explain new `up-list' arguments. | ||
| 7 | Mention `backward-up-list'. | ||
| 8 | |||
| 1 | 2014-04-08 Daniel Colascione <dancol@dancol.org> | 9 | 2014-04-08 Daniel Colascione <dancol@dancol.org> |
| 2 | 10 | ||
| 3 | * minibuf.texi (Programmed Completion): Improve phrasing, remove | 11 | * minibuf.texi (Programmed Completion): Improve phrasing, remove |
diff --git a/doc/lispref/errors.texi b/doc/lispref/errors.texi index e00496e3478..dba8d219774 100644 --- a/doc/lispref/errors.texi +++ b/doc/lispref/errors.texi | |||
| @@ -157,7 +157,10 @@ The message is @samp{Attempt to modify a protected file}. | |||
| 157 | @item scan-error | 157 | @item scan-error |
| 158 | The message is @samp{Scan error}. This happens when certain | 158 | The message is @samp{Scan error}. This happens when certain |
| 159 | syntax-parsing functions find invalid syntax or mismatched | 159 | syntax-parsing functions find invalid syntax or mismatched |
| 160 | parentheses. @xref{List Motion}, and @xref{Parsing Expressions}. | 160 | parentheses. Conventionally raised with three argument: a |
| 161 | human-readable error message, the start of the obstacle that cannot be | ||
| 162 | moved over, and the end of the obstacle. @xref{List Motion}, and | ||
| 163 | @xref{Parsing Expressions}. | ||
| 161 | 164 | ||
| 162 | @item search-failed | 165 | @item search-failed |
| 163 | The message is @samp{Search failed}. @xref{Searching and Matching}. | 166 | The message is @samp{Search failed}. @xref{Searching and Matching}. |
diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi index f83173e2038..5a77b37e7e1 100644 --- a/doc/lispref/positions.texi +++ b/doc/lispref/positions.texi | |||
| @@ -647,9 +647,19 @@ parentheses. (Other syntactic entities such as words or paired string | |||
| 647 | quotes are ignored.) | 647 | quotes are ignored.) |
| 648 | @end deffn | 648 | @end deffn |
| 649 | 649 | ||
| 650 | @deffn Command up-list &optional arg | 650 | @deffn Command up-list &optional arg escape-strings no-syntax-crossing |
| 651 | This function moves forward out of @var{arg} (default 1) levels of parentheses. | 651 | This function moves forward out of @var{arg} (default 1) levels of |
| 652 | A negative argument means move backward but still to a less deep spot. | 652 | parentheses. A negative argument means move backward but still to a |
| 653 | less deep spot. If @var{escape-strings} is non-nil (as it is | ||
| 654 | interactively), move out of enclosing strings as well. If | ||
| 655 | @var{no-syntax-crossing} is non-nil (as it is interactively), prefer | ||
| 656 | to break out of any enclosing string instead of moving to the start of | ||
| 657 | a list broken across multiple strings. On error, location of point is | ||
| 658 | unspecified. | ||
| 659 | @end deffn | ||
| 660 | |||
| 661 | @deffn Command backward-up-list &optional arg escape-strings no-syntax-crossing | ||
| 662 | This function is just like @code{up-list}, but with a negated argument. | ||
| 653 | @end deffn | 663 | @end deffn |
| 654 | 664 | ||
| 655 | @deffn Command down-list &optional arg | 665 | @deffn Command down-list &optional arg |
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 8f27ffbf636..bab1edaffda 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,11 @@ | |||
| 1 | 2014-04-09 Daniel Colascione <dancol@dancol.org> | ||
| 2 | |||
| 3 | * emacs-lisp/lisp.el (backward-up-list): Add `escape-strings', | ||
| 4 | `no-syntax-crossing' arguments. Forward to `up-list'. | ||
| 5 | (up-list): Add `escape-strings', `no-syntax-crossing' arguments. | ||
| 6 | Implement logic for escaping from strings. Use narrowing to deal | ||
| 7 | with corner cases. | ||
| 8 | |||
| 1 | 2014-04-09 Leo Liu <sdl.web@gmail.com> | 9 | 2014-04-09 Leo Liu <sdl.web@gmail.com> |
| 2 | 10 | ||
| 3 | * net/rcirc.el (rcirc-connection-info): New variable. | 11 | * net/rcirc.el (rcirc-connection-info): New variable. |
diff --git a/lisp/emacs-lisp/lisp.el b/lisp/emacs-lisp/lisp.el index 0487515a142..3ff65ff11cd 100644 --- a/lisp/emacs-lisp/lisp.el +++ b/lisp/emacs-lisp/lisp.el | |||
| @@ -57,10 +57,14 @@ Should take the same arguments and behave similarly to `forward-sexp'.") | |||
| 57 | 57 | ||
| 58 | (defun forward-sexp (&optional arg) | 58 | (defun forward-sexp (&optional arg) |
| 59 | "Move forward across one balanced expression (sexp). | 59 | "Move forward across one balanced expression (sexp). |
| 60 | With ARG, do it that many times. Negative arg -N means | 60 | With ARG, do it that many times. Negative arg -N means move |
| 61 | move backward across N balanced expressions. | 61 | backward across N balanced expressions. This command assumes |
| 62 | This command assumes point is not in a string or comment. | 62 | point is not in a string or comment. Calls |
| 63 | Calls `forward-sexp-function' to do the work, if that is non-nil." | 63 | `forward-sexp-function' to do the work, if that is non-nil. If |
| 64 | unable to move over a sexp, signal `scan-error' with three | ||
| 65 | arguments: a message, the start of the obstacle (usually a | ||
| 66 | parenthesis or list marker of some kind), and end of the | ||
| 67 | obstacle." | ||
| 64 | (interactive "^p") | 68 | (interactive "^p") |
| 65 | (or arg (setq arg 1)) | 69 | (or arg (setq arg 1)) |
| 66 | (if forward-sexp-function | 70 | (if forward-sexp-function |
| @@ -140,38 +144,92 @@ This command assumes point is not in a string or comment." | |||
| 140 | (goto-char (or (scan-lists (point) inc -1) (buffer-end arg))) | 144 | (goto-char (or (scan-lists (point) inc -1) (buffer-end arg))) |
| 141 | (setq arg (- arg inc))))) | 145 | (setq arg (- arg inc))))) |
| 142 | 146 | ||
| 143 | (defun backward-up-list (&optional arg) | 147 | (defun backward-up-list (&optional arg escape-strings no-syntax-crossing) |
| 144 | "Move backward out of one level of parentheses. | 148 | "Move backward out of one level of parentheses. |
| 145 | This command will also work on other parentheses-like expressions | 149 | This command will also work on other parentheses-like expressions |
| 146 | defined by the current language mode. | 150 | defined by the current language mode. With ARG, do this that |
| 147 | With ARG, do this that many times. | 151 | many times. A negative argument means move forward but still to |
| 148 | A negative argument means move forward but still to a less deep spot. | 152 | a less deep spot. If ESCAPE-STRINGS is non-nil (as it is |
| 149 | This command assumes point is not in a string or comment." | 153 | interactively), move out of enclosing strings as well. If |
| 150 | (interactive "^p") | 154 | NO-SYNTAX-CROSSING is non-nil (as it is interactively), prefer to |
| 151 | (up-list (- (or arg 1)))) | 155 | break out of any enclosing string instead of moving to the start |
| 152 | 156 | of a list broken across multiple strings. On error, location of | |
| 153 | (defun up-list (&optional arg) | 157 | point is unspecified." |
| 158 | (interactive "^p\nd\nd") | ||
| 159 | (up-list (- (or arg 1)) escape-strings no-syntax-crossing)) | ||
| 160 | |||
| 161 | (defun up-list (&optional arg escape-strings no-syntax-crossing) | ||
| 154 | "Move forward out of one level of parentheses. | 162 | "Move forward out of one level of parentheses. |
| 155 | This command will also work on other parentheses-like expressions | 163 | This command will also work on other parentheses-like expressions |
| 156 | defined by the current language mode. | 164 | defined by the current language mode. With ARG, do this that |
| 157 | With ARG, do this that many times. | 165 | many times. A negative argument means move backward but still to |
| 158 | A negative argument means move backward but still to a less deep spot. | 166 | a less deep spot. If ESCAPE-STRINGS is non-nil (as it is |
| 159 | This command assumes point is not in a string or comment." | 167 | interactively), move out of enclosing strings as well. If |
| 160 | (interactive "^p") | 168 | NO-SYNTAX-CROSSING is non-nil (as it is interactively), prefer to |
| 169 | break out of any enclosing string instead of moving to the start | ||
| 170 | of a list broken across multiple strings. On error, location of | ||
| 171 | point is unspecified." | ||
| 172 | (interactive "^p\nd\nd") | ||
| 161 | (or arg (setq arg 1)) | 173 | (or arg (setq arg 1)) |
| 162 | (let ((inc (if (> arg 0) 1 -1)) | 174 | (let ((inc (if (> arg 0) 1 -1)) |
| 163 | pos) | 175 | (pos nil)) |
| 164 | (while (/= arg 0) | 176 | (while (/= arg 0) |
| 165 | (if (null forward-sexp-function) | 177 | (condition-case err |
| 166 | (goto-char (or (scan-lists (point) inc 1) (buffer-end arg))) | 178 | (save-restriction |
| 167 | (condition-case err | 179 | ;; If we've been asked not to cross string boundaries |
| 168 | (while (progn (setq pos (point)) | 180 | ;; and we're inside a string, narrow to that string so |
| 169 | (forward-sexp inc) | 181 | ;; that scan-lists doesn't find a match in a different |
| 170 | (/= (point) pos))) | 182 | ;; string. |
| 171 | (scan-error (goto-char (nth (if (> arg 0) 3 2) err)))) | 183 | (when no-syntax-crossing |
| 172 | (if (= (point) pos) | 184 | (let* ((syntax (syntax-ppss)) |
| 173 | (signal 'scan-error | 185 | (string-comment-start (nth 8 syntax))) |
| 174 | (list "Unbalanced parentheses" (point) (point))))) | 186 | (when string-comment-start |
| 187 | (save-excursion | ||
| 188 | (goto-char string-comment-start) | ||
| 189 | (narrow-to-region | ||
| 190 | (point) | ||
| 191 | (if (nth 3 syntax) ; in string | ||
| 192 | (condition-case nil | ||
| 193 | (progn (forward-sexp) (point)) | ||
| 194 | (scan-error (point-max))) | ||
| 195 | (forward-comment 1) | ||
| 196 | (point))))))) | ||
| 197 | (if (null forward-sexp-function) | ||
| 198 | (goto-char (or (scan-lists (point) inc 1) | ||
| 199 | (buffer-end arg))) | ||
| 200 | (condition-case err | ||
| 201 | (while (progn (setq pos (point)) | ||
| 202 | (forward-sexp inc) | ||
| 203 | (/= (point) pos))) | ||
| 204 | (scan-error (goto-char (nth (if (> arg 0) 3 2) err)))) | ||
| 205 | (if (= (point) pos) | ||
| 206 | (signal 'scan-error | ||
| 207 | (list "Unbalanced parentheses" (point) (point)))))) | ||
| 208 | (scan-error | ||
| 209 | (let ((syntax nil)) | ||
| 210 | (or | ||
| 211 | ;; If we bumped up against the end of a list, see whether | ||
| 212 | ;; we're inside a string: if so, just go to the beginning | ||
| 213 | ;; or end of that string. | ||
| 214 | (and escape-strings | ||
| 215 | (or syntax (setf syntax (syntax-ppss))) | ||
| 216 | (nth 3 syntax) | ||
| 217 | (goto-char (nth 8 syntax)) | ||
| 218 | (progn (when (> inc 0) | ||
| 219 | (forward-sexp)) | ||
| 220 | t)) | ||
| 221 | ;; If we narrowed to a comment above and failed to escape | ||
| 222 | ;; it, the error might be our fault, not an indication | ||
| 223 | ;; that we're out of syntax. Try again from beginning or | ||
| 224 | ;; end of the comment. | ||
| 225 | (and no-syntax-crossing | ||
| 226 | (or syntax (setf syntax (syntax-ppss))) | ||
| 227 | (nth 4 syntax) | ||
| 228 | (goto-char (nth 8 syntax)) | ||
| 229 | (or (< inc 0) | ||
| 230 | (forward-comment 1)) | ||
| 231 | (setf arg (+ arg inc))) | ||
| 232 | (signal (car err) (cdr err)))))) | ||
| 175 | (setq arg (- arg inc))))) | 233 | (setq arg (- arg inc))))) |
| 176 | 234 | ||
| 177 | (defun kill-sexp (&optional arg) | 235 | (defun kill-sexp (&optional arg) |
diff --git a/test/ChangeLog b/test/ChangeLog index c27b9b5f437..ebba4f01e93 100644 --- a/test/ChangeLog +++ b/test/ChangeLog | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | 2014-04-09 Daniel Colascione <dancol@dancol.org> | ||
| 2 | |||
| 3 | * automated/syntax-tests.el: New file. | ||
| 4 | |||
| 1 | 2014-04-09 Glenn Morris <rgm@gnu.org> | 5 | 2014-04-09 Glenn Morris <rgm@gnu.org> |
| 2 | 6 | ||
| 3 | * automated/python-tests.el (python-triple-quote-pairing): | 7 | * automated/python-tests.el (python-triple-quote-pairing): |
diff --git a/test/automated/syntax-tests.el b/test/automated/syntax-tests.el new file mode 100644 index 00000000000..9b97001a14e --- /dev/null +++ b/test/automated/syntax-tests.el | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | ;;; syntax-tests.el --- Testing syntax rules and basic movement -*- lexical-binding: t -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2014 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Daniel Colascione <dancol@dancol.org> | ||
| 6 | ;; Keywords: | ||
| 7 | |||
| 8 | ;; This program is free software; you can redistribute it and/or modify | ||
| 9 | ;; it under the terms of the GNU General Public License as published by | ||
| 10 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 11 | ;; (at your option) any later version. | ||
| 12 | |||
| 13 | ;; This program is distributed in the hope that it will be useful, | ||
| 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | ;; GNU General Public License for more details. | ||
| 17 | |||
| 18 | ;; You should have received a copy of the GNU General Public License | ||
| 19 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | |||
| 21 | ;;; Commentary: | ||
| 22 | |||
| 23 | ;; | ||
| 24 | |||
| 25 | ;;; Code: | ||
| 26 | (require 'ert) | ||
| 27 | (require 'cl-lib) | ||
| 28 | |||
| 29 | (defun run-up-list-test (fn data start instructions) | ||
| 30 | (cl-labels ((posof (thing) | ||
| 31 | (and (symbolp thing) | ||
| 32 | (= (length (symbol-name thing)) 1) | ||
| 33 | (- (aref (symbol-name thing) 0) ?a -1)))) | ||
| 34 | (with-temp-buffer | ||
| 35 | (set-syntax-table (make-syntax-table)) | ||
| 36 | ;; Use a syntax table in which single quote is a string | ||
| 37 | ;; character so that we can embed the test data in a lisp string | ||
| 38 | ;; literal. | ||
| 39 | (modify-syntax-entry ?\' "\"") | ||
| 40 | (insert data) | ||
| 41 | (goto-char (posof start)) | ||
| 42 | (dolist (instruction instructions) | ||
| 43 | (cond ((posof instruction) | ||
| 44 | (funcall fn) | ||
| 45 | (should (eql (point) (posof instruction)))) | ||
| 46 | ((symbolp instruction) | ||
| 47 | (should-error (funcall fn) | ||
| 48 | :type instruction)) | ||
| 49 | (t (cl-assert nil nil "unknown ins"))))))) | ||
| 50 | |||
| 51 | (defmacro define-up-list-test (name fn data start &rest expected) | ||
| 52 | `(ert-deftest ,name () | ||
| 53 | (run-up-list-test ,fn ,data ',start ',expected))) | ||
| 54 | |||
| 55 | (define-up-list-test up-list-basic | ||
| 56 | (lambda () (up-list)) | ||
| 57 | (or "(1 1 (1 1) 1 (1 1) 1)") | ||
| 58 | ;; abcdefghijklmnopqrstuv | ||
| 59 | i k v scan-error) | ||
| 60 | |||
| 61 | (define-up-list-test up-list-with-forward-sexp-function | ||
| 62 | (lambda () | ||
| 63 | (let ((forward-sexp-function | ||
| 64 | (lambda (&optional arg) | ||
| 65 | (let ((forward-sexp-function nil)) | ||
| 66 | (forward-sexp arg))))) | ||
| 67 | (up-list))) | ||
| 68 | (or "(1 1 (1 1) 1 (1 1) 1)") | ||
| 69 | ;; abcdefghijklmnopqrstuv | ||
| 70 | i k v scan-error) | ||
| 71 | |||
| 72 | (define-up-list-test up-list-out-of-string | ||
| 73 | (lambda () (up-list 1 t)) | ||
| 74 | (or "1 (1 '2 2 (2 2 2' 1) 1") | ||
| 75 | ;; abcdefghijklmnopqrstuvwxy | ||
| 76 | o r u scan-error) | ||
| 77 | |||
| 78 | (define-up-list-test up-list-cross-string | ||
| 79 | (lambda () (up-list 1 t)) | ||
| 80 | (or "(1 '2 ( 2' 1 '2 ) 2' 1)") | ||
| 81 | ;; abcdefghijklmnopqrstuvwxy | ||
| 82 | i r u x scan-error) | ||
| 83 | |||
| 84 | (define-up-list-test up-list-no-cross-string | ||
| 85 | (lambda () (up-list 1 t t)) | ||
| 86 | (or "(1 '2 ( 2' 1 '2 ) 2' 1)") | ||
| 87 | ;; abcdefghijklmnopqrstuvwxy | ||
| 88 | i k x scan-error) | ||
| 89 | |||
| 90 | (define-up-list-test backward-up-list-basic | ||
| 91 | (lambda () (backward-up-list)) | ||
| 92 | (or "(1 1 (1 1) 1 (1 1) 1)") | ||
| 93 | ;; abcdefghijklmnopqrstuv | ||
| 94 | i f a scan-error) | ||
| 95 | |||
| 96 | (provide 'syntax-tests) | ||
| 97 | ;;; syntax-tests.el ends here | ||