History

Add zig support

Allan Calix committed 2 years ago

Commit e5b71cc1 parent 37647a610 files · +430 −0Browse files

Unified · Split

10 files
  1. Cargo.lock +10 −0
  2. Cargo.toml +1 −0
  3. crates/zed/Cargo.toml +1 −0
  4. crates/zed/src/languages.rs +6 −0
  5. crates/zed/src/languages/zig.rs +125 −0
  6. crates/zed/src/languages/zig/config.toml +10 −0
  7. crates/zed/src/languages/zig/folds.scm +16 −0
  8. crates/zed/src/languages/zig/highlights.scm +234 −0
  9. crates/zed/src/languages/zig/indents.scm +22 −0
  10. crates/zed/src/languages/zig/injections.scm +5 −0
Cargo.lock+10−0
@@ -8716,6 +8716,15 @@ dependencies = [ · 8715 unmodified lines
87168716 "tree-sitter",
87178717 ]
87188718
8719+[[package]]
8720+name = "tree-sitter-zig"
8721+version = "0.0.1"
8722+source = "git+https://github.com/maxxnino/tree-sitter-zig?rev=0d08703e4c3f426ec61695d7617415fff97029bd#0d08703e4c3f426ec61695d7617415fff97029bd"
8723+dependencies = [
8724+ "cc",
8725+ "tree-sitter",
8726+]
8727+
87198728 [[package]]
87208729 name = "try-lock"
87218730 version = "0.2.4"
@@ -9812,6 +9821,7 @@ dependencies = [ · 1090 unmodified lines
98129821 "tree-sitter-uiua",
98139822 "tree-sitter-vue",
98149823 "tree-sitter-yaml",
9824+ "tree-sitter-zig",
98159825 "unindent",
98169826 "url",
98179827 "urlencoding",
Cargo.toml+1−0
@@ -161,6 +161,7 @@ tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", re · 160 unmodified lines
161161 tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "26bbaecda0039df4067861ab38ea8ea169f7f5aa"}
162162 tree-sitter-vue = {git = "https://github.com/zed-industries/tree-sitter-vue", rev = "6608d9d60c386f19d80af7d8132322fa11199c42"}
163163 tree-sitter-uiua = {git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2"}
164+tree-sitter-zig = { git = "https://github.com/maxxnino/tree-sitter-zig", rev = "0d08703e4c3f426ec61695d7617415fff97029bd" }
164165
165166 [patch.crates-io]
166167 tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "31c40449749c4263a91a43593831b82229049a4c" }
crates/zed/Cargo.toml+1−0
@@ -142,6 +142,7 @@ tree-sitter-nix.workspace = true · 141 unmodified lines
142142 tree-sitter-nu.workspace = true
143143 tree-sitter-vue.workspace = true
144144 tree-sitter-uiua.workspace = true
145+tree-sitter-zig.workspace = true
145146
146147 url = "2.2"
147148 urlencoding = "2.1.2"
crates/zed/src/languages.rs+6−0
@@ -31,6 +31,7 @@ mod typescript; · 30 unmodified lines
3131 mod uiua;
3232 mod vue;
3333 mod yaml;
34+mod zig;
3435
3536 // 1. Add tree-sitter-{language} parser to zed crate
3637 // 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( · 75 unmodified lines
112113 tree_sitter_go::language(),
113114 vec![Arc::new(go::GoLspAdapter)],
114115 );
116+ language(
117+ "zig",
118+ tree_sitter_zig::language(),
119+ vec![Arc::new(zig::ZlsAdapter)],
120+ );
115121 language(
116122 "heex",
117123 tree_sitter_heex::language(),
crates/zed/src/languages/zig.rs+125−0
@@ -0,0 +1,125 @@
1+use anyhow::{anyhow, Context, Result};
2+use async_compression::futures::bufread::GzipDecoder;
3+use async_tar::Archive;
4+use async_trait::async_trait;
5+use futures::{io::BufReader, StreamExt};
6+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
7+use lsp::LanguageServerBinary;
8+use smol::fs;
9+use std::env::consts::ARCH;
10+use std::{any::Any, path::PathBuf};
11+use util::async_maybe;
12+use util::github::latest_github_release;
13+use util::{github::GitHubLspBinaryVersion, ResultExt};
14+
15+pub struct ZlsAdapter;
16+
17+#[async_trait]
18+impl LspAdapter for ZlsAdapter {
19+ fn name(&self) -> LanguageServerName {
20+ LanguageServerName("zls".into())
21+ }
22+
23+ fn short_name(&self) -> &'static str {
24+ "zls"
25+ }
26+
27+ async fn fetch_latest_server_version(
28+ &self,
29+ delegate: &dyn LspAdapterDelegate,
30+ ) -> Result<Box<dyn 'static + Send + Any>> {
31+ let release = latest_github_release("zigtools/zls", false, delegate.http_client()).await?;
32+ let asset_name = format!("zls-{}-macos.tar.gz", ARCH);
33+ let asset = release
34+ .assets
35+ .iter()
36+ .find(|asset| asset.name == asset_name)
37+ .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
38+ let version = GitHubLspBinaryVersion {
39+ name: release.name,
40+ url: asset.browser_download_url.clone(),
41+ };
42+
43+ Ok(Box::new(version) as Box<_>)
44+ }
45+
46+ async fn fetch_server_binary(
47+ &self,
48+ version: Box<dyn 'static + Send + Any>,
49+ container_dir: PathBuf,
50+ delegate: &dyn LspAdapterDelegate,
51+ ) -> Result<LanguageServerBinary> {
52+ let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
53+ let binary_path = container_dir.join("bin/zls");
54+
55+ if fs::metadata(&binary_path).await.is_err() {
56+ let mut response = delegate
57+ .http_client()
58+ .get(&version.url, Default::default(), true)
59+ .await
60+ .context("error downloading release")?;
61+ let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
62+ let archive = Archive::new(decompressed_bytes);
63+ archive.unpack(container_dir).await?;
64+ }
65+
66+ fs::set_permissions(
67+ &binary_path,
68+ <fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
69+ )
70+ .await?;
71+ Ok(LanguageServerBinary {
72+ path: binary_path,
73+ arguments: vec![],
74+ })
75+ }
76+
77+ async fn cached_server_binary(
78+ &self,
79+ container_dir: PathBuf,
80+ _: &dyn LspAdapterDelegate,
81+ ) -> Option<LanguageServerBinary> {
82+ get_cached_server_binary(container_dir).await
83+ }
84+
85+ async fn installation_test_binary(
86+ &self,
87+ container_dir: PathBuf,
88+ ) -> Option<LanguageServerBinary> {
89+ get_cached_server_binary(container_dir)
90+ .await
91+ .map(|mut binary| {
92+ binary.arguments = vec!["--help".into()];
93+ binary
94+ })
95+ }
96+}
97+
98+async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
99+ async_maybe!({
100+ let mut last_binary_path = None;
101+ let mut entries = fs::read_dir(&container_dir).await?;
102+ while let Some(entry) = entries.next().await {
103+ let entry = entry?;
104+ if entry.file_type().await?.is_file()
105+ && entry
106+ .file_name()
107+ .to_str()
108+ .map_or(false, |name| name == "zls")
109+ {
110+ last_binary_path = Some(entry.path());
111+ }
112+ }
113+
114+ if let Some(path) = last_binary_path {
115+ Ok(LanguageServerBinary {
116+ path,
117+ arguments: Vec::new(),
118+ })
119+ } else {
120+ Err(anyhow!("no cached binary"))
121+ }
122+ })
123+ .await
124+ .log_err()
125+}
crates/zed/src/languages/zig/config.toml+10−0
@@ -0,0 +1,10 @@
1+name = "Zig"
2+path_suffixes = ["zig"]
3+line_comment = "// "
4+autoclose_before = ";:.,=}])>"
5+brackets = [
6+ { start = "{", end = "}", close = true, newline = true },
7+ { start = "[", end = "]", close = true, newline = true },
8+ { start = "(", end = ")", close = true, newline = true },
9+ { start = "<", end = ">", close = true, newline = true },
10+]
crates/zed/src/languages/zig/folds.scm+16−0
@@ -0,0 +1,16 @@
1+[
2+ (Block)
3+ (ContainerDecl)
4+ (SwitchExpr)
5+ (InitList)
6+ (AsmExpr)
7+ (ErrorSetDecl)
8+ (LINESTRING)
9+ (
10+ [
11+ (IfPrefix)
12+ (WhilePrefix)
13+ (ForPrefix)
14+ ]
15+ )
16+] @fold
crates/zed/src/languages/zig/highlights.scm+234−0
@@ -0,0 +1,234 @@
1+[
2+ (container_doc_comment)
3+ (doc_comment)
4+ (line_comment)
5+] @comment
6+
7+[
8+ variable: (IDENTIFIER)
9+ variable_type_function: (IDENTIFIER)
10+] @variable
11+
12+parameter: (IDENTIFIER) @parameter
13+
14+[
15+ field_member: (IDENTIFIER)
16+ field_access: (IDENTIFIER)
17+] @field
18+
19+;; assume TitleCase is a type
20+(
21+ [
22+ variable_type_function: (IDENTIFIER)
23+ field_access: (IDENTIFIER)
24+ parameter: (IDENTIFIER)
25+ ] @type
26+ (#match? @type "^[A-Z]([a-z]+[A-Za-z0-9]*)*$")
27+)
28+;; assume camelCase is a function
29+(
30+ [
31+ variable_type_function: (IDENTIFIER)
32+ field_access: (IDENTIFIER)
33+ parameter: (IDENTIFIER)
34+ ] @function
35+ (#match? @function "^[a-z]+([A-Z][a-z0-9]*)+$")
36+)
37+
38+;; assume all CAPS_1 is a constant
39+(
40+ [
41+ variable_type_function: (IDENTIFIER)
42+ field_access: (IDENTIFIER)
43+ ] @constant
44+ (#match? @constant "^[A-Z][A-Z_0-9]+$")
45+)
46+
47+[
48+ function_call: (IDENTIFIER)
49+ function: (IDENTIFIER)
50+] @function
51+
52+exception: "!" @exception
53+
54+(
55+ (IDENTIFIER) @variable.builtin
56+ (#eq? @variable.builtin "_")
57+)
58+
59+(PtrTypeStart "c" @variable.builtin)
60+
61+(
62+ (ContainerDeclType
63+ [
64+ (ErrorUnionExpr)
65+ "enum"
66+ ]
67+ )
68+ (ContainerField (IDENTIFIER) @constant)
69+)
70+
71+field_constant: (IDENTIFIER) @constant
72+
73+(BUILTINIDENTIFIER) @keyword
74+
75+; No idea why this doesnt work
76+; ((BUILTINIDENTIFIER) @include
77+; (#any-of? @include "@import" "@cImport"))
78+
79+(INTEGER) @number
80+
81+(FLOAT) @float
82+
83+[
84+ "true"
85+ "false"
86+] @boolean
87+
88+[
89+ (LINESTRING)
90+ (STRINGLITERALSINGLE)
91+] @string
92+
93+(CHAR_LITERAL) @character
94+(EscapeSequence) @string.escape
95+(FormatSequence) @string.special
96+
97+(BreakLabel (IDENTIFIER) @label)
98+(BlockLabel (IDENTIFIER) @label)
99+
100+[
101+ "asm"
102+ "defer"
103+ "errdefer"
104+ "test"
105+ "struct"
106+ "union"
107+ "enum"
108+ "opaque"
109+ "error"
110+] @keyword
111+
112+[
113+ "async"
114+ "await"
115+ "suspend"
116+ "nosuspend"
117+ "resume"
118+] @keyword.coroutine
119+
120+[
121+ "fn"
122+] @keyword.function
123+
124+[
125+ "and"
126+ "or"
127+ "orelse"
128+] @keyword.operator
129+
130+[
131+ "return"
132+] @keyword.return
133+
134+[
135+ "if"
136+ "else"
137+ "switch"
138+] @conditional
139+
140+[
141+ "for"
142+ "while"
143+ "break"
144+ "continue"
145+] @keyword
146+
147+[
148+ "usingnamespace"
149+] @include
150+
151+[
152+ "try"
153+ "catch"
154+] @keyword
155+
156+[
157+ "anytype"
158+ (BuildinTypeExpr)
159+] @type.builtin
160+
161+[
162+ "const"
163+ "var"
164+ "volatile"
165+ "allowzero"
166+ "noalias"
167+] @type.qualifier
168+
169+[
170+ "addrspace"
171+ "align"
172+ "callconv"
173+ "linksection"
174+] @storageclass
175+
176+[
177+ "comptime"
178+ "export"
179+ "extern"
180+ "inline"
181+ "noinline"
182+ "packed"
183+ "pub"
184+ "threadlocal"
185+] @attribute
186+
187+[
188+ "null"
189+ "unreachable"
190+ "undefined"
191+] @constant.builtin
192+
193+[
194+ (CompareOp)
195+ (BitwiseOp)
196+ (BitShiftOp)
197+ (AdditionOp)
198+ (AssignOp)
199+ (MultiplyOp)
200+ (PrefixOp)
201+ "*"
202+ "**"
203+ "->"
204+ ".?"
205+ ".*"
206+ "?"
207+] @operator
208+
209+[
210+ ";"
211+ "."
212+ ","
213+ ":"
214+] @punctuation.delimiter
215+
216+[
217+ ".."
218+ "..."
219+] @punctuation.special
220+
221+[
222+ "["
223+ "]"
224+ "("
225+ ")"
226+ "{"
227+ "}"
228+ (Payload "|")
229+ (PtrPayload "|")
230+ (PtrIndexPayload "|")
231+] @punctuation.bracket
232+
233+; Error
234+(ERROR) @error
crates/zed/src/languages/zig/indents.scm+22−0
@@ -0,0 +1,22 @@
1+[
2+ (Block)
3+ (ContainerDecl)
4+ (SwitchExpr)
5+ (InitList)
6+] @indent
7+
8+[
9+ "("
10+ ")"
11+ "["
12+ "]"
13+ "{"
14+ "}"
15+] @branch
16+
17+[
18+ (line_comment)
19+ (container_doc_comment)
20+ (doc_comment)
21+ (LINESTRING)
22+] @ignore
crates/zed/src/languages/zig/injections.scm+5−0
@@ -0,0 +1,5 @@
1+[
2+ (container_doc_comment)
3+ (doc_comment)
4+ (line_comment)
5+] @comment