New defun movement commands.
authorFabián Ezequiel Gallina <fgallina@gnu.org>
Wed, 17 Apr 2013 05:08:20 +0000 (02:08 -0300)
committerFabián Ezequiel Gallina <fgallina@gnu.org>
Wed, 17 Apr 2013 05:08:20 +0000 (02:08 -0300)
* lisp/progmodes/python.el (python-nav--syntactically)
(python-nav--forward-defun, python-nav-backward-defun)
(python-nav-forward-defun): New functions.

* test/automated/python-tests.el (python-nav-backward-defun-1)
(python-nav-forward-defun-1): New tests.

lisp/ChangeLog
lisp/progmodes/python.el
test/ChangeLog
test/automated/python-tests.el

index cbb63e3..11666c6 100644 (file)
@@ -1,3 +1,10 @@
+2013-04-17  Fabián Ezequiel Gallina  <fgallina@gnu.org>
+
+       New defun movement commands.
+       * progmodes/python.el (python-nav--syntactically)
+       (python-nav--forward-defun, python-nav-backward-defun)
+       (python-nav-forward-defun): New functions.
+
 2013-04-17  Fabián Ezequiel Gallina  <fgallina@gnu.org>
 
        * progmodes/python.el (python-syntax--context-compiler-macro): New defun.
index b0c00a3..1d7cf02 100644 (file)
@@ -1192,6 +1192,66 @@ Returns nil if point is not in a def or class."
       ;; Ensure point moves forward.
       (and (> beg-pos (point)) (goto-char beg-pos)))))
 
+(defun python-nav--syntactically (fn poscompfn &optional pos)
+  "Move to point using FN ignoring non-code or paren context.
+FN must take no arguments and could be used to set match-data.
+POSCOMPFN is a two arguments function used to compare current and
+previous point after it is moved using FN, this is normally a
+less-than or greater-than comparison.  Optional argument POS is
+internally used in recursive calls and should not be explicitly
+passed."
+  (let* ((newpos
+          (and (funcall fn)
+               (save-match-data
+                 (and
+                  (not (python-syntax-context-type))
+                  (point-marker)))))
+         (current-match-data (match-data)))
+    (cond ((or (and (not pos) newpos)
+               (and pos newpos (funcall poscompfn newpos pos)))
+           (set-match-data current-match-data)
+           (point-marker))
+          ((and (not pos) (not newpos)) nil)
+          (t (python-nav--syntactically
+              fn poscompfn (point-marker))))))
+
+(defun python-nav--forward-defun (arg)
+  "Internal implementation of python-nav-{backward,forward}-defun.
+Uses ARG to define which function to call, and how many times
+repeat it."
+  (let ((found))
+    (while (and (> arg 0)
+                (setq found
+                      (python-nav--syntactically
+                       (lambda ()
+                         (re-search-forward
+                          python-nav-beginning-of-defun-regexp nil t))
+                       '>)))
+      (setq arg (1- arg)))
+    (while (and (< arg 0)
+                (setq found
+                      (python-nav--syntactically
+                       (lambda ()
+                         (re-search-backward
+                          python-nav-beginning-of-defun-regexp nil t))
+                       '<)))
+      (setq arg (1+ arg)))
+    found))
+
+(defun python-nav-backward-defun (&optional arg)
+  "Navigate to closer defun backward ARG times.
+Unlikely `python-nav-beginning-of-defun' this doesn't care about
+nested definitions."
+  (interactive "^p")
+  (python-nav--forward-defun (- (or arg 1))))
+
+(defun python-nav-forward-defun (&optional arg)
+  "Navigate to closer defun forward ARG times.
+Unlikely `python-nav-beginning-of-defun' this doesn't care about
+nested definitions."
+  (interactive "^p")
+  (python-nav--forward-defun (or arg 1)))
+
 (defun python-nav-beginning-of-statement ()
   "Move to start of current statement."
   (interactive "^")
index bf68984..7c25ad1 100644 (file)
@@ -1,3 +1,8 @@
+2013-04-17  Fabián Ezequiel Gallina  <fgallina@gnu.org>
+
+       * automated/python-tests.el (python-nav-backward-defun-1)
+       (python-nav-forward-defun-1): New tests.
+
 2013-04-09  Masatake YAMATO  <yamato@redhat.com>
 
        * automated/add-log-tests.el: New file. (Bug#14112)
index 1a741b4..a7c7aab 100644 (file)
@@ -674,6 +674,93 @@ def decoratorFunctionWithArguments(arg1, arg2, arg3):
                 (python-tests-look-at "return wrapped_f")
                 (line-beginning-position))))))
 
+(ert-deftest python-nav-backward-defun-1 ()
+  (python-tests-with-temp-buffer
+   "
+class A(object): # A
+
+    def a(self): # a
+        pass
+
+    def b(self): # b
+        pass
+
+    class B(object): # B
+
+        class C(object): # C
+
+            def d(self): # d
+                pass
+
+            # def e(self): # e
+            #     pass
+
+    def c(self): # c
+        pass
+
+    # def d(self): # d
+    #     pass
+"
+   (goto-char (point-max))
+   (should (= (save-excursion (python-nav-backward-defun))
+              (python-tests-look-at "    def c(self): # c" -1)))
+   (should (= (save-excursion (python-nav-backward-defun))
+              (python-tests-look-at "            def d(self): # d" -1)))
+   (should (= (save-excursion (python-nav-backward-defun))
+              (python-tests-look-at "        class C(object): # C" -1)))
+   (should (= (save-excursion (python-nav-backward-defun))
+              (python-tests-look-at "    class B(object): # B" -1)))
+   (should (= (save-excursion (python-nav-backward-defun))
+              (python-tests-look-at "    def b(self): # b" -1)))
+   (should (= (save-excursion (python-nav-backward-defun))
+              (python-tests-look-at "    def a(self): # a" -1)))
+   (should (= (save-excursion (python-nav-backward-defun))
+              (python-tests-look-at "class A(object): # A" -1)))
+   (should (not (python-nav-backward-defun)))))
+
+(ert-deftest python-nav-forward-defun-1 ()
+  (python-tests-with-temp-buffer
+   "
+class A(object): # A
+
+    def a(self): # a
+        pass
+
+    def b(self): # b
+        pass
+
+    class B(object): # B
+
+        class C(object): # C
+
+            def d(self): # d
+                pass
+
+            # def e(self): # e
+            #     pass
+
+    def c(self): # c
+        pass
+
+    # def d(self): # d
+    #     pass
+"
+   (goto-char (point-min))
+   (should (= (save-excursion (python-nav-forward-defun))
+              (python-tests-look-at "(object): # A")))
+   (should (= (save-excursion (python-nav-forward-defun))
+              (python-tests-look-at "(self): # a")))
+   (should (= (save-excursion (python-nav-forward-defun))
+              (python-tests-look-at "(self): # b")))
+   (should (= (save-excursion (python-nav-forward-defun))
+              (python-tests-look-at "(object): # B")))
+   (should (= (save-excursion (python-nav-forward-defun))
+              (python-tests-look-at "(object): # C")))
+   (should (= (save-excursion (python-nav-forward-defun))
+              (python-tests-look-at "(self): # d")))
+   (should (= (save-excursion (python-nav-forward-defun))
+              (python-tests-look-at "(self): # c")))
+   (should (not (python-nav-forward-defun)))))
 
 (ert-deftest python-nav-beginning-of-statement-1 ()
   (python-tests-with-temp-buffer