python.el: Indent docstring lines to base-indent

Fixes: debbugs:19595

Thanks to immerrr <immerrr@gmail.com> for reporting and providing
an initial patch.

* lisp/progmodes/python.el
(python-indent-context): Add :inside-docstring context.
(python-indent--calculate-indentation): Handle :inside-docstring.
(python-indent-region): Re-indent docstrings.

* test/automated/python-tests.el (python-indent-region-5)
(python-indent-inside-string-2): Fix tests.
parent 911ed2eb
......@@ -844,7 +844,9 @@ keyword
;; Inside a string.
((let ((start (python-syntax-context 'string ppss)))
(when start
(cons :inside-string start))))
(cons (if (python-info-docstring-p)
:inside-docstring
:inside-string) start))))
;; Inside a paren.
((let* ((start (python-syntax-context 'paren ppss))
(starts-in-newline
......@@ -989,6 +991,12 @@ possibilities can be narrowed to specific indentation points."
;; Copy previous indentation.
(goto-char start)
(current-indentation))
(`(:inside-docstring . ,start)
(let* ((line-indentation (current-indentation))
(base-indent (progn
(goto-char start)
(current-indentation))))
(max line-indentation base-indent)))
(`(,(or :after-block-start
:after-backslash-first-line
:inside-paren-newline-start) . ,start)
......@@ -1138,14 +1146,15 @@ Called from a program, START and END specify the region to indent."
(not line-is-comment-p))
(python-info-current-line-empty-p)))))
;; Don't mess with strings, unless it's the
;; enclosing set of quotes.
;; enclosing set of quotes or a docstring.
(or (not (python-syntax-context 'string))
(eq
(syntax-after
(+ (1- (point))
(current-indentation)
(python-syntax-count-quotes (char-after) (point))))
(string-to-syntax "|")))
(string-to-syntax "|"))
(python-info-docstring-p))
;; Skip if current line is a block start, a
;; dedenter or block ender.
(save-excursion
......
......@@ -1014,7 +1014,7 @@ lines
def fn(a, b, c=True):
'''docstring
bunch
of
of
lines
'''
"
......@@ -1022,16 +1022,17 @@ def fn(a, b, c=True):
(should (eq (car (python-indent-context)) :after-block-start))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "bunch")
(should (eq (car (python-indent-context)) :inside-string))
(should (eq (car (python-indent-context)) :inside-docstring))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "of")
(should (eq (car (python-indent-context)) :inside-string))
(should (= (python-indent-calculate-indentation) 4))
(should (eq (car (python-indent-context)) :inside-docstring))
;; Any indentation deeper than the base-indent must remain unmodified.
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "lines")
(should (eq (car (python-indent-context)) :inside-string))
(should (eq (car (python-indent-context)) :inside-docstring))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "'''")
(should (eq (car (python-indent-context)) :inside-string))
(should (eq (car (python-indent-context)) :inside-docstring))
(should (= (python-indent-calculate-indentation) 4))))
(ert-deftest python-indent-inside-string-3 ()
......@@ -1189,21 +1190,33 @@ def f():
expected)))))
(ert-deftest python-indent-region-5 ()
"Test region indentation leaves strings untouched (start delimiter)."
"Test region indentation for docstrings."
(let ((contents "
def f():
'''
this is
a multiline
a multiline
string
'''
x = \\
'''
this is an arbitrarily
indented multiline
string
'''
")
(expected "
def f():
'''
this is
a multiline
string
this is
a multiline
string
'''
x = \\
'''
this is an arbitrarily
indented multiline
string
'''
"))
(python-tests-with-temp-buffer
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment