-
Notifications
You must be signed in to change notification settings - Fork 132
/
tree-sitter-langs.el
302 lines (278 loc) · 13.3 KB
/
tree-sitter-langs.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
;;; tree-sitter-langs.el --- Grammar bundle for tree-sitter -*- lexical-binding: t; coding: utf-8 -*-
;; Copyright (C) 2021 Tuấn-Anh Nguyễn
;;
;; Author: Tuấn-Anh Nguyễn <[email protected]>
;; Keywords: languages tools parsers tree-sitter
;; Homepage: https://github.com/emacs-tree-sitter/tree-sitter-langs
;; Version: 0.12.237
;; Package-Requires: ((emacs "25.1") (tree-sitter "0.15.0"))
;; SPDX-License-Identifier: MIT
;;; Commentary:
;; This is a convenient language bundle for the Emacs package `tree-sitter'.
;; It serves as an interim distribution mechanism, until `tree-sitter' is
;; widespread enough for language-specific major modes to incorporate its
;; functionalities.
;;
;; For each supported language, this package provides:
;;
;; 1. Pre-compiled grammar binaries for 3 major platforms: macOS, Linux and
;; Windows, on x86_64. In the future, `tree-sitter-langs' may provide tooling
;; for major modes to do this on their own.
;;
;; 2. Optional highlighting patterns. This is mainly intended for major modes
;; that are not aware of `tree-sitter'. A language major mode that wants to
;; use `tree-sitter' for syntax highlighting should instead provide the query
;; patterns on its own, using the mechanisms defined by `tree-sitter-hl'.
;;
;; 3. Optional query patterns for other minor modes that provide high-level
;; functionalities on top of `tree-sitter', such as code folding, evil text
;; objects... As with highlighting patterns, major modes that are directly
;; aware of `tree-sitter' should provide the query patterns on their own.
;;; Code:
(require 'cl-lib)
(require 'tree-sitter)
(require 'tree-sitter-load)
(require 'tree-sitter-hl)
(require 'tree-sitter-langs-build)
(eval-when-compile
(require 'pcase))
;; Not everyone uses a package manager that properly checks dependencies. We check it ourselves, and
;; ask users to upgrade `tree-sitter' if necessary. Otherwise, they would get `tsc-lang-abi-too-new'
;; errors, without an actionable message.
(let ((min-version "0.15.0"))
(when (version< tsc-dyn--version min-version)
(display-warning 'tree-sitter-langs
(format "Please upgrade `tree-sitter'. This bundle requires version %s or later." min-version)
:emergency)))
(defgroup tree-sitter-langs nil
"Grammar bundle for `tree-sitter'."
:group 'tree-sitter)
(defvar tree-sitter-langs--testing)
(eval-and-compile
(unless (bound-and-true-p tree-sitter-langs--testing)
(tree-sitter-langs-install-grammars :skip-if-installed)))
(defun tree-sitter-langs-ensure (lang-symbol)
"Return the language object identified by LANG-SYMBOL.
If it cannot be loaded, this function tries to compile the grammar.
This function also tries to copy highlight query from the language repo, if it
exists.
See `tree-sitter-langs-repos'."
(unwind-protect
(condition-case nil
(tree-sitter-require lang-symbol)
(error
(display-warning 'tree-sitter-langs
(format "Could not load grammar for `%s', trying to compile it"
lang-symbol))
(tree-sitter-langs-compile lang-symbol)
(tree-sitter-require lang-symbol)))
(tree-sitter-langs--copy-query lang-symbol)))
;;;###autoload
(defun tree-sitter-langs--init-load-path (&rest _args)
"Add the directory containing compiled grammars to `tree-sitter-load-path'."
(cl-pushnew (tree-sitter-langs--bin-dir) tree-sitter-load-path
:test #'string-equal)
(advice-remove 'tree-sitter-load #'tree-sitter-langs--init-load-path))
;;;###autoload
(advice-add 'tree-sitter-load :before #'tree-sitter-langs--init-load-path)
;;;###autoload
(defun tree-sitter-langs--init-major-mode-alist (&rest _args)
"Link known major modes to languages provided by the bundle."
(let ((alist '((actionscript-mode . actionscript)
(ada-mode . ada)
(agda-mode . agda)
(agda2-mode . agda)
(arduino-mode . arduino)
(astro-mode . astro)
(fish-mode . fish)
(asm-mode . asm)
(fasm-mode . asm)
(masm-mode . asm)
(nasm-mode . asm)
(gas-mode . asm)
(sh-mode . bash)
(beancount-mode . beancount)
(bibtex-mode . bibtex)
(c-mode . c)
(caml-mode . ocaml)
(clojure-mode . clojure)
(lisp-mode . commonlisp)
(lisp-interaction-mode . commonlisp)
(csharp-mode . c-sharp)
(c++-mode . cpp)
(cmake-mode . cmake)
(d-mode . d)
(dart-mode . dart)
(dockerfile-mode . dockerfile)
(css-mode . css)
(csv-mode . csv)
(editorconfig-conf-mode . editorconfig)
(elm-mode . elm)
(elixir-mode . elixir)
(emacs-lisp-mode . elisp)
(erlang-mode . erlang)
(ess-r-mode . r)
(fennel-mode . fennel)
(f90-mode . fortran)
(fortran-mode . fortran)
(gdscript-mode . gdscript)
(git-commit-mode . gitcommit)
(git-rebase-mode . git-rebase)
(gitattributes-mode . gitattributes)
(gitignore-mode . gitignore)
(gleam-mode . gleam)
(glsl-mode . glsl)
(go-mode . go)
(gpr-mode . gpr)
(groovy-mode . groovy)
(jenkinsfile-mode . groovy)
(haskell-mode . haskell)
(haxe-mode . haxe)
(hcl-mode . hcl)
(terraform-mode . hcl)
(heex-mode . heex)
(hlsl-mode . hlsl)
(html-mode . html)
(markdown-mode . markdown)
(mhtml-mode . html)
(nix-mode . nix)
(jai-mode . jai)
(java-mode . java)
(javascript-mode . javascript)
(js-mode . javascript)
(js2-mode . javascript)
(js3-mode . javascript)
(json-mode . json)
(jsonc-mode . json)
(jsonnet-mode . jsonnet)
(julia-mode . julia)
(kotlin-mode . kotlin)
(latex-mode . latex)
(LaTeX-mode . latex)
(llvm-mode . llvm)
(llvm-mir-mode . llvm-mir)
(lua-mode . lua)
(magik-mode . magik)
(makefile-mode . make)
(makefile-automake-mode . make)
(makefile-gmake-mode . make)
(makefile-makepp-mode . make)
(makefile-bsdmake-mode . make)
(makefile-imake-mode . make)
(matlab-mode . matlab)
(mermaid-mode . mermaid)
(meson-mode . meson)
(ninja-mode . ninja)
(noir-mode . noir)
(ocaml-mode . ocaml)
(org-mode . org)
(pascal-mode . pascal)
(perl-mode . perl)
(purescript-mode . purescript)
(cperl-mode . perl)
(php-mode . php)
(qss-mode . css)
(prisma-mode . prisma)
(python-mode . python)
(pygn-mode . pgn)
(racket-mode . racket)
(rjsx-mode . javascript)
(rst-mode . rst)
(ruby-mode . ruby)
(rust-mode . rust)
(rustic-mode . rust)
(scala-mode . scala)
(scheme-mode . scheme)
(solidity-mode . solidity)
(smithy-mode . smithy)
(sql-mode . sql)
(svelte-mode . svelte)
(swift-mode . swift)
(tablegen-mode . tablegen)
(toml-mode . toml)
(conf-toml-mode . toml)
(tcl-mode . tcl)
(tuareg-mode . ocaml)
(twig-mode . twig)
(typescript-mode . typescript)
(typescript-tsx-mode . tsx)
(typst-mode . typst)
(verilog-mode . verilog)
(vhdl-mode . vhdl)
(nxml-mode . xml)
(yaml-mode . yaml)
(k8s-mode . yaml)
(zig-mode . zig))))
(cond
;; If never been set; update the value directly!
((null tree-sitter-major-mode-language-alist)
(setq tree-sitter-major-mode-language-alist alist))
;; Else, we try not to change the custom value from user.
(t
(dolist (entry alist)
;; TODO: The `cl-pushnew' operation is very slow; better if we can use
;; `hash-table' instead?
;;
;; I've run the benchmark tests with 123 entries (alist length);
;; compare to set value directly. Obviously, set the value directly cost
;; constant time but how fast compare to `cl-pushnew'?
;;
;; It's average 325 to 650 times faster! The average time for each
;; `cl-pushnew' operation is 2.6 to 5.2 times slower.
(cl-pushnew entry tree-sitter-major-mode-language-alist :key #'car))))
(advice-remove 'tree-sitter--setup #'tree-sitter-langs--init-major-mode-alist)))
;;;###autoload
(advice-add 'tree-sitter--setup :before #'tree-sitter-langs--init-major-mode-alist)
;;; Normal load.
(tree-sitter-langs--init-major-mode-alist)
(defun tree-sitter-langs--hl-query-path (lang-symbol &optional mode)
"Return the highlighting query file for LANG-SYMBOL.
If MODE is non-nil, return the file containing additional MODE-specfic patterns
instead. An example is `terraform-mode'-specific highlighting patterns for HCL."
(concat (file-name-as-directory
(concat tree-sitter-langs--queries-dir
(symbol-name lang-symbol)))
(if mode
(format "highlights.%s.scm" mode)
"highlights.scm")))
(defun tree-sitter-langs--hl-default-patterns (lang-symbol &optional mode)
"Return the bundled default syntax highlighting patterns for LANG-SYMBOL.
Return nil if there are no bundled patterns."
(condition-case nil
(with-temp-buffer
;; TODO: Make this less ad-hoc.
(dolist (sym (cons lang-symbol
(pcase lang-symbol
('arduino '(cpp c))
('astro '(html_tags html))
('cpp '(c))
('csv '(tsv))
('hlsl '(cpp c))
('html '(html_tags))
('svelte '(html_tags))
('javascript '(ecma))
('typescript '(ecma))
('tsx '(ecma typescript))
(_ nil))))
(when mode
(ignore-error 'file-missing
(insert-file-contents (tree-sitter-langs--hl-query-path sym mode))
(goto-char (point-max))
(insert "\n")))
(insert-file-contents (tree-sitter-langs--hl-query-path sym))
(goto-char (point-max))
(insert "\n"))
(buffer-string))
(file-missing nil)))
;;;###autoload
(defun tree-sitter-langs--set-hl-default-patterns (&rest _args)
"Use syntax highlighting patterns provided by `tree-sitter-langs'."
(unless tree-sitter-hl-default-patterns
(let ((lang-symbol (tsc--lang-symbol tree-sitter-language)))
(setq tree-sitter-hl-default-patterns
(tree-sitter-langs--hl-default-patterns lang-symbol major-mode)))))
;;;###autoload
(advice-add 'tree-sitter-hl--setup :before
#'tree-sitter-langs--set-hl-default-patterns)
(provide 'tree-sitter-langs)
;;; tree-sitter-langs.el ends here