;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2021 Pierre Langlois ;;; ;;; This file is part of GNU Guix. ;;; ;;; GNU Guix 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. ;;; ;;; GNU Guix 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 GNU Guix. If not, see . (define-module (gnu packages tree-sitter) #:use-module ((guix licenses) #:prefix license:) #:use-module (guix build-system cargo) #:use-module (guix build-system emacs) #:use-module (guix build-system node) #:use-module (guix download) #:use-module (guix git-download) #:use-module (guix packages) #:use-module (guix utils) #:use-module (gnu packages algebra) #:use-module (gnu packages crates-io) #:use-module (gnu packages crates-graphics) #:use-module (gnu packages llvm) #:use-module (gnu packages node-xyz)) (define-public tree-sitter (package (name "tree-sitter") (version "0.19.5") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "1qmb0sva28zv6r3c3j7xs9pc8bpwwhkb9vxxndw2zbdn9wkvmbmn")))) (build-system cargo-build-system) (arguments `(;; Running test requires downloading fixtures, see the ;; script/fetch-fixtures script. #:tests? #f ;; FIXME: Installing the sources for the tree-sitter Rust bindings ;; doesn't work out of the box due to tree-sitter having multiple ;; Rust packages in the same repository (bindings and CLI). #:install-source? #f #:cargo-inputs (("rust-ansi-term" ,rust-ansi-term-0.12) ("rust-atty" ,rust-atty-0.2) ("rust-clap" ,rust-clap-2) ("rust-difference" ,rust-difference-2) ("rust-dirs" ,rust-dirs-3) ("rust-html-escape" ,rust-html-escape-0.2) ("rust-libloading" ,rust-libloading-0.7) ("rust-smallbitvec" ,rust-smallbitvec-2) ("rust-spin" ,rust-spin-0.7) ("rust-tiny-http" ,rust-tiny-http-0.8) ("rust-walkdir" ,rust-walkdir-2) ("rust-webbrowser" ,rust-webbrowser-0.5) ("rust-which" ,rust-which-4)) #:phases (modify-phases %standard-phases (add-after 'unpack 'delete-cargo.lock (lambda _ (delete-file "Cargo.lock"))) (add-after 'build 'build-lib (lambda _ (invoke "make"))) (replace 'install (lambda* (#:key outputs #:allow-other-keys) (let* ((out (assoc-ref outputs "out")) (bin (string-append out "/bin")) (lib (string-append out "/lib"))) (mkdir-p bin) (install-file "target/release/tree-sitter" bin) (setenv "PREFIX" out) (invoke "make" "install"))))))) (home-page "https://tree-sitter.github.io/tree-sitter/") (synopsis "Incremental parsing system for programming tools") (description "Tree-sitter is a parser generator tool and an incremental parsing library. It can build a concrete syntax tree for a source file and efficiently update the syntax tree as the source file is edited. Tree-sitter aims to be: @enumerate @item General enough to parse any programming language. @item Fast enough to parse on every keystroke in a text editor. @item Robust enough to provide useful results even in the presence of syntax errors. @item Dependency-free so that the runtime library (which is written in pure C) can be embedded in any application. @end enumerate This package includes the @command{tree-sitter} tool as well as the runtime library.") (license license:expat))) (define-public rust-tree-sitter-0.19 (package (name "rust-tree-sitter") (version "0.19.5") (source (origin (method url-fetch) (uri (crate-uri "tree-sitter" version)) (file-name (string-append name "-" version ".tar.gz")) (sha256 (base32 "1h6adq5kqf4izzsklch5lfxx2aisxga463zz7w44rgwnck16wwmd")))) (build-system cargo-build-system) (arguments `(#:tests? #f ;; Running tests misinterprets comments as doc-tests. #:cargo-inputs (("rust-cc" ,rust-cc-1) ("rust-lazy-static" ,rust-lazy-static-1) ("rust-regex" ,rust-regex-1) ("rust-spin" ,rust-spin-0.7)))) (home-page "https://tree-sitter.github.io/tree-sitter/") (synopsis "Rust bindings to the Tree-sitter parsing library") (description "This package provides Rust bindings to the Tree-sitter parsing library.") (license license:expat))) (define-public tree-sitter-c (package (name "tree-sitter-c") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-c") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "1diys8yigvhm4ppbmp3a473yxjg2d5lk11y0ay7qprcz7233lakv")))) (build-system node-build-system) (native-inputs `(("tree-sitter" ,tree-sitter) ("node-nan" ,node-nan))) (arguments `(#:phases (modify-phases %standard-phases ;; tree-sitter-cli is listed as a Node.js dependency, however the ;; node tree-sitter-cli package is just a wrapper which downloads a ;; tree-sitter binary, see ;; https://github.com/tree-sitter/tree-sitter/tree/master/cli/npm ;; Instead we remove it as a dependency so that we can use our own ;; tree-sitter package. (add-after 'unpack 'fix-configure (lambda _ (with-atomic-file-replacement "package.json" (lambda (in out) (use-modules ((guix build json))) (let ((package-meta (read-json in))) (assoc-remove! (assoc-ref package-meta "devDependencies") "tree-sitter-cli") (write-json package-meta out)))))) (add-before 'build 'set-cc (lambda _ (setenv "CC" "gcc"))) (add-before 'build 'make-files-writable (lambda _ (for-each make-file-writable (find-files "src" ".*")))) (add-after 'install 'install-native-lib (lambda* (#:key outputs #:allow-other-keys) (let ((lib (string-append (assoc-ref outputs "out") "/lib/tree-sitter"))) ;; Invoking `tree-sitter test' loads the grammar which ;; compiles it to a .so binary that we install. (invoke "tree-sitter" "test") (mkdir-p lib) (copy-recursively (string-append (getenv "HOME") "/.tree-sitter/bin") lib))))))) (home-page "https://github.com/tree-sitter/tree-sitter-c") (synopsis "Tree-sitter C grammar") (description "This package provides a C grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-cpp (package (inherit tree-sitter-c) (name "tree-sitter-cpp") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-cpp") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "08ywv6n80sa541rr08bqz4zyg7byvjcabp68lvxmcahjk8xzcgwk")))) (native-inputs `(("tree-sitter" ,tree-sitter) ("tree-sitter-c" ,tree-sitter-c) ("node-nan" ,node-nan))) (home-page "https://github.com/tree-sitter/tree-sitter-cpp") (synopsis "Tree-sitter C++ grammar") (description "This package provides a C++ grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-css (package (inherit tree-sitter-c) (name "tree-sitter-css") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-css") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "014jrlgi7zfza9g38hsr4vlbi8964i5p7iglaih6qmzaiml7bja2")))) (home-page "https://github.com/tree-sitter/tree-sitter-css") (synopsis "Tree-sitter CSS grammar") (description "This package provides a CSS grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-go (package (inherit tree-sitter-c) (name "tree-sitter-go") (version "0.19.1") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-go") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "0nxs47vd2fc2fr0qlxq496y852rwg39flhg334s7dlyq7d3lcx4x")))) (arguments (substitute-keyword-arguments (package-arguments tree-sitter-c) ((#:phases phases) `(modify-phases ,phases ;; The parse-example script tries to clone git repositories. (add-after 'unpack 'remove-parse-example (lambda _ (substitute* "package.json" ((" && script\\/parse-examples") "")))))))) (home-page "https://github.com/tree-sitter/tree-sitter-go") (synopsis "Tree-sitter Go grammar") (description "This package provides a Golang grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-html (package (inherit tree-sitter-c) (name "tree-sitter-html") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-html") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "1hg7vbcy7bir6b8x11v0a4x0glvqnsqc3i2ixiarbxmycbgl3axy")))) (home-page "https://github.com/tree-sitter/tree-sitter-html") (synopsis "Tree-sitter HTML grammar") (description "This package provides a HTML grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-java (package (inherit tree-sitter-c) (name "tree-sitter-java") (version "0.19.1") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-java") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "07zw9ygb45hnvlx9qlz7rlz8hc3byjy03d24v72i5iyhpiiwlhvl")))) (arguments (substitute-keyword-arguments (package-arguments tree-sitter-c) ((#:phases phases) `(modify-phases ,phases ;; The parse-example script tries to clone git repositories. (add-after 'unpack 'remove-parse-example (lambda _ (substitute* "package.json" ((" && script\\/parse-examples") "")))))))) (home-page "https://github.com/tree-sitter/tree-sitter-java") (synopsis "Tree-sitter Java grammar") (description "This package provides a Java grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-javascript (package (inherit tree-sitter-c) (name "tree-sitter-javascript") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-javascript") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "063va0s727yzhy1cz04fipzwwbq4af6fhgka6g970hk9yf7ggmnj")))) (arguments (substitute-keyword-arguments (package-arguments tree-sitter-c) ((#:phases phases) `(modify-phases ,phases ;; The parse-example script tries to clone git repositories. (add-after 'unpack 'remove-parse-example (lambda _ (substitute* "package.json" ((" && script\\/parse-examples") "")))))))) (home-page "https://github.com/tree-sitter/tree-sitter-javascript") (synopsis "Tree-sitter Javascript grammar") (description "This package provides a Javascript grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-json (package (inherit tree-sitter-c) (name "tree-sitter-json") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-json") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "06pjh31bv9ja9hlnykk257a6zh8bsxg2fqa54al7qk1r4n9ksnff")))) (home-page "https://github.com/tree-sitter/tree-sitter-json") (synopsis "Tree-sitter JSON grammar") (description "This package provides a JSON grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-julia (package (inherit tree-sitter-c) (name "tree-sitter-julia") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-julia") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "1pbnmvhy2gq4vg1b0sjzmjm4s2gsgdjh7h01yj8qrrqbcl29c463")))) (arguments (substitute-keyword-arguments (package-arguments tree-sitter-c) ((#:phases phases) `(modify-phases ,phases ;; The parse-example script tries to clone git repositories. (add-after 'unpack 'remove-parse-example (lambda _ (substitute* "package.json" ((" && script\\/parse-examples") "")))))))) (home-page "https://github.com/tree-sitter/tree-sitter-julia") (synopsis "Tree-sitter Julia grammar") (description "This package provides a Julia grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-php (package (inherit tree-sitter-c) (name "tree-sitter-php") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-php") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "17cmybgpprw7w9d2v7lmc6zmr90d70g0jqq279gzg0mpwfzla53s")))) (arguments (substitute-keyword-arguments (package-arguments tree-sitter-c) ((#:phases phases) `(modify-phases ,phases ;; The parse-example script tries to clone git repositories. (add-after 'unpack 'remove-parse-example (lambda _ (substitute* "package.json" ((" && script\\/parse-examples") "")))))))) (home-page "https://github.com/tree-sitter/tree-sitter-php") (synopsis "Tree-sitter PHP grammar") (description "This package provides a PHP grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-python (package (inherit tree-sitter-c) (name "tree-sitter-python") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-python") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "04b85qxqs64x6nhbpcgrzkbilxaiwvr9yd3h065rynv7rsdg0hii")))) (arguments (substitute-keyword-arguments (package-arguments tree-sitter-c) ((#:phases phases) `(modify-phases ,phases ;; The parse-example script tries to clone git repositories. (add-after 'unpack 'remove-parse-example (lambda _ (substitute* "package.json" ((" && script\\/parse-examples") "")))))))) (home-page "https://github.com/tree-sitter/tree-sitter-python") (synopsis "Tree-sitter Python grammar") (description "This package provides a Python grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-rust (package (inherit tree-sitter-c) (name "tree-sitter-rust") (version "0.19.1") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-rust") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "118vkhv7n3sw8y9pi0987cgdcd74sjqwviijw01mhnk3bkyczi3l")))) (native-inputs `(("bc" ,bc) ("tree-sitter" ,tree-sitter) ("node-nan" ,node-nan))) (home-page "https://github.com/tree-sitter/tree-sitter-rust") (synopsis "Tree-sitter Rust grammar") (description "This package provides a Rust grammar for the Tree-sitter library.") (license license:expat))) (define-public tree-sitter-typescript (package (inherit tree-sitter-c) (name "tree-sitter-typescript") (version "0.19.0") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/tree-sitter/tree-sitter-typescript") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "01pkmwwmbv6kxda0n6g4cfg72ldmpi9gmp11a5gygn472vhrh2xw")))) (native-inputs `(("tree-sitter" ,tree-sitter) ("tree-sitter-javascript" ,tree-sitter-javascript) ("node-nan" ,node-nan))) (arguments (substitute-keyword-arguments (package-arguments tree-sitter-c) ((#:phases phases) `(modify-phases ,phases ;; The parse-example script tries to clone git repositories. (add-after 'unpack 'remove-parse-example (lambda _ (substitute* "package.json" ((" && script\\/parse-examples") "")))) (add-after 'build 'build-gyp (lambda* (#:key inputs #:allow-other-keys) (let ((node (assoc-ref inputs "node"))) (invoke (string-append node "/lib/node_modules/npm/node_modules" "/node-gyp/bin/node-gyp.js") "build")))) (replace 'make-files-writable (lambda _ (for-each make-file-writable (find-files "typescript" ".*")) (for-each make-file-writable (find-files "tsx" ".*")))) (replace 'install-native-lib (lambda* (#:key outputs #:allow-other-keys) (let ((lib (string-append (assoc-ref outputs "out") "/lib/tree-sitter"))) ;; Invoking `tree-sitter test' loads the grammar which ;; compiles it to a .so binary that we install. (with-directory-excursion "typescript" (invoke "tree-sitter" "test")) (with-directory-excursion "tsx" (invoke "tree-sitter" "test")) (mkdir-p lib) (copy-recursively (string-append (getenv "HOME") "/.tree-sitter/bin") lib)))))))) (home-page "https://github.com/tree-sitter/tree-sitter-typescript") (synopsis "Tree-sitter Typescript grammar") (description "This package provides Typescript and TSX grammars for the Tree-sitter library.") (license license:expat))) ;; Local package definition solely for building the native emacs module ;; written in Rust. (define tree-sitter-emacs-module (package (name "tree-sitter-emacs-module") (version "0.15.1") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/emacs-tree-sitter/elisp-tree-sitter") (commit version))) (file-name (git-file-name name version)) (sha256 (base32 "15y0wjnck8rbfhl0xrl71ci7clbcp11lhqil5l8ykprsdjv0c2as")))) (build-system cargo-build-system) (inputs `(("clang" ,clang))) (arguments `(#:cargo-inputs (("rust-anyhow" ,rust-anyhow-1) ("rust-emacs" ,rust-emacs-0.17) ("rust-libloading" ,rust-libloading-0.7) ("rust-once-cell" ,rust-once-cell-1) ("rust-tree-sitter" ,rust-tree-sitter-0.19)) #:phases (modify-phases %standard-phases (add-after 'unpack 'delete-cargo.lock (lambda _ (delete-file "Cargo.lock"))) (add-after 'delete-cargo.lock 'do-not-fetch-from-github (lambda _ (substitute* "Cargo.toml" (("\\[patch.*") "") (("git = .*") "")))) (add-after 'do-not-fetch-from-github 'chdir (lambda _ (chdir "core"))) (replace 'install (lambda* (#:key outputs #:allow-other-keys) (let ((lib (string-append (assoc-ref outputs "out") "/lib"))) (install-file "../target/release/libtsc_dyn.so" lib))))))) (home-page #f) (synopsis #f) (description #f) (license license:expat))) (define-public emacs-tree-sitter-core (package (name "emacs-tree-sitter-core") (version (package-version tree-sitter-emacs-module)) (source (package-source tree-sitter-emacs-module)) (build-system emacs-build-system) (native-inputs `(("tree-sitter-emacs-module" ,tree-sitter-emacs-module))) (arguments `(#:phases (modify-phases %standard-phases (add-after 'unpack 'chdir (lambda _ (chdir "core"))) (add-after 'install 'install-module (lambda* (#:key inputs outputs #:allow-other-keys) (let ((elpa (elpa-directory (assoc-ref outputs "out"))) (module (string-append (assoc-ref inputs "tree-sitter-emacs-module") "/lib/libtsc_dyn.so"))) ;; Writing "LOCAL" in this file prevents the package from ;; trying to download the module from the internet. (call-with-output-file (string-append elpa "/DYN-VERSION") (lambda (port) (display "LOCAL" port))) (substitute* "tsc-dyn-get.el" (("defcustom tsc-dyn-dir tsc--dir") (string-append "defcustom tsc-dyn-dir \"" elpa "\""))) (copy-file module (string-append elpa "/tsc-dyn.so")))))))) (home-page "https://github.com/emacs-tree-sitter/elisp-tree-sitter") (synopsis "Tree-sitter bindings for Emacs Lisp, core library") (description "This package provides core APIs of the Emacs binding for Tree-sitter, an incremental parsing system.") (license license:expat)))