branch: elpa/zig-mode
commit b7731275e12ccb5cfea92153b9cc6ea8effc4bab
Author: Matthew D. Steele <[email protected]>
Commit: Matthew D. Steele <[email protected]>
Initial implementation of indent-line-function
---
run_tests.sh | 9 ++++
tests.el | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
zig-mode.el | 74 ++++++++++++++++++++++++++++----
3 files changed, 211 insertions(+), 9 deletions(-)
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100755
index 0000000..bbb57f0
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+if [ -z "${EMACS}" ]; then
+ EMACS="emacs"
+else
+ echo "Running with EMACS=${EMACS}"
+fi
+
+${EMACS} --batch -l zig-mode.el -l tests.el -f ert-run-tests-batch-and-exit
diff --git a/tests.el b/tests.el
new file mode 100644
index 0000000..9086175
--- /dev/null
+++ b/tests.el
@@ -0,0 +1,137 @@
+;; Tests for zig-mode.
+
+(require 'ert)
+(require 'zig-mode)
+
+;;===========================================================================;;
+;; Indentation tests
+
+(defun zig-test-indent-region (original expected)
+ (with-temp-buffer
+ (zig-mode)
+ (insert original)
+ (indent-region 1 (+ 1 (buffer-size)))
+ (should (equal expected (buffer-string)))))
+
+(ert-deftest test-indent-top-level ()
+ (zig-test-indent-region
+ " const four = 4;"
+ "const four = 4;"))
+
+(ert-deftest test-indent-fn-def-body ()
+ (zig-test-indent-region
+ "
+pub fn plus1(value: u32) u32 {
+return value + 1;
+}"
+ "
+pub fn plus1(value: u32) u32 {
+ return value + 1;
+}"))
+
+(ert-deftest test-indent-fn-def-args ()
+ (zig-test-indent-region
+ "
+pub fn add(value1: u32,
+value2: u32) u32 {
+return value1 + value2;
+}"
+ "
+pub fn add(value1: u32,
+ value2: u32) u32 {
+ return value1 + value2;
+}"))
+
+(ert-deftest test-indent-fn-call-args ()
+ (zig-test-indent-region
+ "
+blarg(foo,
+foo + bar + baz +
+quux,
+quux);"
+ "
+blarg(foo,
+ foo + bar + baz +
+ quux,
+ quux);"))
+
+(ert-deftest test-indent-if-else ()
+ (zig-test-indent-region
+ "
+fn sign(value: i32) i32 {
+if (value > 0) return 1;
+else if (value < 0) {
+return -1;
+} else {
+return 0;
+}
+}"
+ "
+fn sign(value: i32) i32 {
+ if (value > 0) return 1;
+ else if (value < 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+}"))
+
+(ert-deftest test-indent-struct ()
+ (zig-test-indent-region
+ "
+const Point = struct {
+x: f32,
+y: f32,
+};
+const origin = Point {
+.x = 0.0,
+.y = 0.0,
+};"
+ "
+const Point = struct {
+ x: f32,
+ y: f32,
+};
+const origin = Point {
+ .x = 0.0,
+ .y = 0.0,
+};"))
+
+(ert-deftest test-indent-multiline-str-literal ()
+ (zig-test-indent-region
+ "
+const code =
+\\\\const foo = []u32{
+\\\\ 12345,
+\\\\};
+;"
+ "
+const code =
+ \\\\const foo = []u32{
+ \\\\ 12345,
+ \\\\};
+;"))
+
+(ert-deftest test-indent-array-literal-1 ()
+ (zig-test-indent-region
+ "
+const msgs = [][]u8{
+\"hello\",
+\"goodbye\",
+};"
+ "
+const msgs = [][]u8{
+ \"hello\",
+ \"goodbye\",
+};"))
+
+(ert-deftest test-indent-array-literal-2 ()
+ (zig-test-indent-region
+ "
+const msg = []u8{'h', 'e', 'l', 'l', 'o',
+'w', 'o', 'r', 'l', 'd'};"
+ "
+const msg = []u8{'h', 'e', 'l', 'l', 'o',
+ 'w', 'o', 'r', 'l', 'd'};"))
+
+;;===========================================================================;;
diff --git a/zig-mode.el b/zig-mode.el
index 221a3a4..36bd031 100644
--- a/zig-mode.el
+++ b/zig-mode.el
@@ -1,6 +1,6 @@
;;; zig-mode.el --- A major mode for the Zig programming language -*-
lexical-binding: t -*-
-;; Version: 0.0.6
+;; Version: 0.0.7
;; Author: Andrea Orru <[email protected]>, Andrew Kelley
<[email protected]>
;; Keywords: zig, languages
;; Package-Requires: ((emacs "24"))
@@ -24,8 +24,6 @@
;;; Code:
-(require 'cc-mode)
-
(defun zig-re-word (inner)
"Construct a regular expression for the word INNER."
(concat "\\<" inner "\\>"))
@@ -104,7 +102,6 @@
;; Other types
"bool" "void" "noreturn" "type" "error" "promise"))
-
(defconst zig-constants
'(
;; Boolean
@@ -113,6 +110,21 @@
;; Other constants
"null" "undefined" "this"))
+(defgroup zig-mode nil
+ "Support for Zig code."
+ :link '(url-link "https://ziglang.org/")
+ :group 'languages)
+
+(defcustom zig-indent-offset 4
+ "Indent Zig code by this number of spaces."
+ :type 'integer
+ :group 'zig-mode
+ :safe #'integerp)
+
+(defface zig-multiline-string-face
+ '((t :inherit font-lock-string-face))
+ "Face for multiline string literals."
+ :group 'zig-mode)
(defvar zig-font-lock-keywords
(append
@@ -138,9 +150,48 @@
("var" . font-lock-variable-name-face)
("fn" . font-lock-function-name-face)))))
+(defun zig-paren-nesting-level () (nth 0 (syntax-ppss)))
+(defun zig-prev-open-paren-pos () (car (last (nth 9 (syntax-ppss)))))
(defun zig-currently-in-str () (nth 3 (syntax-ppss)))
(defun zig-start-of-current-str-or-comment () (nth 8 (syntax-ppss)))
+(defun zig-skip-backwards-past-whitespace-and-comments ()
+ (while (or
+ ;; If inside a comment, jump to start of comment.
+ (let ((start (zig-start-of-current-str-or-comment)))
+ (and start
+ (not (zig-currently-in-str))
+ (goto-char start)))
+ ;; Skip backwards past whitespace and comment end delimiters.
+ (/= 0 (skip-syntax-backward " >")))))
+
+(defun zig-mode-indent-line ()
+ (interactive)
+ (let ((indent-col
+ (save-excursion
+ (back-to-indentation)
+ (let ((paren-level
+ (let ((level (zig-paren-nesting-level)))
+ (if (looking-at "[]})]") (1- level) level))))
+ (+ (if (<= paren-level 0)
+ 0
+ (or (save-excursion
+ (goto-char (1+ (zig-prev-open-paren-pos)))
+ (and (not (looking-at "\n"))
+ (current-column)))
+ (* zig-indent-offset paren-level)))
+ (if (and
+ (not (looking-at ";"))
+ (save-excursion
+ (zig-skip-backwards-past-whitespace-and-comments)
+ (when (> (point) 1)
+ (backward-char)
+ (not (looking-at "[,;([{}]")))))
+ zig-indent-offset 0))))))
+ (if (<= (current-column) (current-indentation))
+ (indent-line-to indent-col)
+ (save-excursion (indent-line-to indent-col)))))
+
(defun zig-syntax-propertize-newline-if-in-multiline-str (end)
(when (and (zig-currently-in-str)
(save-excursion
@@ -164,7 +215,12 @@
(point) end))
(defun zig-mode-syntactic-face-function (state)
- (if (nth 3 state) 'font-lock-string-face
+ (if (nth 3 state)
+ (save-excursion
+ (goto-char (nth 8 state))
+ (if (looking-at "\\\\\\\\")
+ 'zig-multiline-string-face
+ 'font-lock-string-face))
(save-excursion
(goto-char (nth 8 state))
(if (looking-at "///[^/]")
@@ -172,12 +228,12 @@
'font-lock-comment-face))))
;;;###autoload
-(define-derived-mode zig-mode c-mode "Zig"
- "A major mode for the zig programming language."
- (set (make-local-variable 'c-basic-offset) 4)
- (set (make-local-variable 'c-syntactic-indentation) nil)
+(define-derived-mode zig-mode prog-mode "Zig"
+ "A major mode for the Zig programming language."
(setq-local comment-start "// ")
(setq-local comment-end "")
+ (setq-local indent-line-function 'zig-mode-indent-line)
+ (setq-local indent-tabs-mode nil) ; Zig forbids tab characters.
(setq-local syntax-propertize-function 'zig-syntax-propertize)
(setq font-lock-defaults '(zig-font-lock-keywords
nil nil nil nil