diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index 801c8f7172..a8874e8d9e 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -203,20 +203,17 @@ impl ActivityIndicator { } // Show any language server installation info. + let mut validating = SmallVec::<[_; 3]>::new(); let mut downloading = SmallVec::<[_; 3]>::new(); let mut checking_for_update = SmallVec::<[_; 3]>::new(); let mut failed = SmallVec::<[_; 3]>::new(); for status in &self.statuses { + let name = status.name.clone(); match status.status { - LanguageServerBinaryStatus::CheckingForUpdate => { - checking_for_update.push(status.name.clone()); - } - LanguageServerBinaryStatus::Downloading => { - downloading.push(status.name.clone()); - } - LanguageServerBinaryStatus::Failed { .. } => { - failed.push(status.name.clone()); - } + LanguageServerBinaryStatus::Validating => validating.push(name), + LanguageServerBinaryStatus::CheckingForUpdate => checking_for_update.push(name), + LanguageServerBinaryStatus::Downloading => downloading.push(name), + LanguageServerBinaryStatus::Failed { .. } => failed.push(name), LanguageServerBinaryStatus::Downloaded | LanguageServerBinaryStatus::Cached => {} } } @@ -245,7 +242,7 @@ impl ActivityIndicator { ), on_click: None, }; - } else if !failed.is_empty() { + } else if !failed.is_empty() || !validating.is_empty() { return Content { icon: Some(WARNING_ICON), message: format!( diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index e73424f0cd..b72f00f361 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -15,7 +15,7 @@ use language::{ ToPointUtf16, }; use log::{debug, error}; -use lsp::{LanguageServer, LanguageServerId}; +use lsp::{LanguageServer, LanguageServerBinaries, LanguageServerBinary, LanguageServerId}; use node_runtime::NodeRuntime; use request::{LogMessage, StatusNotification}; use settings::SettingsStore; @@ -361,11 +361,18 @@ impl Copilot { let start_language_server = async { let server_path = get_copilot_lsp(http).await?; let node_path = node_runtime.binary_path().await?; - let arguments: &[OsString] = &[server_path.into(), "--stdio".into()]; + let arguments: Vec = vec![server_path.into(), "--stdio".into()]; + let binary = LanguageServerBinary { + path: node_path, + arguments, + }; + let binaries = LanguageServerBinaries { + binary: binary.clone(), + installation_test_binary: binary, + }; let server = LanguageServer::new( LanguageServerId(0), - &node_path, - arguments, + binaries, Path::new("/"), None, cx.clone(), diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index fa85d0e21f..2ed896e03f 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -20,7 +20,7 @@ use futures::{ use gpui::{executor::Background, AppContext, Task}; use highlight_map::HighlightMap; use lazy_static::lazy_static; -use lsp::CodeActionKind; +use lsp::{CodeActionKind, LanguageServer, LanguageServerBinaries, LanguageServerBinary}; use parking_lot::{Mutex, RwLock}; use postage::watch; use regex::Regex; @@ -30,17 +30,18 @@ use std::{ any::Any, borrow::Cow, cell::RefCell, - ffi::OsString, fmt::Debug, hash::Hash, mem, ops::{Not, Range}, path::{Path, PathBuf}, + process::Stdio, str, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, }, + time::Duration, }; use syntax_map::SyntaxSnapshot; use theme::{SyntaxTheme, Theme}; @@ -86,12 +87,6 @@ pub trait ToLspPosition { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct LanguageServerName(pub Arc); -#[derive(Debug, Clone, Deserialize)] -pub struct LanguageServerBinary { - pub path: PathBuf, - pub arguments: Vec, -} - /// Represents a Language Server, with certain cached sync properties. /// Uses [`LspAdapter`] under the hood, but calls all 'static' methods /// once at startup, and caches the results. @@ -148,6 +143,10 @@ impl CachedLspAdapter { self.adapter.cached_server_binary(container_dir).await } + async fn installation_test_binary(&self, container_dir: PathBuf) -> LanguageServerBinary { + self.adapter.installation_test_binary(container_dir) + } + pub fn code_action_kinds(&self) -> Option> { self.adapter.code_action_kinds() } @@ -205,6 +204,10 @@ pub trait LspAdapter: 'static + Send + Sync { async fn cached_server_binary(&self, container_dir: PathBuf) -> Option; + fn installation_test_binary(&self, _container_dir: PathBuf) -> LanguageServerBinary { + unimplemented!(); + } + async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} async fn process_completion(&self, _: &mut lsp::CompletionItem) {} @@ -485,6 +488,7 @@ struct BracketConfig { #[derive(Clone)] pub enum LanguageServerBinaryStatus { + Validating, CheckingForUpdate, Downloading, Downloaded, @@ -515,7 +519,7 @@ pub struct LanguageRegistry { lsp_binary_paths: Mutex< HashMap< LanguageServerName, - Shared>>>, + Shared>>>, >, >, executor: Option>, @@ -891,8 +895,7 @@ impl LanguageRegistry { let binary = entry.clone().map_err(|e| anyhow!(e)).await?; let server = lsp::LanguageServer::new( server_id, - &binary.path, - &binary.arguments, + binary, &root_path, adapter.code_action_kinds(), cx, @@ -909,6 +912,56 @@ impl LanguageRegistry { ) -> async_broadcast::Receiver<(Arc, LanguageServerBinaryStatus)> { self.lsp_binary_statuses_rx.clone() } + + pub async fn check_errored_lsp_installation( + &self, + language_server: Arc, + cx: &mut AppContext, + ) { + // Check if child process is running + if !language_server.is_dead() { + return; + } + + // If not, get check binary + let test_binary = match language_server.test_installation_binary() { + Some(test_binary) => test_binary.clone(), + None => return, + }; + + // Run + const PROCESS_TIMEOUT: Duration = Duration::from_secs(5); + let mut timeout = cx.background().timer(PROCESS_TIMEOUT).fuse(); + + let mut errored = false; + let result = smol::process::Command::new(&test_binary.path) + .current_dir(&test_binary.path) + .args(test_binary.arguments) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .kill_on_drop(true) + .spawn(); + + if let Ok(mut process) = result { + futures::select! { + _ = process.status().fuse() => {} + _ = timeout => errored = true, + } + } else { + errored = true; + } + + dbg!(errored); + + // If failure clear container dir + + // Prompt binary retrieval + + // Start language server + + // Update project server state + } } impl LanguageRegistryState { @@ -961,7 +1014,7 @@ async fn get_binary( http_client: Arc, download_dir: Arc, statuses: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, -) -> Result { +) -> Result { let container_dir = download_dir.join(adapter.name.0.as_ref()); if !container_dir.exists() { smol::fs::create_dir_all(&container_dir) @@ -979,11 +1032,15 @@ async fn get_binary( .await; if let Err(error) = binary.as_ref() { - if let Some(cached) = adapter.cached_server_binary(container_dir).await { + if let Some(binary) = adapter.cached_server_binary(container_dir.clone()).await { statuses .broadcast((language.clone(), LanguageServerBinaryStatus::Cached)) .await?; - return Ok(cached); + let installation_test_binary = adapter.installation_test_binary(container_dir).await; + return Ok(LanguageServerBinaries { + binary, + installation_test_binary, + }); } else { statuses .broadcast(( @@ -995,6 +1052,7 @@ async fn get_binary( .await?; } } + binary } @@ -1004,7 +1062,7 @@ async fn fetch_latest_binary( http_client: Arc, container_dir: &Path, lsp_binary_statuses_tx: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, -) -> Result { +) -> Result { let container_dir: Arc = container_dir.into(); lsp_binary_statuses_tx .broadcast(( @@ -1012,19 +1070,28 @@ async fn fetch_latest_binary( LanguageServerBinaryStatus::CheckingForUpdate, )) .await?; + let version_info = adapter .fetch_latest_server_version(http_client.clone()) .await?; lsp_binary_statuses_tx .broadcast((language.clone(), LanguageServerBinaryStatus::Downloading)) .await?; + let binary = adapter .fetch_server_binary(version_info, http_client, container_dir.to_path_buf()) .await?; + let installation_test_binary = adapter + .installation_test_binary(container_dir.to_path_buf()) + .await; lsp_binary_statuses_tx .broadcast((language.clone(), LanguageServerBinaryStatus::Downloaded)) .await?; - Ok(binary) + + Ok(LanguageServerBinaries { + binary, + installation_test_binary, + }) } impl Language { diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index f05acbb1a2..94a5096c5f 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -16,6 +16,7 @@ use smol::{ process::{self, Child}, }; use std::{ + ffi::OsString, fmt, future::Future, io::Write, @@ -36,6 +37,18 @@ type NotificationHandler = Box, &str, AsyncAppCon type ResponseHandler = Box)>; type IoHandler = Box; +#[derive(Debug, Clone, Deserialize)] +pub struct LanguageServerBinary { + pub path: PathBuf, + pub arguments: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct LanguageServerBinaries { + pub binary: LanguageServerBinary, + pub installation_test_binary: LanguageServerBinary, +} + pub struct LanguageServer { server_id: LanguageServerId, next_id: AtomicUsize, @@ -51,7 +64,8 @@ pub struct LanguageServer { io_tasks: Mutex>, Task>)>>, output_done_rx: Mutex>, root_path: PathBuf, - _server: Option, + server: Option>, + test_installation_binary: Option, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -119,10 +133,9 @@ struct Error { } impl LanguageServer { - pub fn new>( + pub fn new( server_id: LanguageServerId, - binary_path: &Path, - arguments: &[T], + binaries: LanguageServerBinaries, root_path: &Path, code_action_kinds: Option>, cx: AsyncAppContext, @@ -133,9 +146,9 @@ impl LanguageServer { root_path.parent().unwrap_or_else(|| Path::new("/")) }; - let mut server = process::Command::new(binary_path) + let mut server = process::Command::new(&binaries.binary.path) .current_dir(working_dir) - .args(arguments) + .args(binaries.binary.arguments) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::inherit()) @@ -149,6 +162,7 @@ impl LanguageServer { stdin, stout, Some(server), + Some(binaries.installation_test_binary), root_path, code_action_kinds, cx, @@ -164,7 +178,7 @@ impl LanguageServer { }, ); - if let Some(name) = binary_path.file_name() { + if let Some(name) = binaries.binary.path.file_name() { server.name = name.to_string_lossy().to_string(); } @@ -176,6 +190,7 @@ impl LanguageServer { stdin: Stdin, stdout: Stdout, server: Option, + test_installation_binary: Option, root_path: &Path, code_action_kinds: Option>, cx: AsyncAppContext, @@ -229,10 +244,28 @@ impl LanguageServer { io_tasks: Mutex::new(Some((input_task, output_task))), output_done_rx: Mutex::new(Some(output_done_rx)), root_path: root_path.to_path_buf(), - _server: server, + server: server.map(|server| Mutex::new(server)), + test_installation_binary, } } + pub fn is_dead(&self) -> bool { + let server = match self.server.as_ref() { + Some(server) => server, + None => return false, // Fake server for tests + }; + + match server.lock().try_status() { + Ok(Some(_)) => true, + Ok(None) => false, + Err(_) => true, + } + } + + pub fn test_installation_binary(&self) -> &Option { + &self.test_installation_binary + } + pub fn code_action_kinds(&self) -> Option> { self.code_action_kinds.clone() } @@ -813,6 +846,7 @@ impl LanguageServer { stdin_writer, stdout_reader, None, + None, Path::new("/"), None, cx.clone(), @@ -824,6 +858,7 @@ impl LanguageServer { stdout_writer, stdin_reader, None, + None, Path::new("/"), None, cx, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f78ff19eae..5c9b79434a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -45,7 +45,7 @@ use language::{ use log::error; use lsp::{ DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, - DocumentHighlightKind, LanguageServer, LanguageServerId, + DocumentHighlightKind, LanguageServer, LanguageServerId, OneOf, }; use lsp_command::*; use postage::watch; @@ -65,6 +65,7 @@ use std::{ num::NonZeroU32, ops::Range, path::{Component, Path, PathBuf}, + process::Stdio, rc::Rc, str, sync::{ @@ -277,6 +278,7 @@ pub enum Event { } pub enum LanguageServerState { + Validating(Task>>), Starting(Task>>), Running { language: Arc, @@ -2447,7 +2449,7 @@ impl Project { Err(err) => { log::warn!("Error starting language server {:?}: {}", server_name, err); - // TODO: Prompt installation validity check + // TODO: Prompt installation validity check LSP ERROR None } } @@ -2831,10 +2833,8 @@ impl Project { let mut root_path = None; let server = match server_state { - Some(LanguageServerState::Starting(started_language_server)) => { - started_language_server.await - } - + Some(LanguageServerState::Validating(task)) => task.await, + Some(LanguageServerState::Starting(task)) => task.await, Some(LanguageServerState::Running { server, .. }) => Some(server), None => None, }; @@ -2945,6 +2945,15 @@ impl Project { .detach(); } + fn check_errored_lsp_installation( + &self, + language_server: Arc, + cx: &mut ModelContext, + ) { + self.languages + .check_errored_lsp_installation(language_server, cx); + } + fn on_lsp_progress( &mut self, progress: lsp::ProgressParams, @@ -3716,29 +3725,26 @@ impl Project { tab_size: NonZeroU32, cx: &mut AsyncAppContext, ) -> Result, String)>> { - let text_document = - lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(abs_path).unwrap()); + let uri = lsp::Url::from_file_path(abs_path) + .map_err(|_| anyhow!("failed to convert abs path to uri"))?; + let text_document = lsp::TextDocumentIdentifier::new(uri); let capabilities = &language_server.capabilities(); - let lsp_edits = if capabilities - .document_formatting_provider - .as_ref() - .map_or(false, |provider| *provider != lsp::OneOf::Left(false)) - { + + let formatting_provider = capabilities.document_formatting_provider.as_ref(); + let range_formatting_provider = capabilities.document_range_formatting_provider.as_ref(); + + let result = if !matches!(formatting_provider, Some(OneOf::Left(false))) { language_server .request::(lsp::DocumentFormattingParams { text_document, options: lsp_command::lsp_formatting_options(tab_size.get()), work_done_progress_params: Default::default(), }) - .await? - } else if capabilities - .document_range_formatting_provider - .as_ref() - .map_or(false, |provider| *provider != lsp::OneOf::Left(false)) - { + .await + } else if !matches!(range_formatting_provider, Some(OneOf::Left(false))) { let buffer_start = lsp::Position::new(0, 0); - let buffer_end = - buffer.read_with(cx, |buffer, _| point_to_lsp(buffer.max_point_utf16())); + let buffer_end = buffer.read_with(cx, |b, _| point_to_lsp(b.max_point_utf16())); + language_server .request::(lsp::DocumentRangeFormattingParams { text_document, @@ -3746,9 +3752,27 @@ impl Project { options: lsp_command::lsp_formatting_options(tab_size.get()), work_done_progress_params: Default::default(), }) - .await? + .await } else { - None + Ok(None) + }; + + let lsp_edits = match result { + Ok(lsp_edits) => lsp_edits, + + Err(err) => { + log::warn!( + "Error firing format request to {}: {}", + language_server.name(), + err + ); + + this.update(cx, |this, cx| { + this.check_errored_lsp_installation(language_server.clone(), cx); + }); + + None + } }; if let Some(lsp_edits) = lsp_edits { @@ -3757,7 +3781,7 @@ impl Project { }) .await } else { - Ok(Default::default()) + Ok(Vec::new()) } } @@ -3865,80 +3889,89 @@ impl Project { let mut requests = Vec::new(); for ((worktree_id, _), server_id) in self.language_server_ids.iter() { let worktree_id = *worktree_id; - if let Some(worktree) = self - .worktree_for_id(worktree_id, cx) - .and_then(|worktree| worktree.read(cx).as_local()) - { - if let Some(LanguageServerState::Running { + let worktree_handle = self.worktree_for_id(worktree_id, cx); + let worktree = match worktree_handle.and_then(|tree| tree.read(cx).as_local()) { + Some(worktree) => worktree, + None => continue, + }; + let worktree_abs_path = worktree.abs_path().clone(); + + let (adapter, language, server) = match self.language_servers.get(server_id) { + Some(LanguageServerState::Running { adapter, language, server, .. - }) = self.language_servers.get(server_id) - { - let adapter = adapter.clone(); - let language = language.clone(); - let worktree_abs_path = worktree.abs_path().clone(); - requests.push( - server - .request::( - lsp::WorkspaceSymbolParams { - query: query.to_string(), - ..Default::default() - }, - ) - .log_err() - .map(move |response| { - let lsp_symbols = response.flatten().map(|symbol_response| match symbol_response { - lsp::WorkspaceSymbolResponse::Flat(flat_responses) => { - flat_responses.into_iter().map(|lsp_symbol| { - (lsp_symbol.name, lsp_symbol.kind, lsp_symbol.location) - }).collect::>() - } - lsp::WorkspaceSymbolResponse::Nested(nested_responses) => { - nested_responses.into_iter().filter_map(|lsp_symbol| { - let location = match lsp_symbol.location { - lsp::OneOf::Left(location) => location, - lsp::OneOf::Right(_) => { - error!("Unexpected: client capabilities forbid symbol resolutions in workspace.symbol.resolveSupport"); - return None - } - }; - Some((lsp_symbol.name, lsp_symbol.kind, location)) - }).collect::>() - } - }).unwrap_or_default(); + }) => (adapter.clone(), language.clone(), server), - ( - adapter, - language, - worktree_id, - worktree_abs_path, - lsp_symbols, - ) - }), - ); - } - } + _ => continue, + }; + + requests.push( + server + .request::( + lsp::WorkspaceSymbolParams { + query: query.to_string(), + ..Default::default() + }, + ) + .map_ok(move |response| { + let lsp_symbols = response.map(|symbol_response| match symbol_response { + lsp::WorkspaceSymbolResponse::Flat(flat_responses) => { + flat_responses.into_iter().map(|lsp_symbol| { + (lsp_symbol.name, lsp_symbol.kind, lsp_symbol.location) + }).collect::>() + } + lsp::WorkspaceSymbolResponse::Nested(nested_responses) => { + nested_responses.into_iter().filter_map(|lsp_symbol| { + let location = match lsp_symbol.location { + OneOf::Left(location) => location, + OneOf::Right(_) => { + error!("Unexpected: client capabilities forbid symbol resolutions in workspace.symbol.resolveSupport"); + return None + } + }; + Some((lsp_symbol.name, lsp_symbol.kind, location)) + }).collect::>() + } + }).unwrap_or_default(); + + ( + adapter, + language, + worktree_id, + worktree_abs_path, + lsp_symbols, + ) + }), + ); } cx.spawn_weak(|this, cx| async move { let responses = futures::future::join_all(requests).await; - let this = if let Some(this) = this.upgrade(&cx) { - this - } else { - return Ok(Default::default()); + let this = match this.upgrade(&cx) { + Some(this) => this, + None => return Ok(Vec::new()), }; + let symbols = this.read_with(&cx, |this, cx| { let mut symbols = Vec::new(); - for ( - adapter, - adapter_language, - source_worktree_id, - worktree_abs_path, - lsp_symbols, - ) in responses - { + for response in responses { + let ( + adapter, + adapter_language, + source_worktree_id, + worktree_abs_path, + lsp_symbols, + ) = match response { + Ok(response) => response, + + Err(err) => { + // TODO: Prompt installation validity check LSP ERROR + return Vec::new(); + } + }; + symbols.extend(lsp_symbols.into_iter().filter_map( |(symbol_name, symbol_kind, symbol_location)| { let abs_path = symbol_location.uri.to_file_path().ok()?; @@ -3985,8 +4018,10 @@ impl Project { }, )); } + symbols }); + Ok(futures::future::join_all(symbols).await) }) } else if let Some(project_id) = self.remote_id() { @@ -4111,9 +4146,17 @@ impl Project { }; cx.spawn(|this, mut cx| async move { - let resolved_completion = lang_server + let resolved_completion = match lang_server .request::(completion.lsp_completion) - .await?; + .await + { + Ok(resolved_completion) => resolved_completion, + + Err(err) => { + // TODO: LSP ERROR + return Ok(None); + } + }; if let Some(edits) = resolved_completion.additional_text_edits { let edits = this @@ -4232,9 +4275,17 @@ impl Project { .and_then(|d| d.get_mut("range")) { *lsp_range = serde_json::to_value(&range_to_lsp(range)).unwrap(); - action.lsp_action = lang_server + action.lsp_action = match lang_server .request::(action.lsp_action) - .await?; + .await + { + Ok(lsp_action) => lsp_action, + + Err(err) => { + // LSP ERROR + return Err(err); + } + }; } else { let actions = this .update(&mut cx, |this, cx| { @@ -4267,13 +4318,20 @@ impl Project { this.last_workspace_edits_by_language_server .remove(&lang_server.server_id()); }); - lang_server + + let result = lang_server .request::(lsp::ExecuteCommandParams { command: command.command, arguments: command.arguments.unwrap_or_default(), ..Default::default() }) - .await?; + .await; + + if let Err(err) = result { + // TODO: LSP ERROR + return Err(err); + } + return Ok(this.update(&mut cx, |this, _| { this.last_workspace_edits_by_language_server .remove(&lang_server.server_id()) @@ -4433,7 +4491,7 @@ impl Project { uri, version: None, }, - edits: edits.into_iter().map(lsp::OneOf::Left).collect(), + edits: edits.into_iter().map(OneOf::Left).collect(), }) })); } @@ -4503,8 +4561,8 @@ impl Project { let edits = this .update(cx, |this, cx| { let edits = op.edits.into_iter().map(|edit| match edit { - lsp::OneOf::Left(edit) => edit, - lsp::OneOf::Right(edit) => edit.text_edit, + OneOf::Left(edit) => edit, + OneOf::Right(edit) => edit.text_edit, }); this.edits_from_lsp( &buffer_to_edit, @@ -4841,10 +4899,20 @@ impl Project { return Ok(Default::default()); } - let response = language_server - .request::(lsp_params) - .await - .context("lsp request failed")?; + let result = language_server.request::(lsp_params).await; + let response = match result { + Ok(response) => response, + + Err(err) => { + log::warn!( + "Generic lsp request to {} failed: {}", + language_server.name(), + err + ); + return Err(err); + } + }; + request .response_from_lsp( response, @@ -7211,11 +7279,10 @@ impl Entity for Project { .language_servers .drain() .map(|(_, server_state)| async { + use LanguageServerState::*; match server_state { - LanguageServerState::Running { server, .. } => server.shutdown()?.await, - LanguageServerState::Starting(starting_server) => { - starting_server.await?.shutdown()?.await - } + Running { server, .. } => server.shutdown()?.await, + Starting(task) | Validating(task) => task.await?.shutdown()?.await, } }) .collect::>(); diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 71ffacebf3..c8beb86aef 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -118,14 +118,15 @@ pub fn merge_non_null_json_value_into(source: serde_json::Value, target: &mut se } } -pub trait ResultExt { +pub trait ResultExt { type Ok; fn log_err(self) -> Option; fn warn_on_err(self) -> Option; + fn inspect_error(self, func: impl FnOnce(&E)) -> Self; } -impl ResultExt for Result +impl ResultExt for Result where E: std::fmt::Debug, { @@ -152,6 +153,15 @@ where } } } + + /// https://doc.rust-lang.org/std/result/enum.Result.html#method.inspect_err + fn inspect_error(self, func: impl FnOnce(&E)) -> Self { + if let Err(err) = &self { + func(err); + } + + self + } } pub trait TryFutureExt { diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 7e4ddcef19..bfb25f55a0 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -2,6 +2,7 @@ use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use futures::StreamExt; pub use language::*; +use lsp::LanguageServerBinary; use smol::fs::{self, File}; use std::{any::Any, path::PathBuf, sync::Arc}; use util::fs::remove_matching; diff --git a/crates/zed/src/languages/elixir.rs b/crates/zed/src/languages/elixir.rs index 2939a0fa5f..8d6ece28c7 100644 --- a/crates/zed/src/languages/elixir.rs +++ b/crates/zed/src/languages/elixir.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use futures::StreamExt; pub use language::*; -use lsp::{CompletionItemKind, SymbolKind}; +use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind}; use smol::fs::{self, File}; use std::{any::Any, path::PathBuf, sync::Arc}; use util::fs::remove_matching; diff --git a/crates/zed/src/languages/go.rs b/crates/zed/src/languages/go.rs index ed24abb45c..9ffd88e53a 100644 --- a/crates/zed/src/languages/go.rs +++ b/crates/zed/src/languages/go.rs @@ -3,6 +3,7 @@ use async_trait::async_trait; use futures::StreamExt; pub use language::*; use lazy_static::lazy_static; +use lsp::LanguageServerBinary; use regex::Regex; use smol::{fs, process}; use std::ffi::{OsStr, OsString}; diff --git a/crates/zed/src/languages/html.rs b/crates/zed/src/languages/html.rs index 68f780c3af..3d5adb88ba 100644 --- a/crates/zed/src/languages/html.rs +++ b/crates/zed/src/languages/html.rs @@ -1,7 +1,8 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use futures::StreamExt; -use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; +use language::{LanguageServerName, LspAdapter}; +use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use serde_json::json; use smol::fs; diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index e1f3da9e02..f6ca3f403f 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -3,7 +3,8 @@ use async_trait::async_trait; use collections::HashMap; use futures::{future::BoxFuture, FutureExt, StreamExt}; use gpui::AppContext; -use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter}; +use language::{LanguageRegistry, LanguageServerName, LspAdapter}; +use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use serde_json::json; use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore}; diff --git a/crates/zed/src/languages/language_plugin.rs b/crates/zed/src/languages/language_plugin.rs index 9b82713d08..4ff4816ae3 100644 --- a/crates/zed/src/languages/language_plugin.rs +++ b/crates/zed/src/languages/language_plugin.rs @@ -3,7 +3,8 @@ use async_trait::async_trait; use collections::HashMap; use futures::lock::Mutex; use gpui::executor::Background; -use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; +use language::{LanguageServerName, LspAdapter}; +use lsp::LanguageServerBinary; use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn}; use std::{any::Any, path::PathBuf, sync::Arc}; use util::http::HttpClient; diff --git a/crates/zed/src/languages/lua.rs b/crates/zed/src/languages/lua.rs index f204eb2555..bb9070c1a2 100644 --- a/crates/zed/src/languages/lua.rs +++ b/crates/zed/src/languages/lua.rs @@ -3,7 +3,8 @@ use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; use async_trait::async_trait; use futures::{io::BufReader, StreamExt}; -use language::{LanguageServerBinary, LanguageServerName}; +use language::LanguageServerName; +use lsp::LanguageServerBinary; use smol::fs; use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc}; use util::{async_iife, github::latest_github_release, http::HttpClient, ResultExt}; diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index 7aaddf5fe8..6dd17660c4 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -1,7 +1,8 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use futures::StreamExt; -use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; +use language::{LanguageServerName, LspAdapter}; +use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use smol::fs; use std::{ diff --git a/crates/zed/src/languages/ruby.rs b/crates/zed/src/languages/ruby.rs index d387f815f0..189641bec5 100644 --- a/crates/zed/src/languages/ruby.rs +++ b/crates/zed/src/languages/ruby.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; -use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; +use language::{LanguageServerName, LspAdapter}; +use lsp::LanguageServerBinary; use std::{any::Any, path::PathBuf, sync::Arc}; use util::http::HttpClient; diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 15700ec80a..67a38d1355 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -4,6 +4,7 @@ use async_trait::async_trait; use futures::{io::BufReader, StreamExt}; pub use language::*; use lazy_static::lazy_static; +use lsp::LanguageServerBinary; use regex::Regex; use smol::fs::{self, File}; use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, str, sync::Arc}; diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 7d2d580857..dd2ac22348 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -4,8 +4,8 @@ use async_tar::Archive; use async_trait::async_trait; use futures::{future::BoxFuture, FutureExt}; use gpui::AppContext; -use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; -use lsp::CodeActionKind; +use language::{LanguageServerName, LspAdapter}; +use lsp::{CodeActionKind, LanguageServerBinary}; use node_runtime::NodeRuntime; use serde_json::{json, Value}; use smol::{fs, io::BufReader, stream::StreamExt}; diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index 7f87a7caed..48ce76721d 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -2,9 +2,8 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use futures::{future::BoxFuture, FutureExt, StreamExt}; use gpui::AppContext; -use language::{ - language_settings::all_language_settings, LanguageServerBinary, LanguageServerName, LspAdapter, -}; +use language::{language_settings::all_language_settings, LanguageServerName, LspAdapter}; +use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use serde_json::Value; use smol::fs; diff --git a/styles/tsconfig.json b/styles/tsconfig.json index 6d24728a0a..fe9682b969 100644 --- a/styles/tsconfig.json +++ b/styles/tsconfig.json @@ -1,4 +1,5 @@ { + // "noErrorTruncation": true, "compilerOptions": { "target": "es2015", "module": "commonjs",