Add typescript language server

Currently not tested for tsx files

Co-authored-by: Max Brunsfeld <max@zed.dev>
This commit is contained in:
Keith Simmons 2022-03-29 13:42:21 -07:00
parent d466768eed
commit 0e1d371a67
7 changed files with 90 additions and 53 deletions

View file

@ -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(),
)

View file

@ -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")?;

View file

@ -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(),
)

View file

@ -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() {