diff options
| author | Randy Taylor | 2022-12-07 20:53:35 -0500 |
|---|---|---|
| committer | Yuan Fu | 2022-12-09 16:46:01 -0800 |
| commit | 8f49137c9bf614b285c19a3a845c7606fcba23a4 (patch) | |
| tree | 5aa0599d5ab9d60e6cf31324d2755fd3ab8cd244 | |
| parent | 1014bcc8e32c8ab7b25e148d13e7e3a82f9635b9 (diff) | |
| download | emacs-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-x | admin/notes/tree-sitter/build-module/batch.sh | 1 | ||||
| -rwxr-xr-x | admin/notes/tree-sitter/build-module/build.sh | 6 | ||||
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/progmodes/dockerfile-ts-mode.el | 176 | ||||
| -rw-r--r-- | lisp/progmodes/eglot.el | 2 |
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 | ||
| 17 | namespace="tree-sitter" | ||
| 17 | repo="tree-sitter-${lang}" | 18 | repo="tree-sitter-${lang}" |
| 18 | sourcedir="tree-sitter-${lang}/src" | 19 | sourcedir="tree-sitter-${lang}/src" |
| 19 | grammardir="tree-sitter-${lang}" | 20 | grammardir="tree-sitter-${lang}" |
| 20 | 21 | ||
| 21 | case "${lang}" in | 22 | case "${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 | ;; |
| 31 | esac | 35 | esac |
| 32 | 36 | ||
| 33 | git clone "https://github.com/tree-sitter/${repo}.git" \ | 37 | git clone "https://github.com/${namespace}/${repo}.git" \ |
| 34 | --depth 1 --quiet | 38 | --depth 1 --quiet |
| 35 | cp "${grammardir}"/grammar.js "${sourcedir}" | 39 | cp "${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 |
| @@ -3068,6 +3068,11 @@ A major mode based on the tree-sitter library for editing Bash shell | |||
| 3068 | scripts. It includes support for font-locking, indentation, Imenu, | 3068 | scripts. It includes support for font-locking, indentation, Imenu, |
| 3069 | which-func, and navigation. | 3069 | which-func, and navigation. |
| 3070 | 3070 | ||
| 3071 | ** New major mode 'dockerfile-ts-mode'. | ||
| 3072 | A major mode based on the tree-sitter library for editing | ||
| 3073 | Dockerfiles. It includes support for font-locking, indentation, Imenu, | ||
| 3074 | and 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'. | ||
| 112 | Find string representation for NODE and set marker, then recurse | ||
| 113 | the 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) |