From: Fabián Ezequiel Gallina Date: Mon, 8 Oct 2012 05:19:15 +0000 (-0300) Subject: Enhancements on forward-sexp movement. X-Git-Tag: emacs-24.2.90~237^2~71 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=8dbce54cc73d16375b8d233da7271054eb4cda34;p=emacs.git Enhancements on forward-sexp movement. * progmodes/python.el (python-nav-beginning-of-statement) (python-nav-end-of-statement): Return point-marker. (python-nav-forward-sexp): lisp-like forward-sexp behavior. (python-info-current-symbol) (python-info-statement-starts-block-p): Rename from python-info-beginning-of-block-p. (python-info-statement-ends-block-p): Rename from python-info-end-of-block-p. (python-info-beginning-of-statement-p) (python-info-end-of-statement-p) (python-info-beginning-of-block-p, python-info-end-of-block-p): New functions. --- diff --git a/lisp/ChangeLog b/lisp/ChangeLog index eaeca11ca09..4f21112f3af 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,19 @@ +2012-10-08 Fabián Ezequiel Gallina + + Enhancements on forward-sexp movement. + * progmodes/python.el (python-nav-beginning-of-statement) + (python-nav-end-of-statement): Return point-marker. + (python-nav-forward-sexp): lisp-like forward-sexp behavior. + (python-info-current-symbol) + (python-info-statement-starts-block-p): Rename from + python-info-beginning-of-block-p. + (python-info-statement-ends-block-p): Rename from + python-info-end-of-block-p. + (python-info-beginning-of-statement-p) + (python-info-end-of-statement-p) + (python-info-beginning-of-block-p, python-info-end-of-block-p): + New functions. + 2012-10-08 Stefan Monnier * comint.el (comint-preinput-scroll-to-bottom): Preserve the diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ffb2e66ca9d..6b0dc954ca7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1169,7 +1169,8 @@ Returns nil if point is not in a def or class." (python-info-line-ends-backslash-p)) (python-syntax-context 'string) (python-syntax-context 'paren)) - (forward-line -1))))) + (forward-line -1)))) + (point-marker)) (defun python-nav-end-of-statement () "Move to end of current statement." @@ -1180,7 +1181,8 @@ Returns nil if point is not in a def or class." (python-info-line-ends-backslash-p) (python-syntax-context 'string) (python-syntax-context 'paren)) - (forward-line 1))))) + (forward-line 1)))) + (point-marker)) (defun python-nav-backward-statement (&optional arg) "Move backward to previous statement. @@ -1295,151 +1297,104 @@ When ARG > 0 move forward, else if ARG is < 0." (while (and (funcall search-fn paren-regexp nil t) (python-syntax-context 'paren))))))) -(defun python-nav--forward-sexp () - "Move to forward sexp." - (case (python-syntax-context-type) - (string - ;; Inside of a string, get out of it. - (while (and (re-search-forward "[\"']" nil t) - (python-syntax-context 'string)))) - (comment - ;; Inside of a comment, just move forward. - (python-util-forward-comment)) - (paren - (python-nav-lisp-forward-sexp-safe 1)) - (t - (if (and (not (eobp)) - (= (syntax-class (syntax-after (point))) 4)) - ;; Looking an open-paren - (python-nav-lisp-forward-sexp-safe 1) - (let ((block-starting-pos - (save-excursion (python-nav-beginning-of-block))) - (block-ending-pos - (save-excursion (python-nav-end-of-block))) - (next-block-starting-pos - (save-excursion (python-nav-forward-block)))) - (cond - ((not block-starting-pos) - ;; Not inside a block, move to closest one. - (and next-block-starting-pos - (goto-char next-block-starting-pos))) - ((= (point) block-starting-pos) - ;; Point is at beginning of block - (if (and next-block-starting-pos - (< next-block-starting-pos block-ending-pos)) - ;; Beginning of next block is closer than current's - ;; end, move to it. - (goto-char next-block-starting-pos) - (goto-char block-ending-pos))) - ((= block-ending-pos (point)) - ;; Point is at end of current block - (let ((parent-block-end-pos - (save-excursion - (python-util-forward-comment) - (python-nav-beginning-of-block) - (python-nav-end-of-block)))) - (if (and parent-block-end-pos - (or (not next-block-starting-pos) - (> next-block-starting-pos parent-block-end-pos))) - ;; If the parent block ends before next block - ;; starts move to it. - (goto-char parent-block-end-pos) - (and next-block-starting-pos - (goto-char next-block-starting-pos))))) - (t (python-nav-end-of-block)))))))) +(defun python-nav--forward-sexp (&optional dir) + "Move to forward sexp. +With positive Optional argument DIR direction move forward, else +backwards." + (setq dir (or dir 1)) + (unless (= dir 0) + (let* ((forward-p (if (> dir 0) + (and (setq dir 1) t) + (and (setq dir -1) nil))) + (re-search-fn (if forward-p + 're-search-forward + 're-search-backward)) + (context-type (python-syntax-context-type))) + (cond + ((eq context-type 'string) + ;; Inside of a string, get out of it. + (while (and (funcall re-search-fn "[\"']" nil t) + (python-syntax-context 'string)))) + ((eq context-type 'comment) + ;; Inside of a comment, just move forward. + (python-util-forward-comment dir)) + ((or (eq context-type 'paren) + (and forward-p (looking-at (python-rx open-paren))) + (and (not forward-p) + (eq (syntax-class (syntax-after (1- (point)))) + (car (string-to-syntax ")"))))) + ;; Inside a paren or looking at it, lisp knows what to do. + (python-nav-lisp-forward-sexp-safe dir)) + (t + ;; This part handles the lispy feel of + ;; `python-nav-forward-sexp'. Knowing everything about the + ;; current context and the context of the next sexp tries to + ;; follow the lisp sexp motion commands in a symmetric manner. + (let* ((context + (cond + ((python-info-beginning-of-block-p) 'block-start) + ((python-info-end-of-block-p) 'block-end) + ((python-info-beginning-of-statement-p) 'statement-start) + ((python-info-end-of-statement-p) 'statement-end))) + (next-sexp-pos + (save-excursion + (python-nav-lisp-forward-sexp-safe dir) + (point))) + (next-sexp-context + (save-excursion + (goto-char next-sexp-pos) + (cond + ((python-info-beginning-of-block-p) 'block-start) + ((python-info-end-of-block-p) 'block-end) + ((python-info-beginning-of-statement-p) 'statement-start) + ((python-info-end-of-statement-p) 'statement-end) + ((python-info-statement-starts-block-p) 'starts-block) + ((python-info-statement-ends-block-p) 'ends-block))))) + (if forward-p + (cond ((and (not (eobp)) + (python-info-current-line-empty-p)) + (python-util-forward-comment dir) + (python-nav--forward-sexp dir)) + ((eq context 'block-start) + (python-nav-end-of-block)) + ((eq context 'statement-start) + (python-nav-end-of-statement)) + ((and (memq context '(statement-end block-end)) + (eq next-sexp-context 'ends-block)) + (goto-char next-sexp-pos) + (python-nav-end-of-block)) + ((and (memq context '(statement-end block-end)) + (eq next-sexp-context 'starts-block)) + (goto-char next-sexp-pos) + (python-nav-end-of-block)) + ((memq context '(statement-end block-end)) + (goto-char next-sexp-pos) + (python-nav-end-of-statement)) + (t (goto-char next-sexp-pos))) + (cond ((and (not (bobp)) + (python-info-current-line-empty-p)) + (python-util-forward-comment dir) + (python-nav--forward-sexp dir)) + ((eq context 'block-end) + (python-nav-beginning-of-block)) + ((eq context 'statement-end) + (python-nav-beginning-of-statement)) + ((and (memq context '(statement-start block-start)) + (eq next-sexp-context 'starts-block)) + (goto-char next-sexp-pos) + (python-nav-beginning-of-block)) + ((and (memq context '(statement-start block-start)) + (eq next-sexp-context 'ends-block)) + (goto-char next-sexp-pos) + (python-nav-beginning-of-block)) + ((memq context '(statement-start block-start)) + (goto-char next-sexp-pos) + (python-nav-beginning-of-statement)) + (t (goto-char next-sexp-pos)))))))))) (defun python-nav--backward-sexp () "Move to backward sexp." - (case (python-syntax-context-type) - (string - ;; Inside of a string, get out of it. - (while (and (re-search-backward "[\"']" nil t) - (python-syntax-context 'string)))) - (comment - ;; Inside of a comment, just move backward. - (python-util-forward-comment -1)) - (paren - ;; Handle parens like we are lisp. - (python-nav-lisp-forward-sexp-safe -1)) - (t - (let* ((block-starting-pos - (save-excursion (python-nav-beginning-of-block))) - (block-ending-pos - (save-excursion (python-nav-end-of-block))) - (prev-block-ending-pos - (save-excursion (when (python-nav-backward-block) - (python-nav-end-of-block)))) - (prev-block-parent-ending-pos - (save-excursion - (when prev-block-ending-pos - (goto-char prev-block-ending-pos) - (python-util-forward-comment) - (python-nav-beginning-of-block) - (python-nav-end-of-block))))) - (if (and (not (bobp)) - (= (syntax-class (syntax-after (1- (point)))) 5)) - ;; Char before point is a paren closing char, handle it - ;; like we are lisp. - (python-nav-lisp-forward-sexp-safe -1) - (cond - ((not block-ending-pos) - ;; Not in and ending pos, move to end of previous block. - (and (python-nav-backward-block) - (python-nav-end-of-block))) - ((= (point) block-ending-pos) - ;; In ending pos, we need to search backwards for the - ;; closest point looking the list of candidates from here. - (let ((candidates)) - (dolist (name - '(prev-block-parent-ending-pos - prev-block-ending-pos - block-ending-pos - block-starting-pos)) - (when (and (symbol-value name) - (< (symbol-value name) (point))) - (add-to-list 'candidates (symbol-value name)))) - (goto-char (apply 'max candidates)))) - ((> (point) block-ending-pos) - ;; After an ending position, move to it. - (goto-char block-ending-pos)) - ((= (point) block-starting-pos) - ;; On a block starting position. - (if (not (> (point) (or prev-block-ending-pos (point)))) - ;; Point is after the end position of the block that - ;; wraps the current one, just move a block backward. - (python-nav-backward-block) - ;; If we got here we are facing a case like this one: - ;; - ;; try: - ;; return here() - ;; except Exception as e: - ;; - ;; Where point is on the "except" and must move to the - ;; end of "here()". - (goto-char prev-block-ending-pos) - (let ((parent-block-ending-pos - (save-excursion - (python-nav-forward-sexp) - (and (not (looking-at (python-rx block-start))) - (point))))) - (when (and parent-block-ending-pos - (> parent-block-ending-pos prev-block-ending-pos)) - ;; If we got here we are facing a case like this one: - ;; - ;; except ImportError: - ;; if predicate(): - ;; processing() - ;; here() - ;; except AttributeError: - ;; - ;; Where point is on the "except" and must move to - ;; the end of "here()". Without this extra step we'd - ;; just get to the end of processing(). - (goto-char parent-block-ending-pos))))) - (t - (if (and prev-block-ending-pos (< prev-block-ending-pos (point))) - (goto-char prev-block-ending-pos) - (python-nav-beginning-of-block))))))))) + (python-nav--forward-sexp -1)) (defun python-nav-forward-sexp (&optional arg) "Move forward across one block of code. @@ -2891,12 +2846,43 @@ parent defun name." ".") ".") name))))))) -(defsubst python-info-beginning-of-block-statement-p () +(defun python-info-statement-starts-block-p () "Return non-nil if current statement opens a block." (save-excursion (python-nav-beginning-of-statement) (looking-at (python-rx block-start)))) +(defun python-info-statement-ends-block-p () + "Return non-nil if point is at end of block." + (let ((end-of-block-pos (save-excursion + (python-nav-end-of-block))) + (end-of-statement-pos (save-excursion + (python-nav-end-of-statement)))) + (and end-of-block-pos end-of-statement-pos + (= end-of-block-pos end-of-statement-pos)))) + +(defun python-info-beginning-of-statement-p () + "Return non-nil if point is at beginning of statement." + (= (point) (save-excursion + (python-nav-beginning-of-statement) + (point)))) + +(defun python-info-end-of-statement-p () + "Return non-nil if point is at end of statement." + (= (point) (save-excursion + (python-nav-end-of-statement) + (point)))) + +(defun python-info-beginning-of-block-p () + "Return non-nil if point is at beginning of block." + (and (python-info-beginning-of-statement-p) + (python-info-statement-starts-block-p))) + +(defun python-info-end-of-block-p () + "Return non-nil if point is at end of block." + (and (python-info-end-of-statement-p) + (python-info-statement-ends-block-p))) + (defun python-info-closing-block () "Return the point of the block the current line closes." (let ((closing-word (save-excursion