mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-06 06:20:55 -08:00
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.
This commit is contained in:
parent
7385c991df
commit
5cf1de683b
2 changed files with 146 additions and 8 deletions
|
|
@ -511,19 +511,28 @@ This variant of `rx' supports common Python named REGEXPS."
|
||||||
(''string
|
(''string
|
||||||
`(let ((ppss (or ,syntax-ppss (syntax-ppss))))
|
`(let ((ppss (or ,syntax-ppss (syntax-ppss))))
|
||||||
(and (nth 3 ppss) (nth 8 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
|
(''paren
|
||||||
`(nth 1 (or ,syntax-ppss (syntax-ppss))))
|
`(nth 1 (or ,syntax-ppss (syntax-ppss))))
|
||||||
(_ form))))
|
(_ form))))
|
||||||
|
|
||||||
(defun python-syntax-context (type &optional syntax-ppss)
|
(defun python-syntax-context (type &optional syntax-ppss)
|
||||||
"Return non-nil if point is on TYPE using 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."
|
character address of the specified TYPE."
|
||||||
(declare (compiler-macro python-syntax--context-compiler-macro))
|
(declare (compiler-macro python-syntax--context-compiler-macro))
|
||||||
(let ((ppss (or syntax-ppss (syntax-ppss))))
|
(let ((ppss (or syntax-ppss (syntax-ppss))))
|
||||||
(pcase type
|
(pcase type
|
||||||
('comment (and (nth 4 ppss) (nth 8 ppss)))
|
('comment (and (nth 4 ppss) (nth 8 ppss)))
|
||||||
('string (and (nth 3 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))
|
('paren (nth 1 ppss))
|
||||||
(_ nil))))
|
(_ nil))))
|
||||||
|
|
||||||
|
|
@ -4805,9 +4814,7 @@ Optional argument JUSTIFY defines if the paragraph should be justified."
|
||||||
((python-syntax-context 'comment)
|
((python-syntax-context 'comment)
|
||||||
(funcall python-fill-comment-function justify))
|
(funcall python-fill-comment-function justify))
|
||||||
;; Strings/Docstrings
|
;; Strings/Docstrings
|
||||||
((save-excursion (or (python-syntax-context 'string)
|
((python-info-triple-quoted-string-p)
|
||||||
(equal (string-to-syntax "|")
|
|
||||||
(syntax-after (point)))))
|
|
||||||
(funcall python-fill-string-function justify))
|
(funcall python-fill-string-function justify))
|
||||||
;; Decorators
|
;; Decorators
|
||||||
((equal (char-after (save-excursion
|
((equal (char-after (save-excursion
|
||||||
|
|
@ -4833,10 +4840,7 @@ JUSTIFY should be used (if applicable) as in `fill-paragraph'."
|
||||||
(let* ((str-start-pos
|
(let* ((str-start-pos
|
||||||
(set-marker
|
(set-marker
|
||||||
(make-marker)
|
(make-marker)
|
||||||
(or (python-syntax-context 'string)
|
(python-info-triple-quoted-string-p)))
|
||||||
(and (equal (string-to-syntax "|")
|
|
||||||
(syntax-after (point)))
|
|
||||||
(point)))))
|
|
||||||
;; JT@2021-09-21: Since bug#49518's fix this will always be 1
|
;; JT@2021-09-21: Since bug#49518's fix this will always be 1
|
||||||
(num-quotes (python-syntax-count-quotes
|
(num-quotes (python-syntax-count-quotes
|
||||||
(char-after str-start-pos) str-start-pos))
|
(char-after str-start-pos) str-start-pos))
|
||||||
|
|
@ -6043,6 +6047,21 @@ point's current `syntax-ppss'."
|
||||||
((python-info-looking-at-beginning-of-defun))
|
((python-info-looking-at-beginning-of-defun))
|
||||||
(t nil))))))
|
(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 ()
|
(defun python-info-encoding-from-cookie ()
|
||||||
"Detect current buffer's encoding from its coding cookie.
|
"Detect current buffer's encoding from its coding cookie.
|
||||||
Returns the encoding as a symbol."
|
Returns the encoding as a symbol."
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,27 @@ aliqua."
|
||||||
|
|
||||||
;;; Font-lock and syntax
|
;;; 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 ()
|
(ert-deftest python-syntax-after-python-backspace ()
|
||||||
;; `python-indent-dedent-line-backspace' garbles syntax
|
;; `python-indent-dedent-line-backspace' garbles syntax
|
||||||
(python-tests-with-temp-buffer
|
(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)
|
(fill-paragraph)
|
||||||
(should (= (current-indentation) 0))))
|
(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
|
;;; Mark
|
||||||
|
|
||||||
|
|
@ -6491,6 +6560,56 @@ class Class:
|
||||||
(python-tests-look-at "'''Not a method docstring.'''")
|
(python-tests-look-at "'''Not a method docstring.'''")
|
||||||
(should (not (python-info-docstring-p)))))
|
(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 ()
|
(ert-deftest python-info-encoding-from-cookie-1 ()
|
||||||
"Should detect it on first line."
|
"Should detect it on first line."
|
||||||
(python-tests-with-temp-buffer
|
(python-tests-with-temp-buffer
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue