diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 5b917c0fdf1e6141e7991b89d8a7f55a1455b4f6..6e400c4a6fa2e5a5f35ee84ee9339cba190a2741 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -20,6 +20,7 @@ use parking_lot::{Mutex, RwLock}; use serde::Deserialize; use serde_json::Value; use std::{ + any::Any, cell::RefCell, ops::Range, path::{Path, PathBuf}, @@ -61,9 +62,9 @@ pub trait ToLspPosition { fn to_lsp_position(self) -> lsp::Position; } -pub struct LspBinaryVersion { +pub struct GitHubLspBinaryVersion { pub name: String, - pub url: Option, + pub url: http::Url, } pub trait LspAdapter: 'static + Send + Sync { @@ -71,10 +72,10 @@ pub trait LspAdapter: 'static + Send + Sync { fn fetch_latest_server_version( &self, http: Arc, - ) -> BoxFuture<'static, Result>; + ) -> BoxFuture<'static, Result>>; fn fetch_server_binary( &self, - version: LspBinaryVersion, + version: Box, http: Arc, container_dir: PathBuf, ) -> BoxFuture<'static, Result>; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 1a41475909d2a0a395ae5fcc7f37541e54cf25fc..f4d5e9ee11e855cbfe43e8a05dd6ad9a06027faf 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2278,11 +2278,12 @@ impl Project { Ok(completions .into_iter() .filter_map(|lsp_completion| { - let (old_range, new_text) = match lsp_completion.text_edit.as_ref()? { - lsp::CompletionTextEdit::Edit(edit) => { + let (old_range, new_text) = match lsp_completion.text_edit.as_ref() { + Some(lsp::CompletionTextEdit::Edit(edit)) => { (range_from_lsp(edit.range), edit.new_text.clone()) } - lsp::CompletionTextEdit::InsertAndReplace(_) => { + None => (position..position, lsp_completion.label.clone()), + Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => { log::info!("unsupported insert/replace completion"); return None; } @@ -2307,6 +2308,7 @@ impl Project { lsp_completion, }) } else { + log::info!("completion out of expected range"); None } }) diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 607f4c30bb756935ce315a0378ce4caf1eb16b71..cc22247025a393cb104ee4317bc76a47155988d3 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -1,4 +1,4 @@ -use client::http::{self, HttpClient, Method}; +use client::http; use gpui::Task; pub use language::*; use rust_embed::RustEmbed; @@ -53,7 +53,7 @@ pub fn build_language_registry(login_shell_env_loaded: Task<()>) -> LanguageRegi ( "tsx", tree_sitter_typescript::language_tsx(), - None, // + Some(Arc::new(typescript::TypeScriptLspAdapter)), ), ( "typescript", diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 9ce3eab2f772d86fdcc7f7a717020480660a5ed5..cf4e2199676614de174a73647dbe67b5dfe67c97 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -3,7 +3,7 @@ use client::http::{self, HttpClient, Method}; use futures::{future::BoxFuture, FutureExt, StreamExt}; pub use language::*; use smol::fs::{self, File}; -use std::{path::PathBuf, str, sync::Arc}; +use std::{any::Any, path::PathBuf, str, sync::Arc}; use util::{ResultExt, TryFutureExt}; use super::GithubRelease; @@ -18,7 +18,7 @@ impl super::LspAdapter for CLspAdapter { fn fetch_latest_server_version( &self, http: Arc, - ) -> BoxFuture<'static, Result> { + ) -> BoxFuture<'static, Result>> { async move { let release = http .send( @@ -43,20 +43,21 @@ impl super::LspAdapter for CLspAdapter { .iter() .find(|asset| asset.name == asset_name) .ok_or_else(|| anyhow!("no release found matching {:?}", asset_name))?; - Ok(LspBinaryVersion { + Ok(Box::new(GitHubLspBinaryVersion { name: release.name, - url: Some(asset.browser_download_url.clone()), - }) + url: asset.browser_download_url.clone(), + }) as Box<_>) } .boxed() } fn fetch_server_binary( &self, - version: LspBinaryVersion, + version: Box, http: Arc, container_dir: PathBuf, ) -> BoxFuture<'static, Result> { + let version = version.downcast::().unwrap(); async move { let zip_path = container_dir.join(format!("clangd_{}.zip", version.name)); let version_dir = container_dir.join(format!("clangd_{}", version.name)); @@ -65,7 +66,7 @@ impl super::LspAdapter for CLspAdapter { if fs::metadata(&binary_path).await.is_err() { let response = http .send( - surf::RequestBuilder::new(Method::Get, version.url.unwrap()) + surf::RequestBuilder::new(Method::Get, version.url) .middleware(surf::middleware::Redirect::default()) .build(), ) diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index bb7744714f415a4db7ee1df25cb0f65027a371b7..c1d0db2ed6d220773094a78c43d2df18c8317c51 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -1,12 +1,12 @@ use anyhow::{anyhow, Context, Result}; use client::http::HttpClient; use futures::{future::BoxFuture, FutureExt, StreamExt}; -use language::{LspAdapter, LspBinaryVersion}; +use language::LspAdapter; use serde::Deserialize; use serde_json::json; use smol::fs; -use std::{path::PathBuf, sync::Arc}; -use util::ResultExt; +use std::{any::Any, path::PathBuf, sync::Arc}; +use util::{ResultExt, TryFutureExt}; pub struct JsonLspAdapter; @@ -27,7 +27,7 @@ impl LspAdapter for JsonLspAdapter { fn fetch_latest_server_version( &self, _: Arc, - ) -> BoxFuture<'static, Result> { + ) -> BoxFuture<'static, Result>> { async move { #[derive(Deserialize)] struct NpmInfo { @@ -43,25 +43,24 @@ impl LspAdapter for JsonLspAdapter { } let mut info: NpmInfo = serde_json::from_slice(&output.stdout)?; - Ok(LspBinaryVersion { - name: info - .versions + Ok(Box::new( + info.versions .pop() .ok_or_else(|| anyhow!("no versions found in npm info"))?, - url: Default::default(), - }) + ) as Box<_>) } .boxed() } fn fetch_server_binary( &self, - version: LspBinaryVersion, + version: Box, _: Arc, container_dir: PathBuf, ) -> BoxFuture<'static, Result> { + let version = version.downcast::().unwrap(); async move { - let version_dir = container_dir.join(&version.name); + let version_dir = container_dir.join(version.as_str()); fs::create_dir_all(&version_dir) .await .context("failed to create version directory")?; @@ -71,7 +70,7 @@ impl LspAdapter for JsonLspAdapter { let output = smol::process::Command::new("npm") .current_dir(&version_dir) .arg("install") - .arg(format!("vscode-json-languageserver@{}", version.name)) + .arg(format!("vscode-json-languageserver@{}", version)) .output() .await .context("failed to run npm install")?; diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 2d4c6b3d3673c1117abbe86fcfc48044f283cf05..8b06c5cbd7c784752cd91fecded506a40f6f5423 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -6,7 +6,7 @@ pub use language::*; use lazy_static::lazy_static; use regex::Regex; use smol::fs::{self, File}; -use std::{borrow::Cow, env::consts, path::PathBuf, str, sync::Arc}; +use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, str, sync::Arc}; use util::{ResultExt, TryFutureExt}; use super::GithubRelease; @@ -21,7 +21,7 @@ impl LspAdapter for RustLspAdapter { fn fetch_latest_server_version( &self, http: Arc, - ) -> BoxFuture<'static, Result> { + ) -> BoxFuture<'static, Result>> { async move { let release = http .send( @@ -46,27 +46,28 @@ impl LspAdapter for RustLspAdapter { .iter() .find(|asset| asset.name == asset_name) .ok_or_else(|| anyhow!("no release found matching {:?}", asset_name))?; - Ok(LspBinaryVersion { + Ok(Box::new(GitHubLspBinaryVersion { name: release.name, - url: Some(asset.browser_download_url.clone()), - }) + url: asset.browser_download_url.clone(), + }) as Box<_>) } .boxed() } fn fetch_server_binary( &self, - version: LspBinaryVersion, + version: Box, http: Arc, container_dir: PathBuf, ) -> BoxFuture<'static, Result> { async move { + let version = version.downcast::().unwrap(); let destination_path = container_dir.join(format!("rust-analyzer-{}", version.name)); if fs::metadata(&destination_path).await.is_err() { let response = http .send( - surf::RequestBuilder::new(Method::Get, version.url.unwrap()) + surf::RequestBuilder::new(Method::Get, version.url) .middleware(surf::middleware::Redirect::default()) .build(), ) diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 59b7d225f9cb4dc1850e3a42280011dd37d7454d..f7547fdd4e651a03bc53028c591a6aa16b60878a 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -1,57 +1,86 @@ +use anyhow::{anyhow, Context, Result}; +use client::http::HttpClient; +use futures::{future::BoxFuture, FutureExt, StreamExt}; +use language::LspAdapter; +use serde::Deserialize; +use serde_json::json; +use smol::fs; +use std::{any::Any, path::PathBuf, sync::Arc}; +use util::{ResultExt, TryFutureExt}; + pub struct TypeScriptLspAdapter; impl TypeScriptLspAdapter { - const BIN_PATH: &'static str = - "node_modules/vscode-json-languageserver/bin/vscode-json-languageserver"; + const BIN_PATH: &'static str = "node_modules/typescript-language-server/lib/cli.js"; +} + +struct Versions { + typescript_version: String, + server_version: String, } -impl super::LspAdapter for TypeScriptLspAdapter { +impl LspAdapter for TypeScriptLspAdapter { fn name(&self) -> &'static str { "typescript-language-server" } fn server_args(&self) -> &[&str] { - &["--stdio"] + &["--stdio", "--tsserver-path", "node_modules/typescript/lib"] } fn fetch_latest_server_version( &self, _: Arc, - ) -> BoxFuture<'static, Result> { + ) -> BoxFuture<'static, Result>> { async move { #[derive(Deserialize)] struct NpmInfo { versions: Vec, } - let output = smol::process::Command::new("npm") - .args(["info", "vscode-json-languageserver", "--json"]) + let typescript_output = smol::process::Command::new("npm") + .args(["info", "typescript", "--json"]) + .output() + .await?; + if !typescript_output.status.success() { + Err(anyhow!("failed to execute npm info"))?; + } + let mut typescript_info: NpmInfo = serde_json::from_slice(&typescript_output.stdout)?; + + let server_output = smol::process::Command::new("npm") + .args(["info", "typescript-language-server", "--json"]) .output() .await?; - if !output.status.success() { + if !server_output.status.success() { Err(anyhow!("failed to execute npm info"))?; } - let mut info: NpmInfo = serde_json::from_slice(&output.stdout)?; + let mut server_info: NpmInfo = serde_json::from_slice(&server_output.stdout)?; - Ok(LspBinaryVersion { - name: info + Ok(Box::new(Versions { + typescript_version: typescript_info .versions .pop() - .ok_or_else(|| anyhow!("no versions found in npm info"))?, - url: Default::default(), - }) + .ok_or_else(|| anyhow!("no versions found in typescript npm info"))?, + server_version: server_info.versions.pop().ok_or_else(|| { + anyhow!("no versions found in typescript language server npm info") + })?, + }) as Box<_>) } .boxed() } fn fetch_server_binary( &self, - version: LspBinaryVersion, + versions: Box, _: Arc, container_dir: PathBuf, ) -> BoxFuture<'static, Result> { + let versions = versions.downcast::().unwrap(); async move { - let version_dir = container_dir.join(&version.name); + let version_dir = container_dir.join(&format!( + "typescript-{}:server-{}", + versions.typescript_version, versions.server_version + )); fs::create_dir_all(&version_dir) .await .context("failed to create version directory")?; @@ -61,12 +90,16 @@ impl super::LspAdapter for TypeScriptLspAdapter { let output = smol::process::Command::new("npm") .current_dir(&version_dir) .arg("install") - .arg(format!("vscode-json-languageserver@{}", version.name)) + .arg(format!("typescript@{}", versions.typescript_version)) + .arg(format!( + "typescript-language-server@{}", + versions.server_version + )) .output() .await .context("failed to run npm install")?; if !output.status.success() { - Err(anyhow!("failed to install vscode-json-languageserver"))?; + Err(anyhow!("failed to install typescript-language-server"))?; } if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {