Many steps toward validating and reinstalling server after failure
This commit is contained in:
parent
ec0409a3d1
commit
bca625a197
19 changed files with 347 additions and 155 deletions
|
@ -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!(
|
||||
|
|
|
@ -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<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(
|
||||
LanguageServerId(0),
|
||||
&node_path,
|
||||
arguments,
|
||||
binaries,
|
||||
Path::new("/"),
|
||||
None,
|
||||
cx.clone(),
|
||||
|
|
|
@ -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<str>);
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct LanguageServerBinary {
|
||||
pub path: PathBuf,
|
||||
pub arguments: Vec<OsString>,
|
||||
}
|
||||
|
||||
/// 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<Vec<CodeActionKind>> {
|
||||
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>;
|
||||
|
||||
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<BoxFuture<'static, Result<LanguageServerBinary, Arc<anyhow::Error>>>>,
|
||||
Shared<BoxFuture<'static, Result<LanguageServerBinaries, Arc<anyhow::Error>>>>,
|
||||
>,
|
||||
>,
|
||||
executor: Option<Arc<Background>>,
|
||||
|
@ -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<Language>, LanguageServerBinaryStatus)> {
|
||||
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 {
|
||||
|
@ -961,7 +1014,7 @@ async fn get_binary(
|
|||
http_client: Arc<dyn HttpClient>,
|
||||
download_dir: Arc<Path>,
|
||||
statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
) -> Result<LanguageServerBinaries> {
|
||||
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<dyn HttpClient>,
|
||||
container_dir: &Path,
|
||||
lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
) -> Result<LanguageServerBinaries> {
|
||||
let container_dir: Arc<Path> = 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 {
|
||||
|
|
|
@ -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<dyn Send + FnMut(Option<usize>, &str, AsyncAppCon
|
|||
type ResponseHandler = Box<dyn Send + FnOnce(Result<&str, Error>)>;
|
||||
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 {
|
||||
server_id: LanguageServerId,
|
||||
next_id: AtomicUsize,
|
||||
|
@ -51,7 +64,8 @@ pub struct LanguageServer {
|
|||
io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
|
||||
output_done_rx: Mutex<Option<barrier::Receiver>>,
|
||||
root_path: PathBuf,
|
||||
_server: Option<Child>,
|
||||
server: Option<Mutex<Child>>,
|
||||
test_installation_binary: Option<LanguageServerBinary>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
@ -119,10 +133,9 @@ struct Error {
|
|||
}
|
||||
|
||||
impl LanguageServer {
|
||||
pub fn new<T: AsRef<std::ffi::OsStr>>(
|
||||
pub fn new(
|
||||
server_id: LanguageServerId,
|
||||
binary_path: &Path,
|
||||
arguments: &[T],
|
||||
binaries: LanguageServerBinaries,
|
||||
root_path: &Path,
|
||||
code_action_kinds: Option<Vec<CodeActionKind>>,
|
||||
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<Child>,
|
||||
test_installation_binary: Option<LanguageServerBinary>,
|
||||
root_path: &Path,
|
||||
code_action_kinds: Option<Vec<CodeActionKind>>,
|
||||
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<LanguageServerBinary> {
|
||||
&self.test_installation_binary
|
||||
}
|
||||
|
||||
pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
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,
|
||||
|
|
|
@ -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<Option<Arc<LanguageServer>>>),
|
||||
Starting(Task<Option<Arc<LanguageServer>>>),
|
||||
Running {
|
||||
language: Arc<Language>,
|
||||
|
@ -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<LanguageServer>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
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<Vec<(Range<Anchor>, 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::request::Formatting>(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::request::RangeFormatting>(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::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();
|
||||
}) => (adapter.clone(), language.clone(), server),
|
||||
|
||||
(
|
||||
adapter,
|
||||
language,
|
||||
worktree_id,
|
||||
worktree_abs_path,
|
||||
lsp_symbols,
|
||||
)
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
requests.push(
|
||||
server
|
||||
.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 {
|
||||
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::<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 {
|
||||
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::<lsp::request::CodeActionResolveRequest>(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::request::ExecuteCommand>(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::<R::LspRequest>(lsp_params)
|
||||
.await
|
||||
.context("lsp request failed")?;
|
||||
let result = language_server.request::<R::LspRequest>(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::<Vec<_>>();
|
||||
|
|
|
@ -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;
|
||||
|
||||
fn log_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
|
||||
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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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::{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
// "noErrorTruncation": true,
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue