summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp/lisp.el
diff options
context:
space:
mode:
authorDaniel Colascione <dancol@dancol.org>2014-04-09 09:58:08 -0700
committerDaniel Colascione <dancol@dancol.org>2014-04-09 09:58:08 -0700
commit3f63a9f7de4f252a0309c2143e6d916d734ffe22 (patch)
tree01a53bee941983edfc24944f9774741eafa87c81 /lisp/emacs-lisp/lisp.el
parent6c971fb0f44169d7f77a9575301a4935106c0360 (diff)
downloademacs-3f63a9f7de4f252a0309c2143e6d916d734ffe22.tar.gz
Make up-list and backward-up-list get out of more spots
Diffstat (limited to 'lisp/emacs-lisp/lisp.el')
-rw-r--r--lisp/emacs-lisp/lisp.el116
1 files changed, 87 insertions, 29 deletions
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'.")
(defun forward-sexp (&optional arg)
"Move forward across one balanced expression (sexp).
-With ARG, do it that many times. Negative arg -N means
-move backward across N balanced expressions.
-This command assumes point is not in a string or comment.
-Calls `forward-sexp-function' to do the work, if that is non-nil."
+With ARG, do it that many times. Negative arg -N means move
+backward across N balanced expressions. This command assumes
+point is not in a string or comment. Calls
+`forward-sexp-function' to do the work, if that is non-nil. If
+unable to move over a sexp, signal `scan-error' with three
+arguments: a message, the start of the obstacle (usually a
+parenthesis or list marker of some kind), and end of the
+obstacle."
(interactive "^p")
(or arg (setq arg 1))
(if forward-sexp-function
@@ -140,38 +144,92 @@ This command assumes point is not in a string or comment."
(goto-char (or (scan-lists (point) inc -1) (buffer-end arg)))
(setq arg (- arg inc)))))
-(defun backward-up-list (&optional arg)
+(defun backward-up-list (&optional arg escape-strings no-syntax-crossing)
"Move backward out of one level of parentheses.
This command will also work on other parentheses-like expressions
-defined by the current language mode.
-With ARG, do this that many times.
-A negative argument means move forward but still to a less deep spot.
-This command assumes point is not in a string or comment."
- (interactive "^p")
- (up-list (- (or arg 1))))
-
-(defun up-list (&optional arg)
+defined by the current language mode. With ARG, do this that
+many times. A negative argument means move forward but still to
+a less deep spot. If ESCAPE-STRINGS is non-nil (as it is
+interactively), move out of enclosing strings as well. If
+NO-SYNTAX-CROSSING is non-nil (as it is interactively), prefer to
+break out of any enclosing string instead of moving to the start
+of a list broken across multiple strings. On error, location of
+point is unspecified."
+ (interactive "^p\nd\nd")
+ (up-list (- (or arg 1)) escape-strings no-syntax-crossing))
+
+(defun up-list (&optional arg escape-strings no-syntax-crossing)
"Move forward out of one level of parentheses.
This command will also work on other parentheses-like expressions
-defined by the current language mode.
-With ARG, do this that many times.
-A negative argument means move backward but still to a less deep spot.
-This command assumes point is not in a string or comment."
- (interactive "^p")
+defined by the current language mode. With ARG, do this that
+many times. A negative argument means move backward but still to
+a less deep spot. If ESCAPE-STRINGS is non-nil (as it is
+interactively), move out of enclosing strings as well. If
+NO-SYNTAX-CROSSING is non-nil (as it is interactively), prefer to
+break out of any enclosing string instead of moving to the start
+of a list broken across multiple strings. On error, location of
+point is unspecified."
+ (interactive "^p\nd\nd")
(or arg (setq arg 1))
(let ((inc (if (> arg 0) 1 -1))
- pos)
+ (pos nil))
(while (/= arg 0)
- (if (null forward-sexp-function)
- (goto-char (or (scan-lists (point) inc 1) (buffer-end arg)))
- (condition-case err
- (while (progn (setq pos (point))
- (forward-sexp inc)
- (/= (point) pos)))
- (scan-error (goto-char (nth (if (> arg 0) 3 2) err))))
- (if (= (point) pos)
- (signal 'scan-error
- (list "Unbalanced parentheses" (point) (point)))))
+ (condition-case err
+ (save-restriction
+ ;; If we've been asked not to cross string boundaries
+ ;; and we're inside a string, narrow to that string so
+ ;; that scan-lists doesn't find a match in a different
+ ;; string.
+ (when no-syntax-crossing
+ (let* ((syntax (syntax-ppss))
+ (string-comment-start (nth 8 syntax)))
+ (when string-comment-start
+ (save-excursion
+ (goto-char string-comment-start)
+ (narrow-to-region
+ (point)
+ (if (nth 3 syntax) ; in string
+ (condition-case nil
+ (progn (forward-sexp) (point))
+ (scan-error (point-max)))
+ (forward-comment 1)
+ (point)))))))
+ (if (null forward-sexp-function)
+ (goto-char (or (scan-lists (point) inc 1)
+ (buffer-end arg)))
+ (condition-case err
+ (while (progn (setq pos (point))
+ (forward-sexp inc)
+ (/= (point) pos)))
+ (scan-error (goto-char (nth (if (> arg 0) 3 2) err))))
+ (if (= (point) pos)
+ (signal 'scan-error
+ (list "Unbalanced parentheses" (point) (point))))))
+ (scan-error
+ (let ((syntax nil))
+ (or
+ ;; If we bumped up against the end of a list, see whether
+ ;; we're inside a string: if so, just go to the beginning
+ ;; or end of that string.
+ (and escape-strings
+ (or syntax (setf syntax (syntax-ppss)))
+ (nth 3 syntax)
+ (goto-char (nth 8 syntax))
+ (progn (when (> inc 0)
+ (forward-sexp))
+ t))
+ ;; If we narrowed to a comment above and failed to escape
+ ;; it, the error might be our fault, not an indication
+ ;; that we're out of syntax. Try again from beginning or
+ ;; end of the comment.
+ (and no-syntax-crossing
+ (or syntax (setf syntax (syntax-ppss)))
+ (nth 4 syntax)
+ (goto-char (nth 8 syntax))
+ (or (< inc 0)
+ (forward-comment 1))
+ (setf arg (+ arg inc)))
+ (signal (car err) (cdr err))))))
(setq arg (- arg inc)))))
(defun kill-sexp (&optional arg)