From 11f73bfa4e63be14523d8fb78d7e3aa7e1158a2c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 22 Jun 2022 16:58:19 -0700 Subject: [PATCH] Integrate pyright language server --- crates/lsp/src/lsp.rs | 7 ++- crates/zed/src/languages.rs | 3 +- crates/zed/src/languages/python.rs | 97 ++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 crates/zed/src/languages/python.rs diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index b1b97b9455..90a6ff529a 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -251,7 +251,7 @@ impl LanguageServer { let params = InitializeParams { process_id: Default::default(), root_path: Default::default(), - root_uri: Some(root_uri), + root_uri: Some(root_uri.clone()), initialization_options: options, capabilities: ClientCapabilities { workspace: Some(WorkspaceClientCapabilities { @@ -312,7 +312,10 @@ impl LanguageServer { ..Default::default() }, trace: Default::default(), - workspace_folders: Default::default(), + workspace_folders: Some(vec![WorkspaceFolder { + uri: root_uri, + name: Default::default(), + }]), client_info: Default::default(), locale: Default::default(), }; diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index f3c1a83f25..d792660d20 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -7,6 +7,7 @@ mod c; mod go; mod installation; mod json; +mod python; mod rust; mod typescript; @@ -46,7 +47,7 @@ pub fn build_language_registry(login_shell_env_loaded: Task<()>) -> LanguageRegi ( "python", tree_sitter_python::language(), - None, // + Some(Arc::new(python::PythonLspAdapter)), ), ( "rust", diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs new file mode 100644 index 0000000000..84f5eda3f7 --- /dev/null +++ b/crates/zed/src/languages/python.rs @@ -0,0 +1,97 @@ +use super::installation::{npm_install_packages, npm_package_latest_version}; +use anyhow::{anyhow, Context, Result}; +use client::http::HttpClient; +use futures::{future::BoxFuture, FutureExt, StreamExt}; +use language::{LanguageServerName, LspAdapter}; +use smol::fs; +use std::{ + any::Any, + path::{Path, PathBuf}, + sync::Arc, +}; +use util::{ResultExt, TryFutureExt}; + +pub struct PythonLspAdapter; + +impl PythonLspAdapter { + const BIN_PATH: &'static str = "node_modules/pyright/langserver.index.js"; +} + +impl LspAdapter for PythonLspAdapter { + fn name(&self) -> LanguageServerName { + LanguageServerName("pyright".into()) + } + + fn server_args(&self) -> &[&str] { + &["--stdio"] + } + + fn fetch_latest_server_version( + &self, + _: Arc, + ) -> BoxFuture<'static, Result>> { + async move { Ok(Box::new(npm_package_latest_version("pyright").await?) as Box<_>) }.boxed() + } + + fn fetch_server_binary( + &self, + version: Box, + _: Arc, + container_dir: Arc, + ) -> BoxFuture<'static, Result> { + let version = version.downcast::().unwrap(); + async move { + let version_dir = container_dir.join(version.as_str()); + fs::create_dir_all(&version_dir) + .await + .context("failed to create version directory")?; + let binary_path = version_dir.join(Self::BIN_PATH); + + if fs::metadata(&binary_path).await.is_err() { + npm_install_packages([("pyright", version.as_str())], &version_dir).await?; + + if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if entry_path.as_path() != version_dir { + fs::remove_dir_all(&entry_path).await.log_err(); + } + } + } + } + } + + Ok(binary_path) + } + .boxed() + } + + fn cached_server_binary( + &self, + container_dir: Arc, + ) -> BoxFuture<'static, Option> { + async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_dir() { + last_version_dir = Some(entry.path()); + } + } + let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; + let bin_path = last_version_dir.join(Self::BIN_PATH); + if bin_path.exists() { + Ok(bin_path) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + } + .log_err() + .boxed() + } +}