|
6 | 6 | ;; Maintainer: Shen, Jen-Chieh <[email protected]> |
7 | 7 | ;; URL: https://github.com/emacs-tree-sitter/treesit-langs |
8 | 8 | ;; Version: 0.1.0 |
9 | | -;; Package-Requires: ((emacs "29.1") (tree-sitter-langs "0.12.18")) |
| 9 | +;; Package-Requires: ((emacs "29.1")) |
10 | 10 | ;; Keywords: languages tools parsers tree-sitter |
11 | 11 |
|
12 | 12 | ;; This file is not part of GNU Emacs. |
|
34 | 34 | (require 'cl-lib) |
35 | 35 | (require 'treesit) |
36 | 36 |
|
37 | | -(require 'tree-sitter-langs-build) |
38 | | - |
39 | 37 | (defgroup treesit-langs nil |
40 | 38 | "Grammar bundle for `treesit.el'." |
41 | 39 | :group 'tree-sitter) |
|
173 | 171 | :type 'hook |
174 | 172 | :group 'treesit-langs) |
175 | 173 |
|
176 | | -(defun treesit-langs--grammar-files (new-bin) |
177 | | - "Return grammar files from NEW-BIN." |
178 | | - (cl-remove-if-not (lambda (file) |
179 | | - (or (string-match-p ".so" file) |
180 | | - (string-match-p ".dylib" file) |
181 | | - (string-match-p ".dll" file))) |
182 | | - (directory-files new-bin t))) |
| 174 | +(defcustom treesit-langs-bundle-version "0.12.208" |
| 175 | + "Version of the grammar bundle. |
| 176 | +
|
| 177 | +Ideally, we want this value to be same as `tree-sitter-langs--bundle-version' |
| 178 | +from `tree-sitter-langs' package." |
| 179 | + :type 'string |
| 180 | + :group 'treesit-langs) |
| 181 | + |
| 182 | +(defconst treesit-langs--bundle-version-file "BUNDLE-VERSION") |
| 183 | + |
| 184 | +(defconst treesit-langs--suffixes '(".dylib" ".dll" ".so") |
| 185 | + "List of suffixes for shared libraries that define tree-sitter languages.") |
| 186 | + |
| 187 | +(defconst treesit-langs--os |
| 188 | + (pcase system-type |
| 189 | + ('darwin "macos") |
| 190 | + ('gnu/linux "linux") |
| 191 | + ('android "linux") |
| 192 | + ('berkeley-unix "freebsd") |
| 193 | + ('windows-nt "windows") |
| 194 | + (_ (error "Unsupported system-type %s" system-type)))) |
| 195 | + |
| 196 | +(defvar treesit-langs--out nil) |
| 197 | + |
| 198 | +;; |
| 199 | +;;; Externals |
| 200 | + |
| 201 | +(declare-function dired-omit-mode "dired-x" (&optional arg)) |
| 202 | + |
| 203 | +;; |
| 204 | +;;; Uitl |
| 205 | + |
| 206 | +;;; TODO: Use (maybe make) an async library, with a proper event loop, instead |
| 207 | +;;; of busy-waiting. |
| 208 | +(defun treesit-langs--call (program &rest args) |
| 209 | + "Call PROGRAM with ARGS, using BUFFER as stdout+stderr. |
| 210 | +If BUFFER is nil, `princ' is used to forward its stdout+stderr." |
| 211 | + (let* ((command `(,program . ,args)) |
| 212 | + (_ (message "[treesit-langs] Running %s in %s" command default-directory)) |
| 213 | + (base `(:name ,program :command ,command)) |
| 214 | + (output (if treesit-langs--out |
| 215 | + `(:buffer ,treesit-langs--out) |
| 216 | + `(:filter (lambda (proc string) |
| 217 | + (princ string))))) |
| 218 | + (proc (apply #'make-process (append base output))) |
| 219 | + (exit-code (progn |
| 220 | + (while (not (memq (process-status proc) |
| 221 | + '(exit failed signal))) |
| 222 | + (sleep-for 0.1)) |
| 223 | + (process-exit-status proc))) |
| 224 | + ;; Flush buffered output. Not doing this caused |
| 225 | + ;; `tree-sitter-langs-git-dir' to be set incorrectly, and |
| 226 | + ;; `tree-sitter-langs-create-bundle's output to be unordered. |
| 227 | + (_ (accept-process-output proc))) |
| 228 | + (unless (= exit-code 0) |
| 229 | + (error "Error calling %s, exit code is %s" command exit-code)))) |
| 230 | + |
| 231 | +;; |
| 232 | +;;; Download |
| 233 | + |
| 234 | +(defun treesit-langs--bundle-file (&optional ext version os) |
| 235 | + "Return the grammar bundle file's name, with optional EXT. |
| 236 | +
|
| 237 | +If VERSION and OS are not spcified, use the defaults of |
| 238 | +`treesit-langs-bundle-version' and `treesit-langs--os'." |
| 239 | + (setq os (or os treesit-langs--os) |
| 240 | + version (or version treesit-langs-bundle-version) |
| 241 | + ext (or ext "")) |
| 242 | + (format "tree-sitter-grammars.%s.v%s.tar%s" |
| 243 | + ;; FIX: Implement this correctly, refactoring 'OS' -> 'platform'. |
| 244 | + (pcase os |
| 245 | + ("windows" "x86_64-pc-windows-msvc") |
| 246 | + ("linux" "x86_64-unknown-linux-gnu") |
| 247 | + ("freebsd" "x86_64-unknown-freebsd") |
| 248 | + ("macos" (if (string-prefix-p "aarch64" system-configuration) |
| 249 | + "aarch64-apple-darwin" |
| 250 | + "x86_64-apple-darwin"))) |
| 251 | + version ext)) |
| 252 | + |
| 253 | +(defun treesit-langs--bundle-url (&optional version os) |
| 254 | + "Return the URL to download the grammar bundle. |
| 255 | +If VERSION and OS are not specified, use the defaults of |
| 256 | +`treesit-langs-bundle-version' and `treesit-langs--os'." |
| 257 | + (format "https://github.com/emacs-tree-sitter/tree-sitter-langs/releases/download/%s/%s" |
| 258 | + version |
| 259 | + (treesit-langs--bundle-file ".gz" version os))) |
| 260 | + |
| 261 | +(defun treesit-langs--bin-dir () |
| 262 | + "Return the directory to stored grammar binaries." |
| 263 | + (locate-user-emacs-file "tree-sitter")) |
183 | 264 |
|
184 | 265 | ;;;###autoload |
185 | | -(defun treesit-langs-install-grammars () |
186 | | - "Install grammars to treesit.el library location." |
| 266 | +(defun treesit-langs-install-grammars (&optional skip-if-installed version os keep-bundle) |
| 267 | + "Download and install the specified VERSION of the language grammar bundle. |
| 268 | +If VERSION or OS is not specified, use the default of |
| 269 | +`treesit-langs-bundle-version' and `treesit-langs--os'. |
| 270 | +
|
| 271 | +This installs the grammar bundle even if the same version was already installed, |
| 272 | +unless SKIP-IF-INSTALLED is non-nil. |
| 273 | +
|
| 274 | +The download bundle file is deleted after installation, unless KEEP-BUNDLE is |
| 275 | +non-nil." |
| 276 | + (interactive (list |
| 277 | + nil |
| 278 | + (read-string "Bundle version: " treesit-langs-bundle-version) |
| 279 | + treesit-langs--os |
| 280 | + nil)) |
| 281 | + (let* ((bin-dir (treesit-langs--bin-dir)) |
| 282 | + (_ (unless (unless (file-directory-p bin-dir) |
| 283 | + (make-directory bin-dir)))) |
| 284 | + (version (or version treesit-langs-bundle-version)) |
| 285 | + (default-directory bin-dir) |
| 286 | + (bundle-file (treesit-langs--bundle-file ".gz" version os)) |
| 287 | + (current-version (when (file-exists-p |
| 288 | + treesit-langs--bundle-version-file) |
| 289 | + (with-temp-buffer |
| 290 | + (let ((coding-system-for-read 'utf-8)) |
| 291 | + (insert-file-contents |
| 292 | + treesit-langs--bundle-version-file) |
| 293 | + (string-trim (buffer-string))))))) |
| 294 | + (cl-block nil |
| 295 | + (if (string= version current-version) |
| 296 | + (if skip-if-installed |
| 297 | + (progn (message "treesit-langs: Grammar bundle v%s was already installed; skipped" version) |
| 298 | + (cl-return)) |
| 299 | + (message "treesit-langs: Grammar bundle v%s was already installed; reinstalling" version)) |
| 300 | + (message "treesit-langs: Installing grammar bundle v%s (was v%s)" version current-version)) |
| 301 | + ;; FIX: Handle HTTP errors properly. |
| 302 | + (url-copy-file (treesit-langs--bundle-url version os) |
| 303 | + bundle-file 'ok-if-already-exists) |
| 304 | + (treesit-langs--call "tar" "-xvzf" bundle-file) |
| 305 | + ;; FIX: This should be a metadata file in the bundle itself. |
| 306 | + (with-temp-file treesit-langs--bundle-version-file |
| 307 | + (let ((coding-system-for-write 'utf-8)) |
| 308 | + (insert version))) |
| 309 | + (unless keep-bundle |
| 310 | + (delete-file bundle-file 'trash)) |
| 311 | + (when (and (called-interactively-p 'any) |
| 312 | + (y-or-n-p (format "Show installed grammars in %s? " bin-dir))) |
| 313 | + (with-current-buffer (find-file bin-dir) |
| 314 | + (when (bound-and-true-p dired-omit-mode) |
| 315 | + (dired-omit-mode -1)))) |
| 316 | + (treesit-langs--rename)))) |
| 317 | + |
| 318 | +(treesit-langs-install-grammars :skip-if-installed) |
| 319 | + |
| 320 | +;; |
| 321 | +;;; Rename |
| 322 | + |
| 323 | +(defun treesit-langs--grammar-files (bin) |
| 324 | + "Return grammar files from BIN." |
| 325 | + (cl-remove-if-not (lambda (file) |
| 326 | + (cl-some (lambda (suffix) |
| 327 | + (string-suffix-p suffix file)) |
| 328 | + treesit-langs--suffixes)) |
| 329 | + (directory-files bin t))) |
| 330 | + |
| 331 | +(defun treesit-langs--rename () |
| 332 | + "Rename installed grammars to `treesit.el' compatible cnaming convention." |
187 | 333 | (interactive) |
188 | | - (let ((bin (tree-sitter-langs--bin-dir)) |
189 | | - (new-bin (locate-user-emacs-file "tree-sitter"))) |
190 | | - (when (and (file-directory-p bin) |
191 | | - (not (file-directory-p new-bin))) |
192 | | - (copy-directory bin new-bin nil t t) |
193 | | - (dolist (filename (treesit-langs--grammar-files new-bin)) |
194 | | - (let* ((dir (file-name-directory filename)) |
195 | | - (file (file-name-nondirectory filename)) |
196 | | - (lang (file-name-sans-extension file)) |
197 | | - (soext (car dynamic-library-suffixes)) |
198 | | - (new-file (expand-file-name (concat "libtree-sitter-" lang soext) |
199 | | - dir))) |
200 | | - (rename-file filename new-file)))))) |
201 | | - |
202 | | -;; Install only once. |
203 | | -(treesit-langs-install-grammars) |
| 334 | + (when-let* ((bin (treesit-langs--bin-dir)) |
| 335 | + ((file-directory-p bin))) |
| 336 | + (dolist (filename (treesit-langs--grammar-files bin)) |
| 337 | + (let* ((dir (file-name-directory filename)) |
| 338 | + (file (file-name-nondirectory filename)) |
| 339 | + (lang (file-name-sans-extension file)) |
| 340 | + (soext (car dynamic-library-suffixes)) |
| 341 | + (new-file (expand-file-name (concat "libtree-sitter-" lang soext) |
| 342 | + dir))) |
| 343 | + (rename-file filename new-file))))) |
| 344 | + |
| 345 | +;; |
| 346 | +;;; Set up |
204 | 347 |
|
205 | 348 | (defun treesit-langs-major-mode-setup () |
206 | 349 | "Activate tree-sitter to power major-mode features." |
|
0 commit comments