Skip to content
This repository was archived by the owner on Nov 26, 2025. It is now read-only.

Commit b4170b7

Browse files
authored
Merge pull request #104 from ziglang/fix-multiline-string-literals
Fix multiline string literals
2 parents a0ab963 + d78231a commit b4170b7

File tree

2 files changed

+99
-103
lines changed

2 files changed

+99
-103
lines changed

test/zig-tests.el

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const string =
4040
;"
4141
'(("const" font-lock-keyword-face)
4242
("string" font-lock-variable-name-face)
43-
("\\\\ This newline is NOT escaped \\\n" zig-multiline-string-face))))
43+
("\\\\ This newline is NOT escaped \\" zig-multiline-string-face))))
4444

4545
(ert-deftest test-font-lock-backslash-in-str-literal ()
4646
(zig-test-font-lock
@@ -100,8 +100,8 @@ const python =
100100
;"
101101
'(("const" font-lock-keyword-face)
102102
("python" font-lock-variable-name-face)
103-
("\\\\def main():\n" zig-multiline-string-face)
104-
("\\\\ print(\"Hello, world!\")\n" zig-multiline-string-face))))
103+
("\\\\def main():" zig-multiline-string-face)
104+
("\\\\ print(\"Hello, world!\")" zig-multiline-string-face))))
105105

106106
(ert-deftest test-font-lock-parameters-pointers-and-arrays ()
107107
(zig-test-font-lock
@@ -164,6 +164,27 @@ const python =
164164
("void" font-lock-type-face)))))
165165
(zig-test-font-lock test-string expected))))
166166

167+
(ert-deftest test-font-lock-int-types ()
168+
(zig-test-font-lock
169+
"const Types = .{ u0, i7, u33, i123, u55555 };"
170+
'(("const" font-lock-keyword-face)
171+
("Types" font-lock-variable-name-face)
172+
("u0" font-lock-type-face)
173+
("i7" font-lock-type-face)
174+
("u33" font-lock-type-face)
175+
("i123" font-lock-type-face)
176+
("u55555" font-lock-type-face))))
177+
178+
(ert-deftest test-font-lock-escaped-backslash ()
179+
(zig-test-font-lock
180+
"const a = foo('\\\\', \"C:\\\\\", \\\\
181+
);"
182+
'(("const" font-lock-keyword-face)
183+
("a" font-lock-variable-name-face)
184+
("'\\\\'" font-lock-string-face)
185+
("\"C:\\\\\"" font-lock-string-face)
186+
("\\\\" zig-multiline-string-face))))
187+
167188
;;===========================================================================;;
168189
;; Indentation tests
169190

zig-mode.el

Lines changed: 75 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -163,55 +163,6 @@ If given a SOURCE, execute the CMD on it."
163163

164164
table))
165165

166-
(defconst zig-keywords
167-
'(;; Storage
168-
"const" "var" "extern" "packed" "export" "pub" "noalias" "inline"
169-
"noinline" "comptime" "callconv" "volatile" "allowzero"
170-
"align" "linksection" "threadlocal" "addrspace"
171-
172-
;; Structure
173-
"struct" "enum" "union" "error" "opaque"
174-
175-
;; Statement
176-
"break" "return" "continue" "asm" "defer" "errdefer" "unreachable"
177-
"try" "catch" "async" "nosuspend" "await" "suspend" "resume"
178-
179-
;; Conditional
180-
"if" "else" "switch" "and" "or" "orelse"
181-
182-
;; Repeat
183-
"while" "for"
184-
185-
;; Other keywords
186-
"fn" "usingnamespace" "test"))
187-
188-
(defconst zig-types
189-
'(;; Integer types
190-
"i2" "u2" "i3" "u3" "i4" "u4" "i5" "u5" "i6" "u6" "i7" "u7" "i8" "u8"
191-
"i16" "u16" "i29" "u29" "i32" "u32" "i64" "u64" "i128" "u128"
192-
"isize" "usize"
193-
194-
;; Floating types
195-
"f16" "f32" "f64" "f80" "f128"
196-
197-
;; C types
198-
"c_char" "c_short" "c_ushort" "c_int" "c_uint" "c_long" "c_ulong"
199-
"c_longlong" "c_ulonglong" "c_longdouble"
200-
201-
;; Comptime types
202-
"comptime_int" "comptime_float"
203-
204-
;; Other types
205-
"bool" "void" "noreturn" "type" "error" "anyerror" "anyframe" "anytype"
206-
"anyopaque"))
207-
208-
(defconst zig-constants
209-
'(;; Boolean
210-
"true" "false"
211-
212-
;; Other constants
213-
"null" "undefined"))
214-
215166
(defconst zig-electric-indent-chars
216167
'(?\; ?\, ?\) ?\] ?\}))
217168

@@ -225,9 +176,62 @@ If given a SOURCE, execute the CMD on it."
225176
(,(concat "@" zig-re-identifier) . font-lock-builtin-face)
226177

227178
;; Keywords, constants and types
228-
(,(regexp-opt zig-keywords 'symbols) . font-lock-keyword-face)
229-
(,(regexp-opt zig-constants 'symbols) . font-lock-constant-face)
230-
(,(regexp-opt zig-types 'symbols) . font-lock-type-face)
179+
(,(rx symbol-start
180+
(|
181+
;; Storage
182+
"const" "var" "extern" "packed" "export" "pub" "noalias" "inline"
183+
"noinline" "comptime" "callconv" "volatile" "allowzero"
184+
"align" "linksection" "threadlocal" "addrspace"
185+
186+
;; Structure
187+
"struct" "enum" "union" "error" "opaque"
188+
189+
;; Statement
190+
"break" "return" "continue" "asm" "defer" "errdefer" "unreachable"
191+
"try" "catch" "async" "nosuspend" "await" "suspend" "resume"
192+
193+
;; Conditional
194+
"if" "else" "switch" "and" "or" "orelse"
195+
196+
;; Repeat
197+
"while" "for"
198+
199+
;; Other keywords
200+
"fn" "usingnamespace" "test")
201+
symbol-end)
202+
. font-lock-keyword-face)
203+
204+
(,(rx symbol-start
205+
(|
206+
;; Boolean
207+
"true" "false"
208+
209+
;; Other constants
210+
"null" "undefined")
211+
symbol-end)
212+
. font-lock-constant-face)
213+
214+
(,(rx symbol-start
215+
(|
216+
;; Integer types
217+
(: (any ?i ?u) (| ?0 (: (any (?1 . ?9)) (* digit))))
218+
"isize" "usize"
219+
220+
;; Floating types
221+
"f16" "f32" "f64" "f80" "f128"
222+
223+
;; C types
224+
"c_char" "c_short" "c_ushort" "c_int" "c_uint" "c_long" "c_ulong"
225+
"c_longlong" "c_ulonglong" "c_longdouble"
226+
227+
;; Comptime types
228+
"comptime_int" "comptime_float"
229+
230+
;; Other types
231+
"bool" "void" "noreturn" "type" "anyerror" "anyframe" "anytype"
232+
"anyopaque")
233+
symbol-end)
234+
. font-lock-type-face)
231235

232236
;; Type annotations (both variable and type)
233237
(,zig-re-type-annotation 1 font-lock-variable-name-face)
@@ -357,18 +361,19 @@ This is written mainly to be used as `end-of-defun-function' for Zig."
357361
(and (not (looking-at " *\\(//[^\n]*\\)?\n"))
358362
(current-column)))
359363
(+ prev-block-indent-col zig-indent-offset))))
360-
;; is-expr-continutation: True if this line continues an
364+
;; is-expr-continuation: True if this line continues an
361365
;; expression from the previous line, false otherwise.
362-
(is-expr-continutation
366+
(is-expr-continuation
363367
(and
364368
(not (looking-at "[]});]\\|else"))
365369
(save-excursion
366370
(zig-skip-backwards-past-whitespace-and-comments)
367371
(when (> (point) 1)
368372
(backward-char)
369-
(not (looking-at "[,;([{}]")))))))
373+
(or (zig-currently-in-str)
374+
(not (looking-at "[,;([{}]"))))))))
370375
;; Now we can calculate indent-col:
371-
(if is-expr-continutation
376+
(if is-expr-continuation
372377
(+ base-indent-col zig-indent-offset)
373378
base-indent-col)))))
374379
;; If point is within the indentation whitespace, move it to the end of the
@@ -379,53 +384,23 @@ This is written mainly to be used as `end-of-defun-function' for Zig."
379384
(indent-line-to indent-col)
380385
(save-excursion (indent-line-to indent-col)))))
381386

382-
(defun zig-syntax-propertize-to-newline-if-in-multiline-str (end)
383-
;; First, we need to check if we're in a multiline string literal; if we're
384-
;; not, do nothing.
385-
(when (zig-currently-in-str)
386-
(let ((start (zig-start-of-current-str-or-comment)))
387-
(when (save-excursion
388-
(goto-char start)
389-
(looking-at "\\\\\\\\"))
390-
;; At this point, we've determined that we're within a multiline string
391-
;; literal. Let `stop' be the position of the closing newline, or
392-
;; `end', whichever comes first.
393-
(let ((stop (if (save-excursion
394-
(goto-char start)
395-
(re-search-forward "\n" end t))
396-
(prog1 (match-end 0)
397-
;; We found the closing newline, so mark it as the
398-
;; end of this string literal.
399-
(put-text-property (match-beginning 0)
400-
(match-end 0)
401-
'syntax-table
402-
(string-to-syntax "|")))
403-
end)))
404-
;; Zig multiline string literals don't support escapes, so mark all
405-
;; backslashes (up to `stop') as punctation instead of escapes.
406-
(save-excursion
407-
(goto-char (1+ start))
408-
(while (re-search-forward "\\\\" stop t)
409-
(put-text-property (match-beginning 0) (match-end 0)
410-
'syntax-table (string-to-syntax "."))
411-
(goto-char (match-end 0))))
412-
;; Move to the end of the string (or `end'), so that
413-
;; zig-syntax-propertize can pick up from there.
414-
(goto-char stop))))))
387+
(defun zig-syntax-propertize-multiline-string (end)
388+
(let* ((eol (save-excursion (search-forward "\n" end t)))
389+
(stop (or eol end)))
390+
(while (search-forward "\\" stop t)
391+
(put-text-property (match-beginning 0) (match-end 0) 'syntax-table (string-to-syntax ".")))
392+
(when eol (put-text-property (- eol 2) (1- eol) 'syntax-table (string-to-syntax "|")))
393+
(goto-char stop)))
415394

416395
(defun zig-syntax-propertize (start end)
417396
(goto-char start)
418-
(zig-syntax-propertize-to-newline-if-in-multiline-str end)
419-
(funcall
420-
(syntax-propertize-rules
421-
;; Multiline strings
422-
;; Do not match backslashes that are preceded by single or
423-
;; double-quotes.
424-
("[^\\'\"]c?\\(\\\\\\)\\\\"
425-
(1 (prog1 "|"
426-
(goto-char (match-end 0))
427-
(zig-syntax-propertize-to-newline-if-in-multiline-str end)))))
428-
(point) end))
397+
(when (eq t (zig-currently-in-str))
398+
(zig-syntax-propertize-multiline-string end))
399+
(while (search-forward "\\\\" end t)
400+
(when (null (save-excursion (backward-char 2) (zig-currently-in-str)))
401+
(backward-char)
402+
(put-text-property (match-beginning 0) (point) 'syntax-table (string-to-syntax "|"))
403+
(zig-syntax-propertize-multiline-string end))))
429404

430405
(defun zig-mode-syntactic-face-function (state)
431406
(save-excursion

0 commit comments

Comments
 (0)