Added experimental support for scheme, racket, and commonlisp

Mikayla Maki created

Change summary

Cargo.lock                                         |  30 ++
crates/zed/Cargo.toml                              |   3 
crates/zed/src/languages.rs                        |   4 
crates/zed/src/languages/commonlisp/config.toml    |   9 
crates/zed/src/languages/commonlisp/folds.scm      |   3 
crates/zed/src/languages/commonlisp/highlights.scm | 189 ++++++++++++++++
crates/zed/src/languages/installation.rs           |  21 +
crates/zed/src/languages/racket/config.toml        |   9 
crates/zed/src/languages/racket/folds.scm          |   3 
crates/zed/src/languages/racket/highlights.scm     |  75 ++++++
crates/zed/src/languages/racket/injections.scm     |   4 
crates/zed/src/languages/ruby/brackets.scm         |  14 -
crates/zed/src/languages/scheme/config.toml        |   9 
crates/zed/src/languages/scheme/folds.scm          |   3 
crates/zed/src/languages/scheme/highlights.scm     | 183 +++++++++++++++
crates/zed/src/languages/scheme/injections.scm     |   3 
16 files changed, 548 insertions(+), 14 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -6461,6 +6461,15 @@ dependencies = [
  "tree-sitter",
 ]
 
+[[package]]
+name = "tree-sitter-commonlisp"
+version = "0.3.0"
+source = "git+https://github.com/theHamsta/tree-sitter-commonlisp?rev=c7e814975ab0d0d04333d1f32391c41180c58919#c7e814975ab0d0d04333d1f32391c41180c58919"
+dependencies = [
+ "cc",
+ "tree-sitter",
+]
+
 [[package]]
 name = "tree-sitter-cpp"
 version = "0.20.0"
@@ -6566,6 +6575,15 @@ dependencies = [
  "tree-sitter",
 ]
 
+[[package]]
+name = "tree-sitter-racket"
+version = "0.0.1"
+source = "git+https://github.com/6cdh/tree-sitter-racket?rev=69ca563af3bcf9d67220532e0814786f2dc34db1#69ca563af3bcf9d67220532e0814786f2dc34db1"
+dependencies = [
+ "cc",
+ "tree-sitter",
+]
+
 [[package]]
 name = "tree-sitter-ruby"
 version = "0.20.0"
@@ -6586,6 +6604,15 @@ dependencies = [
  "tree-sitter",
 ]
 
+[[package]]
+name = "tree-sitter-scheme"
+version = "0.2.0"
+source = "git+https://github.com/6cdh/tree-sitter-scheme?rev=af0fd1fa452cb2562dc7b5c8a8c55551c39273b9#af0fd1fa452cb2562dc7b5c8a8c55551c39273b9"
+dependencies = [
+ "cc",
+ "tree-sitter",
+]
+
 [[package]]
 name = "tree-sitter-toml"
 version = "0.5.1"
@@ -7796,6 +7823,7 @@ dependencies = [
  "toml",
  "tree-sitter",
  "tree-sitter-c",
+ "tree-sitter-commonlisp",
  "tree-sitter-cpp",
  "tree-sitter-css",
  "tree-sitter-elixir",
@@ -7805,8 +7833,10 @@ dependencies = [
  "tree-sitter-json 0.20.0",
  "tree-sitter-markdown",
  "tree-sitter-python",
+ "tree-sitter-racket",
  "tree-sitter-ruby",
  "tree-sitter-rust",
+ "tree-sitter-scheme",
  "tree-sitter-toml",
  "tree-sitter-typescript",
  "unindent",

crates/zed/Cargo.toml 🔗

@@ -104,6 +104,9 @@ tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", re
 tree-sitter-typescript = "0.20.1"
 tree-sitter-ruby = "0.20.0"
 tree-sitter-html = "0.19.0"
+tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9"}
+tree-sitter-racket = { git = "https://github.com/6cdh/tree-sitter-racket", rev = "69ca563af3bcf9d67220532e0814786f2dc34db1"}
+tree-sitter-commonlisp = { git = "https://github.com/theHamsta/tree-sitter-commonlisp", rev = "c7e814975ab0d0d04333d1f32391c41180c58919" }
 url = "2.2"
 
 [dev-dependencies]

crates/zed/src/languages.rs 🔗

@@ -14,6 +14,7 @@ mod language_plugin;
 mod python;
 mod ruby;
 mod rust;
+
 mod typescript;
 
 // 1. Add tree-sitter-{language} parser to zed crate
@@ -127,6 +128,9 @@ pub async fn init(languages: Arc<LanguageRegistry>, _executor: Arc<Background>)
             tree_sitter_embedded_template::language(),
             Some(CachedLspAdapter::new(ruby::RubyLanguageServer).await),
         ),
+        ("scheme", tree_sitter_scheme::language(), None),
+        // ("racket", tree_sitter_racket::language(), None),
+        ("commonlisp", tree_sitter_commonlisp::language(), None),
     ] {
         languages.add(language(name, grammar, lsp_adapter));
     }

crates/zed/src/languages/commonlisp/config.toml 🔗

@@ -0,0 +1,9 @@
+name = "Racket"
+path_suffixes = ["lisp", "lsp", "l", "cl"]
+line_comment = "; "
+autoclose_before = "])"
+brackets = [
+    { start = "[", end = "]", close = true, newline = true },
+    { start = "(", end = ")", close = true, newline = true },
+    { start = "'", end = "'", close = false, newline = false },
+]

crates/zed/src/languages/commonlisp/highlights.scm 🔗

@@ -0,0 +1,189 @@
+;; Copied from nvim: https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/master/queries/commonlisp/highlights.scm
+
+(sym_lit) @variable
+
+;; A highlighting for functions/macros in th cl namespace is available in theHamsta/nvim-treesitter-commonlisp
+;(list_lit . (sym_lit) @function.builtin (#cl-standard-function? @function.builtin))
+;(list_lit . (sym_lit) @function.builtin (#cl-standard-macro? @function.macro))
+
+(dis_expr) @comment
+
+(defun_keyword) @function.macro
+(defun_header
+  function_name: (_) @function)
+(defun_header
+  lambda_list: (list_lit (sym_lit) @parameter))
+(defun_header
+  keyword: (defun_keyword "defmethod")
+  lambda_list: (list_lit (list_lit . (sym_lit) . (sym_lit) @symbol)))
+(defun_header
+  lambda_list: (list_lit (list_lit . (sym_lit) @parameter . (_))))
+(defun_header
+  specifier: (sym_lit) @symbol)
+
+[":" "::" "."] @punctuation.special
+
+[
+  (accumulation_verb)
+  (for_clause_word)
+  "for"
+  "and"
+  "finally"
+  "thereis"
+  "always"
+  "when"
+  "if"
+  "unless"
+  "else"
+  "do"
+  "loop"
+  "below"
+  "in"
+  "from"
+  "across"
+  "repeat"
+  "being"
+  "into"
+  "with"
+  "as"
+  "while"
+  "until"
+  "return"
+  "initially"
+] @function.macro
+"=" @operator
+
+(include_reader_macro) @symbol
+["#C" "#c"] @number
+
+[(kwd_lit) (self_referential_reader_macro)] @symbol
+
+(package_lit
+  package: (_) @namespace)
+"cl" @namespace
+
+(str_lit) @string
+
+(num_lit) @number
+
+((sym_lit)  @boolean (#match? @boolean "^(t|T)$"))
+
+(nil_lit) @constant.builtin
+
+(comment) @comment
+
+;; dynamic variables
+((sym_lit) @variable.builtin
+ (#match? @variable.builtin "^[*].+[*]$"))
+
+;; quote
+"'" @string.escape
+(format_specifier) @string.escape
+(quoting_lit) @string.escape
+
+;; syntax quote
+"`" @string.escape
+"," @string.escape
+",@" @string.escape
+(syn_quoting_lit) @string.escape
+(unquoting_lit) @none
+(unquote_splicing_lit) @none
+
+
+["(" ")"] @punctuation.bracket
+
+(block_comment) @comment
+
+
+(with_clause
+  type: (_) @type)
+(for_clause
+  type: (_) @type)
+
+;; defun-like things
+(list_lit
+ .
+ (sym_lit) @function.macro
+ .
+ (sym_lit) @function
+ (#eq? @function.macro "deftest"))
+
+;;; Macros and Special Operators
+(list_lit
+ .
+ (sym_lit) @function.macro
+ ;; For a complete and more efficient version install theHamsta/nvim-treesitter-commonlisp
+ (#any-of? @function.macro
+          "let"
+          "function"
+          "the"
+          "unwind-protect"
+          "labels"
+          "flet"
+          "tagbody"
+          "go"
+          "symbol-macrolet"
+          "symbol-macrolet"
+          "progn"
+          "prog1"
+          "error"
+          "or"
+          "and"
+          "defvar"
+          "defparameter"
+          "in-package"
+          "defpackage"
+          "case"
+          "ecase"
+          "typecase"
+          "etypecase"
+          "defstruct"
+          "defclass"
+          "if"
+          "when"
+          "unless"
+          "cond"
+          "switch"
+          "declaim"
+          "optimize"))
+
+;; constant
+((sym_lit) @constant
+ (#match? @constant "^[+].+[+]$"))
+
+(var_quoting_lit
+  marker: "#'" @symbol
+  value: (_) @symbol)
+
+["#" "#p" "#P"] @symbol
+
+(list_lit
+ .
+ (sym_lit) @function.builtin
+ ;; For a complete and more efficient version install theHamsta/nvim-treesitter-commonlisp
+ (#any-of? @function.builtin
+           "mapcar"
+           "reduce"
+           "remove-if-not"
+           "cons"
+           "car"
+           "last"
+           "nth"
+           "equal"
+           "cdr"
+           "first"
+           "rest"
+           "format"))
+
+(list_lit
+ .
+ (sym_lit) @operator
+ (#match? @operator "^([+*-+=<>]|<=|>=|/=)$"))
+
+
+((sym_lit) @symbol
+(#match? @symbol "^[&]"))
+
+[(array_dimension) "#0A" "#0a"] @number
+
+(char_lit) @character

crates/zed/src/languages/installation.rs 🔗

@@ -35,6 +35,18 @@ pub(crate) struct GithubReleaseAsset {
     pub browser_download_url: String,
 }
 
+#[derive(Deserialize)]
+pub(crate) struct CodebergReleaseAsset {
+    pub name: String,
+    pub assets: Vec<GithubReleaseAsset>,
+}
+
+#[derive(Deserialize)]
+pub(crate) struct CodebergRelease {
+    pub name: String,
+    pub browser_download_url: String,
+}
+
 pub async fn npm_package_latest_version(name: &str) -> Result<String> {
     let output = smol::process::Command::new("npm")
         .args(["info", name, "--json"])
@@ -103,3 +115,12 @@ pub(crate) async fn latest_github_release(
         serde_json::from_slice(body.as_slice()).context("error deserializing latest release")?;
     Ok(release)
 }
+
+// pub(crate) async fn latest_codeberg_release(
+//     repo_name_with_owner: &str,
+//     http: Arc<dyn HttpClient>,
+// ) -> anyhow::Result<CodebergRelease> {
+//     let mut response = http.get(uri, body, follow_redirects);
+
+//     bail!("unimplemented :(");
+// }

crates/zed/src/languages/racket/config.toml 🔗

@@ -0,0 +1,9 @@
+name = "Racket"
+path_suffixes = ["rkt"]
+line_comment = "; "
+autoclose_before = "])"
+brackets = [
+    { start = "[", end = "]", close = true, newline = true },
+    { start = "(", end = ")", close = true, newline = true },
+    { start = "'", end = "'", close = false, newline = false },
+]

crates/zed/src/languages/racket/highlights.scm 🔗

@@ -0,0 +1,140 @@
+;; Copied from nvim: https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/master/queries/racket/highlights.scm
+
+;; A highlight query can override the highlights queries before it.
+;; So the order is important.
+;; We should highlight general rules, then highlight special forms.
+
+;;------------------------------------------------------------------;;
+;;                         Basic highlights                         ;;
+;;------------------------------------------------------------------;;
+
+(ERROR) @error
+
+;; basic ;;
+
+(number) @number
+(character) @character
+(boolean) @boolean
+(keyword) @symbol
+
+;; string ;;
+
+[(string)
+ (here_string)
+ (byte_string)] @string
+
+(escape_sequence) @string.escape
+
+(regex) @string.regex
+
+;; comment ;;
+
+[(comment)
+ (block_comment)
+ (sexp_comment)] @comment
+
+;; symbol ;;
+
+(symbol) @variable
+
+((symbol) @comment
+ (#match? @comment "^#[cC][iIsS]$"))
+
+;; extension ;;
+
+(extension) @keyword
+(lang_name) @variable.builtin
+
+;; quote ;;
+
+(quote) @symbol
+
+;; list ;;
+
+["(" ")" "[" "]" "{" "}"] @punctuation.bracket
+
+;; procedure ;;
+
+(list
+ .
+ (symbol) @function)
+
+;;------------------------------------------------------------------;;
+;;                        Builtin highlights                        ;;
+;;------------------------------------------------------------------;;
+
+;; The following lists are generated by a racket script:
+;; https://gist.github.com/6cdh/65619e761753eb4166d15185a6236040
+;; Don't edit them directly.
+
+;; keyword ;;
+
+(list
+  .
+  (symbol) @keyword
+  (#any-of? @keyword

crates/zed/src/languages/ruby/brackets.scm 🔗

@@ -1,14 +0,0 @@
-("[" @open "]" @close)
-("{" @open "}" @close)
-("\"" @open "\"" @close)
-("do" @open "end" @close)
-
-(block_parameters "|" @open "|" @close)
-(interpolation "#{" @open "}" @close)
-
-(if "if" @open "end" @close)
-(unless "unless" @open "end" @close)
-(begin "begin" @open "end" @close)
-(module "module" @open "end" @close)
-(_ . "def" @open "end" @close)
-(_ . "class" @open "end" @close)

crates/zed/src/languages/scheme/config.toml 🔗

@@ -0,0 +1,9 @@
+name = "Scheme"
+path_suffixes = ["scm", "ss", "mjs"]
+line_comment = "; "
+autoclose_before = "])"
+brackets = [
+    { start = "[", end = "]", close = true, newline = true },
+    { start = "(", end = ")", close = true, newline = true },
+    { start = "'", end = "'", close = false, newline = false },
+]

crates/zed/src/languages/scheme/highlights.scm 🔗

@@ -0,0 +1,183 @@
+;; Copied from nvim: https://github.com/nvim-treesitter/nvim-treesitter/blob/master/queries/scheme/highlights.scm
+
+;; A highlight query can override the highlights queries before it.
+;; So the order is important.
+;; We should highlight general rules, then highlight special forms.
+
+(number) @number
+(character) @character
+(boolean) @boolean
+(string) @string
+[(comment)
+ (block_comment)] @comment
+
+;; highlight for datum comment
+;; copied from ../clojure/highlights.scm
+([(comment) (directive)] @comment
+ (#set! "priority" 105))
+
+(escape_sequence) @string.escape
+
+["(" ")" "[" "]" "{" "}"] @punctuation.bracket
+
+;; variables
+
+(symbol) @variable
+((symbol) @variable.builtin
+ (#any-of? @variable.builtin "..." "."))
+
+;; procedure
+
+(list
+ .
+ (symbol) @function)
+
+;; special forms
+
+(list
+ "["
+ (symbol)+ @variable
+ "]")
+
+(list
+ .
+ (symbol) @_f
+ .
+ (list
+   (symbol) @variable)
+ (#any-of? @_f "lambda" "λ"))
+
+(list
+ .
+ (symbol) @_f
+ .
+ (list
+   (list
+     (symbol) @variable))
+ (#any-of? @_f
+  "let" "let*" "let-syntax" "let-values" "let*-values" "letrec" "letrec*" "letrec-syntax"))
+
+;; operators
+
+((symbol) @operator
+ (#any-of? @operator
+  "+" "-" "*" "/" "=" "<=" ">=" "<" ">"))
+
+;; keyword
+
+((symbol) @keyword
+ (#any-of? @keyword
+  "define" "lambda" "λ" "begin" "do" "define-syntax"
+  "and" "or"
+  "if" "cond" "case" "when" "unless" "else" "=>"
+  "let" "let*" "let-syntax" "let-values" "let*-values" "letrec" "letrec*" "letrec-syntax"
+  "set!"
+  "syntax-rules" "identifier-syntax"
+  "quote" "unquote" "quote-splicing" "quasiquote" "unquote-splicing"
+  "delay"
+  "assert"
+  "library" "export" "import" "rename" "only" "except" "prefix"))
+
+((symbol) @conditional
+ (#any-of? @conditional "if" "cond" "case" "when" "unless"))
+
+;; quote
+
+(abbreviation
+ "'"
+ (symbol)) @symbol
+
+(list
+ .
+ (symbol) @_f
+ (#eq? @_f "quote")) @symbol
+
+;; library
+
+(list
+ .
+ (symbol) @_lib
+ .
+ (symbol) @namespace
+
+ (#eq? @_lib "library"))
+
+;; builtin procedures
+;; procedures in R5RS and R6RS but not in R6RS-lib
+
+((symbol) @function.builtin
+ (#any-of? @function.builtin
+  ;; eq
+  "eqv?" "eq?" "equal?"
+  ;; number
+  "number?" "complex?" "real?" "rational?" "integer?"
+  "exact?" "inexact?"
+  "zero?" "positive?" "negative?" "odd?" "even?" "finite?" "infinite?" "nan?"
+  "max" "min"
+  "abs" "quotient" "remainder" "modulo"
+  "div" "div0" "mod" "mod0" "div-and-mod" "div0-and-mod0"
+  "gcd" "lcm" "numerator" "denominator"
+  "floor" "ceiling" "truncate" "round"
+  "rationalize"
+  "exp" "log" "sin" "cos" "tan" "asin" "acos" "atan"
+  "sqrt" "expt"
+  "exact-integer-sqrt"
+  "make-rectangular" "make-polar" "real-part" "imag-part" "magnitude" "angle"
+  "real-valued" "rational-valued?" "integer-valued?"
+  "exact" "inexact" "exact->inexact" "inexact->exact"
+  "number->string" "string->number"
+  ;; boolean
+  "boolean?" "not" "boolean=?"
+  ;; pair
+  "pair?" "cons" 
+  "car" "cdr" 
+  "caar" "cadr" "cdar" "cddr" 
+  "caaar" "caadr" "cadar" "caddr" "cdaar" "cdadr" "cddar" "cdddr"
+  "caaaar" "caaadr" "caadar" "caaddr" "cadaar" "cadadr" "caddar" "cadddr"
+  "cdaaar" "cdaadr" "cdadar" "cdaddr" "cddaar" "cddadr" "cdddar" "cddddr"
+  "set-car!" "set-cdr!"
+  ;; list
+  "null?" "list?"
+  "list" "length" "append" "reverse" "list-tail" "list-ref"
+  "map" "for-each"
+  "memq" "memv" "member" "assq" "assv" "assoc"
+  ;; symbol
+  "symbol?" "symbol->string" "string->symbol" "symbol=?"
+  ;; char
+  "char?" "char=?" "char<?" "char>?" "char<=?" "char>=?"
+  "char-ci=?" "char-ci<?" "char-ci>?" "char-ci<=?" "char-ci>=?"
+  "char-alphabetic?" "char-numeric?" "char-whitespace?" "char-upper-case?" "char-lower-case?"
+  "char->integer" "integer->char"
+  "char-upcase" "char-downcase"
+  ;; string
+  "string?" "make-string" "string" "string-length" "string-ref" "string-set!"
+  "string=?" "string-ci=?" "string<?" "string>?" "string<=?" "string>=?"
+  "string-ci<?" "string-ci>?" "string-ci<=?" "string-ci>=?"
+  "substring" "string-append" "string->list" "list->string"
+  "string-for-each"
+  "string-copy" "string-fill!"
+  "string-upcase" "string-downcase"
+  ;; vector
+  "vector?" "make-vector" "vector" "vector-length" "vector-ref" "vector-set!"
+  "vector->list" "list->vector" "vector-fill!" "vector-map" "vector-for-each"
+  ;; bytevector
+  "bytevector?" "native-endianness"
+  "make-bytevector" "bytevector-length" "bytevector=?" "bytevector-fill!"
+  "bytevector-copy!" "bytevector-copy"
+  ;; error
+  "error" "assertion-violation"
+  ;; control
+  "procedure?" "apply" "force"
+  "call-with-current-continuation" "call/cc"
+  "values" "call-with-values" "dynamic-wind"
+  "eval" "scheme-report-environment" "null-environment" "interaction-environment"
+  ;; IO
+  "call-with-input-file" "call-with-output-file" "input-port?" "output-port?"
+  "current-input-port" "current-output-port" "with-input-from-file" "with-output-to-file"
+  "open-input-file" "open-output-file" "close-input-port" "close-output-port"
+  ;; input
+  "read" "read-char" "peek-char" "eof-object?" "char-ready?"
+  ;; output
+  "write" "display" "newline" "write-char"
+  ;; system
+  "load" "transcript-on" "transcript-off"))