summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkobarity <kobarity@gmail.com>2023-03-12 17:05:54 +0900
committerEli Zaretskii <eliz@gnu.org>2023-03-16 16:59:06 +0200
commit5cf1de683b2414927e521c34daeee460fb7649f5 (patch)
tree8f3602006d4454ddb7be376fea41dfe15e5f3c94
parent7385c991dff3466b37cf50628e7685cd53e71921 (diff)
downloademacs-5cf1de683b2.tar.gz
Fix python-fill-paragraph problems on filling strings (bug#62142)
* lisp/progmodes/python.el (python-syntax--context-compiler-macro) (python-syntax-context): Add single-quoted-string and triple-quoted-string as TYPE argument. (python-info-triple-quoted-string-p): New helper function. (python-fill-paragraph) (python-fill-string): Use it. * test/lisp/progmodes/python-tests.el (python-syntax-context-1) (python-fill-paragraph-single-quoted-string-1) (python-fill-paragraph-single-quoted-string-2) (python-fill-paragraph-triple-quoted-string-1) (python-info-triple-quoted-string-p-1) (python-info-triple-quoted-string-p-2) (python-info-triple-quoted-string-p-3): New tests.
-rw-r--r--lisp/progmodes/python.el35
-rw-r--r--test/lisp/progmodes/python-tests.el119
2 files changed, 146 insertions, 8 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 8793fdc6458..2fe88323c35 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -511,19 +511,28 @@ This variant of `rx' supports common Python named REGEXPS."
(''string
`(let ((ppss (or ,syntax-ppss (syntax-ppss))))
(and (nth 3 ppss) (nth 8 ppss))))
+ (''single-quoted-string
+ `(let ((ppss (or ,syntax-ppss (syntax-ppss))))
+ (and (characterp (nth 3 ppss)) (nth 8 ppss))))
+ (''triple-quoted-string
+ `(let ((ppss (or ,syntax-ppss (syntax-ppss))))
+ (and (eq t (nth 3 ppss)) (nth 8 ppss))))
(''paren
`(nth 1 (or ,syntax-ppss (syntax-ppss))))
(_ form))))
(defun python-syntax-context (type &optional syntax-ppss)
"Return non-nil if point is on TYPE using SYNTAX-PPSS.
-TYPE can be `comment', `string' or `paren'. It returns the start
+TYPE can be `comment', `string', `single-quoted-string',
+`triple-quoted-string' or `paren'. It returns the start
character address of the specified TYPE."
(declare (compiler-macro python-syntax--context-compiler-macro))
(let ((ppss (or syntax-ppss (syntax-ppss))))
(pcase type
('comment (and (nth 4 ppss) (nth 8 ppss)))
('string (and (nth 3 ppss) (nth 8 ppss)))
+ ('single-quoted-string (and (characterp (nth 3 ppss)) (nth 8 ppss)))
+ ('triple-quoted-string (and (eq t (nth 3 ppss)) (nth 8 ppss)))
('paren (nth 1 ppss))
(_ nil))))
@@ -4805,9 +4814,7 @@ Optional argument JUSTIFY defines if the paragraph should be justified."
((python-syntax-context 'comment)
(funcall python-fill-comment-function justify))
;; Strings/Docstrings
- ((save-excursion (or (python-syntax-context 'string)
- (equal (string-to-syntax "|")
- (syntax-after (point)))))
+ ((python-info-triple-quoted-string-p)
(funcall python-fill-string-function justify))
;; Decorators
((equal (char-after (save-excursion
@@ -4833,10 +4840,7 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'."
(let* ((str-start-pos
(set-marker
(make-marker)
- (or (python-syntax-context 'string)
- (and (equal (string-to-syntax "|")
- (syntax-after (point)))
- (point)))))
+ (python-info-triple-quoted-string-p)))
;; JT@2021-09-21: Since bug#49518's fix this will always be 1
(num-quotes (python-syntax-count-quotes
(char-after str-start-pos) str-start-pos))
@@ -6043,6 +6047,21 @@ point's current `syntax-ppss'."
((python-info-looking-at-beginning-of-defun))
(t nil))))))
+(defun python-info-triple-quoted-string-p ()
+ "Check if point is in a triple quoted string including quotes.
+It returns the position of the third quote character of the start
+of the string."
+ (save-excursion
+ (let ((pos (point)))
+ (cl-loop
+ for offset in '(0 3 -2 2 -1 1)
+ if (let ((check-pos (+ pos offset)))
+ (and (>= check-pos (point-min))
+ (<= check-pos (point-max))
+ (python-syntax-context
+ 'triple-quoted-string (syntax-ppss check-pos))))
+ return it))))
+
(defun python-info-encoding-from-cookie ()
"Detect current buffer's encoding from its coding cookie.
Returns the encoding as a symbol."
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
index e5a9d128bc5..ed4a08da6ab 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -255,6 +255,27 @@ aliqua."
;;; Font-lock and syntax
+(ert-deftest python-syntax-context-1 ()
+ (python-tests-with-temp-buffer
+ "
+# Comment
+s = 'Single Quoted String'
+t = '''Triple Quoted String'''
+p = (1 + 2)
+"
+ (python-tests-look-at "Comment")
+ (should (= (python-syntax-context 'comment) (pos-bol)))
+ (python-tests-look-at "Single")
+ (should (= (python-syntax-context 'string) (1- (point))))
+ (should (= (python-syntax-context 'single-quoted-string) (1- (point))))
+ (should-not (python-syntax-context 'triple-quoted-string))
+ (python-tests-look-at "Triple")
+ (should (= (python-syntax-context 'string) (1- (point))))
+ (should-not (python-syntax-context 'single-quoted-string))
+ (should (= (python-syntax-context 'triple-quoted-string) (1- (point))))
+ (python-tests-look-at "1 + 2")
+ (should (= (python-syntax-context 'paren) (1- (point))))))
+
(ert-deftest python-syntax-after-python-backspace ()
;; `python-indent-dedent-line-backspace' garbles syntax
(python-tests-with-temp-buffer
@@ -2052,6 +2073,54 @@ this is a test this is a test this is a test this is a test this is a test this
(fill-paragraph)
(should (= (current-indentation) 0))))
+(ert-deftest python-fill-paragraph-single-quoted-string-1 ()
+ "Single quoted string should not be filled."
+ (let ((contents "
+s = 'abc def ghi jkl mno pqr stu vwx yz'
+")
+ (fill-column 20))
+ (python-tests-with-temp-buffer
+ contents
+ (python-tests-look-at "abc")
+ (fill-paragraph)
+ (should (string= (buffer-substring-no-properties (point-min) (point-max))
+ contents)))))
+
+(ert-deftest python-fill-paragraph-single-quoted-string-2 ()
+ "Ensure no fill is performed after the end of the single quoted string."
+ (let ((contents "
+s1 = 'abc'
+s2 = 'def'
+"))
+ (python-tests-with-temp-buffer
+ contents
+ (python-tests-look-at "abc")
+ (fill-paragraph)
+ (should (string= (buffer-substring-no-properties (point-min) (point-max))
+ contents)))))
+
+(ert-deftest python-fill-paragraph-triple-quoted-string-1 ()
+ "Triple quoted string should be filled."
+ (let ((contents "
+s = '''abc def ghi jkl mno pqr stu vwx yz'''
+")
+ (expected "
+s = '''abc def ghi
+jkl mno pqr stu vwx
+yz'''
+")
+ (fill-column 20))
+ (dolist (look-at '("'''abc" "z'''"))
+ (dolist (offset '(0 1 2 3))
+ (python-tests-with-temp-buffer
+ contents
+ (python-tests-look-at look-at)
+ (forward-char offset)
+ (fill-paragraph)
+ (should (string=
+ (buffer-substring-no-properties (point-min) (point-max))
+ expected)))))))
+
;;; Mark
@@ -6491,6 +6560,56 @@ class Class:
(python-tests-look-at "'''Not a method docstring.'''")
(should (not (python-info-docstring-p)))))
+(ert-deftest python-info-triple-quoted-string-p-1 ()
+ "Test triple quoted string."
+ (python-tests-with-temp-buffer
+ "
+t = '''Triple'''
+"
+ (python-tests-look-at " '''Triple")
+ (should-not
+ (python-tests-should-not-move
+ #'python-info-triple-quoted-string-p))
+ (forward-char)
+ (let ((start-pos (+ (point) 2))
+ (eol (pos-eol)))
+ (while (< (point) eol)
+ (should (= (python-tests-should-not-move
+ #'python-info-triple-quoted-string-p)
+ start-pos))
+ (forward-char)))
+ (dolist (pos `(,(point) ,(point-min) ,(point-max)))
+ (goto-char pos)
+ (should-not
+ (python-tests-should-not-move
+ #'python-info-triple-quoted-string-p)))))
+
+(ert-deftest python-info-triple-quoted-string-p-2 ()
+ "Test empty triple quoted string."
+ (python-tests-with-temp-buffer
+ "
+e = ''''''
+"
+ (python-tests-look-at "''''''")
+ (let ((start-pos (+ (point) 2))
+ (eol (pos-eol)))
+ (while (< (point) eol)
+ (should (= (python-tests-should-not-move
+ #'python-info-triple-quoted-string-p)
+ start-pos))
+ (forward-char)))))
+
+(ert-deftest python-info-triple-quoted-string-p-3 ()
+ "Test single quoted string."
+ (python-tests-with-temp-buffer
+ "
+s = 'Single'
+"
+ (while (< (point) (point-max))
+ (should-not (python-tests-should-not-move
+ #'python-info-triple-quoted-string-p))
+ (forward-char))))
+
(ert-deftest python-info-encoding-from-cookie-1 ()
"Should detect it on first line."
(python-tests-with-temp-buffer