Fix hideshow integration.

Fixes: debbugs:19761

* lisp/progmodes/python.el
(python-hideshow-forward-sexp-function): New function based on
Carlos Pita <carlosjosepita@gmail.com> patch.
(python-mode): Make `hs-special-modes-alist` use it and initialize
the end regexp with the empty string to avoid skipping parens.

* test/automated/python-tests.el
(python-tests-visible-string): New function.
(python-parens-electric-indent-1)
(python-triple-quote-pairing): Fix indentation, move require calls.
(python-hideshow-hide-levels-1)
(python-hideshow-hide-levels-2): New tests.
This commit is contained in:
Fabián Ezequiel Gallina 2015-02-07 16:43:47 -03:00
parent 86c50b9af1
commit 2d467a0ff0
4 changed files with 193 additions and 50 deletions

View file

@ -1,3 +1,13 @@
2015-02-07 Fabián Ezequiel Gallina <fgallina@gnu.org>
Fix hideshow integration. (Bug#19761)
* progmodes/python.el
(python-hideshow-forward-sexp-function): New function based on
Carlos Pita <carlosjosepita@gmail.com> patch.
(python-mode): Make `hs-special-modes-alist` use it and initialize
the end regexp with the empty string to avoid skipping parens.
2015-02-07 Fabián Ezequiel Gallina <fgallina@gnu.org>
* progmodes/python.el (python-check-custom-command): Do not use

View file

@ -3957,6 +3957,17 @@ Interactively, prompt for symbol."
nil nil symbol))))
(message (python-eldoc--get-doc-at-point symbol)))
;;; Hideshow
(defun python-hideshow-forward-sexp-function (arg)
"Python specific `forward-sexp' function for `hs-minor-mode'.
Argument ARG is ignored."
arg ; Shut up, byte compiler.
(python-nav-end-of-defun)
(unless (python-info-current-line-empty-p)
(backward-char)))
;;; Imenu
@ -4693,11 +4704,16 @@ Arguments START and END narrow the buffer region to work on."
(add-function :before-until (local 'eldoc-documentation-function)
#'python-eldoc-function))
(add-to-list 'hs-special-modes-alist
`(python-mode "^\\s-*\\(?:def\\|class\\)\\>" nil "#"
,(lambda (_arg)
(python-nav-end-of-defun))
nil))
(add-to-list
'hs-special-modes-alist
`(python-mode
"\\s-*\\(?:def\\|class\\)\\>"
;; Use the empty string as end regexp so it doesn't default to
;; "\\s)". This way parens at end of defun are properly hidden.
""
"#"
python-hideshow-forward-sexp-function
nil))
(set (make-local-variable 'outline-regexp)
(python-rx (* space) block-start))

View file

@ -1,3 +1,12 @@
2015-02-07 Fabián Ezequiel Gallina <fgallina@gnu.org>
* automated/python-tests.el
(python-tests-visible-string): New function.
(python-parens-electric-indent-1)
(python-triple-quote-pairing): Fix indentation, move require calls.
(python-hideshow-hide-levels-1)
(python-hideshow-hide-levels-2): New tests.
2015-02-07 Dmitry Gutov <dgutov@yandex.ru>
* automated/vc-tests.el (vc-test--working-revision): Fix

View file

@ -24,6 +24,11 @@
(require 'ert)
(require 'python)
;; Dependencies for testing:
(require 'electric)
(require 'hideshow)
(defmacro python-tests-with-temp-buffer (contents &rest body)
"Create a `python-mode' enabled temp buffer with CONTENTS.
BODY is code to be executed within the temp buffer. Point is
@ -104,6 +109,28 @@ STRING, it is skipped so the next STRING occurrence is selected."
(call-interactively 'self-insert-command)))
chars)))
(defun python-tests-visible-string (&optional min max)
"Return the buffer string excluding invisible overlays.
Argument MIN and MAX delimit the region to be returned and
default to `point-min' and `point-max' respectively."
(let* ((min (or min (point-min)))
(max (or max (point-max)))
(buffer (current-buffer))
(buffer-contents (buffer-substring-no-properties min max))
(overlays
(sort (overlays-in min max)
(lambda (a b)
(let ((overlay-end-a (overlay-end a))
(overlay-end-b (overlay-end b)))
(> overlay-end-a overlay-end-b))))))
(with-temp-buffer
(insert buffer-contents)
(dolist (overlay overlays)
(if (overlay-get overlay 'invisible)
(delete-region (overlay-start overlay)
(overlay-end overlay))))
(buffer-substring-no-properties (point-min) (point-max)))))
;;; Tests for your tests, so you can test while you test.
@ -4358,12 +4385,11 @@ def foo(a, b, c):
;;; Electricity
(ert-deftest python-parens-electric-indent-1 ()
(require 'electric)
(let ((eim electric-indent-mode))
(unwind-protect
(progn
(python-tests-with-temp-buffer
"
"
from django.conf.urls import patterns, include, url
from django.contrib import admin
@ -4375,66 +4401,148 @@ urlpatterns = patterns('',
url(r'^$', views.index
)
"
(electric-indent-mode 1)
(python-tests-look-at "views.index")
(end-of-line)
(electric-indent-mode 1)
(python-tests-look-at "views.index")
(end-of-line)
;; Inserting commas within the same line should leave
;; indentation unchanged.
(python-tests-self-insert ",")
(should (= (current-indentation) 4))
;; Inserting commas within the same line should leave
;; indentation unchanged.
(python-tests-self-insert ",")
(should (= (current-indentation) 4))
;; As well as any other input happening within the same
;; set of parens.
(python-tests-self-insert " name='index')")
(should (= (current-indentation) 4))
;; As well as any other input happening within the same
;; set of parens.
(python-tests-self-insert " name='index')")
(should (= (current-indentation) 4))
;; But a comma outside it, should trigger indentation.
(python-tests-self-insert ",")
(should (= (current-indentation) 23))
;; But a comma outside it, should trigger indentation.
(python-tests-self-insert ",")
(should (= (current-indentation) 23))
;; Newline indents to the first argument column
(python-tests-self-insert "\n")
(should (= (current-indentation) 23))
;; Newline indents to the first argument column
(python-tests-self-insert "\n")
(should (= (current-indentation) 23))
;; All this input must not change indentation
(indent-line-to 4)
(python-tests-self-insert "url(r'^/login$', views.login)")
(should (= (current-indentation) 4))
;; All this input must not change indentation
(indent-line-to 4)
(python-tests-self-insert "url(r'^/login$', views.login)")
(should (= (current-indentation) 4))
;; But this comma does
(python-tests-self-insert ",")
(should (= (current-indentation) 23))))
;; But this comma does
(python-tests-self-insert ",")
(should (= (current-indentation) 23))))
(or eim (electric-indent-mode -1)))))
(ert-deftest python-triple-quote-pairing ()
(require 'electric)
(let ((epm electric-pair-mode))
(unwind-protect
(progn
(python-tests-with-temp-buffer
"\"\"\n"
(or epm (electric-pair-mode 1))
(goto-char (1- (point-max)))
(python-tests-self-insert ?\")
(should (string= (buffer-string)
"\"\"\"\"\"\"\n"))
(should (= (point) 4)))
"\"\"\n"
(or epm (electric-pair-mode 1))
(goto-char (1- (point-max)))
(python-tests-self-insert ?\")
(should (string= (buffer-string)
"\"\"\"\"\"\"\n"))
(should (= (point) 4)))
(python-tests-with-temp-buffer
"\n"
(python-tests-self-insert (list ?\" ?\" ?\"))
(should (string= (buffer-string)
"\"\"\"\"\"\"\n"))
(should (= (point) 4)))
"\n"
(python-tests-self-insert (list ?\" ?\" ?\"))
(should (string= (buffer-string)
"\"\"\"\"\"\"\n"))
(should (= (point) 4)))
(python-tests-with-temp-buffer
"\"\n\"\"\n"
(goto-char (1- (point-max)))
(python-tests-self-insert ?\")
(should (= (point) (1- (point-max))))
(should (string= (buffer-string)
"\"\n\"\"\"\n"))))
"\"\n\"\"\n"
(goto-char (1- (point-max)))
(python-tests-self-insert ?\")
(should (= (point) (1- (point-max))))
(should (string= (buffer-string)
"\"\n\"\"\"\n"))))
(or epm (electric-pair-mode -1)))))
;;; Hideshow support
(ert-deftest python-hideshow-hide-levels-1 ()
"Should hide all methods when called after class start."
(let ((enabled hs-minor-mode))
(unwind-protect
(progn
(python-tests-with-temp-buffer
"
class SomeClass:
def __init__(self, arg, kwarg=1):
self.arg = arg
self.kwarg = kwarg
def filter(self, nums):
def fn(item):
return item in [self.arg, self.kwarg]
return filter(fn, nums)
def __str__(self):
return '%s-%s' % (self.arg, self.kwarg)
"
(hs-minor-mode 1)
(python-tests-look-at "class SomeClass:")
(forward-line)
(hs-hide-level 1)
(should
(string=
(python-tests-visible-string)
"
class SomeClass:
def __init__(self, arg, kwarg=1):
def filter(self, nums):
def __str__(self):"))))
(or enabled (hs-minor-mode -1)))))
(ert-deftest python-hideshow-hide-levels-2 ()
"Should hide nested methods and parens at end of defun."
(let ((enabled hs-minor-mode))
(unwind-protect
(progn
(python-tests-with-temp-buffer
"
class SomeClass:
def __init__(self, arg, kwarg=1):
self.arg = arg
self.kwarg = kwarg
def filter(self, nums):
def fn(item):
return item in [self.arg, self.kwarg]
return filter(fn, nums)
def __str__(self):
return '%s-%s' % (self.arg, self.kwarg)
"
(hs-minor-mode 1)
(python-tests-look-at "def fn(item):")
(hs-hide-block)
(should
(string=
(python-tests-visible-string)
"
class SomeClass:
def __init__(self, arg, kwarg=1):
self.arg = arg
self.kwarg = kwarg
def filter(self, nums):
def fn(item):
return filter(fn, nums)
def __str__(self):
return '%s-%s' % (self.arg, self.kwarg)
"))))
(or enabled (hs-minor-mode -1)))))
(provide 'python-tests)