Skip to content

Commit

Permalink
feat: #14 Improve multi-line string indentation (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
antifuchs authored May 6, 2024
1 parent 2dddc6e commit a1cf595
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 1 deletion.
32 changes: 31 additions & 1 deletion nix-ts-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,44 @@
"Tree-sitter font-lock settings for `nix-ts-mode'.")

;; Indentation
(defun nix-ts-indent-multiline-string (n parent bol &rest rest)
"Return the indent prefix for the current multi-line string line.
For the first line, this is the previous line offset+nix-indent-offset,
and for subsequent lines it's the previous line's indentation."
;; If the current line is the first relevant one in the multiline
;; string, indent it to the default level (2 spaces past the
;; previous line's offset):
(if (and (equal (treesit-node-child (treesit-node-parent parent) 1)
parent)
(<= (count-lines (treesit-node-start parent) (point)) 1))
(+ (apply (alist-get 'parent-bol treesit-simple-indent-presets)
n parent bol rest)
nix-ts-mode-indent-offset)
;; If the current line is already indented to some level, leave it alone:
(if (/= bol
(save-excursion
(beginning-of-line)
(point)))
bol
;; otherwise, indent to the level of the previous line by default.
(save-excursion
(forward-line -1)
(if (looking-at "\s+")
(match-end 0)
;; in case neither line has discernable indentation, just
;; indent to bol
bol)))))

(defvar nix-ts-mode-indent-rules
`((nix
((parent-is "source_code") column-0 0)
((node-is "]") parent-bol 0)
((node-is ")") parent-bol 0)
((node-is "}") parent-bol 0)
((node-is "binding_set") parent-bol nix-ts-mode-indent-offset)
((node-is "indented_string_expression") parent-bol nix-ts-mode-indent-offset)
((match "interpolation" "indented_string_expression" nil nil nil) nix-ts-indent-multiline-string 0)
((parent-is "indented_string_expression") parent-bol 0)
((parent-is "string_fragment") nix-ts-indent-multiline-string 0)
((parent-is "formals") parent-bol 0)
((parent-is "binding_set") parent-bol 0)
((parent-is "binding") parent-bol nix-ts-mode-indent-offset)
Expand Down
45 changes: 45 additions & 0 deletions test/nix-ts-mode-indent-test.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
;;; nix-ts-mode-indent-test.el --- Indentation tests for nix-ts-mode. -*- lexical-binding: t; -*-

;; Copyright (C) 2024 Andreas Fuchs

;; Author: Andreas Fuchs <[email protected]>
;; Keywords: nix
;; Package-Requires: ((emacs "29.1"))

;; This file is NOT part of GNU Emacs.

;;; Commentary:


;; Tests for tree-sitter powered indentation in `nix-ts-mode`.

;;; Code:
(require 'ert)
(require 'ert-x)
(require 'nix-ts-mode)

(defmacro with-nix-buffer-contents (&rest body)
"Run `BODY` in the context of a new buffer set to `nix-ts-mode`."
`(with-temp-buffer
(delay-mode-hooks (nix-ts-mode))
,@body))

(defun check-indentation (contents)
"Reindent CONTENTS according to nix-ts-mode's rules and check that it matches.
CONTENT is a correctly-indented nix expression; all its lines' leading
whitespace is stripped, then re-indented and checked that the
output is identical to the given expression."
(let ((dedented (replace-regexp-in-string "^\\s+" "" contents)))
(insert dedented)
(indent-region (point-min) (point-max))
(should (equal (buffer-substring (point-min) (point-max))
contents))))

;;; Features

(ert-deftest nix-multiline-string ()
(ert-test-erts-file (ert-resource-file "indent-multiline-string.erts")
(lambda ()
(nix-ts-mode)
(indent-region (point-min) (point-max)))))
59 changes: 59 additions & 0 deletions test/resources/indent-multiline-string.erts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
Name: simple-multiline-string

=-=
{
a = ''
hi [
test
]
'';
}
=-=
{
a = ''
hi [
test
]
'';
}
=-=-=

Name: multiline-string-with-interpolations

=-=
{
b = "hi";
a = ''
${b}
but also
bye
'';
}
=-=
{
b = "hi";
a = ''
${b}
but also
bye
'';
}
=-=-=

Name: multiline-string-with-escaped-interpolations

=-=
{
a = ''
what=''${1:-hi}
echo "$what"
'';
}
=-=
{
a = ''
what=''${1:-hi}
echo "$what"
'';
}
=-=-=

0 comments on commit a1cf595

Please sign in to comment.