diff options
| author | Fabián Ezequiel Gallina | 2015-02-07 16:43:47 -0300 |
|---|---|---|
| committer | Fabián Ezequiel Gallina | 2015-02-07 16:43:47 -0300 |
| commit | 2d467a0ff0cd446ec0d83044a0be819cbf874cdf (patch) | |
| tree | f719433f1e65da7a6a70f4bd3f1d9231eb626f9d | |
| parent | 86c50b9af1e68ca87bfc9e6d0cdb28ae2e53cc32 (diff) | |
| download | emacs-2d467a0ff0cd446ec0d83044a0be819cbf874cdf.tar.gz emacs-2d467a0ff0cd446ec0d83044a0be819cbf874cdf.zip | |
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.
| -rw-r--r-- | lisp/ChangeLog | 10 | ||||
| -rw-r--r-- | lisp/progmodes/python.el | 26 | ||||
| -rw-r--r-- | test/ChangeLog | 9 | ||||
| -rw-r--r-- | test/automated/python-tests.el | 198 |
4 files changed, 193 insertions, 50 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index a02f9642e55..655ae574468 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,5 +1,15 @@ | |||
| 1 | 2015-02-07 Fabián Ezequiel Gallina <fgallina@gnu.org> | 1 | 2015-02-07 Fabián Ezequiel Gallina <fgallina@gnu.org> |
| 2 | 2 | ||
| 3 | Fix hideshow integration. (Bug#19761) | ||
| 4 | |||
| 5 | * progmodes/python.el | ||
| 6 | (python-hideshow-forward-sexp-function): New function based on | ||
| 7 | Carlos Pita <carlosjosepita@gmail.com> patch. | ||
| 8 | (python-mode): Make `hs-special-modes-alist` use it and initialize | ||
| 9 | the end regexp with the empty string to avoid skipping parens. | ||
| 10 | |||
| 11 | 2015-02-07 Fabián Ezequiel Gallina <fgallina@gnu.org> | ||
| 12 | |||
| 3 | * progmodes/python.el (python-check-custom-command): Do not use | 13 | * progmodes/python.el (python-check-custom-command): Do not use |
| 4 | defvar-local for compat with Emacs<24.3. | 14 | defvar-local for compat with Emacs<24.3. |
| 5 | 15 | ||
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index de251181c14..3399429538f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el | |||
| @@ -3958,6 +3958,17 @@ Interactively, prompt for symbol." | |||
| 3958 | (message (python-eldoc--get-doc-at-point symbol))) | 3958 | (message (python-eldoc--get-doc-at-point symbol))) |
| 3959 | 3959 | ||
| 3960 | 3960 | ||
| 3961 | ;;; Hideshow | ||
| 3962 | |||
| 3963 | (defun python-hideshow-forward-sexp-function (arg) | ||
| 3964 | "Python specific `forward-sexp' function for `hs-minor-mode'. | ||
| 3965 | Argument ARG is ignored." | ||
| 3966 | arg ; Shut up, byte compiler. | ||
| 3967 | (python-nav-end-of-defun) | ||
| 3968 | (unless (python-info-current-line-empty-p) | ||
| 3969 | (backward-char))) | ||
| 3970 | |||
| 3971 | |||
| 3961 | ;;; Imenu | 3972 | ;;; Imenu |
| 3962 | 3973 | ||
| 3963 | (defvar python-imenu-format-item-label-function | 3974 | (defvar python-imenu-format-item-label-function |
| @@ -4693,11 +4704,16 @@ Arguments START and END narrow the buffer region to work on." | |||
| 4693 | (add-function :before-until (local 'eldoc-documentation-function) | 4704 | (add-function :before-until (local 'eldoc-documentation-function) |
| 4694 | #'python-eldoc-function)) | 4705 | #'python-eldoc-function)) |
| 4695 | 4706 | ||
| 4696 | (add-to-list 'hs-special-modes-alist | 4707 | (add-to-list |
| 4697 | `(python-mode "^\\s-*\\(?:def\\|class\\)\\>" nil "#" | 4708 | 'hs-special-modes-alist |
| 4698 | ,(lambda (_arg) | 4709 | `(python-mode |
| 4699 | (python-nav-end-of-defun)) | 4710 | "\\s-*\\(?:def\\|class\\)\\>" |
| 4700 | nil)) | 4711 | ;; Use the empty string as end regexp so it doesn't default to |
| 4712 | ;; "\\s)". This way parens at end of defun are properly hidden. | ||
| 4713 | "" | ||
| 4714 | "#" | ||
| 4715 | python-hideshow-forward-sexp-function | ||
| 4716 | nil)) | ||
| 4701 | 4717 | ||
| 4702 | (set (make-local-variable 'outline-regexp) | 4718 | (set (make-local-variable 'outline-regexp) |
| 4703 | (python-rx (* space) block-start)) | 4719 | (python-rx (* space) block-start)) |
diff --git a/test/ChangeLog b/test/ChangeLog index e0d4eeb323a..b1e21511d65 100644 --- a/test/ChangeLog +++ b/test/ChangeLog | |||
| @@ -1,3 +1,12 @@ | |||
| 1 | 2015-02-07 Fabián Ezequiel Gallina <fgallina@gnu.org> | ||
| 2 | |||
| 3 | * automated/python-tests.el | ||
| 4 | (python-tests-visible-string): New function. | ||
| 5 | (python-parens-electric-indent-1) | ||
| 6 | (python-triple-quote-pairing): Fix indentation, move require calls. | ||
| 7 | (python-hideshow-hide-levels-1) | ||
| 8 | (python-hideshow-hide-levels-2): New tests. | ||
| 9 | |||
| 1 | 2015-02-07 Dmitry Gutov <dgutov@yandex.ru> | 10 | 2015-02-07 Dmitry Gutov <dgutov@yandex.ru> |
| 2 | 11 | ||
| 3 | * automated/vc-tests.el (vc-test--working-revision): Fix | 12 | * automated/vc-tests.el (vc-test--working-revision): Fix |
diff --git a/test/automated/python-tests.el b/test/automated/python-tests.el index 672b05c39de..e5fcda95012 100644 --- a/test/automated/python-tests.el +++ b/test/automated/python-tests.el | |||
| @@ -24,6 +24,11 @@ | |||
| 24 | (require 'ert) | 24 | (require 'ert) |
| 25 | (require 'python) | 25 | (require 'python) |
| 26 | 26 | ||
| 27 | ;; Dependencies for testing: | ||
| 28 | (require 'electric) | ||
| 29 | (require 'hideshow) | ||
| 30 | |||
| 31 | |||
| 27 | (defmacro python-tests-with-temp-buffer (contents &rest body) | 32 | (defmacro python-tests-with-temp-buffer (contents &rest body) |
| 28 | "Create a `python-mode' enabled temp buffer with CONTENTS. | 33 | "Create a `python-mode' enabled temp buffer with CONTENTS. |
| 29 | BODY is code to be executed within the temp buffer. Point is | 34 | 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." | |||
| 104 | (call-interactively 'self-insert-command))) | 109 | (call-interactively 'self-insert-command))) |
| 105 | chars))) | 110 | chars))) |
| 106 | 111 | ||
| 112 | (defun python-tests-visible-string (&optional min max) | ||
| 113 | "Return the buffer string excluding invisible overlays. | ||
| 114 | Argument MIN and MAX delimit the region to be returned and | ||
| 115 | default to `point-min' and `point-max' respectively." | ||
| 116 | (let* ((min (or min (point-min))) | ||
| 117 | (max (or max (point-max))) | ||
| 118 | (buffer (current-buffer)) | ||
| 119 | (buffer-contents (buffer-substring-no-properties min max)) | ||
| 120 | (overlays | ||
| 121 | (sort (overlays-in min max) | ||
| 122 | (lambda (a b) | ||
| 123 | (let ((overlay-end-a (overlay-end a)) | ||
| 124 | (overlay-end-b (overlay-end b))) | ||
| 125 | (> overlay-end-a overlay-end-b)))))) | ||
| 126 | (with-temp-buffer | ||
| 127 | (insert buffer-contents) | ||
| 128 | (dolist (overlay overlays) | ||
| 129 | (if (overlay-get overlay 'invisible) | ||
| 130 | (delete-region (overlay-start overlay) | ||
| 131 | (overlay-end overlay)))) | ||
| 132 | (buffer-substring-no-properties (point-min) (point-max))))) | ||
| 133 | |||
| 107 | 134 | ||
| 108 | ;;; Tests for your tests, so you can test while you test. | 135 | ;;; Tests for your tests, so you can test while you test. |
| 109 | 136 | ||
| @@ -4358,12 +4385,11 @@ def foo(a, b, c): | |||
| 4358 | ;;; Electricity | 4385 | ;;; Electricity |
| 4359 | 4386 | ||
| 4360 | (ert-deftest python-parens-electric-indent-1 () | 4387 | (ert-deftest python-parens-electric-indent-1 () |
| 4361 | (require 'electric) | ||
| 4362 | (let ((eim electric-indent-mode)) | 4388 | (let ((eim electric-indent-mode)) |
| 4363 | (unwind-protect | 4389 | (unwind-protect |
| 4364 | (progn | 4390 | (progn |
| 4365 | (python-tests-with-temp-buffer | 4391 | (python-tests-with-temp-buffer |
| 4366 | " | 4392 | " |
| 4367 | from django.conf.urls import patterns, include, url | 4393 | from django.conf.urls import patterns, include, url |
| 4368 | 4394 | ||
| 4369 | from django.contrib import admin | 4395 | from django.contrib import admin |
| @@ -4375,66 +4401,148 @@ urlpatterns = patterns('', | |||
| 4375 | url(r'^$', views.index | 4401 | url(r'^$', views.index |
| 4376 | ) | 4402 | ) |
| 4377 | " | 4403 | " |
| 4378 | (electric-indent-mode 1) | 4404 | (electric-indent-mode 1) |
| 4379 | (python-tests-look-at "views.index") | 4405 | (python-tests-look-at "views.index") |
| 4380 | (end-of-line) | 4406 | (end-of-line) |
| 4381 | 4407 | ||
| 4382 | ;; Inserting commas within the same line should leave | 4408 | ;; Inserting commas within the same line should leave |
| 4383 | ;; indentation unchanged. | 4409 | ;; indentation unchanged. |
| 4384 | (python-tests-self-insert ",") | 4410 | (python-tests-self-insert ",") |
| 4385 | (should (= (current-indentation) 4)) | 4411 | (should (= (current-indentation) 4)) |
| 4386 | 4412 | ||
| 4387 | ;; As well as any other input happening within the same | 4413 | ;; As well as any other input happening within the same |
| 4388 | ;; set of parens. | 4414 | ;; set of parens. |
| 4389 | (python-tests-self-insert " name='index')") | 4415 | (python-tests-self-insert " name='index')") |
| 4390 | (should (= (current-indentation) 4)) | 4416 | (should (= (current-indentation) 4)) |
| 4391 | 4417 | ||
| 4392 | ;; But a comma outside it, should trigger indentation. | 4418 | ;; But a comma outside it, should trigger indentation. |
| 4393 | (python-tests-self-insert ",") | 4419 | (python-tests-self-insert ",") |
| 4394 | (should (= (current-indentation) 23)) | 4420 | (should (= (current-indentation) 23)) |
| 4395 | 4421 | ||
| 4396 | ;; Newline indents to the first argument column | 4422 | ;; Newline indents to the first argument column |
| 4397 | (python-tests-self-insert "\n") | 4423 | (python-tests-self-insert "\n") |
| 4398 | (should (= (current-indentation) 23)) | 4424 | (should (= (current-indentation) 23)) |
| 4399 | 4425 | ||
| 4400 | ;; All this input must not change indentation | 4426 | ;; All this input must not change indentation |
| 4401 | (indent-line-to 4) | 4427 | (indent-line-to 4) |
| 4402 | (python-tests-self-insert "url(r'^/login$', views.login)") | 4428 | (python-tests-self-insert "url(r'^/login$', views.login)") |
| 4403 | (should (= (current-indentation) 4)) | 4429 | (should (= (current-indentation) 4)) |
| 4404 | 4430 | ||
| 4405 | ;; But this comma does | 4431 | ;; But this comma does |
| 4406 | (python-tests-self-insert ",") | 4432 | (python-tests-self-insert ",") |
| 4407 | (should (= (current-indentation) 23)))) | 4433 | (should (= (current-indentation) 23)))) |
| 4408 | (or eim (electric-indent-mode -1))))) | 4434 | (or eim (electric-indent-mode -1))))) |
| 4409 | 4435 | ||
| 4410 | (ert-deftest python-triple-quote-pairing () | 4436 | (ert-deftest python-triple-quote-pairing () |
| 4411 | (require 'electric) | ||
| 4412 | (let ((epm electric-pair-mode)) | 4437 | (let ((epm electric-pair-mode)) |
| 4413 | (unwind-protect | 4438 | (unwind-protect |
| 4414 | (progn | 4439 | (progn |
| 4415 | (python-tests-with-temp-buffer | 4440 | (python-tests-with-temp-buffer |
| 4416 | "\"\"\n" | 4441 | "\"\"\n" |
| 4417 | (or epm (electric-pair-mode 1)) | 4442 | (or epm (electric-pair-mode 1)) |
| 4418 | (goto-char (1- (point-max))) | 4443 | (goto-char (1- (point-max))) |
| 4419 | (python-tests-self-insert ?\") | 4444 | (python-tests-self-insert ?\") |
| 4420 | (should (string= (buffer-string) | 4445 | (should (string= (buffer-string) |
| 4421 | "\"\"\"\"\"\"\n")) | 4446 | "\"\"\"\"\"\"\n")) |
| 4422 | (should (= (point) 4))) | 4447 | (should (= (point) 4))) |
| 4423 | (python-tests-with-temp-buffer | 4448 | (python-tests-with-temp-buffer |
| 4424 | "\n" | 4449 | "\n" |
| 4425 | (python-tests-self-insert (list ?\" ?\" ?\")) | 4450 | (python-tests-self-insert (list ?\" ?\" ?\")) |
| 4426 | (should (string= (buffer-string) | 4451 | (should (string= (buffer-string) |
| 4427 | "\"\"\"\"\"\"\n")) | 4452 | "\"\"\"\"\"\"\n")) |
| 4428 | (should (= (point) 4))) | 4453 | (should (= (point) 4))) |
| 4429 | (python-tests-with-temp-buffer | 4454 | (python-tests-with-temp-buffer |
| 4430 | "\"\n\"\"\n" | 4455 | "\"\n\"\"\n" |
| 4431 | (goto-char (1- (point-max))) | 4456 | (goto-char (1- (point-max))) |
| 4432 | (python-tests-self-insert ?\") | 4457 | (python-tests-self-insert ?\") |
| 4433 | (should (= (point) (1- (point-max)))) | 4458 | (should (= (point) (1- (point-max)))) |
| 4434 | (should (string= (buffer-string) | 4459 | (should (string= (buffer-string) |
| 4435 | "\"\n\"\"\"\n")))) | 4460 | "\"\n\"\"\"\n")))) |
| 4436 | (or epm (electric-pair-mode -1))))) | 4461 | (or epm (electric-pair-mode -1))))) |
| 4437 | 4462 | ||
| 4463 | |||
| 4464 | ;;; Hideshow support | ||
| 4465 | |||
| 4466 | (ert-deftest python-hideshow-hide-levels-1 () | ||
| 4467 | "Should hide all methods when called after class start." | ||
| 4468 | (let ((enabled hs-minor-mode)) | ||
| 4469 | (unwind-protect | ||
| 4470 | (progn | ||
| 4471 | (python-tests-with-temp-buffer | ||
| 4472 | " | ||
| 4473 | class SomeClass: | ||
| 4474 | |||
| 4475 | def __init__(self, arg, kwarg=1): | ||
| 4476 | self.arg = arg | ||
| 4477 | self.kwarg = kwarg | ||
| 4478 | |||
| 4479 | def filter(self, nums): | ||
| 4480 | def fn(item): | ||
| 4481 | return item in [self.arg, self.kwarg] | ||
| 4482 | return filter(fn, nums) | ||
| 4483 | |||
| 4484 | def __str__(self): | ||
| 4485 | return '%s-%s' % (self.arg, self.kwarg) | ||
| 4486 | " | ||
| 4487 | (hs-minor-mode 1) | ||
| 4488 | (python-tests-look-at "class SomeClass:") | ||
| 4489 | (forward-line) | ||
| 4490 | (hs-hide-level 1) | ||
| 4491 | (should | ||
| 4492 | (string= | ||
| 4493 | (python-tests-visible-string) | ||
| 4494 | " | ||
| 4495 | class SomeClass: | ||
| 4496 | |||
| 4497 | def __init__(self, arg, kwarg=1): | ||
| 4498 | def filter(self, nums): | ||
| 4499 | def __str__(self):")))) | ||
| 4500 | (or enabled (hs-minor-mode -1))))) | ||
| 4501 | |||
| 4502 | (ert-deftest python-hideshow-hide-levels-2 () | ||
| 4503 | "Should hide nested methods and parens at end of defun." | ||
| 4504 | (let ((enabled hs-minor-mode)) | ||
| 4505 | (unwind-protect | ||
| 4506 | (progn | ||
| 4507 | (python-tests-with-temp-buffer | ||
| 4508 | " | ||
| 4509 | class SomeClass: | ||
| 4510 | |||
| 4511 | def __init__(self, arg, kwarg=1): | ||
| 4512 | self.arg = arg | ||
| 4513 | self.kwarg = kwarg | ||
| 4514 | |||
| 4515 | def filter(self, nums): | ||
| 4516 | def fn(item): | ||
| 4517 | return item in [self.arg, self.kwarg] | ||
| 4518 | return filter(fn, nums) | ||
| 4519 | |||
| 4520 | def __str__(self): | ||
| 4521 | return '%s-%s' % (self.arg, self.kwarg) | ||
| 4522 | " | ||
| 4523 | (hs-minor-mode 1) | ||
| 4524 | (python-tests-look-at "def fn(item):") | ||
| 4525 | (hs-hide-block) | ||
| 4526 | (should | ||
| 4527 | (string= | ||
| 4528 | (python-tests-visible-string) | ||
| 4529 | " | ||
| 4530 | class SomeClass: | ||
| 4531 | |||
| 4532 | def __init__(self, arg, kwarg=1): | ||
| 4533 | self.arg = arg | ||
| 4534 | self.kwarg = kwarg | ||
| 4535 | |||
| 4536 | def filter(self, nums): | ||
| 4537 | def fn(item): | ||
| 4538 | return filter(fn, nums) | ||
| 4539 | |||
| 4540 | def __str__(self): | ||
| 4541 | return '%s-%s' % (self.arg, self.kwarg) | ||
| 4542 | ")))) | ||
| 4543 | (or enabled (hs-minor-mode -1))))) | ||
| 4544 | |||
| 4545 | |||
| 4438 | 4546 | ||
| 4439 | (provide 'python-tests) | 4547 | (provide 'python-tests) |
| 4440 | 4548 | ||