aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRandy Taylor2022-12-07 20:53:35 -0500
committerYuan Fu2022-12-09 16:46:01 -0800
commit8f49137c9bf614b285c19a3a845c7606fcba23a4 (patch)
tree5aa0599d5ab9d60e6cf31324d2755fd3ab8cd244
parent1014bcc8e32c8ab7b25e148d13e7e3a82f9635b9 (diff)
downloademacs-8f49137c9bf614b285c19a3a845c7606fcba23a4.tar.gz
emacs-8f49137c9bf614b285c19a3a845c7606fcba23a4.zip
Add dockerfile-ts-mode (Bug#59894)
* admin/notes/tree-sitter/build-module/batch.sh: Add dockerfile support. * admin/notes/tree-sitter/build-module/build.sh: Support different namespaces and add dockerfile support. * etc/NEWS: Mention it. * lisp/progmodes/dockerfile-ts-mode.el: New major mode with tree-sitter support. * lisp/progmodes/eglot.el (eglot-server-programs): Add it.
-rwxr-xr-xadmin/notes/tree-sitter/build-module/batch.sh1
-rwxr-xr-xadmin/notes/tree-sitter/build-module/build.sh6
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/progmodes/dockerfile-ts-mode.el176
-rw-r--r--lisp/progmodes/eglot.el2
5 files changed, 188 insertions, 2 deletions
diff --git a/admin/notes/tree-sitter/build-module/batch.sh b/admin/notes/tree-sitter/build-module/batch.sh
index d45f37f4b64..6dce000caa6 100755
--- a/admin/notes/tree-sitter/build-module/batch.sh
+++ b/admin/notes/tree-sitter/build-module/batch.sh
@@ -5,6 +5,7 @@ languages=(
5 'cpp' 5 'cpp'
6 'css' 6 'css'
7 'c-sharp' 7 'c-sharp'
8 'dockerfile'
8 'go' 9 'go'
9 'html' 10 'html'
10 'javascript' 11 'javascript'
diff --git a/admin/notes/tree-sitter/build-module/build.sh b/admin/notes/tree-sitter/build-module/build.sh
index d562f1a7846..cc31e3f6f02 100755
--- a/admin/notes/tree-sitter/build-module/build.sh
+++ b/admin/notes/tree-sitter/build-module/build.sh
@@ -14,11 +14,15 @@ echo "Building ${lang}"
14 14
15### Retrieve sources 15### Retrieve sources
16 16
17namespace="tree-sitter"
17repo="tree-sitter-${lang}" 18repo="tree-sitter-${lang}"
18sourcedir="tree-sitter-${lang}/src" 19sourcedir="tree-sitter-${lang}/src"
19grammardir="tree-sitter-${lang}" 20grammardir="tree-sitter-${lang}"
20 21
21case "${lang}" in 22case "${lang}" in
23 "dockerfile")
24 namespace="camdencheek"
25 ;;
22 "typescript") 26 "typescript")
23 sourcedir="tree-sitter-typescript/typescript/src" 27 sourcedir="tree-sitter-typescript/typescript/src"
24 grammardir="tree-sitter-typescript/typescript" 28 grammardir="tree-sitter-typescript/typescript"
@@ -30,7 +34,7 @@ case "${lang}" in
30 ;; 34 ;;
31esac 35esac
32 36
33git clone "https://github.com/tree-sitter/${repo}.git" \ 37git clone "https://github.com/${namespace}/${repo}.git" \
34 --depth 1 --quiet 38 --depth 1 --quiet
35cp "${grammardir}"/grammar.js "${sourcedir}" 39cp "${grammardir}"/grammar.js "${sourcedir}"
36# We have to go into the source directory to compile, because some 40# We have to go into the source directory to compile, because some
diff --git a/etc/NEWS b/etc/NEWS
index e4a19e29869..233ef3f5729 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3068,6 +3068,11 @@ A major mode based on the tree-sitter library for editing Bash shell
3068scripts. It includes support for font-locking, indentation, Imenu, 3068scripts. It includes support for font-locking, indentation, Imenu,
3069which-func, and navigation. 3069which-func, and navigation.
3070 3070
3071** New major mode 'dockerfile-ts-mode'.
3072A major mode based on the tree-sitter library for editing
3073Dockerfiles. It includes support for font-locking, indentation, Imenu,
3074and which-func.
3075
3071 3076
3072* Incompatible Lisp Changes in Emacs 29.1 3077* Incompatible Lisp Changes in Emacs 29.1
3073 3078
diff --git a/lisp/progmodes/dockerfile-ts-mode.el b/lisp/progmodes/dockerfile-ts-mode.el
new file mode 100644
index 00000000000..e08387ad969
--- /dev/null
+++ b/lisp/progmodes/dockerfile-ts-mode.el
@@ -0,0 +1,176 @@
1;;; dockerfile-ts-mode.el --- tree-sitter support for Dockerfiles -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2022 Free Software Foundation, Inc.
4
5;; Author : Randy Taylor <dev@rjt.dev>
6;; Maintainer : Randy Taylor <dev@rjt.dev>
7;; Created : December 2022
8;; Keywords : dockerfile languages tree-sitter
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
24
25;;; Commentary:
26;;
27
28;;; Code:
29
30(require 'treesit)
31(eval-when-compile (require 'rx))
32
33(declare-function treesit-parser-create "treesit.c")
34(declare-function treesit-induce-sparse-tree "treesit.c")
35(declare-function treesit-node-child "treesit.c")
36(declare-function treesit-node-start "treesit.c")
37(declare-function treesit-node-type "treesit.c")
38
39(defvar dockerfile-ts-mode--syntax-table
40 (let ((table (make-syntax-table)))
41 (modify-syntax-entry ?# "<" table)
42 (modify-syntax-entry ?\n ">" table)
43 table)
44 "Syntax table for `dockerfile-ts-mode'.")
45
46(defvar dockerfile-ts-mode--indent-rules
47 `((dockerfile
48 ((parent-is "copy_instruction") (nth-sibling 1) 0)
49 ((parent-is "env_instruction") (nth-sibling 1) 0)
50 ((parent-is "expose_instruction") (nth-sibling 1) 0)
51 ((parent-is "label_instruction") (nth-sibling 1) 0)
52 ((parent-is "shell_command") first-sibling 0)
53 ((parent-is "string_array") first-sibling 1)))
54 "Tree-sitter indent rules.")
55
56(defvar dockerfile-ts-mode--keywords
57 '("ADD" "ARG" "AS" "CMD" "COPY" "CROSS_BUILD" "ENTRYPOINT" "ENV"
58 "EXPOSE" "FROM" "HEALTHCHECK" "LABEL" "MAINTAINER" "ONBUILD" "RUN"
59 "SHELL" "STOPSIGNAL" "USER" "VOLUME" "WORKDIR")
60 "Dockerfile keywords for tree-sitter font-locking.")
61
62(defvar dockerfile-ts-mode--font-lock-settings
63 (treesit-font-lock-rules
64 :language 'dockerfile
65 :feature 'bracket
66 '((["[" "]"]) @font-lock-bracket-face)
67
68 :language 'dockerfile
69 :feature 'comment
70 '((comment) @font-lock-comment-face)
71
72 :language 'dockerfile
73 :feature 'delimiter
74 '(([","]) @font-lock-delimiter-face)
75
76 :language 'dockerfile
77 :feature 'image-spec
78 '((image_spec) @font-lock-constant-face)
79
80 :language 'dockerfile
81 :feature 'keyword
82 `([,@dockerfile-ts-mode--keywords] @font-lock-keyword-face)
83
84 :language 'dockerfile
85 :feature 'number
86 '((expose_port) @font-lock-number-face)
87
88 :language 'dockerfile
89 :feature 'operator
90 '((["="]) @font-lock-operator-face)
91
92 :language 'dockerfile
93 :feature 'string
94 '((double_quoted_string) @font-lock-string-face)
95
96 :language 'dockerfile
97 :feature 'error
98 :override t
99 '((ERROR) @font-lock-warning-face))
100 "Tree-sitter font-lock settings.")
101
102(defun dockerfile-ts-mode--imenu ()
103 "Return Imenu alist for the current buffer."
104 (let* ((node (treesit-buffer-root-node))
105 (stage-tree (treesit-induce-sparse-tree
106 node "from_instruction"
107 nil 1000)))
108 `(("Stage" . ,(dockerfile-ts-mode--imenu-1 stage-tree)))))
109
110(defun dockerfile-ts-mode--imenu-1 (node)
111 "Helper for `dockerfile-ts-mode--imenu'.
112Find string representation for NODE and set marker, then recurse
113the subtrees."
114 (let* ((ts-node (car node))
115 (children (cdr node))
116 (subtrees (mapcan #'dockerfile-ts-mode--imenu-1
117 children))
118 (name (when ts-node
119 (pcase (treesit-node-type ts-node)
120 ("from_instruction" (treesit-node-text
121 (treesit-node-child ts-node 1) t)))))
122 (marker (when ts-node
123 (set-marker (make-marker)
124 (treesit-node-start ts-node)))))
125 (cond
126 ((or (null ts-node) (null name)) subtrees)
127 (subtrees
128 `((,name ,(cons name marker) ,@subtrees)))
129 (t
130 `((,name . ,marker))))))
131
132;;;###autoload
133(add-to-list 'auto-mode-alist
134 `(,(rx (|
135 (: "Dockerfile" (? "." (* nonl)))
136 (: "." (any "dD") "ockerfile"))
137 eol)
138 . dockerfile-ts-mode))
139
140;;;###autoload
141(define-derived-mode dockerfile-ts-mode prog-mode "Dockerfile"
142 "Major mode for editing Dockerfiles, powered by tree-sitter."
143 :group 'dockerfile
144 :syntax-table dockerfile-ts-mode--syntax-table
145
146 (when (treesit-ready-p 'dockerfile)
147 (treesit-parser-create 'dockerfile)
148
149 ;; Comments.
150 (setq-local comment-start "# ")
151 (setq-local comment-end "")
152 (setq-local comment-start-skip (rx "#" (* (syntax whitespace))))
153
154 ;; Imenu.
155 (setq-local imenu-create-index-function
156 #'dockerfile-ts-mode--imenu)
157 (setq-local which-func-functions nil)
158
159 ;; Indent.
160 (setq-local treesit-simple-indent-rules
161 dockerfile-ts-mode--indent-rules)
162
163 ;; Font-lock.
164 (setq-local treesit-font-lock-settings
165 dockerfile-ts-mode--font-lock-settings)
166 (setq-local treesit-font-lock-feature-list
167 '((comment)
168 (keyword string)
169 (image-spec number)
170 (bracket delimiter error operator)))
171
172 (treesit-major-mode-setup)))
173
174(provide 'dockerfile-ts-mode)
175
176;;; dockerfile-ts-mode.el ends here
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index a53f62fc565..2ef022992e7 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -236,7 +236,7 @@ chosen (interactively or automatically)."
236 . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio") 236 . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio")
237 ("css-languageserver" "--stdio")))) 237 ("css-languageserver" "--stdio"))))
238 (html-mode . ,(eglot-alternatives '(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio")))) 238 (html-mode . ,(eglot-alternatives '(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio"))))
239 (dockerfile-mode . ("docker-langserver" "--stdio")) 239 ((dockerfile-mode dockerfile-ts-mode) . ("docker-langserver" "--stdio"))
240 ((clojure-mode clojurescript-mode clojurec-mode) 240 ((clojure-mode clojurescript-mode clojurec-mode)
241 . ("clojure-lsp")) 241 . ("clojure-lsp"))
242 ((csharp-mode csharp-ts-mode) 242 ((csharp-mode csharp-ts-mode)