From e380e42f09b1b11c72841c61f4c5d25c2a5f02f3 Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Sat, 18 Sep 2010 18:56:16 +0000 Subject: Implement `paredit-{for,back}ward-{up,down}'. Ignore-this: ce362505e717451523aa5fd361a8730f These are like `{backward-,}{up,down}-list', but also enter and exit strings when appropriate. The keys formerly bound to those commands (C-M-u, C-M-d, C-M-p, C-M-n) are now bound to their new paredit analogues. darcs-hash:20100918185616-00fcc-13b875689b8915083c41a6d05c9cf04506799aae --- paredit.el | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 4 deletions(-) (limited to 'paredit.el') diff --git a/paredit.el b/paredit.el index 58dbf0f..be9c6fb 100644 --- a/paredit.el +++ b/paredit.el @@ -403,10 +403,10 @@ Paredit behaves badly if parentheses are imbalanced, so exercise "(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" @@ -1655,6 +1655,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) -- cgit v1.2.1