From a5e39bfae8fe8950a01e01b1ae1ad864f5f523b4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Fabi=C3=A1n=20Ezequiel=20Gallina?= Date: Mon, 6 Jul 2015 01:03:46 -0300 Subject: [PATCH] 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. --- lisp/progmodes/python.el | 16 ++++ test/automated/python-tests.el | 152 +++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f641880428c..fbf944f9c68 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -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 diff --git a/test/automated/python-tests.el b/test/automated/python-tests.el index 4585e7f9614..2ed07464df6 100644 --- a/test/automated/python-tests.el +++ b/test/automated/python-tests.el @@ -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 -- 2.39.2