Skip to content

Commit 7ff1cce

Browse files
committed
Initial commit based on the elm extension
0 parents  commit 7ff1cce

8 files changed

Lines changed: 259 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store

Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "zed_rescript"
3+
version = "0.0.1"
4+
edition = "2021"
5+
publish = false
6+
license = "Apache-2.0"
7+
8+
[lints]
9+
workspace = true
10+
11+
[lib]
12+
path = "src/rescript.rs"
13+
crate-type = ["cdylib"]
14+
15+
[dependencies]
16+
zed_extension_api = "0.0.6"

extension.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
id = "rescript"
2+
name = "ReScript"
3+
description = "ReScript support."
4+
version = "0.0.1"
5+
schema_version = 1
6+
authors = ["Karolis Narkevicius <hey@kn8.lt>"]
7+
repository = "https://github.com/zed-industries/zed"
8+
9+
[language_servers.rescript-language-server]
10+
name = "rescript-language-server"
11+
language = "ReScript"
12+
13+
[grammars.rescript]
14+
repository = "https://github.com/rescript-lang/tree-sitter-rescript"
15+
commit = "17b0805c9eef8e9fc451a33cddc7156dcf20a409"

languages/rescript/config.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name = "ReScript"
2+
grammar = "rescript"
3+
path_suffixes = ["rescript"]
4+
line_comments = ["-- "]
5+
block_comment = ["{- ", " -}"]
6+
brackets = [
7+
{ start = "{", end = "}", close = true, newline = true },
8+
{ start = "[", end = "]", close = true, newline = true },
9+
{ start = "(", end = ")", close = true, newline = true },
10+
{ start = "\"", end = "\"", close = true, newline = false, not_in = [
11+
"string",
12+
] },
13+
{ start = "'", end = "'", close = true, newline = false, not_in = [
14+
"string",
15+
"comment",
16+
] },
17+
]
18+
tab_size = 2

languages/rescript/highlights.scm

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
[
2+
"if"
3+
"then"
4+
"else"
5+
"let"
6+
"in"
7+
(case)
8+
(of)
9+
(backslash)
10+
(as)
11+
(port)
12+
(exposing)
13+
(alias)
14+
(import)
15+
(module)
16+
(type)
17+
(arrow)
18+
] @keyword
19+
20+
[
21+
(eq)
22+
(operator_identifier)
23+
(colon)
24+
] @operator
25+
26+
(type_annotation(lower_case_identifier) @function)
27+
(port_annotation(lower_case_identifier) @function)
28+
(function_declaration_left(lower_case_identifier) @function.definition)
29+
30+
(function_call_expr
31+
target: (value_expr
32+
name: (value_qid (lower_case_identifier) @function)))
33+
34+
(exposed_value(lower_case_identifier) @function)
35+
(exposed_type(upper_case_identifier) @type)
36+
37+
(field_access_expr(value_expr(value_qid)) @identifier)
38+
(lower_pattern) @variable
39+
(record_base_identifier) @identifier
40+
41+
[
42+
"("
43+
")"
44+
] @punctuation.bracket
45+
46+
[
47+
"|"
48+
","
49+
] @punctuation.delimiter
50+
51+
(number_constant_expr) @constant
52+
53+
(type_declaration(upper_case_identifier) @type)
54+
(type_ref) @type
55+
(type_alias_declaration name: (upper_case_identifier) @type)
56+
57+
(value_expr(upper_case_qid(upper_case_identifier)) @type)
58+
59+
[
60+
(line_comment)
61+
(block_comment)
62+
] @comment
63+
64+
(string_escape) @string.escape
65+
66+
[
67+
(open_quote)
68+
(close_quote)
69+
(regular_string_part)
70+
(open_char)
71+
(close_char)
72+
] @string

languages/rescript/injections.scm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
((glsl_content) @content
2+
(#set! "language" "glsl"))

languages/rescript/outline.scm

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
(type_declaration
2+
(type) @context
3+
(upper_case_identifier) @name) @item
4+
5+
(type_alias_declaration
6+
(type) @context
7+
(alias) @context
8+
name: (upper_case_identifier) @name) @item
9+
10+
(type_alias_declaration
11+
typeExpression:
12+
(type_expression
13+
part: (record_type
14+
(field_type
15+
name: (lower_case_identifier) @name) @item)))
16+
17+
(union_variant
18+
name: (upper_case_identifier) @name) @item
19+
20+
(value_declaration
21+
functionDeclarationLeft:
22+
(function_declaration_left(lower_case_identifier) @name)) @item

src/rescript.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use std::{env, fs};
2+
use zed::{
3+
serde_json::{self, Value},
4+
settings::LspSettings,
5+
};
6+
use zed_extension_api::{self as zed, Result};
7+
8+
const SERVER_PATH: &str = "node_modules/@rescript/language-server/out/cli.js";
9+
const PACKAGE_NAME: &str = "@rescript/language-server";
10+
11+
struct ReScriptExtension {
12+
did_find_server: bool,
13+
}
14+
15+
impl ReScriptExtension {
16+
fn server_exists(&self) -> bool {
17+
fs::metadata(SERVER_PATH).map_or(false, |stat| stat.is_file())
18+
}
19+
20+
fn server_script_path(&mut self, server_id: &zed::LanguageServerId) -> Result<String> {
21+
let server_exists = self.server_exists();
22+
if self.did_find_server && server_exists {
23+
return Ok(SERVER_PATH.to_string());
24+
}
25+
26+
zed::set_language_server_installation_status(
27+
&server_id,
28+
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
29+
);
30+
let version = zed::npm_package_latest_version(PACKAGE_NAME)?;
31+
32+
if !server_exists
33+
|| zed::npm_package_installed_version(PACKAGE_NAME)?.as_ref() != Some(&version)
34+
{
35+
zed::set_language_server_installation_status(
36+
&server_id,
37+
&zed::LanguageServerInstallationStatus::Downloading,
38+
);
39+
let result = zed::npm_install_package(PACKAGE_NAME, &version);
40+
match result {
41+
Ok(()) => {
42+
if !self.server_exists() {
43+
Err(format!(
44+
"installed package '{PACKAGE_NAME}' did not contain expected path '{SERVER_PATH}'",
45+
))?;
46+
}
47+
}
48+
Err(error) => {
49+
if !self.server_exists() {
50+
Err(error)?;
51+
}
52+
}
53+
}
54+
}
55+
56+
self.did_find_server = true;
57+
Ok(SERVER_PATH.to_string())
58+
}
59+
}
60+
61+
impl zed::Extension for ReScriptExtension {
62+
fn new() -> Self {
63+
Self {
64+
did_find_server: false,
65+
}
66+
}
67+
68+
fn language_server_command(
69+
&mut self,
70+
server_id: &zed::LanguageServerId,
71+
_worktree: &zed::Worktree,
72+
) -> Result<zed::Command> {
73+
let server_path = self.server_script_path(server_id)?;
74+
Ok(zed::Command {
75+
command: zed::node_binary_path()?,
76+
args: vec![
77+
env::current_dir()
78+
.unwrap()
79+
.join(&server_path)
80+
.to_string_lossy()
81+
.to_string(),
82+
"--stdio".to_string(),
83+
],
84+
env: Default::default(),
85+
})
86+
}
87+
88+
fn language_server_workspace_configuration(
89+
&mut self,
90+
server_id: &zed::LanguageServerId,
91+
worktree: &zed::Worktree,
92+
) -> Result<Option<Value>> {
93+
// @rescript/language-server expects workspace didChangeConfiguration notification
94+
// params to be the same as lsp initialization_options
95+
let initialization_options = LspSettings::for_worktree(server_id.as_ref(), worktree)?
96+
.initialization_options
97+
.clone()
98+
.unwrap_or_default();
99+
100+
Ok(Some(match initialization_options.clone().as_object_mut() {
101+
Some(op) => {
102+
// @rescript/language-server requests workspace configuration
103+
// for the `resLS` section, so we have to nest
104+
// another copy of initialization_options there
105+
op.insert("rescriptLS".into(), initialization_options);
106+
serde_json::to_value(op).unwrap_or_default()
107+
}
108+
None => initialization_options,
109+
}))
110+
}
111+
}
112+
113+
zed::register_extension!(ReScriptExtension);

0 commit comments

Comments
 (0)