-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathpi-coding-agent-grammars.el
More file actions
292 lines (262 loc) · 13.6 KB
/
pi-coding-agent-grammars.el
File metadata and controls
292 lines (262 loc) · 13.6 KB
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
;;; pi-coding-agent-grammars.el --- Tree-sitter grammar recipes -*- lexical-binding: t; -*-
;; Copyright (C) 2024-2026 Daniel Nouri
;; Author: Daniel Nouri <daniel.nouri@gmail.com>
;; SPDX-License-Identifier: GPL-3.0-or-later
;; This file is not part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Tree-sitter grammar recipes and installation helpers for Emacs 29+.
;;
;; All built-in `-ts-mode's (python-ts-mode, bash-ts-mode, etc.) are
;; available on Emacs 29, 30, and 31. The modes are there; the
;; *grammars* (compiled .so files) are what users need to install.
;;
;; On Emacs 29/30, `treesit-language-source-alist' starts empty — users
;; have no way to install grammars without knowing Git URLs. This file
;; registers version-pinned recipes so `treesit-install-language-grammar'
;; works out of the box.
;;
;; On Emacs 31, `treesit-ensure-installed' handles grammars natively for
;; modes that register recipes. Our entries serve as fallbacks for modes
;; that don't (add-to-list with APPEND preserves user/mode entries).
;;; Code:
(require 'treesit)
(require 'seq)
(defconst pi-coding-agent-essential-grammar-recipes
'((markdown "https://github.com/tree-sitter-grammars/tree-sitter-markdown"
"v0.4.1" "tree-sitter-markdown/src")
(markdown-inline "https://github.com/tree-sitter-grammars/tree-sitter-markdown"
"v0.4.1" "tree-sitter-markdown-inline/src"))
"Tree-sitter grammar recipes required for chat rendering.
Each entry is (LANG URL REVISION [SOURCE-DIR]).")
(defconst pi-coding-agent-optional-grammar-recipes
'((python "https://github.com/tree-sitter/tree-sitter-python" "v0.23.6")
(javascript "https://github.com/tree-sitter/tree-sitter-javascript" "v0.23.1")
(typescript "https://github.com/tree-sitter/tree-sitter-typescript" "v0.23.2" "typescript/src")
(tsx "https://github.com/tree-sitter/tree-sitter-typescript" "v0.23.2" "tsx/src")
(bash "https://github.com/tree-sitter/tree-sitter-bash" "v0.23.3")
(json "https://github.com/tree-sitter/tree-sitter-json" "v0.24.8")
(yaml "https://github.com/tree-sitter-grammars/tree-sitter-yaml" "v0.7.0")
(c "https://github.com/tree-sitter/tree-sitter-c" "v0.23.5")
(cpp "https://github.com/tree-sitter/tree-sitter-cpp" "v0.23.4")
(rust "https://github.com/tree-sitter/tree-sitter-rust" "v0.23.2")
(go "https://github.com/tree-sitter/tree-sitter-go" "v0.23.4")
(ruby "https://github.com/tree-sitter/tree-sitter-ruby" "v0.23.1")
(css "https://github.com/tree-sitter/tree-sitter-css" "v0.23.2")
(html "https://github.com/tree-sitter/tree-sitter-html" "v0.23.2")
(java "https://github.com/tree-sitter/tree-sitter-java" "v0.23.5")
(lua "https://github.com/tree-sitter-grammars/tree-sitter-lua" "v0.3.0")
(toml "https://github.com/tree-sitter-grammars/tree-sitter-toml" "v0.7.0")
(cmake "https://github.com/uyha/tree-sitter-cmake" "v0.5.0")
(dockerfile "https://github.com/camdencheek/tree-sitter-dockerfile" "v0.2.0")
(c-sharp "https://github.com/tree-sitter/tree-sitter-c-sharp" "v0.23.1")
(clojure "https://github.com/sogaiu/tree-sitter-clojure" "unstable-20250526")
(elixir "https://github.com/elixir-lang/tree-sitter-elixir" "v0.3.4")
(haskell "https://github.com/tree-sitter/tree-sitter-haskell" "v0.23.1")
(heex "https://github.com/phoenixframework/tree-sitter-heex" "v0.8.0")
(kotlin "https://github.com/fwcd/tree-sitter-kotlin" "0.3.8")
(gomod "https://github.com/camdencheek/tree-sitter-go-mod" "v1.1.0")
(php "https://github.com/tree-sitter/tree-sitter-php" "v0.23.11" "php/src")
(scala "https://github.com/tree-sitter/tree-sitter-scala" "v0.23.4"))
"Tree-sitter grammar recipes used for optional embedded code highlighting.
Each entry is (LANG URL REVISION [SOURCE-DIR]).")
(defconst pi-coding-agent-grammar-recipes
(append pi-coding-agent-essential-grammar-recipes
pi-coding-agent-optional-grammar-recipes)
"All tree-sitter grammar recipes needed by pi-coding-agent.")
;; Register recipes so `M-x treesit-install-language-grammar' works
;; on Emacs 29/30 (which ship with zero recipes). Use APPEND so user
;; entries and Emacs 31 mode-registered entries take precedence.
(dolist (recipe pi-coding-agent-grammar-recipes)
(add-to-list 'treesit-language-source-alist recipe t))
;;;; Detection
(defconst pi-coding-agent--essential-grammars
(mapcar #'car pi-coding-agent-essential-grammar-recipes)
"Grammars required for the chat buffer to render properly.")
(defconst pi-coding-agent--optional-grammars
(mapcar #'car pi-coding-agent-optional-grammar-recipes)
"Recipe grammars used only for optional embedded code highlighting.")
(defun pi-coding-agent--missing-essential-grammars ()
"Return list of essential grammars that are not installed."
(seq-filter (lambda (lang)
(not (treesit-language-available-p lang)))
pi-coding-agent--essential-grammars))
(defun pi-coding-agent--missing-optional-grammars ()
"Return optional recipe grammars that are not installed."
(seq-filter (lambda (lang)
(not (treesit-language-available-p lang)))
pi-coding-agent--optional-grammars))
(defun pi-coding-agent--installed-optional-grammars ()
"Return optional recipe grammars that are installed."
(seq-filter #'treesit-language-available-p
pi-coding-agent--optional-grammars))
;;;; Installation
(defun pi-coding-agent--install-grammars (grammars)
"Install tree-sitter GRAMMARS, showing progress.
Returns the number of successfully installed grammars."
(let ((total (length grammars))
(idx 0))
(condition-case err
(dolist (lang grammars)
(cl-incf idx)
(message "[pi-coding-agent] Installing grammar %d/%d: %s..."
idx total lang)
(treesit-install-language-grammar lang))
(error
(display-warning
'pi-coding-agent
(format "Failed to install grammar `%s': %s\n\
A C compiler (gcc or cc) is required.\n\
%d/%d grammars installed before failure."
(nth (1- idx) grammars)
(error-message-string err)
(1- idx) total)
:error)
(setq idx (1- idx))))
(when (> idx 0)
(message "[pi-coding-agent] Installed %d/%d grammars." idx total))
idx))
(defcustom pi-coding-agent-essential-grammar-action 'prompt
"What to do when essential tree-sitter grammars are missing.
Essential grammars (markdown, markdown-inline) are required for the
chat buffer to render properly.
`prompt' — ask the user before installing (default).
`auto' — install without prompting.
`warn' — warn only; never install. For users who manage
tree-sitter grammars via a system package manager."
:type '(choice (const :tag "Install automatically" auto)
(const :tag "Ask before installing" prompt)
(const :tag "Warn only (never install)" warn))
:group 'pi-coding-agent)
(defun pi-coding-agent--maybe-install-essential-grammars ()
"Handle missing essential grammars.
Behavior comes from `pi-coding-agent-essential-grammar-action'.
Respects `noninteractive' (batch mode always skips)."
(unless noninteractive
(let ((missing (pi-coding-agent--missing-essential-grammars)))
(when missing
(let* ((names (mapconcat #'symbol-name missing ", "))
(should-install
(pcase pi-coding-agent-essential-grammar-action
('auto t)
('prompt
(y-or-n-p
(format "Essential grammars (%s) missing — install now? "
names)))
('warn nil))))
(if should-install
(progn
(message "[pi-coding-agent] Installing essential grammars: %s"
names)
(let ((installed (pi-coding-agent--install-grammars missing)))
(when (< installed (length missing))
(display-warning
'pi-coding-agent
"Essential tree-sitter grammars (markdown, markdown-inline) \
could not be installed.\nThe chat buffer will not render properly.\n\
A C compiler is required. Install gcc and restart Emacs."
:error))))
(display-warning
'pi-coding-agent
(format "Essential tree-sitter grammars (%s) are not installed.\n\
The chat buffer will not render properly.\n\
Install them manually, run M-x `pi-coding-agent-install-grammars',\n\
or set `pi-coding-agent-essential-grammar-action' to `auto'."
names)
:warning)))))))
(defcustom pi-coding-agent-grammar-declined-set nil
"Grammars that were missing when the user declined installation.
When non-nil, the optional grammar prompt is suppressed unless new
grammars appear that are not in this set. Managed automatically;
reset by setting to nil."
:type '(repeat symbol)
:group 'pi-coding-agent)
(defvar pi-coding-agent--grammar-prompt-done nil
"Non-nil when the optional grammar prompt has been shown this session.")
(defun pi-coding-agent--new-missing-grammars ()
"Return missing grammars not covered by a previous decline.
If the user never declined, all missing grammars are \"new\".
If they declined, only grammars not in the declined set are new."
(let ((missing (pi-coding-agent--missing-optional-grammars)))
(if pi-coding-agent-grammar-declined-set
(seq-remove (lambda (g) (memq g pi-coding-agent-grammar-declined-set))
missing)
missing)))
(defun pi-coding-agent--maybe-install-optional-grammars ()
"Offer to install optional grammars for code highlighting.
Prompts at most once per Emacs session. A decline is persisted: the
set of missing grammars is saved so the prompt does not reappear.
The prompt returns when new grammars are added to the recipe list
or the declined set is cleared."
(unless (or noninteractive
pi-coding-agent--grammar-prompt-done)
(setq pi-coding-agent--grammar-prompt-done t)
(let ((missing (pi-coding-agent--missing-optional-grammars))
(new (pi-coding-agent--new-missing-grammars)))
(when new
(if (y-or-n-p
(format "%d tree-sitter grammars missing; install for code\
highlighting (M-x `pi-coding-agent-install-grammars' to do later)? "
(length missing)))
(pi-coding-agent--install-grammars missing)
(customize-save-variable 'pi-coding-agent-grammar-declined-set missing)
(message "[pi-coding-agent] Skipped. \
Run M-x pi-coding-agent-install-grammars anytime."))))))
;;;; Interactive Command
;;;###autoload
(defun pi-coding-agent-install-grammars ()
"Show grammar status and install missing tree-sitter grammars.
Displays which grammars are installed and which are missing,
then offers to install the missing ones."
(interactive)
(let ((installed (pi-coding-agent--installed-optional-grammars))
(missing (pi-coding-agent--missing-optional-grammars))
(essential-missing (pi-coding-agent--missing-essential-grammars)))
(if (and (null missing) (null essential-missing))
(message "All %d tree-sitter grammars are installed. ✓"
(length installed))
(let ((buf (get-buffer-create "*pi-coding-agent-grammars*")))
(with-current-buffer buf
(let ((inhibit-read-only t))
(erase-buffer)
(insert (format "pi-coding-agent Tree-sitter Grammars\n\
====================================\n\n"))
(when essential-missing
(insert (format "⚠ ESSENTIAL (required for chat rendering):\n"))
(dolist (g essential-missing)
(insert (format " ✗ %s\n" g)))
(insert "\n"))
(when missing
(insert (format "Missing (%d):\n" (length missing)))
(dolist (g missing)
(insert (format " ✗ %s\n" g)))
(insert "\n"))
(when installed
(insert (format "Installed (%d):\n" (length installed)))
(dolist (g installed)
(insert (format " ✓ %s\n" g)))
(insert "\n"))
(insert "Press `i' to install missing grammars, `q' to close.\n"))
(special-mode)
(local-set-key "i" (lambda ()
(interactive)
(let ((to-install (append essential-missing missing)))
(when to-install
(pi-coding-agent--install-grammars to-install)
(pi-coding-agent-install-grammars)))))
(local-set-key "q" #'quit-window)
(goto-char (point-min)))
(pop-to-buffer buf)))))
(provide 'pi-coding-agent-grammars)
;;; pi-coding-agent-grammars.el ends here