Detailed changes
@@ -8503,6 +8503,15 @@ dependencies = [
"tree-sitter",
]
+[[package]]
+name = "tree-sitter-gleam"
+version = "0.34.0"
+source = "git+https://github.com/gleam-lang/tree-sitter-gleam?rev=58b7cac8fc14c92b0677c542610d8738c373fa81#58b7cac8fc14c92b0677c542610d8738c373fa81"
+dependencies = [
+ "cc",
+ "tree-sitter",
+]
+
[[package]]
name = "tree-sitter-glsl"
version = "0.1.4"
@@ -9781,6 +9790,7 @@ dependencies = [
"tree-sitter-elixir",
"tree-sitter-elm",
"tree-sitter-embedded-template",
+ "tree-sitter-gleam",
"tree-sitter-glsl",
"tree-sitter-go",
"tree-sitter-heex",
@@ -140,6 +140,7 @@ tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir"
tree-sitter-elm = { git = "https://github.com/elm-tooling/tree-sitter-elm", rev = "692c50c0b961364c40299e73c1306aecb5d20f40"}
tree-sitter-embedded-template = "0.20.0"
tree-sitter-glsl = { git = "https://github.com/theHamsta/tree-sitter-glsl", rev = "2a56fb7bc8bb03a1892b4741279dd0a8758b7fb3" }
+tree-sitter-gleam = { git = "https://github.com/gleam-lang/tree-sitter-gleam", rev = "58b7cac8fc14c92b0677c542610d8738c373fa81" }
tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" }
tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "2e1348c3cf2c9323e87c2744796cf3f3868aa82a" }
tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" }
@@ -121,6 +121,7 @@ tree-sitter-elixir.workspace = true
tree-sitter-elm.workspace = true
tree-sitter-embedded-template.workspace = true
tree-sitter-glsl.workspace = true
+tree-sitter-gleam.workspace = true
tree-sitter-go.workspace = true
tree-sitter-heex.workspace = true
tree-sitter-json.workspace = true
@@ -12,6 +12,7 @@ use self::elixir::ElixirSettings;
mod c;
mod css;
mod elixir;
+mod gleam;
mod go;
mod html;
mod json;
@@ -99,6 +100,11 @@ pub fn init(
),
}
+ language(
+ "gleam",
+ tree_sitter_gleam::language(),
+ vec![Arc::new(gleam::GleamLspAdapter)],
+ );
language(
"go",
tree_sitter_go::language(),
@@ -0,0 +1,118 @@
+use std::any::Any;
+use std::ffi::OsString;
+use std::path::PathBuf;
+
+use anyhow::{anyhow, Result};
+use async_compression::futures::bufread::GzipDecoder;
+use async_tar::Archive;
+use async_trait::async_trait;
+use futures::io::BufReader;
+use futures::StreamExt;
+use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
+use lsp::LanguageServerBinary;
+use smol::fs;
+use util::github::{latest_github_release, GitHubLspBinaryVersion};
+use util::{async_maybe, ResultExt};
+
+fn server_binary_arguments() -> Vec<OsString> {
+ vec!["lsp".into()]
+}
+
+pub struct GleamLspAdapter;
+
+#[async_trait]
+impl LspAdapter for GleamLspAdapter {
+ fn name(&self) -> LanguageServerName {
+ LanguageServerName("gleam".into())
+ }
+
+ fn short_name(&self) -> &'static str {
+ "gleam"
+ }
+
+ async fn fetch_latest_server_version(
+ &self,
+ delegate: &dyn LspAdapterDelegate,
+ ) -> Result<Box<dyn 'static + Send + Any>> {
+ let release =
+ latest_github_release("gleam-lang/gleam", false, delegate.http_client()).await?;
+
+ let asset_name = format!(
+ "gleam-{version}-{arch}-apple-darwin.tar.gz",
+ version = release.name,
+ arch = std::env::consts::ARCH
+ );
+ let asset = release
+ .assets
+ .iter()
+ .find(|asset| asset.name == asset_name)
+ .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
+ Ok(Box::new(GitHubLspBinaryVersion {
+ name: release.name,
+ url: asset.browser_download_url.clone(),
+ }))
+ }
+
+ 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("gleam");
+
+ if fs::metadata(&binary_path).await.is_err() {
+ let mut response = delegate
+ .http_client()
+ .get(&version.url, Default::default(), true)
+ .await
+ .map_err(|err| anyhow!("error downloading release: {}", err))?;
+ let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
+ let archive = Archive::new(decompressed_bytes);
+ archive.unpack(container_dir).await?;
+ }
+
+ Ok(LanguageServerBinary {
+ path: binary_path,
+ arguments: server_binary_arguments(),
+ })
+ }
+
+ 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!["--version".into()];
+ binary
+ })
+ }
+}
+
+async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
+ async_maybe!({
+ let mut last = None;
+ let mut entries = fs::read_dir(&container_dir).await?;
+ while let Some(entry) = entries.next().await {
+ last = Some(entry?.path());
+ }
+
+ anyhow::Ok(LanguageServerBinary {
+ path: last.ok_or_else(|| anyhow!("no cached binary"))?,
+ arguments: server_binary_arguments(),
+ })
+ })
+ .await
+ .log_err()
+}
@@ -0,0 +1,10 @@
+name = "Gleam"
+path_suffixes = ["gleam"]
+line_comments = ["// ", "/// "]
+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 = false, not_in = ["string", "comment"] },
+]
@@ -0,0 +1,130 @@
+; Comments
+(module_comment) @comment
+(statement_comment) @comment
+(comment) @comment
+
+; Constants
+(constant
+ name: (identifier) @constant)
+
+; Modules
+(module) @module
+(import alias: (identifier) @module)
+(remote_type_identifier
+ module: (identifier) @module)
+(remote_constructor_name
+ module: (identifier) @module)
+((field_access
+ record: (identifier) @module
+ field: (label) @function)
+ (#is-not? local))
+
+; Functions
+(unqualified_import (identifier) @function)
+(unqualified_import "type" (type_identifier) @type)
+(unqualified_import (type_identifier) @constructor)
+(function
+ name: (identifier) @function)
+(external_function
+ name: (identifier) @function)
+(function_parameter
+ name: (identifier) @variable.parameter)
+((function_call
+ function: (identifier) @function)
+ (#is-not? local))
+((binary_expression
+ operator: "|>"
+ right: (identifier) @function)
+ (#is-not? local))
+
+; "Properties"
+; Assumed to be intended to refer to a name for a field; something that comes
+; before ":" or after "."
+; e.g. record field names, tuple indices, names for named arguments, etc
+(label) @property
+(tuple_access
+ index: (integer) @property)
+
+; Attributes
+(attribute
+ "@" @attribute
+ name: (identifier) @attribute)
+
+(attribute_value (identifier) @constant)
+
+; Type names
+(remote_type_identifier) @type
+(type_identifier) @type
+
+; Data constructors
+(constructor_name) @constructor
+
+; Literals
+(string) @string
+((escape_sequence) @warning
+ ; Deprecated in v0.33.0-rc2:
+ (#eq? @warning "\\e"))
+(escape_sequence) @string.escape
+(bit_string_segment_option) @function.builtin
+(integer) @number
+(float) @number
+
+; Reserved identifiers
+; TODO: when tree-sitter supports `#any-of?` in the Rust bindings,
+; refactor this to use `#any-of?` rather than `#match?`
+((identifier) @warning
+ (#match? @warning "^(auto|delegate|derive|else|implement|macro|test|echo)$"))
+
+; Variables
+(identifier) @variable
+(discard) @comment.unused
+
+; Keywords
+[
+ (visibility_modifier) ; "pub"
+ (opacity_modifier) ; "opaque"
+ "as"
+ "assert"
+ "case"
+ "const"
+ ; DEPRECATED: 'external' was removed in v0.30.
+ "external"
+ "fn"
+ "if"
+ "import"
+ "let"
+ "panic"
+ "todo"
+ "type"
+ "use"
+] @keyword
+
+; Operators
+(binary_expression
+ operator: _ @operator)
+(boolean_negation "!" @operator)
+(integer_negation "-" @operator)
+
+; Punctuation
+[
+ "("
+ ")"
+ "["
+ "]"
+ "{"
+ "}"
+ "<<"
+ ">>"
+] @punctuation.bracket
+[
+ "."
+ ","
+ ;; Controversial -- maybe some are operators?
+ ":"
+ "#"
+ "="
+ "->"
+ ".."
+ "-"
+ "<-"
+] @punctuation.delimiter
@@ -0,0 +1,4 @@
+(function
+ (visibility_modifier)? @context
+ "fn" @context
+ name: (_) @name) @item