Skip to content

Commit 8e6d0a1

Browse files
committed
Fix reader syntaxes distrupted by newlines
Fixes bug#647. Lispy would assume an implicit nil after the reader syntax, like this: ```emacs-lisp (defun foo () ' (bar baz) ) ;; point here, press i to format ``` Gets this incorrect result: ```emacs-lisp (defun foo () '() (bar baz)) ``` * lispy.el (lispy--delete-insignificant-sexps): allow removing sexps that are implicitly-created by lispy, and serve no purpose in the real input and output sexp other than formatting. At the moment, just remove newline nodes. (lispy--read-reader-syntax): helper function to avoid code duplication; replaces a reader syntax RS, e.g., "`", plus its immediately-next sexp into an internal representation with TAG. (lispy--read): update handling of "#'", ",@", "'", "`", "," to make use of `lispy--read-reader-syntax'. (lispy--insert): fixed float and quasiquote handling (currently it calls `(insert (caddr sxp))', which is incorrect because `insert' considers it a character; float was not problematic because this code path was never triggered: things like 1.2 have not been converted into `ly-raw float' constructs anyway. * lispy-test.el (lispy-read-quote-newline): added 5 simple tests for each of the addressed reader syntaxes.
1 parent 8175e9a commit 8e6d0a1

File tree

2 files changed

+64
-65
lines changed

2 files changed

+64
-65
lines changed

lispy-test.el

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,23 @@ pressed."
181181
:buffer "(. f)"
182182
:point 'max))
183183

184+
(ert-deftest lispy-read-quote-newline ()
185+
(lispy-simulate-expect "(progn0'O"
186+
:buffer "(progn '0)"
187+
:point 'max)
188+
(lispy-simulate-expect "(prognignore#'O"
189+
:buffer "(progn #'ignore)"
190+
:point 'max)
191+
(lispy-simulate-expect "(progn0`O"
192+
:buffer "(progn `0)"
193+
:point 'max)
194+
(lispy-simulate-expect "`(progn0,O"
195+
:buffer "`(progn ,0)"
196+
:point 'max)
197+
(lispy-simulate-expect "`(prognnil,@O"
198+
:buffer "`(progn ,@nil)"
199+
:point 'max))
200+
184201
(ert-deftest lispy-decode-keysequence ()
185202
(should (equal (lispy-decode-keysequence "23ab50c")
186203
'(23 "a" "b" 50 "c")))

lispy.el

Lines changed: 47 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7286,6 +7286,34 @@ See https://clojure.org/guides/weird_characters#_character_literal.")
72867286
(match-string subexp)))
72877287
t t nil subexp)))))
72887288

7289+
(defun lispy--delete-insignificant-sexps ()
7290+
"Delete insignificant sexps.
7291+
That is, delete internal representations such as \\=(ly-raw
7292+
newline), which serves no purpose in the input sexp."
7293+
(rx-let ((sp0 (* space))
7294+
(sp1 (+ space)))
7295+
(save-match-data
7296+
(while (looking-at (rx sp0 ?\( sp0
7297+
"ly-raw" sp1
7298+
"newline" sp0 ?\)))
7299+
(replace-match "")))))
7300+
7301+
(defun lispy--read-reader-syntax (rs tag)
7302+
"Handle reading a reader syntax RS.
7303+
The matched reader syntax has TAG as its internal tag."
7304+
(save-excursion
7305+
(goto-char (point-min))
7306+
(save-match-data
7307+
(while (re-search-forward
7308+
(rx (or bol (not ?\\)) (group-n 1 (regexp rs)))
7309+
nil t)
7310+
(unless (lispy--in-string-or-comment-p)
7311+
(lispy--delete-insignificant-sexps)
7312+
(forward-sexp)
7313+
(insert ")")
7314+
(replace-match (format "(ly-raw %s " tag)
7315+
nil nil nil 1))))))
7316+
72897317
;; TODO: Make the read test pass on string with semi-colon
72907318
(defun lispy--read (str)
72917319
"Read STR including comments and newlines."
@@ -7416,25 +7444,9 @@ See https://clojure.org/guides/weird_characters#_character_literal.")
74167444
(replace-match (format "(ly-raw clojure-keyword %S)"
74177445
(match-string-no-properties 1)))))
74187446
;; ——— #' —————————————————————
7419-
(goto-char (point-min))
7420-
(while (re-search-forward "#'" nil t)
7421-
(unless (lispy--in-string-or-comment-p)
7422-
(forward-sexp)
7423-
(insert ")")
7424-
(replace-match "(ly-raw function ")))
7447+
(lispy--read-reader-syntax "#'" "function")
74257448
;; ——— ,@ —————————————————————
7426-
(goto-char (point-min))
7427-
(while (re-search-forward "\\(?:[^\\]\\|^\\),@" nil t)
7428-
(unless (lispy--in-string-or-comment-p)
7429-
(backward-char 2)
7430-
(let ((beg (point))
7431-
(sxp (ignore-errors (read (current-buffer)))))
7432-
(when (and (consp sxp)
7433-
(eq (car sxp) '\,@))
7434-
(insert ")")
7435-
(goto-char beg)
7436-
(delete-char 2)
7437-
(insert "(ly-raw comma-splice ")))))
7449+
(lispy--read-reader-syntax ",@" "comma-splice")
74387450
;; ——— #_ —————————————————————
74397451
(goto-char (point-min))
74407452
(while (re-search-forward "#_[({[]" nil t)
@@ -7457,53 +7469,23 @@ See https://clojure.org/guides/weird_characters#_character_literal.")
74577469
(match-string 1)))
74587470
nil nil nil 1)))
74597471
;; ——— ' ——————————————————————
7460-
(goto-char (point-min))
7461-
(while (re-search-forward "'" nil t)
7462-
(unless (lispy--in-string-or-comment-p)
7463-
(backward-char 1)
7464-
(let ((beg (point))
7465-
(sxp (ignore-errors (read (current-buffer)))))
7466-
(when (and (consp sxp)
7467-
(eq (car sxp) 'quote))
7468-
(insert ")")
7469-
(goto-char beg)
7470-
(delete-char 1)
7471-
(insert "(ly-raw quote ")))))
7472+
(lispy--read-reader-syntax "'" "quote")
74727473
;; ——— ` ——————————————————————
7473-
(goto-char (point-min))
7474-
(while (re-search-forward "\\(?:[^\\]\\|^\\)`" nil t)
7475-
(unless (lispy--in-string-or-comment-p)
7476-
(cond ((looking-at lispy-left)
7477-
(delete-char -1)
7478-
(insert "(ly-raw \\` ")
7479-
(forward-list 1)
7480-
(insert ")")
7481-
(backward-list 1)
7482-
(forward-char 7))
7483-
((looking-at "\\sw\\|\\s_\\|[,@]")
7484-
(let ((beg (point)))
7485-
(forward-sexp 1)
7486-
(insert "\")")
7487-
(goto-char (1- beg))
7488-
(insert "(ly-raw quasiquote \""))))))
7474+
(lispy--read-reader-syntax "`" "quasiquote")
74897475
;; ——— , ——————————————————————
74907476
(lispy--replace-regexp-in-code "\\\\," "(ly-raw comma-symbol)")
7491-
(goto-char (point-min))
7492-
(while (re-search-forward "[^\\]?,[^@\"]" nil t)
7493-
(unless (lispy--in-string-or-comment-p)
7494-
(backward-char 2)
7495-
(if (memq major-mode lispy-clojure-modes)
7496-
(progn
7497-
(delete-char 1)
7498-
(insert "(ly-raw clojure-comma)"))
7499-
(let ((beg (point))
7500-
(sxp (ignore-errors (read (current-buffer)))))
7501-
(when (and (consp sxp)
7502-
(eq (car sxp) '\,))
7503-
(insert ")")
7504-
(goto-char beg)
7505-
(delete-char 1)
7506-
(insert "(ly-raw \\, "))))))
7477+
(if (memq major-mode lispy-clojure-modes)
7478+
;; I don't know clojure, so I'll leave that
7479+
;; execution path untouched
7480+
(progn
7481+
(goto-char (point-min))
7482+
(while (re-search-forward "[^\\]?,[^@\"]" nil t)
7483+
(unless (lispy--in-string-or-comment-p)
7484+
(backward-char 2)
7485+
(progn
7486+
(delete-char 1)
7487+
(insert "(ly-raw clojure-comma)")))))
7488+
(lispy--read-reader-syntax "," "comma"))
75077489
;; ——— angle syntax —————————
75087490
;; used for markers/buffers/windows/overlays
75097491
(goto-char (point-min))
@@ -8128,7 +8110,7 @@ The outer delimiters are stripped."
81288110
(delete-char
81298111
(- (skip-chars-backward " ")))
81308112
(insert "\n"))
8131-
((string comment symbol float quasiquote)
8113+
((string comment symbol)
81328114
(delete-region beg (point))
81338115
(insert (cl-caddr sxp)))
81348116
(comma-symbol
@@ -8249,7 +8231,7 @@ The outer delimiters are stripped."
82498231
(reference
82508232
(delete-region beg (point))
82518233
(insert (cl-caddr sxp)))
8252-
(\`
8234+
((quasiquote \`)
82538235
(if (> (length sxp) 3)
82548236
(progn
82558237
(goto-char beg)
@@ -8260,7 +8242,7 @@ The outer delimiters are stripped."
82608242
(insert "`")
82618243
(prin1 (cl-caddr sxp) (current-buffer)))
82628244
(goto-char beg))
8263-
(\,
8245+
((comma \,)
82648246
(delete-region beg (point))
82658247
(insert ",")
82668248
(prin1 (cl-caddr sxp) (current-buffer))

0 commit comments

Comments
 (0)