Detailed changes
@@ -8716,6 +8716,15 @@ dependencies = [
"tree-sitter",
]
+[[package]]
+name = "tree-sitter-zig"
+version = "0.0.1"
+source = "git+https://github.com/maxxnino/tree-sitter-zig?rev=0d08703e4c3f426ec61695d7617415fff97029bd#0d08703e4c3f426ec61695d7617415fff97029bd"
+dependencies = [
+ "cc",
+ "tree-sitter",
+]
+
[[package]]
name = "try-lock"
version = "0.2.4"
@@ -9812,6 +9821,7 @@ dependencies = [
"tree-sitter-uiua",
"tree-sitter-vue",
"tree-sitter-yaml",
+ "tree-sitter-zig",
"unindent",
"url",
"urlencoding",
@@ -161,6 +161,7 @@ tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", re
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "26bbaecda0039df4067861ab38ea8ea169f7f5aa"}
tree-sitter-vue = {git = "https://github.com/zed-industries/tree-sitter-vue", rev = "6608d9d60c386f19d80af7d8132322fa11199c42"}
tree-sitter-uiua = {git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2"}
+tree-sitter-zig = { git = "https://github.com/maxxnino/tree-sitter-zig", rev = "0d08703e4c3f426ec61695d7617415fff97029bd" }
[patch.crates-io]
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "31c40449749c4263a91a43593831b82229049a4c" }
@@ -142,6 +142,7 @@ tree-sitter-nix.workspace = true
tree-sitter-nu.workspace = true
tree-sitter-vue.workspace = true
tree-sitter-uiua.workspace = true
+tree-sitter-zig.workspace = true
url = "2.2"
urlencoding = "2.1.2"
@@ -31,6 +31,7 @@ mod typescript;
mod uiua;
mod vue;
mod yaml;
+mod zig;
// 1. Add tree-sitter-{language} parser to zed crate
// 2. Create a language directory in zed/crates/zed/src/languages and add the language to init function below
@@ -112,6 +113,11 @@ pub fn init(
tree_sitter_go::language(),
vec![Arc::new(go::GoLspAdapter)],
);
+ language(
+ "zig",
+ tree_sitter_zig::language(),
+ vec![Arc::new(zig::ZlsAdapter)],
+ );
language(
"heex",
tree_sitter_heex::language(),
@@ -0,0 +1,125 @@
+use anyhow::{anyhow, Context, Result};
+use async_compression::futures::bufread::GzipDecoder;
+use async_tar::Archive;
+use async_trait::async_trait;
+use futures::{io::BufReader, StreamExt};
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
+use smol::fs;
+use std::env::consts::ARCH;
+use std::{any::Any, path::PathBuf};
+use util::async_maybe;
+use util::github::latest_github_release;
+use util::{github::GitHubLspBinaryVersion, ResultExt};
+
+pub struct ZlsAdapter;
+
+#[async_trait]
+impl LspAdapter for ZlsAdapter {
+ fn name(&self) -> LanguageServerName {
+ LanguageServerName("zls".into())
+ }
+
+ fn short_name(&self) -> &'static str {
+ "zls"
+ }
+
+ async fn fetch_latest_server_version(
+ &self,
+ delegate: &dyn LspAdapterDelegate,
+ ) -> Result<Box<dyn 'static + Send + Any>> {
+ let release = latest_github_release("zigtools/zls", false, delegate.http_client()).await?;
+ let asset_name = format!("zls-{}-macos.tar.gz", ARCH);
+ let asset = release
+ .assets
+ .iter()
+ .find(|asset| asset.name == asset_name)
+ .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
+ let version = GitHubLspBinaryVersion {
+ name: release.name,
+ url: asset.browser_download_url.clone(),
+ };
+
+ Ok(Box::new(version) as Box<_>)
+ }
+
+ async fn fetch_server_binary(
+ &self,
+ version: Box<dyn 'static + Send + Any>,
+ container_dir: PathBuf,
+ delegate: &dyn LspAdapterDelegate,
+ ) -> Result<LanguageServerBinary> {
+ let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
+ let binary_path = container_dir.join("bin/zls");
+
+ if fs::metadata(&binary_path).await.is_err() {
+ let mut response = delegate
+ .http_client()
+ .get(&version.url, Default::default(), true)
+ .await
+ .context("error downloading release")?;
+ let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
+ let archive = Archive::new(decompressed_bytes);
+ archive.unpack(container_dir).await?;
+ }
+
+ fs::set_permissions(
+ &binary_path,
+ <fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
+ )
+ .await?;
+ Ok(LanguageServerBinary {
+ path: binary_path,
+ arguments: vec![],
+ })
+ }
+
+ async fn cached_server_binary(
+ &self,
+ container_dir: PathBuf,
+ _: &dyn LspAdapterDelegate,
+ ) -> Option<LanguageServerBinary> {
+ get_cached_server_binary(container_dir).await
+ }
+
+ async fn installation_test_binary(
+ &self,
+ container_dir: PathBuf,
+ ) -> Option<LanguageServerBinary> {
+ get_cached_server_binary(container_dir)
+ .await
+ .map(|mut binary| {
+ binary.arguments = vec!["--help".into()];
+ binary
+ })
+ }
+}
+
+async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
+ async_maybe!({
+ let mut last_binary_path = None;
+ let mut entries = fs::read_dir(&container_dir).await?;
+ while let Some(entry) = entries.next().await {
+ let entry = entry?;
+ if entry.file_type().await?.is_file()
+ && entry
+ .file_name()
+ .to_str()
+ .map_or(false, |name| name == "zls")
+ {
+ last_binary_path = Some(entry.path());
+ }
+ }
+
+ if let Some(path) = last_binary_path {
+ Ok(LanguageServerBinary {
+ path,
+ arguments: Vec::new(),
+ })
+ } else {
+ Err(anyhow!("no cached binary"))
+ }
+ })
+ .await
+ .log_err()
+}
@@ -0,0 +1,10 @@
+name = "Zig"
+path_suffixes = ["zig"]
+line_comment = "// "
+autoclose_before = ";:.,=}])>"
+brackets = [
+ { start = "{", end = "}", close = true, newline = true },
+ { start = "[", end = "]", close = true, newline = true },
+ { start = "(", end = ")", close = true, newline = true },
+ { start = "<", end = ">", close = true, newline = true },
+]
@@ -0,0 +1,16 @@
+[
+ (Block)
+ (ContainerDecl)
+ (SwitchExpr)
+ (InitList)
+ (AsmExpr)
+ (ErrorSetDecl)
+ (LINESTRING)
+ (
+ [
+ (IfPrefix)
+ (WhilePrefix)
+ (ForPrefix)
+ ]
+ )
+] @fold
@@ -0,0 +1,234 @@
+[
+ (container_doc_comment)
+ (doc_comment)
+ (line_comment)
+] @comment
+
+[
+ variable: (IDENTIFIER)
+ variable_type_function: (IDENTIFIER)
+] @variable
+
+parameter: (IDENTIFIER) @parameter
+
+[
+ field_member: (IDENTIFIER)
+ field_access: (IDENTIFIER)
+] @field
+
+;; assume TitleCase is a type
+(
+ [
+ variable_type_function: (IDENTIFIER)
+ field_access: (IDENTIFIER)
+ parameter: (IDENTIFIER)
+ ] @type
+ (#match? @type "^[A-Z]([a-z]+[A-Za-z0-9]*)*$")
+)
+;; assume camelCase is a function
+(
+ [
+ variable_type_function: (IDENTIFIER)
+ field_access: (IDENTIFIER)
+ parameter: (IDENTIFIER)
+ ] @function
+ (#match? @function "^[a-z]+([A-Z][a-z0-9]*)+$")
+)
+
+;; assume all CAPS_1 is a constant
+(
+ [
+ variable_type_function: (IDENTIFIER)
+ field_access: (IDENTIFIER)
+ ] @constant
+ (#match? @constant "^[A-Z][A-Z_0-9]+$")
+)
+
+[
+ function_call: (IDENTIFIER)
+ function: (IDENTIFIER)
+] @function
+
+exception: "!" @exception
+
+(
+ (IDENTIFIER) @variable.builtin
+ (#eq? @variable.builtin "_")
+)
+
+(PtrTypeStart "c" @variable.builtin)
+
+(
+ (ContainerDeclType
+ [
+ (ErrorUnionExpr)
+ "enum"
+ ]
+ )
+ (ContainerField (IDENTIFIER) @constant)
+)
+
+field_constant: (IDENTIFIER) @constant
+
+(BUILTINIDENTIFIER) @keyword
+
+; No idea why this doesnt work
+; ((BUILTINIDENTIFIER) @include
+; (#any-of? @include "@import" "@cImport"))
+
+(INTEGER) @number
+
+(FLOAT) @float
+
+[
+ "true"
+ "false"
+] @boolean
+
+[
+ (LINESTRING)
+ (STRINGLITERALSINGLE)
+] @string
+
+(CHAR_LITERAL) @character
+(EscapeSequence) @string.escape
+(FormatSequence) @string.special
+
+(BreakLabel (IDENTIFIER) @label)
+(BlockLabel (IDENTIFIER) @label)
+
+[
+ "asm"
+ "defer"
+ "errdefer"
+ "test"
+ "struct"
+ "union"
+ "enum"
+ "opaque"
+ "error"
+] @keyword
+
+[
+ "async"
+ "await"
+ "suspend"
+ "nosuspend"
+ "resume"
+] @keyword.coroutine
+
+[
+ "fn"
+] @keyword.function
+
+[
+ "and"
+ "or"
+ "orelse"
+] @keyword.operator
+
+[
+ "return"
+] @keyword.return
+
+[
+ "if"
+ "else"
+ "switch"
+] @conditional
+
+[
+ "for"
+ "while"
+ "break"
+ "continue"
+] @keyword
+
+[
+ "usingnamespace"
+] @include
+
+[
+ "try"
+ "catch"
+] @keyword
+
+[
+ "anytype"
+ (BuildinTypeExpr)
+] @type.builtin
+
+[
+ "const"
+ "var"
+ "volatile"
+ "allowzero"
+ "noalias"
+] @type.qualifier
+
+[
+ "addrspace"
+ "align"
+ "callconv"
+ "linksection"
+] @storageclass
+
+[
+ "comptime"
+ "export"
+ "extern"
+ "inline"
+ "noinline"
+ "packed"
+ "pub"
+ "threadlocal"
+] @attribute
+
+[
+ "null"
+ "unreachable"
+ "undefined"
+] @constant.builtin
+
+[
+ (CompareOp)
+ (BitwiseOp)
+ (BitShiftOp)
+ (AdditionOp)
+ (AssignOp)
+ (MultiplyOp)
+ (PrefixOp)
+ "*"
+ "**"
+ "->"
+ ".?"
+ ".*"
+ "?"
+] @operator
+
+[
+ ";"
+ "."
+ ","
+ ":"
+] @punctuation.delimiter
+
+[
+ ".."
+ "..."
+] @punctuation.special
+
+[
+ "["
+ "]"
+ "("
+ ")"
+ "{"
+ "}"
+ (Payload "|")
+ (PtrPayload "|")
+ (PtrIndexPayload "|")
+] @punctuation.bracket
+
+; Error
+(ERROR) @error
@@ -0,0 +1,22 @@
+[
+ (Block)
+ (ContainerDecl)
+ (SwitchExpr)
+ (InitList)
+] @indent
+
+[
+ "("
+ ")"
+ "["
+ "]"
+ "{"
+ "}"
+] @branch
+
+[
+ (line_comment)
+ (container_doc_comment)
+ (doc_comment)
+ (LINESTRING)
+] @ignore
@@ -0,0 +1,5 @@
+[
+ (container_doc_comment)
+ (doc_comment)
+ (line_comment)
+] @comment