(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."
(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.
(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.
".") ".")
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