Skip to content

Commit 2cd111f

Browse files
committed
Fix indentation of multiline strings
1 parent 9892096 commit 2cd111f

File tree

4 files changed

+149
-3
lines changed

4 files changed

+149
-3
lines changed

swift-mode-indent.el

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,20 @@ declaration and its offset is `swift-mode:basic-offset'."
151151
"Return the indentation of the current line."
152152
(back-to-indentation)
153153

154-
(if (nth 4 (syntax-ppss))
154+
(let ((parser-state (syntax-ppss)))
155+
(cond
156+
((nth 4 parser-state)
155157
;; If the 4th element of `(syntax-ppss)' is non-nil, the cursor is on
156158
;; the 2nd or following lines of a multiline comment, because:
157159
;;
158160
;; - The 4th element of `(syntax-ppss)' is nil on the comment starter.
159161
;; - We have called `back-to-indentation`.
160-
(swift-mode:calculate-indent-of-multiline-comment)
161-
(swift-mode:calculate-indent-of-code)))
162+
(swift-mode:calculate-indent-of-multiline-comment))
163+
164+
((eq (nth 3 parser-state) t)
165+
(swift-mode:calculate-indent-of-multiline-string))
166+
(t
167+
(swift-mode:calculate-indent-of-code)))))
162168

163169
(defun swift-mode:calculate-indent-of-multiline-comment ()
164170
"Return the indentation of the current line inside a multiline comment."
@@ -180,6 +186,32 @@ declaration and its offset is `swift-mode:basic-offset'."
180186
(swift-mode:calculate-indent-of-multiline-comment)
181187
(swift-mode:indentation (point) 0)))))
182188

189+
(defun swift-mode:calculate-indent-of-multiline-string ()
190+
"Return the indentation of the current line inside a multiline string."
191+
(back-to-indentation)
192+
(let ((string-beginning-position (nth 8 (syntax-ppss))))
193+
(if (looking-at "\"\"\"")
194+
;; The last line.
195+
(progn
196+
(goto-char string-beginning-position)
197+
(swift-mode:calculate-indent-of-expression
198+
swift-mode:multiline-statement-offset))
199+
(forward-line -1)
200+
(back-to-indentation)
201+
(if (<= (point) string-beginning-position)
202+
;; The cursor was on the 2nd line of the comment, so aligns with
203+
;; that line with offset.
204+
(progn
205+
(goto-char string-beginning-position)
206+
(swift-mode:calculate-indent-of-expression
207+
swift-mode:multiline-statement-offset))
208+
;; The cursor was on the 3rd or following lines of the comment, so
209+
;; aligns with a non-empty preceding line.
210+
(if (eolp)
211+
;; The cursor is on an empty line, so seeks a non-empty-line.
212+
(swift-mode:calculate-indent-of-multiline-string)
213+
(swift-mode:indentation (point) 0))))))
214+
183215
(defun swift-mode:calculate-indent-of-code ()
184216
"Return the indentation of the current line outside multiline comments."
185217
(back-to-indentation)

swift-mode-lexer.el

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,81 @@ END is the point after the token."
152152

153153
table))
154154

155+
(defun swift-mode:syntax-propertize (start end)
156+
"Update text properties for multiline strings.
157+
Mark the beginning of and the end of multiline strings as general string
158+
delimiters between position START and END.
159+
Intended for `syntax-propertize-function'"
160+
(remove-text-properties start end '(syntax-table nil))
161+
(let* ((parser-state (syntax-ppss start))
162+
(inside-string (nth 3 parser-state))
163+
(comment-nesting (nth 4 parser-state))
164+
(comment-beginning-position (nth 8 parser-state)))
165+
(cond
166+
((eq inside-string t)
167+
(swift-mode:syntax-propertize:end-of-multiline-string end))
168+
169+
(inside-string
170+
(swift-mode:syntax-propertize:end-of-single-line-string end))
171+
172+
(comment-nesting
173+
(goto-char comment-beginning-position)
174+
(forward-comment (point-max)))))
175+
176+
(while (search-forward-regexp
177+
(mapconcat #'regexp-quote '("\"\"\"" "\"" "//" "/*") "\\|")
178+
end t)
179+
(cond
180+
((equal "\"\"\"" (match-string-no-properties 0))
181+
(put-text-property (match-beginning 0) (1+ (match-beginning 0))
182+
'syntax-table
183+
(string-to-syntax "|"))
184+
(swift-mode:syntax-propertize:end-of-multiline-string end))
185+
186+
((equal "\"" (match-string-no-properties 0))
187+
(swift-mode:syntax-propertize:end-of-single-line-string end))
188+
189+
((equal "//" (match-string-no-properties 0))
190+
(goto-char (match-beginning 0))
191+
(forward-comment (point-max)))
192+
193+
((equal "/*" (match-string-no-properties 0))
194+
(goto-char (match-beginning 0))
195+
(forward-comment (point-max))))))
196+
197+
(defun swift-mode:syntax-propertize:end-of-multiline-string (end)
198+
"Move point to the end of multiline string.
199+
Assuming the cursor is on a multiline string.
200+
If the end of the string found, put a text property on it.
201+
If the multiline string go beyond END, stop there."
202+
;; FIXME string interpolation
203+
(if (search-forward "\"\"\"" end t)
204+
(if (swift-mode:escaped-p (match-beginning 0))
205+
(swift-mode:syntax-propertize:end-of-multiline-string end)
206+
(put-text-property (1- (point)) (point)
207+
'syntax-table
208+
(string-to-syntax "|")))
209+
(goto-char end)))
210+
211+
(defun swift-mode:syntax-propertize:end-of-single-line-string (end)
212+
"Move point to the end of string.
213+
Assuming the cursor is on a string.
214+
If the multiline string go beyond END, stop there."
215+
;; FIXME string interpolation
216+
(if (search-forward "\"" end t)
217+
(when (swift-mode:escaped-p (match-beginning 0))
218+
(swift-mode:syntax-propertize:end-of-single-line-string end))
219+
(goto-char end)))
220+
221+
(defun swift-mode:escaped-p (position)
222+
"Return t if the POSITION is proceeded by odd number of backslashes.
223+
Return nil otherwise."
224+
(let ((p position)
225+
(count 0))
226+
(while (eq (char-before p) ?\\)
227+
(setq count (1+ count))
228+
(setq p (1- p)))
229+
(= (mod count 2) 1)))
155230

156231
;;; Lexers
157232

swift-mode.el

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ See `forward-sexp for ARG."
145145
(setq-local adaptive-fill-regexp comment-start-skip)
146146
(setq-local comment-multi-line t)
147147

148+
(setq-local parse-sexp-lookup-properties t)
149+
(setq-local syntax-propertize-function #'swift-mode:syntax-propertize)
150+
148151
(setq-local indent-tabs-mode nil)
149152
(setq-local indent-line-function #'swift-mode:indent-line)
150153

test/swift-files/strings.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// swift-mode:test:eval (setq-local swift-mode:basic-offset 4)
2+
// swift-mode:test:eval (setq-local swift-mode:parenthesized-expression-offset 2)
3+
// swift-mode:test:eval (setq-local swift-mode:multiline-statement-offset 2)
4+
// swift-mode:test:eval (setq-local swift-mode:switch-case-offset 0)
5+
6+
func f() {
7+
let x = """
8+
aaa
9+
"
10+
aaa
11+
""
12+
aaa
13+
\"""
14+
aaa
15+
""
16+
aaa
17+
"
18+
aaa
19+
""" +
20+
"abc"
21+
22+
let x = """
23+
aaa
24+
"
25+
aaa // swift-mode:test:keep-indent
26+
""
27+
aaa
28+
\"""
29+
aaa
30+
""
31+
aaa
32+
"
33+
aaa
34+
""" +
35+
"abc"
36+
}

0 commit comments

Comments
 (0)