aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabián Ezequiel Gallina2015-04-05 23:58:13 -0300
committerFabián Ezequiel Gallina2015-04-05 23:58:13 -0300
commitdeea36f0ece7b1b14afe2a833b6a0f66d59f4459 (patch)
treee3e41e0a5a03b0ea83b293c0c65835be038d56d4
parent7514b24b6a512d85b762c603e9e0107d2c8a52f1 (diff)
downloademacs-deea36f0ece7b1b14afe2a833b6a0f66d59f4459.tar.gz
emacs-deea36f0ece7b1b14afe2a833b6a0f66d59f4459.zip
python.el: Enhance docstring detection following PEP-257.
* lisp/progmodes/python.el (python-docstring-at-p): Remove function. (python-info-assignment-statement-p): New function. (python-info-assignment-continuation-line-p): Use it. (python-info-docstring-p): New function. (python-font-lock-syntactic-face-function) (python-fill-string): Use it. * test/automated/python-tests.el (python-info-assignment-statement-p-1) (python-info-assignment-statement-p-2) (python-info-assignment-statement-p-3, python-info-docstring-p-1) (python-info-docstring-p-2, python-info-docstring-p-3) (python-info-docstring-p-4, python-info-docstring-p-5) (python-info-docstring-p-6): New tests.
-rw-r--r--lisp/ChangeLog11
-rw-r--r--lisp/progmodes/python.el96
-rw-r--r--test/ChangeLog9
-rw-r--r--test/automated/python-tests.el173
4 files changed, 262 insertions, 27 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 8ea7f4a3cc8..c7d4dd5bac0 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,14 @@
12015-04-06 Fabián Ezequiel Gallina <fgallina@gnu.org>
2
3 python.el: Enhance docstring detection following PEP-257.
4
5 * progmodes/python.el (python-docstring-at-p): Remove function.
6 (python-info-assignment-statement-p): New function.
7 (python-info-assignment-continuation-line-p): Use it.
8 (python-info-docstring-p): New function.
9 (python-font-lock-syntactic-face-function)
10 (python-fill-string): Use it.
11
12015-04-05 Eli Zaretskii <eliz@gnu.org> 122015-04-05 Eli Zaretskii <eliz@gnu.org>
2 13
3 * ses.el (ses-sym-rowcol): Move up, before the first use, to avoid 14 * ses.el (ses-sym-rowcol): Move up, before the first use, to avoid
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 67b44aa1bbe..f402ad83cca 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -482,19 +482,10 @@ The type returned can be `comment', `string' or `paren'."
482 'python-info-ppss-comment-or-string-p 482 'python-info-ppss-comment-or-string-p
483 #'python-syntax-comment-or-string-p "24.3") 483 #'python-syntax-comment-or-string-p "24.3")
484 484
485(defun python-docstring-at-p (pos)
486 "Check to see if there is a docstring at POS."
487 (save-excursion
488 (goto-char pos)
489 (if (looking-at-p "'''\\|\"\"\"")
490 (progn
491 (python-nav-backward-statement)
492 (looking-at "\\`\\|class \\|def "))
493 nil)))
494
495(defun python-font-lock-syntactic-face-function (state) 485(defun python-font-lock-syntactic-face-function (state)
486 "Return syntactic face given STATE."
496 (if (nth 3 state) 487 (if (nth 3 state)
497 (if (python-docstring-at-p (nth 8 state)) 488 (if (python-info-docstring-p state)
498 font-lock-doc-face 489 font-lock-doc-face
499 font-lock-string-face) 490 font-lock-string-face)
500 font-lock-comment-face)) 491 font-lock-comment-face))
@@ -3587,17 +3578,12 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'."
3587 (`pep-257 (and multi-line-p (cons nil 2))) 3578 (`pep-257 (and multi-line-p (cons nil 2)))
3588 (`pep-257-nn (and multi-line-p (cons nil 1))) 3579 (`pep-257-nn (and multi-line-p (cons nil 1)))
3589 (`symmetric (and multi-line-p (cons 1 1))))) 3580 (`symmetric (and multi-line-p (cons 1 1)))))
3590 (docstring-p (save-excursion
3591 ;; Consider docstrings those strings which
3592 ;; start on a line by themselves.
3593 (python-nav-beginning-of-statement)
3594 (and (= (point) str-start-pos))))
3595 (fill-paragraph-function)) 3581 (fill-paragraph-function))
3596 (save-restriction 3582 (save-restriction
3597 (narrow-to-region str-start-pos str-end-pos) 3583 (narrow-to-region str-start-pos str-end-pos)
3598 (fill-paragraph justify)) 3584 (fill-paragraph justify))
3599 (save-excursion 3585 (save-excursion
3600 (when (and docstring-p python-fill-docstring-style) 3586 (when (and (python-info-docstring-p) python-fill-docstring-style)
3601 ;; Add the number of newlines indicated by the selected style 3587 ;; Add the number of newlines indicated by the selected style
3602 ;; at the start of the docstring. 3588 ;; at the start of the docstring.
3603 (goto-char (+ str-start-pos num-quotes)) 3589 (goto-char (+ str-start-pos num-quotes))
@@ -4423,23 +4409,40 @@ where the continued line ends."
4423 (when (looking-at (python-rx block-start)) 4409 (when (looking-at (python-rx block-start))
4424 (point-marker))))) 4410 (point-marker)))))
4425 4411
4412(defun python-info-assignment-statement-p (&optional current-line-only)
4413 "Check if current line is an assignment.
4414With argument CURRENT-LINE-ONLY is non-nil, don't follow any
4415continuations, just check the if current line is an assignment."
4416 (save-excursion
4417 (let ((found nil))
4418 (if current-line-only
4419 (back-to-indentation)
4420 (python-nav-beginning-of-statement))
4421 (while (and
4422 (re-search-forward (python-rx not-simple-operator
4423 assignment-operator
4424 (group not-simple-operator))
4425 (line-end-position) t)
4426 (not found))
4427 (save-excursion
4428 ;; The assignment operator should not be inside a string.
4429 (backward-char (length (match-string-no-properties 1)))
4430 (setq found (not (python-syntax-context-type)))))
4431 (when found
4432 (skip-syntax-forward " ")
4433 (point-marker)))))
4434
4435;; TODO: rename to clarify this is only for the first continuation
4436;; line or remove it and move its body to `python-indent-context'.
4426(defun python-info-assignment-continuation-line-p () 4437(defun python-info-assignment-continuation-line-p ()
4427 "Check if current line is a continuation of an assignment. 4438 "Check if current line is the first continuation of an assignment.
4428When current line is continuation of another with an assignment 4439When current line is continuation of another with an assignment
4429return the point of the first non-blank character after the 4440return the point of the first non-blank character after the
4430operator." 4441operator."
4431 (save-excursion 4442 (save-excursion
4432 (when (python-info-continuation-line-p) 4443 (when (python-info-continuation-line-p)
4433 (forward-line -1) 4444 (forward-line -1)
4434 (back-to-indentation) 4445 (python-info-assignment-statement-p t))))
4435 (when (and (not (looking-at (python-rx block-start)))
4436 (and (re-search-forward (python-rx not-simple-operator
4437 assignment-operator
4438 not-simple-operator)
4439 (line-end-position) t)
4440 (not (python-syntax-context-type))))
4441 (skip-syntax-forward "\s")
4442 (point-marker)))))
4443 4446
4444(defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss) 4447(defun python-info-looking-at-beginning-of-defun (&optional syntax-ppss)
4445 "Check if point is at `beginning-of-defun' using SYNTAX-PPSS." 4448 "Check if point is at `beginning-of-defun' using SYNTAX-PPSS."
@@ -4464,6 +4467,45 @@ operator."
4464 (* whitespace) line-end)) 4467 (* whitespace) line-end))
4465 (string-equal "" (match-string-no-properties 1)))) 4468 (string-equal "" (match-string-no-properties 1))))
4466 4469
4470(defun python-info-docstring-p (&optional syntax-ppss)
4471 "Return non-nil if point is in a docstring.
4472When optional argument SYNTAX-PPSS is given, use that instead of
4473point's current `syntax-ppss'."
4474 ;;; https://www.python.org/dev/peps/pep-0257/#what-is-a-docstring
4475 (save-excursion
4476 (when (and syntax-ppss (python-syntax-context 'string syntax-ppss))
4477 (goto-char (nth 8 syntax-ppss)))
4478 (python-nav-beginning-of-statement)
4479 (let ((counter 1)
4480 (indentation (current-indentation))
4481 (backward-sexp-point)
4482 (re (concat "[uU]?[rR]?"
4483 (python-rx string-delimiter))))
4484 (when (and
4485 (not (python-info-assignment-statement-p))
4486 (looking-at-p re)
4487 ;; Allow up to two consecutive docstrings only.
4488 (>=
4489 2
4490 (progn
4491 (while (save-excursion
4492 (python-nav-backward-sexp)
4493 (setq backward-sexp-point (point))
4494 (and (= indentation (current-indentation))
4495 (looking-at-p
4496 (concat "[uU]?[rR]?"
4497 (python-rx string-delimiter)))))
4498 ;; Previous sexp was a string, restore point.
4499 (goto-char backward-sexp-point)
4500 (cl-incf counter))
4501 counter)))
4502 (python-util-forward-comment -1)
4503 (python-nav-beginning-of-statement)
4504 (cond ((bobp))
4505 ((python-info-assignment-statement-p) t)
4506 ((python-info-looking-at-beginning-of-defun))
4507 (t nil))))))
4508
4467(defun python-info-encoding-from-cookie () 4509(defun python-info-encoding-from-cookie ()
4468 "Detect current buffer's encoding from its coding cookie. 4510 "Detect current buffer's encoding from its coding cookie.
4469Returns the encoding as a symbol." 4511Returns the encoding as a symbol."
diff --git a/test/ChangeLog b/test/ChangeLog
index f7bec2ee119..813f5dd42b7 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,12 @@
12015-04-06 Fabián Ezequiel Gallina <fgallina@gnu.org>
2
3 * automated/python-tests.el (python-info-assignment-statement-p-1)
4 (python-info-assignment-statement-p-2)
5 (python-info-assignment-statement-p-3, python-info-docstring-p-1)
6 (python-info-docstring-p-2, python-info-docstring-p-3)
7 (python-info-docstring-p-4, python-info-docstring-p-5)
8 (python-info-docstring-p-6): New tests.
9
12015-04-01 Artur Malabarba <bruce.connor.am@gmail.com> 102015-04-01 Artur Malabarba <bruce.connor.am@gmail.com>
2 11
3 * automated/package-test.el: Avoid async while testing. 12 * automated/package-test.el: Avoid async while testing.
diff --git a/test/automated/python-tests.el b/test/automated/python-tests.el
index b377a26f77a..22c111fc04a 100644
--- a/test/automated/python-tests.el
+++ b/test/automated/python-tests.el
@@ -4273,6 +4273,49 @@ def foo(a,
4273 (python-tests-look-at "c):") 4273 (python-tests-look-at "c):")
4274 (should (not (python-info-block-continuation-line-p))))) 4274 (should (not (python-info-block-continuation-line-p)))))
4275 4275
4276(ert-deftest python-info-assignment-statement-p-1 ()
4277 (python-tests-with-temp-buffer
4278 "
4279data = foo(), bar() \\\\
4280 baz(), 4 \\\\
4281 5, 6
4282"
4283 (python-tests-look-at "data = foo(), bar()")
4284 (should (python-info-assignment-statement-p))
4285 (should (python-info-assignment-statement-p t))
4286 (python-tests-look-at "baz(), 4")
4287 (should (python-info-assignment-statement-p))
4288 (should (not (python-info-assignment-statement-p t)))
4289 (python-tests-look-at "5, 6")
4290 (should (python-info-assignment-statement-p))
4291 (should (not (python-info-assignment-statement-p t)))))
4292
4293(ert-deftest python-info-assignment-statement-p-2 ()
4294 (python-tests-with-temp-buffer
4295 "
4296data = (foo(), bar()
4297 baz(), 4
4298 5, 6)
4299"
4300 (python-tests-look-at "data = (foo(), bar()")
4301 (should (python-info-assignment-statement-p))
4302 (should (python-info-assignment-statement-p t))
4303 (python-tests-look-at "baz(), 4")
4304 (should (python-info-assignment-statement-p))
4305 (should (not (python-info-assignment-statement-p t)))
4306 (python-tests-look-at "5, 6)")
4307 (should (python-info-assignment-statement-p))
4308 (should (not (python-info-assignment-statement-p t)))))
4309
4310(ert-deftest python-info-assignment-statement-p-3 ()
4311 (python-tests-with-temp-buffer
4312 "
4313data '=' 42
4314"
4315 (python-tests-look-at "data '=' 42")
4316 (should (not (python-info-assignment-statement-p)))
4317 (should (not (python-info-assignment-statement-p t)))))
4318
4276(ert-deftest python-info-assignment-continuation-line-p-1 () 4319(ert-deftest python-info-assignment-continuation-line-p-1 ()
4277 (python-tests-with-temp-buffer 4320 (python-tests-with-temp-buffer
4278 " 4321 "
@@ -4360,6 +4403,136 @@ foo = True # another comment
4360 (forward-line 1) 4403 (forward-line 1)
4361 (should (python-info-current-line-empty-p)))) 4404 (should (python-info-current-line-empty-p))))
4362 4405
4406(ert-deftest python-info-docstring-p-1 ()
4407 "Test module docstring detection."
4408 (python-tests-with-temp-buffer
4409 "# -*- coding: utf-8 -*-
4410#!/usr/bin/python
4411
4412'''
4413Module Docstring Django style.
4414'''
4415u'''Additional module docstring.'''
4416'''Not a module docstring.'''
4417"
4418 (python-tests-look-at "Module Docstring Django style.")
4419 (should (python-info-docstring-p))
4420 (python-tests-look-at "u'''Additional module docstring.'''")
4421 (should (python-info-docstring-p))
4422 (python-tests-look-at "'''Not a module docstring.'''")
4423 (should (not (python-info-docstring-p)))))
4424
4425(ert-deftest python-info-docstring-p-2 ()
4426 "Test variable docstring detection."
4427 (python-tests-with-temp-buffer
4428 "
4429variable = 42
4430U'''Variable docstring.'''
4431'''Additional variable docstring.'''
4432'''Not a variable docstring.'''
4433"
4434 (python-tests-look-at "Variable docstring.")
4435 (should (python-info-docstring-p))
4436 (python-tests-look-at "u'''Additional variable docstring.'''")
4437 (should (python-info-docstring-p))
4438 (python-tests-look-at "'''Not a variable docstring.'''")
4439 (should (not (python-info-docstring-p)))))
4440
4441(ert-deftest python-info-docstring-p-3 ()
4442 "Test function docstring detection."
4443 (python-tests-with-temp-buffer
4444 "
4445def func(a, b):
4446 r'''
4447 Function docstring.
4448
4449 onetwo style.
4450 '''
4451 R'''Additional function docstring.'''
4452 '''Not a function docstring.'''
4453 return a + b
4454"
4455 (python-tests-look-at "Function docstring.")
4456 (should (python-info-docstring-p))
4457 (python-tests-look-at "R'''Additional function docstring.'''")
4458 (should (python-info-docstring-p))
4459 (python-tests-look-at "'''Not a function docstring.'''")
4460 (should (not (python-info-docstring-p)))))
4461
4462(ert-deftest python-info-docstring-p-4 ()
4463 "Test class docstring detection."
4464 (python-tests-with-temp-buffer
4465 "
4466class Class:
4467 ur'''
4468 Class docstring.
4469
4470 symmetric style.
4471 '''
4472 uR'''
4473 Additional class docstring.
4474 '''
4475 '''Not a class docstring.'''
4476 pass
4477"
4478 (python-tests-look-at "Class docstring.")
4479 (should (python-info-docstring-p))
4480 (python-tests-look-at "uR'''") ;; Additional class docstring
4481 (should (python-info-docstring-p))
4482 (python-tests-look-at "'''Not a class docstring.'''")
4483 (should (not (python-info-docstring-p)))))
4484
4485(ert-deftest python-info-docstring-p-5 ()
4486 "Test class attribute docstring detection."
4487 (python-tests-with-temp-buffer
4488 "
4489class Class:
4490 attribute = 42
4491 Ur'''
4492 Class attribute docstring.
4493
4494 pep-257 style.
4495
4496 '''
4497 UR'''
4498 Additional class attribute docstring.
4499 '''
4500 '''Not a class attribute docstring.'''
4501 pass
4502"
4503 (python-tests-look-at "Class attribute docstring.")
4504 (should (python-info-docstring-p))
4505 (python-tests-look-at "UR'''") ;; Additional class attr docstring
4506 (should (python-info-docstring-p))
4507 (python-tests-look-at "'''Not a class attribute docstring.'''")
4508 (should (not (python-info-docstring-p)))))
4509
4510(ert-deftest python-info-docstring-p-6 ()
4511 "Test class method docstring detection."
4512 (python-tests-with-temp-buffer
4513 "
4514class Class:
4515
4516 def __init__(self, a, b):
4517 self.a = a
4518 self.b = b
4519
4520 def __call__(self):
4521 '''Method docstring.
4522
4523 pep-257-nn style.
4524 '''
4525 '''Additional method docstring.'''
4526 '''Not a method docstring.'''
4527 return self.a + self.b
4528"
4529 (python-tests-look-at "Method docstring.")
4530 (should (python-info-docstring-p))
4531 (python-tests-look-at "'''Additional method docstring.'''")
4532 (should (python-info-docstring-p))
4533 (python-tests-look-at "'''Not a method docstring.'''")
4534 (should (not (python-info-docstring-p)))))
4535
4363(ert-deftest python-info-encoding-from-cookie-1 () 4536(ert-deftest python-info-encoding-from-cookie-1 ()
4364 "Should detect it on first line." 4537 "Should detect it on first line."
4365 (python-tests-with-temp-buffer 4538 (python-tests-with-temp-buffer