jump to the end of line when moving forward searching for the end
of the statement."
(interactive "^")
- (let (string-start bs-pos)
+ (let (string-start bs-pos (last-string-end 0))
(while (and (or noend (goto-char (line-end-position)))
(not (eobp))
(cond ((setq string-start (python-syntax-context 'string))
+ ;; The assertion can only fail if syntax table
+ ;; text properties and the `syntax-ppss' cache
+ ;; are somehow out of whack. This has been
+ ;; observed when using `syntax-ppss' during
+ ;; narrowing.
+ (cl-assert (> string-start last-string-end)
+ :show-args
+ "Overlapping strings detected")
(goto-char string-start)
(if (python-syntax-context 'paren)
;; Ended up inside a paren, roll again.
(goto-char (+ (point)
(python-syntax-count-quotes
(char-after (point)) (point))))
- (or (re-search-forward (rx (syntax string-delimiter)) nil t)
- (goto-char (point-max)))))
+ (setq last-string-end
+ (or (re-search-forward
+ (rx (syntax string-delimiter)) nil t)
+ (goto-char (point-max))))))
((python-syntax-context 'paren)
;; The statement won't end before we've escaped
;; at least one level of parenthesis.
(or enabled (hs-minor-mode -1)))))
+(ert-deftest python-tests--python-nav-end-of-statement--infloop ()
+ "Checks that `python-nav-end-of-statement' doesn't infloop in a
+buffer with overlapping strings."
+ (python-tests-with-temp-buffer "''' '\n''' ' '\n"
+ (syntax-propertize (point-max))
+ ;; Create a situation where strings nominally overlap. This
+ ;; shouldn't happen in practice, but apparently it can happen when
+ ;; a package calls `syntax-ppss' in a narrowed buffer during JIT
+ ;; lock.
+ (put-text-property 4 5 'syntax-table (string-to-syntax "|"))
+ (remove-text-properties 8 9 '(syntax-table nil))
+ (goto-char 4)
+ (setq-local syntax-propertize-function nil)
+ ;; The next form should not infloop. We have to disable
+ ;; ‘debug-on-error’ so that ‘cl-assert’ doesn’t call the debugger.
+ (should-error (let ((debug-on-error nil))
+ (python-nav-end-of-statement)))
+ (should (eolp))))
+
(provide 'python-tests)