Many steps toward validating and reinstalling server after failure

This commit is contained in:
Julia 2023-06-12 14:25:45 -04:00
parent ec0409a3d1
commit bca625a197
19 changed files with 347 additions and 155 deletions

View file

@ -203,20 +203,17 @@ impl ActivityIndicator {
} }
// Show any language server installation info. // Show any language server installation info.
let mut validating = SmallVec::<[_; 3]>::new();
let mut downloading = SmallVec::<[_; 3]>::new(); let mut downloading = SmallVec::<[_; 3]>::new();
let mut checking_for_update = SmallVec::<[_; 3]>::new(); let mut checking_for_update = SmallVec::<[_; 3]>::new();
let mut failed = SmallVec::<[_; 3]>::new(); let mut failed = SmallVec::<[_; 3]>::new();
for status in &self.statuses { for status in &self.statuses {
let name = status.name.clone();
match status.status { match status.status {
LanguageServerBinaryStatus::CheckingForUpdate => { LanguageServerBinaryStatus::Validating => validating.push(name),
checking_for_update.push(status.name.clone()); LanguageServerBinaryStatus::CheckingForUpdate => checking_for_update.push(name),
} LanguageServerBinaryStatus::Downloading => downloading.push(name),
LanguageServerBinaryStatus::Downloading => { LanguageServerBinaryStatus::Failed { .. } => failed.push(name),
downloading.push(status.name.clone());
}
LanguageServerBinaryStatus::Failed { .. } => {
failed.push(status.name.clone());
}
LanguageServerBinaryStatus::Downloaded | LanguageServerBinaryStatus::Cached => {} LanguageServerBinaryStatus::Downloaded | LanguageServerBinaryStatus::Cached => {}
} }
} }
@ -245,7 +242,7 @@ impl ActivityIndicator {
), ),
on_click: None, on_click: None,
}; };
} else if !failed.is_empty() { } else if !failed.is_empty() || !validating.is_empty() {
return Content { return Content {
icon: Some(WARNING_ICON), icon: Some(WARNING_ICON),
message: format!( message: format!(

View file

@ -15,7 +15,7 @@ use language::{
ToPointUtf16, ToPointUtf16,
}; };
use log::{debug, error}; use log::{debug, error};
use lsp::{LanguageServer, LanguageServerId}; use lsp::{LanguageServer, LanguageServerBinaries, LanguageServerBinary, LanguageServerId};
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use request::{LogMessage, StatusNotification}; use request::{LogMessage, StatusNotification};
use settings::SettingsStore; use settings::SettingsStore;
@ -361,11 +361,18 @@ impl Copilot {
let start_language_server = async { let start_language_server = async {
let server_path = get_copilot_lsp(http).await?; let server_path = get_copilot_lsp(http).await?;
let node_path = node_runtime.binary_path().await?; let node_path = node_runtime.binary_path().await?;
let arguments: &[OsString] = &[server_path.into(), "--stdio".into()]; let arguments: Vec<OsString> = 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( let server = LanguageServer::new(
LanguageServerId(0), LanguageServerId(0),
&node_path, binaries,
arguments,
Path::new("/"), Path::new("/"),
None, None,
cx.clone(), cx.clone(),

View file

@ -20,7 +20,7 @@ use futures::{
use gpui::{executor::Background, AppContext, Task}; use gpui::{executor::Background, AppContext, Task};
use highlight_map::HighlightMap; use highlight_map::HighlightMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use lsp::CodeActionKind; use lsp::{CodeActionKind, LanguageServer, LanguageServerBinaries, LanguageServerBinary};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use postage::watch; use postage::watch;
use regex::Regex; use regex::Regex;
@ -30,17 +30,18 @@ use std::{
any::Any, any::Any,
borrow::Cow, borrow::Cow,
cell::RefCell, cell::RefCell,
ffi::OsString,
fmt::Debug, fmt::Debug,
hash::Hash, hash::Hash,
mem, mem,
ops::{Not, Range}, ops::{Not, Range},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Stdio,
str, str,
sync::{ sync::{
atomic::{AtomicUsize, Ordering::SeqCst}, atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Arc,
}, },
time::Duration,
}; };
use syntax_map::SyntaxSnapshot; use syntax_map::SyntaxSnapshot;
use theme::{SyntaxTheme, Theme}; use theme::{SyntaxTheme, Theme};
@ -86,12 +87,6 @@ pub trait ToLspPosition {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct LanguageServerName(pub Arc<str>); pub struct LanguageServerName(pub Arc<str>);
#[derive(Debug, Clone, Deserialize)]
pub struct LanguageServerBinary {
pub path: PathBuf,
pub arguments: Vec<OsString>,
}
/// Represents a Language Server, with certain cached sync properties. /// Represents a Language Server, with certain cached sync properties.
/// Uses [`LspAdapter`] under the hood, but calls all 'static' methods /// Uses [`LspAdapter`] under the hood, but calls all 'static' methods
/// once at startup, and caches the results. /// once at startup, and caches the results.
@ -148,6 +143,10 @@ impl CachedLspAdapter {
self.adapter.cached_server_binary(container_dir).await 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<Vec<CodeActionKind>> { pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
self.adapter.code_action_kinds() 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<LanguageServerBinary>; async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<LanguageServerBinary>;
fn installation_test_binary(&self, _container_dir: PathBuf) -> LanguageServerBinary {
unimplemented!();
}
async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
async fn process_completion(&self, _: &mut lsp::CompletionItem) {} async fn process_completion(&self, _: &mut lsp::CompletionItem) {}
@ -485,6 +488,7 @@ struct BracketConfig {
#[derive(Clone)] #[derive(Clone)]
pub enum LanguageServerBinaryStatus { pub enum LanguageServerBinaryStatus {
Validating,
CheckingForUpdate, CheckingForUpdate,
Downloading, Downloading,
Downloaded, Downloaded,
@ -515,7 +519,7 @@ pub struct LanguageRegistry {
lsp_binary_paths: Mutex< lsp_binary_paths: Mutex<
HashMap< HashMap<
LanguageServerName, LanguageServerName,
Shared<BoxFuture<'static, Result<LanguageServerBinary, Arc<anyhow::Error>>>>, Shared<BoxFuture<'static, Result<LanguageServerBinaries, Arc<anyhow::Error>>>>,
>, >,
>, >,
executor: Option<Arc<Background>>, executor: Option<Arc<Background>>,
@ -891,8 +895,7 @@ impl LanguageRegistry {
let binary = entry.clone().map_err(|e| anyhow!(e)).await?; let binary = entry.clone().map_err(|e| anyhow!(e)).await?;
let server = lsp::LanguageServer::new( let server = lsp::LanguageServer::new(
server_id, server_id,
&binary.path, binary,
&binary.arguments,
&root_path, &root_path,
adapter.code_action_kinds(), adapter.code_action_kinds(),
cx, cx,
@ -909,6 +912,56 @@ impl LanguageRegistry {
) -> async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)> { ) -> async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)> {
self.lsp_binary_statuses_rx.clone() self.lsp_binary_statuses_rx.clone()
} }
pub async fn check_errored_lsp_installation(
&self,
language_server: Arc<LanguageServer>,
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 { impl LanguageRegistryState {
@ -961,7 +1014,7 @@ async fn get_binary(
http_client: Arc<dyn HttpClient>, http_client: Arc<dyn HttpClient>,
download_dir: Arc<Path>, download_dir: Arc<Path>,
statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>, statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
) -> Result<LanguageServerBinary> { ) -> Result<LanguageServerBinaries> {
let container_dir = download_dir.join(adapter.name.0.as_ref()); let container_dir = download_dir.join(adapter.name.0.as_ref());
if !container_dir.exists() { if !container_dir.exists() {
smol::fs::create_dir_all(&container_dir) smol::fs::create_dir_all(&container_dir)
@ -979,11 +1032,15 @@ async fn get_binary(
.await; .await;
if let Err(error) = binary.as_ref() { 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 statuses
.broadcast((language.clone(), LanguageServerBinaryStatus::Cached)) .broadcast((language.clone(), LanguageServerBinaryStatus::Cached))
.await?; .await?;
return Ok(cached); let installation_test_binary = adapter.installation_test_binary(container_dir).await;
return Ok(LanguageServerBinaries {
binary,
installation_test_binary,
});
} else { } else {
statuses statuses
.broadcast(( .broadcast((
@ -995,6 +1052,7 @@ async fn get_binary(
.await?; .await?;
} }
} }
binary binary
} }
@ -1004,7 +1062,7 @@ async fn fetch_latest_binary(
http_client: Arc<dyn HttpClient>, http_client: Arc<dyn HttpClient>,
container_dir: &Path, container_dir: &Path,
lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>, lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
) -> Result<LanguageServerBinary> { ) -> Result<LanguageServerBinaries> {
let container_dir: Arc<Path> = container_dir.into(); let container_dir: Arc<Path> = container_dir.into();
lsp_binary_statuses_tx lsp_binary_statuses_tx
.broadcast(( .broadcast((
@ -1012,19 +1070,28 @@ async fn fetch_latest_binary(
LanguageServerBinaryStatus::CheckingForUpdate, LanguageServerBinaryStatus::CheckingForUpdate,
)) ))
.await?; .await?;
let version_info = adapter let version_info = adapter
.fetch_latest_server_version(http_client.clone()) .fetch_latest_server_version(http_client.clone())
.await?; .await?;
lsp_binary_statuses_tx lsp_binary_statuses_tx
.broadcast((language.clone(), LanguageServerBinaryStatus::Downloading)) .broadcast((language.clone(), LanguageServerBinaryStatus::Downloading))
.await?; .await?;
let binary = adapter let binary = adapter
.fetch_server_binary(version_info, http_client, container_dir.to_path_buf()) .fetch_server_binary(version_info, http_client, container_dir.to_path_buf())
.await?; .await?;
let installation_test_binary = adapter
.installation_test_binary(container_dir.to_path_buf())
.await;
lsp_binary_statuses_tx lsp_binary_statuses_tx
.broadcast((language.clone(), LanguageServerBinaryStatus::Downloaded)) .broadcast((language.clone(), LanguageServerBinaryStatus::Downloaded))
.await?; .await?;
Ok(binary)
Ok(LanguageServerBinaries {
binary,
installation_test_binary,
})
} }
impl Language { impl Language {

View file

@ -16,6 +16,7 @@ use smol::{
process::{self, Child}, process::{self, Child},
}; };
use std::{ use std::{
ffi::OsString,
fmt, fmt,
future::Future, future::Future,
io::Write, io::Write,
@ -36,6 +37,18 @@ type NotificationHandler = Box<dyn Send + FnMut(Option<usize>, &str, AsyncAppCon
type ResponseHandler = Box<dyn Send + FnOnce(Result<&str, Error>)>; type ResponseHandler = Box<dyn Send + FnOnce(Result<&str, Error>)>;
type IoHandler = Box<dyn Send + FnMut(bool, &str)>; type IoHandler = Box<dyn Send + FnMut(bool, &str)>;
#[derive(Debug, Clone, Deserialize)]
pub struct LanguageServerBinary {
pub path: PathBuf,
pub arguments: Vec<OsString>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct LanguageServerBinaries {
pub binary: LanguageServerBinary,
pub installation_test_binary: LanguageServerBinary,
}
pub struct LanguageServer { pub struct LanguageServer {
server_id: LanguageServerId, server_id: LanguageServerId,
next_id: AtomicUsize, next_id: AtomicUsize,
@ -51,7 +64,8 @@ pub struct LanguageServer {
io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>, io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
output_done_rx: Mutex<Option<barrier::Receiver>>, output_done_rx: Mutex<Option<barrier::Receiver>>,
root_path: PathBuf, root_path: PathBuf,
_server: Option<Child>, server: Option<Mutex<Child>>,
test_installation_binary: Option<LanguageServerBinary>,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -119,10 +133,9 @@ struct Error {
} }
impl LanguageServer { impl LanguageServer {
pub fn new<T: AsRef<std::ffi::OsStr>>( pub fn new(
server_id: LanguageServerId, server_id: LanguageServerId,
binary_path: &Path, binaries: LanguageServerBinaries,
arguments: &[T],
root_path: &Path, root_path: &Path,
code_action_kinds: Option<Vec<CodeActionKind>>, code_action_kinds: Option<Vec<CodeActionKind>>,
cx: AsyncAppContext, cx: AsyncAppContext,
@ -133,9 +146,9 @@ impl LanguageServer {
root_path.parent().unwrap_or_else(|| Path::new("/")) 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) .current_dir(working_dir)
.args(arguments) .args(binaries.binary.arguments)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::inherit()) .stderr(Stdio::inherit())
@ -149,6 +162,7 @@ impl LanguageServer {
stdin, stdin,
stout, stout,
Some(server), Some(server),
Some(binaries.installation_test_binary),
root_path, root_path,
code_action_kinds, code_action_kinds,
cx, 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(); server.name = name.to_string_lossy().to_string();
} }
@ -176,6 +190,7 @@ impl LanguageServer {
stdin: Stdin, stdin: Stdin,
stdout: Stdout, stdout: Stdout,
server: Option<Child>, server: Option<Child>,
test_installation_binary: Option<LanguageServerBinary>,
root_path: &Path, root_path: &Path,
code_action_kinds: Option<Vec<CodeActionKind>>, code_action_kinds: Option<Vec<CodeActionKind>>,
cx: AsyncAppContext, cx: AsyncAppContext,
@ -229,10 +244,28 @@ impl LanguageServer {
io_tasks: Mutex::new(Some((input_task, output_task))), io_tasks: Mutex::new(Some((input_task, output_task))),
output_done_rx: Mutex::new(Some(output_done_rx)), output_done_rx: Mutex::new(Some(output_done_rx)),
root_path: root_path.to_path_buf(), 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<LanguageServerBinary> {
&self.test_installation_binary
}
pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> { pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
self.code_action_kinds.clone() self.code_action_kinds.clone()
} }
@ -813,6 +846,7 @@ impl LanguageServer {
stdin_writer, stdin_writer,
stdout_reader, stdout_reader,
None, None,
None,
Path::new("/"), Path::new("/"),
None, None,
cx.clone(), cx.clone(),
@ -824,6 +858,7 @@ impl LanguageServer {
stdout_writer, stdout_writer,
stdin_reader, stdin_reader,
None, None,
None,
Path::new("/"), Path::new("/"),
None, None,
cx, cx,

View file

@ -45,7 +45,7 @@ use language::{
use log::error; use log::error;
use lsp::{ use lsp::{
DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
DocumentHighlightKind, LanguageServer, LanguageServerId, DocumentHighlightKind, LanguageServer, LanguageServerId, OneOf,
}; };
use lsp_command::*; use lsp_command::*;
use postage::watch; use postage::watch;
@ -65,6 +65,7 @@ use std::{
num::NonZeroU32, num::NonZeroU32,
ops::Range, ops::Range,
path::{Component, Path, PathBuf}, path::{Component, Path, PathBuf},
process::Stdio,
rc::Rc, rc::Rc,
str, str,
sync::{ sync::{
@ -277,6 +278,7 @@ pub enum Event {
} }
pub enum LanguageServerState { pub enum LanguageServerState {
Validating(Task<Option<Arc<LanguageServer>>>),
Starting(Task<Option<Arc<LanguageServer>>>), Starting(Task<Option<Arc<LanguageServer>>>),
Running { Running {
language: Arc<Language>, language: Arc<Language>,
@ -2447,7 +2449,7 @@ impl Project {
Err(err) => { Err(err) => {
log::warn!("Error starting language server {:?}: {}", server_name, err); log::warn!("Error starting language server {:?}: {}", server_name, err);
// TODO: Prompt installation validity check // TODO: Prompt installation validity check LSP ERROR
None None
} }
} }
@ -2831,10 +2833,8 @@ impl Project {
let mut root_path = None; let mut root_path = None;
let server = match server_state { let server = match server_state {
Some(LanguageServerState::Starting(started_language_server)) => { Some(LanguageServerState::Validating(task)) => task.await,
started_language_server.await Some(LanguageServerState::Starting(task)) => task.await,
}
Some(LanguageServerState::Running { server, .. }) => Some(server), Some(LanguageServerState::Running { server, .. }) => Some(server),
None => None, None => None,
}; };
@ -2945,6 +2945,15 @@ impl Project {
.detach(); .detach();
} }
fn check_errored_lsp_installation(
&self,
language_server: Arc<LanguageServer>,
cx: &mut ModelContext<Self>,
) {
self.languages
.check_errored_lsp_installation(language_server, cx);
}
fn on_lsp_progress( fn on_lsp_progress(
&mut self, &mut self,
progress: lsp::ProgressParams, progress: lsp::ProgressParams,
@ -3716,29 +3725,26 @@ impl Project {
tab_size: NonZeroU32, tab_size: NonZeroU32,
cx: &mut AsyncAppContext, cx: &mut AsyncAppContext,
) -> Result<Vec<(Range<Anchor>, String)>> { ) -> Result<Vec<(Range<Anchor>, String)>> {
let text_document = let uri = lsp::Url::from_file_path(abs_path)
lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(abs_path).unwrap()); .map_err(|_| anyhow!("failed to convert abs path to uri"))?;
let text_document = lsp::TextDocumentIdentifier::new(uri);
let capabilities = &language_server.capabilities(); let capabilities = &language_server.capabilities();
let lsp_edits = if capabilities
.document_formatting_provider let formatting_provider = capabilities.document_formatting_provider.as_ref();
.as_ref() let range_formatting_provider = capabilities.document_range_formatting_provider.as_ref();
.map_or(false, |provider| *provider != lsp::OneOf::Left(false))
{ let result = if !matches!(formatting_provider, Some(OneOf::Left(false))) {
language_server language_server
.request::<lsp::request::Formatting>(lsp::DocumentFormattingParams { .request::<lsp::request::Formatting>(lsp::DocumentFormattingParams {
text_document, text_document,
options: lsp_command::lsp_formatting_options(tab_size.get()), options: lsp_command::lsp_formatting_options(tab_size.get()),
work_done_progress_params: Default::default(), work_done_progress_params: Default::default(),
}) })
.await? .await
} else if capabilities } else if !matches!(range_formatting_provider, Some(OneOf::Left(false))) {
.document_range_formatting_provider
.as_ref()
.map_or(false, |provider| *provider != lsp::OneOf::Left(false))
{
let buffer_start = lsp::Position::new(0, 0); let buffer_start = lsp::Position::new(0, 0);
let buffer_end = let buffer_end = buffer.read_with(cx, |b, _| point_to_lsp(b.max_point_utf16()));
buffer.read_with(cx, |buffer, _| point_to_lsp(buffer.max_point_utf16()));
language_server language_server
.request::<lsp::request::RangeFormatting>(lsp::DocumentRangeFormattingParams { .request::<lsp::request::RangeFormatting>(lsp::DocumentRangeFormattingParams {
text_document, text_document,
@ -3746,9 +3752,27 @@ impl Project {
options: lsp_command::lsp_formatting_options(tab_size.get()), options: lsp_command::lsp_formatting_options(tab_size.get()),
work_done_progress_params: Default::default(), work_done_progress_params: Default::default(),
}) })
.await? .await
} else { } 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 { if let Some(lsp_edits) = lsp_edits {
@ -3757,7 +3781,7 @@ impl Project {
}) })
.await .await
} else { } else {
Ok(Default::default()) Ok(Vec::new())
} }
} }
@ -3865,80 +3889,89 @@ impl Project {
let mut requests = Vec::new(); let mut requests = Vec::new();
for ((worktree_id, _), server_id) in self.language_server_ids.iter() { for ((worktree_id, _), server_id) in self.language_server_ids.iter() {
let worktree_id = *worktree_id; let worktree_id = *worktree_id;
if let Some(worktree) = self let worktree_handle = self.worktree_for_id(worktree_id, cx);
.worktree_for_id(worktree_id, cx) let worktree = match worktree_handle.and_then(|tree| tree.read(cx).as_local()) {
.and_then(|worktree| worktree.read(cx).as_local()) Some(worktree) => worktree,
{ None => continue,
if let Some(LanguageServerState::Running { };
let worktree_abs_path = worktree.abs_path().clone();
let (adapter, language, server) = match self.language_servers.get(server_id) {
Some(LanguageServerState::Running {
adapter, adapter,
language, language,
server, server,
.. ..
}) = self.language_servers.get(server_id) }) => (adapter.clone(), language.clone(), server),
{
let adapter = adapter.clone();
let language = language.clone();
let worktree_abs_path = worktree.abs_path().clone();
requests.push(
server
.request::<lsp::request::WorkspaceSymbolRequest>(
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::<Vec<_>>()
}
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::<Vec<_>>()
}
}).unwrap_or_default();
( _ => continue,
adapter, };
language,
worktree_id, requests.push(
worktree_abs_path, server
lsp_symbols, .request::<lsp::request::WorkspaceSymbolRequest>(
) 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::<Vec<_>>()
}
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::<Vec<_>>()
}
}).unwrap_or_default();
(
adapter,
language,
worktree_id,
worktree_abs_path,
lsp_symbols,
)
}),
);
} }
cx.spawn_weak(|this, cx| async move { cx.spawn_weak(|this, cx| async move {
let responses = futures::future::join_all(requests).await; let responses = futures::future::join_all(requests).await;
let this = if let Some(this) = this.upgrade(&cx) { let this = match this.upgrade(&cx) {
this Some(this) => this,
} else { None => return Ok(Vec::new()),
return Ok(Default::default());
}; };
let symbols = this.read_with(&cx, |this, cx| { let symbols = this.read_with(&cx, |this, cx| {
let mut symbols = Vec::new(); let mut symbols = Vec::new();
for ( for response in responses {
adapter, let (
adapter_language, adapter,
source_worktree_id, adapter_language,
worktree_abs_path, source_worktree_id,
lsp_symbols, worktree_abs_path,
) in responses 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( symbols.extend(lsp_symbols.into_iter().filter_map(
|(symbol_name, symbol_kind, symbol_location)| { |(symbol_name, symbol_kind, symbol_location)| {
let abs_path = symbol_location.uri.to_file_path().ok()?; let abs_path = symbol_location.uri.to_file_path().ok()?;
@ -3985,8 +4018,10 @@ impl Project {
}, },
)); ));
} }
symbols symbols
}); });
Ok(futures::future::join_all(symbols).await) Ok(futures::future::join_all(symbols).await)
}) })
} else if let Some(project_id) = self.remote_id() { } else if let Some(project_id) = self.remote_id() {
@ -4111,9 +4146,17 @@ impl Project {
}; };
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
let resolved_completion = lang_server let resolved_completion = match lang_server
.request::<lsp::request::ResolveCompletionItem>(completion.lsp_completion) .request::<lsp::request::ResolveCompletionItem>(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 { if let Some(edits) = resolved_completion.additional_text_edits {
let edits = this let edits = this
@ -4232,9 +4275,17 @@ impl Project {
.and_then(|d| d.get_mut("range")) .and_then(|d| d.get_mut("range"))
{ {
*lsp_range = serde_json::to_value(&range_to_lsp(range)).unwrap(); *lsp_range = serde_json::to_value(&range_to_lsp(range)).unwrap();
action.lsp_action = lang_server action.lsp_action = match lang_server
.request::<lsp::request::CodeActionResolveRequest>(action.lsp_action) .request::<lsp::request::CodeActionResolveRequest>(action.lsp_action)
.await?; .await
{
Ok(lsp_action) => lsp_action,
Err(err) => {
// LSP ERROR
return Err(err);
}
};
} else { } else {
let actions = this let actions = this
.update(&mut cx, |this, cx| { .update(&mut cx, |this, cx| {
@ -4267,13 +4318,20 @@ impl Project {
this.last_workspace_edits_by_language_server this.last_workspace_edits_by_language_server
.remove(&lang_server.server_id()); .remove(&lang_server.server_id());
}); });
lang_server
let result = lang_server
.request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams { .request::<lsp::request::ExecuteCommand>(lsp::ExecuteCommandParams {
command: command.command, command: command.command,
arguments: command.arguments.unwrap_or_default(), arguments: command.arguments.unwrap_or_default(),
..Default::default() ..Default::default()
}) })
.await?; .await;
if let Err(err) = result {
// TODO: LSP ERROR
return Err(err);
}
return Ok(this.update(&mut cx, |this, _| { return Ok(this.update(&mut cx, |this, _| {
this.last_workspace_edits_by_language_server this.last_workspace_edits_by_language_server
.remove(&lang_server.server_id()) .remove(&lang_server.server_id())
@ -4433,7 +4491,7 @@ impl Project {
uri, uri,
version: None, 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 let edits = this
.update(cx, |this, cx| { .update(cx, |this, cx| {
let edits = op.edits.into_iter().map(|edit| match edit { let edits = op.edits.into_iter().map(|edit| match edit {
lsp::OneOf::Left(edit) => edit, OneOf::Left(edit) => edit,
lsp::OneOf::Right(edit) => edit.text_edit, OneOf::Right(edit) => edit.text_edit,
}); });
this.edits_from_lsp( this.edits_from_lsp(
&buffer_to_edit, &buffer_to_edit,
@ -4841,10 +4899,20 @@ impl Project {
return Ok(Default::default()); return Ok(Default::default());
} }
let response = language_server let result = language_server.request::<R::LspRequest>(lsp_params).await;
.request::<R::LspRequest>(lsp_params) let response = match result {
.await Ok(response) => response,
.context("lsp request failed")?;
Err(err) => {
log::warn!(
"Generic lsp request to {} failed: {}",
language_server.name(),
err
);
return Err(err);
}
};
request request
.response_from_lsp( .response_from_lsp(
response, response,
@ -7211,11 +7279,10 @@ impl Entity for Project {
.language_servers .language_servers
.drain() .drain()
.map(|(_, server_state)| async { .map(|(_, server_state)| async {
use LanguageServerState::*;
match server_state { match server_state {
LanguageServerState::Running { server, .. } => server.shutdown()?.await, Running { server, .. } => server.shutdown()?.await,
LanguageServerState::Starting(starting_server) => { Starting(task) | Validating(task) => task.await?.shutdown()?.await,
starting_server.await?.shutdown()?.await
}
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View file

@ -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<E> {
type Ok; type Ok;
fn log_err(self) -> Option<Self::Ok>; fn log_err(self) -> Option<Self::Ok>;
fn warn_on_err(self) -> Option<Self::Ok>; fn warn_on_err(self) -> Option<Self::Ok>;
fn inspect_error(self, func: impl FnOnce(&E)) -> Self;
} }
impl<T, E> ResultExt for Result<T, E> impl<T, E> ResultExt<E> for Result<T, E>
where where
E: std::fmt::Debug, 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 { pub trait TryFutureExt {

View file

@ -2,6 +2,7 @@ use anyhow::{anyhow, Context, Result};
use async_trait::async_trait; use async_trait::async_trait;
use futures::StreamExt; use futures::StreamExt;
pub use language::*; pub use language::*;
use lsp::LanguageServerBinary;
use smol::fs::{self, File}; use smol::fs::{self, File};
use std::{any::Any, path::PathBuf, sync::Arc}; use std::{any::Any, path::PathBuf, sync::Arc};
use util::fs::remove_matching; use util::fs::remove_matching;

View file

@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result};
use async_trait::async_trait; use async_trait::async_trait;
use futures::StreamExt; use futures::StreamExt;
pub use language::*; pub use language::*;
use lsp::{CompletionItemKind, SymbolKind}; use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind};
use smol::fs::{self, File}; use smol::fs::{self, File};
use std::{any::Any, path::PathBuf, sync::Arc}; use std::{any::Any, path::PathBuf, sync::Arc};
use util::fs::remove_matching; use util::fs::remove_matching;

View file

@ -3,6 +3,7 @@ use async_trait::async_trait;
use futures::StreamExt; use futures::StreamExt;
pub use language::*; pub use language::*;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use lsp::LanguageServerBinary;
use regex::Regex; use regex::Regex;
use smol::{fs, process}; use smol::{fs, process};
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};

View file

@ -1,7 +1,8 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use async_trait::async_trait; use async_trait::async_trait;
use futures::StreamExt; use futures::StreamExt;
use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; use language::{LanguageServerName, LspAdapter};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use serde_json::json; use serde_json::json;
use smol::fs; use smol::fs;

View file

@ -3,7 +3,8 @@ use async_trait::async_trait;
use collections::HashMap; use collections::HashMap;
use futures::{future::BoxFuture, FutureExt, StreamExt}; use futures::{future::BoxFuture, FutureExt, StreamExt};
use gpui::AppContext; use gpui::AppContext;
use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter}; use language::{LanguageRegistry, LanguageServerName, LspAdapter};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use serde_json::json; use serde_json::json;
use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore}; use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};

View file

@ -3,7 +3,8 @@ use async_trait::async_trait;
use collections::HashMap; use collections::HashMap;
use futures::lock::Mutex; use futures::lock::Mutex;
use gpui::executor::Background; use gpui::executor::Background;
use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; use language::{LanguageServerName, LspAdapter};
use lsp::LanguageServerBinary;
use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn}; use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn};
use std::{any::Any, path::PathBuf, sync::Arc}; use std::{any::Any, path::PathBuf, sync::Arc};
use util::http::HttpClient; use util::http::HttpClient;

View file

@ -3,7 +3,8 @@ use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive; use async_tar::Archive;
use async_trait::async_trait; use async_trait::async_trait;
use futures::{io::BufReader, StreamExt}; use futures::{io::BufReader, StreamExt};
use language::{LanguageServerBinary, LanguageServerName}; use language::LanguageServerName;
use lsp::LanguageServerBinary;
use smol::fs; use smol::fs;
use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc}; use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc};
use util::{async_iife, github::latest_github_release, http::HttpClient, ResultExt}; use util::{async_iife, github::latest_github_release, http::HttpClient, ResultExt};

View file

@ -1,7 +1,8 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use async_trait::async_trait; use async_trait::async_trait;
use futures::StreamExt; use futures::StreamExt;
use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; use language::{LanguageServerName, LspAdapter};
use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use smol::fs; use smol::fs;
use std::{ use std::{

View file

@ -1,6 +1,7 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use async_trait::async_trait; 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 std::{any::Any, path::PathBuf, sync::Arc};
use util::http::HttpClient; use util::http::HttpClient;

View file

@ -4,6 +4,7 @@ use async_trait::async_trait;
use futures::{io::BufReader, StreamExt}; use futures::{io::BufReader, StreamExt};
pub use language::*; pub use language::*;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use lsp::LanguageServerBinary;
use regex::Regex; use regex::Regex;
use smol::fs::{self, File}; use smol::fs::{self, File};
use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, str, sync::Arc}; use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, str, sync::Arc};

View file

@ -4,8 +4,8 @@ use async_tar::Archive;
use async_trait::async_trait; use async_trait::async_trait;
use futures::{future::BoxFuture, FutureExt}; use futures::{future::BoxFuture, FutureExt};
use gpui::AppContext; use gpui::AppContext;
use language::{LanguageServerBinary, LanguageServerName, LspAdapter}; use language::{LanguageServerName, LspAdapter};
use lsp::CodeActionKind; use lsp::{CodeActionKind, LanguageServerBinary};
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use serde_json::{json, Value}; use serde_json::{json, Value};
use smol::{fs, io::BufReader, stream::StreamExt}; use smol::{fs, io::BufReader, stream::StreamExt};

View file

@ -2,9 +2,8 @@ use anyhow::{anyhow, Result};
use async_trait::async_trait; use async_trait::async_trait;
use futures::{future::BoxFuture, FutureExt, StreamExt}; use futures::{future::BoxFuture, FutureExt, StreamExt};
use gpui::AppContext; use gpui::AppContext;
use language::{ use language::{language_settings::all_language_settings, LanguageServerName, LspAdapter};
language_settings::all_language_settings, LanguageServerBinary, LanguageServerName, LspAdapter, use lsp::LanguageServerBinary;
};
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use serde_json::Value; use serde_json::Value;
use smol::fs; use smol::fs;

View file

@ -1,4 +1,5 @@
{ {
// "noErrorTruncation": true,
"compilerOptions": { "compilerOptions": {
"target": "es2015", "target": "es2015",
"module": "commonjs", "module": "commonjs",