python.el: Fix mark-defun behavior (Bug#19665)

* lisp/progmodes/python.el: (python-mark-defun): New function.

* test/automated/python-tests.el (python-mark-defun-1)
(python-mark-defun-2, python-mark-defun-3): New tests.
parent 342ed936
......@@ -284,6 +284,7 @@
(define-key map [remap backward-sentence] 'python-nav-backward-block)
(define-key map [remap forward-sentence] 'python-nav-forward-block)
(define-key map [remap backward-up-list] 'python-nav-backward-up-list)
(define-key map [remap mark-defun] 'python-mark-defun)
(define-key map "\C-c\C-j" 'imenu)
;; Indent specific
(define-key map "\177" 'python-indent-dedent-line-backspace)
......@@ -1250,6 +1251,21 @@ the line will be re-indented automatically if needed."
;; Reindent region if this is a multiline statement
(python-indent-region dedenter-pos current-pos)))))))))
;;; Mark
(defun python-mark-defun (&optional allow-extend)
"Put mark at end of this defun, point at beginning.
The defun marked is the one that contains point or follows point.
Interactively (or with ALLOW-EXTEND non-nil), if this command is
repeated or (in Transient Mark mode) if the mark is active, it
marks the next defun after the ones already marked."
(interactive "p")
(when (python-info-looking-at-beginning-of-defun)
(end-of-line 1))
(mark-defun allow-extend))
;;; Navigation
......
......@@ -1225,6 +1225,158 @@ this is an arbitrarily
(should (string= (buffer-substring-no-properties (point-min) (point-max))
expected)))))
;;; Mark
(ert-deftest python-mark-defun-1 ()
"""Test `python-mark-defun' with point at defun symbol start."""
(python-tests-with-temp-buffer
"
def foo(x):
return x
class A:
pass
class B:
def __init__(self):
self.b = 'b'
def fun(self):
return self.b
class C:
'''docstring'''
"
(let ((expected-mark-beginning-position
(progn
(python-tests-look-at "class A:")
(1- (point))))
(expected-mark-end-position-1
(save-excursion
(python-tests-look-at "pass")
(forward-line)
(point)))
(expected-mark-end-position-2
(save-excursion
(python-tests-look-at "return self.b")
(forward-line)
(point)))
(expected-mark-end-position-3
(save-excursion
(python-tests-look-at "'''docstring'''")
(forward-line)
(point))))
;; Select class A only, with point at bol.
(python-mark-defun 1)
(should (= (point) expected-mark-beginning-position))
(should (= (marker-position (mark-marker))
expected-mark-end-position-1))
;; expand to class B, start position should remain the same.
(python-mark-defun 1)
(should (= (point) expected-mark-beginning-position))
(should (= (marker-position (mark-marker))
expected-mark-end-position-2))
;; expand to class C, start position should remain the same.
(python-mark-defun 1)
(should (= (point) expected-mark-beginning-position))
(should (= (marker-position (mark-marker))
expected-mark-end-position-3)))))
(ert-deftest python-mark-defun-2 ()
"""Test `python-mark-defun' with point at nested defun symbol start."""
(python-tests-with-temp-buffer
"
def foo(x):
return x
class A:
pass
class B:
def __init__(self):
self.b = 'b'
def fun(self):
return self.b
class C:
'''docstring'''
"
(let ((expected-mark-beginning-position
(progn
(python-tests-look-at "def __init__(self):")
(1- (line-beginning-position))))
(expected-mark-end-position-1
(save-excursion
(python-tests-look-at "self.b = 'b'")
(forward-line)
(point)))
(expected-mark-end-position-2
(save-excursion
(python-tests-look-at "return self.b")
(forward-line)
(point)))
(expected-mark-end-position-3
(save-excursion
(python-tests-look-at "'''docstring'''")
(forward-line)
(point))))
;; Select B.__init only, with point at its start.
(python-mark-defun 1)
(should (= (point) expected-mark-beginning-position))
(should (= (marker-position (mark-marker))
expected-mark-end-position-1))
;; expand to B.fun, start position should remain the same.
(python-mark-defun 1)
(should (= (point) expected-mark-beginning-position))
(should (= (marker-position (mark-marker))
expected-mark-end-position-2))
;; expand to class C, start position should remain the same.
(python-mark-defun 1)
(should (= (point) expected-mark-beginning-position))
(should (= (marker-position (mark-marker))
expected-mark-end-position-3)))))
(ert-deftest python-mark-defun-3 ()
"""Test `python-mark-defun' with point inside defun symbol."""
(python-tests-with-temp-buffer
"
def foo(x):
return x
class A:
pass
class B:
def __init__(self):
self.b = 'b'
def fun(self):
return self.b
class C:
'''docstring'''
"
(let ((expected-mark-beginning-position
(progn
(python-tests-look-at "def fun(self):")
(python-tests-look-at "(self):")
(1- (line-beginning-position))))
(expected-mark-end-position
(save-excursion
(python-tests-look-at "return self.b")
(forward-line)
(point))))
;; Should select B.fun, despite point is inside the defun symbol.
(python-mark-defun 1)
(should (= (point) expected-mark-beginning-position))
(should (= (marker-position (mark-marker))
expected-mark-end-position)))))
;;; Navigation
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment