Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix multiline string literals #104

Merged
merged 2 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions test/zig-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const string =
;"
'(("const" font-lock-keyword-face)
("string" font-lock-variable-name-face)
("\\\\ This newline is NOT escaped \\\n" zig-multiline-string-face))))
("\\\\ This newline is NOT escaped \\" zig-multiline-string-face))))

(ert-deftest test-font-lock-backslash-in-str-literal ()
(zig-test-font-lock
Expand Down Expand Up @@ -100,8 +100,8 @@ const python =
;"
'(("const" font-lock-keyword-face)
("python" font-lock-variable-name-face)
("\\\\def main():\n" zig-multiline-string-face)
("\\\\ print(\"Hello, world!\")\n" zig-multiline-string-face))))
("\\\\def main():" zig-multiline-string-face)
("\\\\ print(\"Hello, world!\")" zig-multiline-string-face))))

(ert-deftest test-font-lock-parameters-pointers-and-arrays ()
(zig-test-font-lock
Expand Down Expand Up @@ -164,6 +164,27 @@ const python =
("void" font-lock-type-face)))))
(zig-test-font-lock test-string expected))))

(ert-deftest test-font-lock-int-types ()
(zig-test-font-lock
"const Types = .{ u0, i7, u33, i123, u55555 };"
'(("const" font-lock-keyword-face)
("Types" font-lock-variable-name-face)
("u0" font-lock-type-face)
("i7" font-lock-type-face)
("u33" font-lock-type-face)
("i123" font-lock-type-face)
("u55555" font-lock-type-face))))

(ert-deftest test-font-lock-escaped-backslash ()
(zig-test-font-lock
"const a = foo('\\\\', \"C:\\\\\", \\\\
);"
'(("const" font-lock-keyword-face)
("a" font-lock-variable-name-face)
("'\\\\'" font-lock-string-face)
("\"C:\\\\\"" font-lock-string-face)
("\\\\" zig-multiline-string-face))))

;;===========================================================================;;
;; Indentation tests

Expand Down
175 changes: 75 additions & 100 deletions zig-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -163,55 +163,6 @@ If given a SOURCE, execute the CMD on it."

table))

(defconst zig-keywords
'(;; Storage
"const" "var" "extern" "packed" "export" "pub" "noalias" "inline"
"noinline" "comptime" "callconv" "volatile" "allowzero"
"align" "linksection" "threadlocal" "addrspace"

;; Structure
"struct" "enum" "union" "error" "opaque"

;; Statement
"break" "return" "continue" "asm" "defer" "errdefer" "unreachable"
"try" "catch" "async" "nosuspend" "await" "suspend" "resume"

;; Conditional
"if" "else" "switch" "and" "or" "orelse"

;; Repeat
"while" "for"

;; Other keywords
"fn" "usingnamespace" "test"))

(defconst zig-types
'(;; Integer types
"i2" "u2" "i3" "u3" "i4" "u4" "i5" "u5" "i6" "u6" "i7" "u7" "i8" "u8"
"i16" "u16" "i29" "u29" "i32" "u32" "i64" "u64" "i128" "u128"
"isize" "usize"

;; Floating types
"f16" "f32" "f64" "f80" "f128"

;; C types
"c_char" "c_short" "c_ushort" "c_int" "c_uint" "c_long" "c_ulong"
"c_longlong" "c_ulonglong" "c_longdouble"

;; Comptime types
"comptime_int" "comptime_float"

;; Other types
"bool" "void" "noreturn" "type" "error" "anyerror" "anyframe" "anytype"
"anyopaque"))

(defconst zig-constants
'(;; Boolean
"true" "false"

;; Other constants
"null" "undefined"))

(defconst zig-electric-indent-chars
'(?\; ?\, ?\) ?\] ?\}))

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

;; Keywords, constants and types
(,(regexp-opt zig-keywords 'symbols) . font-lock-keyword-face)
(,(regexp-opt zig-constants 'symbols) . font-lock-constant-face)
(,(regexp-opt zig-types 'symbols) . font-lock-type-face)
(,(rx symbol-start
(|
;; Storage
"const" "var" "extern" "packed" "export" "pub" "noalias" "inline"
"noinline" "comptime" "callconv" "volatile" "allowzero"
"align" "linksection" "threadlocal" "addrspace"

;; Structure
"struct" "enum" "union" "error" "opaque"

;; Statement
"break" "return" "continue" "asm" "defer" "errdefer" "unreachable"
"try" "catch" "async" "nosuspend" "await" "suspend" "resume"

;; Conditional
"if" "else" "switch" "and" "or" "orelse"

;; Repeat
"while" "for"

;; Other keywords
"fn" "usingnamespace" "test")
symbol-end)
. font-lock-keyword-face)

(,(rx symbol-start
(|
;; Boolean
"true" "false"

;; Other constants
"null" "undefined")
symbol-end)
. font-lock-constant-face)

(,(rx symbol-start
(|
;; Integer types
(: (any ?i ?u) (| ?0 (: (any (?1 . ?9)) (* digit))))
"isize" "usize"

;; Floating types
"f16" "f32" "f64" "f80" "f128"

;; C types
"c_char" "c_short" "c_ushort" "c_int" "c_uint" "c_long" "c_ulong"
"c_longlong" "c_ulonglong" "c_longdouble"

;; Comptime types
"comptime_int" "comptime_float"

;; Other types
"bool" "void" "noreturn" "type" "anyerror" "anyframe" "anytype"
"anyopaque")
symbol-end)
. font-lock-type-face)

;; Type annotations (both variable and type)
(,zig-re-type-annotation 1 font-lock-variable-name-face)
Expand Down Expand Up @@ -357,18 +361,19 @@ This is written mainly to be used as `end-of-defun-function' for Zig."
(and (not (looking-at " *\\(//[^\n]*\\)?\n"))
(current-column)))
(+ prev-block-indent-col zig-indent-offset))))
;; is-expr-continutation: True if this line continues an
;; is-expr-continuation: True if this line continues an
;; expression from the previous line, false otherwise.
(is-expr-continutation
(is-expr-continuation
(and
(not (looking-at "[]});]\\|else"))
(save-excursion
(zig-skip-backwards-past-whitespace-and-comments)
(when (> (point) 1)
(backward-char)
(not (looking-at "[,;([{}]")))))))
(or (zig-currently-in-str)
(not (looking-at "[,;([{}]"))))))))
;; Now we can calculate indent-col:
(if is-expr-continutation
(if is-expr-continuation
(+ base-indent-col zig-indent-offset)
base-indent-col)))))
;; If point is within the indentation whitespace, move it to the end of the
Expand All @@ -379,53 +384,23 @@ This is written mainly to be used as `end-of-defun-function' for Zig."
(indent-line-to indent-col)
(save-excursion (indent-line-to indent-col)))))

(defun zig-syntax-propertize-to-newline-if-in-multiline-str (end)
;; First, we need to check if we're in a multiline string literal; if we're
;; not, do nothing.
(when (zig-currently-in-str)
(let ((start (zig-start-of-current-str-or-comment)))
(when (save-excursion
(goto-char start)
(looking-at "\\\\\\\\"))
;; At this point, we've determined that we're within a multiline string
;; literal. Let `stop' be the position of the closing newline, or
;; `end', whichever comes first.
(let ((stop (if (save-excursion
(goto-char start)
(re-search-forward "\n" end t))
(prog1 (match-end 0)
;; We found the closing newline, so mark it as the
;; end of this string literal.
(put-text-property (match-beginning 0)
(match-end 0)
'syntax-table
(string-to-syntax "|")))
end)))
;; Zig multiline string literals don't support escapes, so mark all
;; backslashes (up to `stop') as punctation instead of escapes.
(save-excursion
(goto-char (1+ start))
(while (re-search-forward "\\\\" stop t)
(put-text-property (match-beginning 0) (match-end 0)
'syntax-table (string-to-syntax "."))
(goto-char (match-end 0))))
;; Move to the end of the string (or `end'), so that
;; zig-syntax-propertize can pick up from there.
(goto-char stop))))))
(defun zig-syntax-propertize-multiline-string (end)
(let* ((eol (save-excursion (search-forward "\n" end t)))
(stop (or eol end)))
(while (search-forward "\\" stop t)
(put-text-property (match-beginning 0) (match-end 0) 'syntax-table (string-to-syntax ".")))
(when eol (put-text-property (- eol 2) (1- eol) 'syntax-table (string-to-syntax "|")))
(goto-char stop)))

(defun zig-syntax-propertize (start end)
(goto-char start)
(zig-syntax-propertize-to-newline-if-in-multiline-str end)
(funcall
(syntax-propertize-rules
;; Multiline strings
;; Do not match backslashes that are preceded by single or
;; double-quotes.
("[^\\'\"]c?\\(\\\\\\)\\\\"
(1 (prog1 "|"
(goto-char (match-end 0))
(zig-syntax-propertize-to-newline-if-in-multiline-str end)))))
(point) end))
(when (eq t (zig-currently-in-str))
(zig-syntax-propertize-multiline-string end))
(while (search-forward "\\\\" end t)
(when (null (save-excursion (backward-char 2) (zig-currently-in-str)))
(backward-char)
(put-text-property (match-beginning 0) (point) 'syntax-table (string-to-syntax "|"))
(zig-syntax-propertize-multiline-string end))))

(defun zig-mode-syntactic-face-function (state)
(save-excursion
Expand Down