From fb9eb6a0fc35e7670406a8170ebb10a378dbf0f1 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 30 Jan 2024 17:50:15 +0800 Subject: [PATCH] Add taplo `toml` LSP (#7034) Release Notes: - Added `toml` LSP using [taplo](https://taplo.tamasfe.dev/) --- crates/zed/src/languages.rs | 11 ++- crates/zed/src/languages/toml.rs | 114 +++++++++++++++++++++++++++++++ docs/src/languages/toml.md | 2 +- 3 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 crates/zed/src/languages/toml.rs diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index ac3d7f2ee8..b487210050 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -29,6 +29,7 @@ mod ruby; mod rust; mod svelte; mod tailwind; +mod toml; mod typescript; mod uiua; mod vue; @@ -149,7 +150,11 @@ pub fn init( tree_sitter_rust::language(), vec![Arc::new(rust::RustLspAdapter)], ); - language("toml", tree_sitter_toml::language(), vec![]); + language( + "toml", + tree_sitter_toml::language(), + vec![Arc::new(toml::TaploLspAdapter)], + ); match &DenoSettings::get(None, cx).enable { true => { language( @@ -291,7 +296,7 @@ pub fn init( let path = child.path(); let config_path = path.join("config.toml"); if let Ok(config) = std::fs::read(&config_path) { - let config: LanguageConfig = toml::from_slice(&config).unwrap(); + let config: LanguageConfig = ::toml::from_slice(&config).unwrap(); if let Some(grammar_name) = config.grammar_name.clone() { languages.register_wasm(path.into(), grammar_name, config); } @@ -317,7 +322,7 @@ pub async fn language( } fn load_config(name: &str) -> LanguageConfig { - toml::from_slice( + ::toml::from_slice( &LanguageDir::get(&format!("{}/config.toml", name)) .unwrap() .data, diff --git a/crates/zed/src/languages/toml.rs b/crates/zed/src/languages/toml.rs new file mode 100644 index 0000000000..129b5fcb66 --- /dev/null +++ b/crates/zed/src/languages/toml.rs @@ -0,0 +1,114 @@ +use anyhow::{Context, Result}; +use async_compression::futures::bufread::GzipDecoder; +use async_trait::async_trait; +use futures::{io::BufReader, StreamExt}; +use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; +use lsp::LanguageServerBinary; +use smol::fs::{self, File}; +use std::{any::Any, path::PathBuf}; +use util::async_maybe; +use util::github::latest_github_release; +use util::{github::GitHubLspBinaryVersion, ResultExt}; + +pub struct TaploLspAdapter; + +#[async_trait] +impl LspAdapter for TaploLspAdapter { + fn name(&self) -> LanguageServerName { + LanguageServerName("taplo-ls".into()) + } + + fn short_name(&self) -> &'static str { + "taplo-ls" + } + + async fn fetch_latest_server_version( + &self, + delegate: &dyn LspAdapterDelegate, + ) -> Result> { + let release = latest_github_release("tamasfe/taplo", false, delegate.http_client()).await?; + let asset_name = format!("taplo-full-darwin-{arch}.gz", arch = std::env::consts::ARCH); + + let asset = release + .assets + .iter() + .find(|asset| asset.name == asset_name) + .context(format!("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, + container_dir: PathBuf, + delegate: &dyn LspAdapterDelegate, + ) -> Result { + let version = version.downcast::().unwrap(); + let binary_path = container_dir.join("taplo"); + + 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 mut file = File::create(&binary_path).await?; + + futures::io::copy(decompressed_bytes, &mut file).await?; + + fs::set_permissions( + &binary_path, + ::from_mode(0o755), + ) + .await?; + } + + Ok(LanguageServerBinary { + path: binary_path, + arguments: vec!["lsp".into(), "stdio".into()], + }) + } + + async fn cached_server_binary( + &self, + container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Option { + get_cached_server_binary(container_dir).await + } + + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + 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 { + 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.context("no cached binary")?, + arguments: Default::default(), + }) + }) + .await + .log_err() +} diff --git a/docs/src/languages/toml.md b/docs/src/languages/toml.md index 52c25de9a0..23826c3489 100644 --- a/docs/src/languages/toml.md +++ b/docs/src/languages/toml.md @@ -2,4 +2,4 @@ - Tree Sitter: [tree-sitter-toml](https://github.com/tree-sitter/tree-sitter-toml) -- Language Server: N/A +- Language Server: [taplo](https://taplo.tamasfe.dev)