Skip to content

Commit

Permalink
[#34] Add better mechanism for closing unclosed strings.
Browse files Browse the repository at this point in the history
  • Loading branch information
Fuco1 committed Dec 29, 2016
1 parent e1c2427 commit 0e49e28
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 22 deletions.
47 changes: 25 additions & 22 deletions smartparens.el
Expand Up @@ -804,6 +804,7 @@ in mode's startup-hook etc.) by calling `smartparens-mode'."
:type 'boolean
:group 'smartparens)

;; TODO: remove this in 1.12
(defcustom sp-autoinsert-quote-if-followed-by-closing-pair nil
"If non-nil autoinsert quotes when the point is followed by closing delimiter.
Expand All @@ -827,6 +828,9 @@ compatibility. The intended meaning is \"insert the pair if
followed by closing pair?\", t = yes."
:type 'boolean
:group 'smartparens)
(make-obsolete-variable
'sp-autoinsert-quote-if-followed-by-closing-pair
"the option was removed and no longer has any effect." "1.10")

(defcustom sp-autoskip-closing-pair 'always-end
"Determine the behaviour when skipping closing delimiters.
Expand Down Expand Up @@ -2999,11 +3003,8 @@ last form; otherwise do nothing."
(sp--setaction action (sp-skip-closing-pair))
(unless action (sp-escape-open-delimiter))
;; if nothing happened, we just inserted a character, so
;; set the apropriate operation. We also need to check
;; for `sp--self-insert-no-escape' not to overwrite
;; it. See `sp-autoinsert-quote-if-followed-by-closing-pair'.
(when (and (not action)
(not (eq sp-last-operation 'sp-self-insert-no-escape)))
;; set the apropriate operation.
(unless action
(setq sp-last-operation 'sp-self-insert))))))))))

;; Unfortunately, some modes rebind "inserting" keys to their own
Expand Down Expand Up @@ -3381,6 +3382,20 @@ Return non-nil if at least one escaping was performed."
(- (point) (length open))
(+ (point) (length close))))))

(defun sp--buffer-is-string-balanced-p ()
"Check if the buffer is string-balanced.
A string-balanced buffer is one where where is no unclosed
string, that is, the string state at the end of the buffer is
\"closed\"."
(save-excursion
(save-restriction
(widen)
(goto-char (point-max))
(let ((syntax (sp--syntax-ppss)))
(or (< (car syntax) 0)
(nth 3 syntax))))))

(defun sp-escape-open-delimiter ()
"Escape just inserted opening pair if `sp-insert-pair' was skipped.
Expand All @@ -3389,7 +3404,11 @@ is disabled. This way, we can control autoescape and closing
delimiter insertion separately."
(-when-let (open (plist-get (sp--pair-to-insert 'escape) :open))
(when (and (sp--do-action-p open 'escape)
sp-point-inside-string)
sp-point-inside-string
;; do not escape if we are looking at a closing
;; delimiter, that means we closed an opened string,
;; most likely.
(sp--buffer-is-string-balanced-p))
(sp--escape-region (list open) (- (point) (length open)) (point)))))

;; kept to not break people's config... remove later
Expand Down Expand Up @@ -3571,22 +3590,6 @@ default."
(not (sp-skip-closing-pair nil t)))
t)
(sp--do-action-p open-pair 'insert t)
(if sp-autoinsert-quote-if-followed-by-closing-pair t
(if (and (eq (char-syntax (preceding-char)) ?\")
;; this is called *after* the character is
;; inserted. Therefore, if we are not in string, it
;; must have been closed just now
(not (sp-point-in-string)))
(let ((pattern (sp--get-closing-regexp)))
;; If we simply insert closing ", we also
;; don't want to escape it. Therefore, we
;; need to set `sp-last-operation'
;; accordingly to be checked in
;; `self-insert-command' advice.
(if (sp--looking-at pattern)
(progn (setq sp-last-operation 'sp-self-insert-no-escape) nil)
t))
t))
;; was sp-autoinsert-if-followed-by-same
(or (not (sp--get-active-overlay 'pair))
(not (sp--looking-at (sp--strict-regexp-quote open-pair)))
Expand Down
10 changes: 10 additions & 0 deletions test/smartparens-insertion-test.el
Expand Up @@ -186,3 +186,13 @@ and therefore we should not escape the just-inserted quote."
(smartparens-strict-mode +1))
(execute-kbd-macro "\"|")
(should (equal (buffer-string) result)))))

(ert-deftest sp-test-insert-quote-do-not-escape-if-string-unbalanced ()
"When we have an unbalanced string we should sometimes just close it.
If the point is in a string, we check the \"parity\" state of the
buffer and decide if to close or escape: if the parity at the end
of the buffer is correct, we escape, otherwise we close."
(sp-test-insertion "[\"asd|]" "\"" "[\"asd\"|]")
(sp-test-insertion "\"foo |] bar\"" "\"" "\"foo \\\"|] bar\"")
(sp-test-insertion "\"first| \"second\"" "\"" "\"first\"| \"second\""))
12 changes: 12 additions & 0 deletions test/smartparens-python-test.el
Expand Up @@ -4,6 +4,12 @@
(defun sp-test--python-mode ()
(shut-up (python-mode)))

(defun sp-test-insertion-python (initial keys result)
(sp-test-with-temp-buffer initial
(sp-test--python-mode)
(execute-kbd-macro keys)
(sp-buffer-equals result)))

(ert-deftest sp-test-dont-reindent-python ()
(sp-test-with-temp-buffer "if foo:
bar()
Expand Down Expand Up @@ -96,3 +102,9 @@ Make sure to skip even in inactive sexps."
(execute-kbd-macro (kbd "'"))
(insert "|")
(should (equal (buffer-string) "a = \"foo '| bar\""))))

(ert-deftest sp-test-python-quote-do-not-escape-if-string-unbalanced ()
"See `sp-test-insert-quote-do-not-escape-if-string-unbalanced'."
(sp-test-insertion-python "[\"asd|]" "\"" "[\"asd\"|]")
(sp-test-insertion-python "\"foo |] bar\"" "\"" "\"foo \\\"|] bar\"")
(sp-test-insertion-python "\"first| \"second\"" "\"" "\"first\"| \"second\""))

0 comments on commit 0e49e28

Please sign in to comment.