Add typescript language server
Currently not tested for tsx files Co-authored-by: Max Brunsfeld <max@zed.dev>
This commit is contained in:
parent
d466768eed
commit
0e1d371a67
7 changed files with 90 additions and 53 deletions
|
@ -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<dyn HttpClient>,
|
||||
) -> BoxFuture<'static, Result<LspBinaryVersion>> {
|
||||
) -> BoxFuture<'static, Result<Box<dyn 'static + Send + Any>>> {
|
||||
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<dyn 'static + Send + Any>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
container_dir: PathBuf,
|
||||
) -> BoxFuture<'static, Result<PathBuf>> {
|
||||
let version = version.downcast::<GitHubLspBinaryVersion>().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(),
|
||||
)
|
||||
|
|
|
@ -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<dyn HttpClient>,
|
||||
) -> BoxFuture<'static, Result<LspBinaryVersion>> {
|
||||
) -> BoxFuture<'static, Result<Box<dyn 'static + Any + Send>>> {
|
||||
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<dyn 'static + Send + Any>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
container_dir: PathBuf,
|
||||
) -> BoxFuture<'static, Result<PathBuf>> {
|
||||
let version = version.downcast::<String>().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")?;
|
||||
|
|
|
@ -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<dyn HttpClient>,
|
||||
) -> BoxFuture<'static, Result<LspBinaryVersion>> {
|
||||
) -> BoxFuture<'static, Result<Box<dyn 'static + Send + Any>>> {
|
||||
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<dyn 'static + Send + Any>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
container_dir: PathBuf,
|
||||
) -> BoxFuture<'static, Result<PathBuf>> {
|
||||
async move {
|
||||
let version = version.downcast::<GitHubLspBinaryVersion>().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(),
|
||||
)
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
impl super::LspAdapter for TypeScriptLspAdapter {
|
||||
struct Versions {
|
||||
typescript_version: String,
|
||||
server_version: String,
|
||||
}
|
||||
|
||||
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<dyn HttpClient>,
|
||||
) -> BoxFuture<'static, Result<LspBinaryVersion>> {
|
||||
) -> BoxFuture<'static, Result<Box<dyn 'static + Send + Any>>> {
|
||||
async move {
|
||||
#[derive(Deserialize)]
|
||||
struct NpmInfo {
|
||||
versions: Vec<String>,
|
||||
}
|
||||
|
||||
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 !output.status.success() {
|
||||
if !typescript_output.status.success() {
|
||||
Err(anyhow!("failed to execute npm info"))?;
|
||||
}
|
||||
let mut info: NpmInfo = serde_json::from_slice(&output.stdout)?;
|
||||
let mut typescript_info: NpmInfo = serde_json::from_slice(&typescript_output.stdout)?;
|
||||
|
||||
Ok(LspBinaryVersion {
|
||||
name: info
|
||||
let server_output = smol::process::Command::new("npm")
|
||||
.args(["info", "typescript-language-server", "--json"])
|
||||
.output()
|
||||
.await?;
|
||||
if !server_output.status.success() {
|
||||
Err(anyhow!("failed to execute npm info"))?;
|
||||
}
|
||||
let mut server_info: NpmInfo = serde_json::from_slice(&server_output.stdout)?;
|
||||
|
||||
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<dyn 'static + Send + Any>,
|
||||
_: Arc<dyn HttpClient>,
|
||||
container_dir: PathBuf,
|
||||
) -> BoxFuture<'static, Result<PathBuf>> {
|
||||
let versions = versions.downcast::<Versions>().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() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue