use crate::{ buffer_store::{BufferStore, BufferStoreEvent}, deserialize_code_actions, environment::ProjectEnvironment, lsp_command::{self, *}, lsp_ext_command, prettier_store::{self, PrettierStore, PrettierStoreEvent}, project_settings::{LspSettings, ProjectSettings}, project_tree::{AdapterQuery, LanguageServerTree, LaunchDisposition, ProjectTree}, relativize_path, resolve_path, toolchain_store::{EmptyToolchainStore, ToolchainStoreEvent}, worktree_store::{WorktreeStore, WorktreeStoreEvent}, yarn::YarnPathStore, CodeAction, Completion, CoreCompletion, Hover, InlayHint, ProjectItem as _, ProjectPath, ProjectTransaction, ResolveState, Symbol, ToolchainStore,};use anyhow::{anyhow, Context as _, Result};use async_trait::async_trait;use client::{proto, TypedEnvelope};use collections::{btree_map, BTreeMap, BTreeSet, HashMap, HashSet};use futures::{ future::{join_all, Shared}, select, stream::FuturesUnordered, AsyncWriteExt, Future, FutureExt, StreamExt,};use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder};use gpui::{ App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, PromptLevel, SharedString, Task, WeakEntity,};use http_client::HttpClient;use itertools::Itertools as _;use language::{ language_settings::{ language_settings, FormatOnSave, Formatter, LanguageSettings, SelectedFormatter, }, point_to_lsp, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, File as _, Language, LanguageRegistry, LanguageServerBinaryStatus, LanguageToolchainStore, LocalFile, LspAdapter, LspAdapterDelegate, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,};use lsp::{ notification::DidRenameFiles, CodeActionKind, CompletionContext, DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, Edit, FileOperationFilter, FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher, InsertTextFormat, LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId, LanguageServerName, LspRequestFuture, MessageActionItem, MessageType, OneOf, RenameFilesParams, ServerHealthStatus, ServerStatus, SymbolKind, TextEdit, WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder,};use node_runtime::read_package_installed_version;use parking_lot::Mutex;use postage::watch;use rand::prelude::*; use rpc::{ proto::{FromProto, ToProto}, AnyProtoClient,};use serde::Serialize;use settings::{Settings, SettingsLocation, SettingsStore};use sha2::{Digest, Sha256};use smol::channel::Sender;use snippet::Snippet;use std::{ any::Any, cell::RefCell, cmp::Ordering, convert::TryInto, ffi::OsStr, iter, mem, ops::{ControlFlow, Range}, path::{self, Path, PathBuf}, rc::Rc, str, sync::Arc, time::{Duration, Instant},};use text::{Anchor, BufferId, LineEnding, OffsetRangeExt, TransactionId};use url::Url;use util::{ debug_panic, defer, maybe, merge_json_value_into, paths::SanitizedPath, post_inc, ResultExt, TryFutureExt as _,}; pub use fs::*;pub use language::Location;#[cfg(any(test, feature = "test-support"))]pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;pub use worktree::{ Entry, EntryKind, File, LocalWorktree, PathChange, ProjectEntryId, UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings, FS_WATCH_LATENCY,}; const SERVER_LAUNCHING_BEFORE_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);pub const SERVER_PROGRESS_THROTTLE_TIMEOUT: Duration = Duration::from_millis(100); #[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum FormatTrigger { Save, Manual,} pub enum LspFormatTarget { Buffers, Ranges(BTreeMap<BufferId, Vec<Range<Anchor>>>),} pub type OpenLspBufferHandle = Entity<Entity<Buffer>>; // Currently, formatting operations are represented differently depending on// whether they come from a language server or an external command.#[derive(Debug)]pub enum FormatOperation { Lsp(Vec<(Range<Anchor>, Arc<str>)>), External(Diff), Prettier(Diff),} impl FormatTrigger { fn from_proto(value: i32) -> FormatTrigger { match value { 0 => FormatTrigger::Save, 1 => FormatTrigger::Manual, _ => FormatTrigger::Save, } }} pub struct LocalLspStore { weak: WeakEntity<LspStore>, worktree_store: Entity<WorktreeStore>, toolchain_store: Entity<ToolchainStore>, http_client: Arc<dyn HttpClient>, environment: Entity<ProjectEnvironment>, fs: Arc<dyn Fs>, languages: Arc<LanguageRegistry>, language_server_ids: HashMap<(WorktreeId, LanguageServerName), BTreeSet<LanguageServerId>>, yarn: Entity<YarnPathStore>, pub language_servers: HashMap<LanguageServerId, LanguageServerState>, buffers_being_formatted: HashSet<BufferId>, last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>, language_server_watched_paths: HashMap<LanguageServerId, LanguageServerWatchedPaths>, language_server_paths_watched_for_rename: HashMap<LanguageServerId, RenamePathsWatchedForServer>, language_server_watcher_registrations: HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>, supplementary_language_servers: HashMap<LanguageServerId, (LanguageServerName, Arc<LanguageServer>)>, prettier_store: Entity<PrettierStore>, next_diagnostic_group_id: usize, diagnostics: HashMap< WorktreeId, HashMap< Arc<Path>, Vec<( LanguageServerId, Vec<DiagnosticEntry<Unclipped<PointUtf16>>>, )>, >, >, buffer_snapshots: HashMap<BufferId, HashMap<LanguageServerId, Vec<LspBufferSnapshot>>>, // buffer_id -> server_id -> vec of snapshots _subscription: gpui::Subscription, lsp_tree: Entity<LanguageServerTree>, registered_buffers: HashMap<BufferId, usize>,} impl LocalLspStore { /// Returns the running language server for the given ID. Note if the language server is starting, it will not be returned. pub fn running_language_server_for_id( &self, id: LanguageServerId, ) -> Option<&Arc<LanguageServer>> { let language_server_state = self.language_servers.get(&id)?; match language_server_state { LanguageServerState::Running { server, .. } => Some(server), LanguageServerState::Starting { .. } => None, } } fn start_language_server( &mut self, worktree_handle: &Entity<Worktree>, delegate: Arc<LocalLspAdapterDelegate>, adapter: Arc<CachedLspAdapter>, settings: Arc<LspSettings>, cx: &mut App, ) -> LanguageServerId { let worktree = worktree_handle.read(cx); let worktree_id = worktree.id(); let root_path = worktree.abs_path(); let key = (worktree_id, adapter.name.clone()); let override_options = settings.initialization_options.clone(); let stderr_capture = Arc::new(Mutex::new(Some(String::new()))); let server_id = self.languages.next_language_server_id(); log::info!( "attempting to start language server {:?}, path: {root_path:?}, id: {server_id}", adapter.name.0 ); let binary = self.get_language_server_binary(adapter.clone(), delegate.clone(), true, cx); let pending_workspace_folders: Arc<Mutex<BTreeSet<Url>>> = Default::default(); let pending_server = cx.spawn({ let adapter = adapter.clone(); let server_name = adapter.name.clone(); let stderr_capture = stderr_capture.clone(); #[cfg(any(test, feature = "test-support"))] let lsp_store = self.weak.clone(); let pending_workspace_folders = pending_workspace_folders.clone(); move |cx| async move { let binary = binary.await?; #[cfg(any(test, feature = "test-support"))] if let Some(server) = lsp_store .update(&mut cx.clone(), |this, cx| { this.languages.create_fake_language_server( server_id, &server_name, binary.clone(), cx.to_async(), ) }) .ok() .flatten() { return Ok(server); } lsp::LanguageServer::new( stderr_capture, server_id, server_name, binary, &root_path, adapter.code_action_kinds(), pending_workspace_folders, cx, ) } }); let startup = { let server_name = adapter.name.0.clone(); let delegate = delegate as Arc<dyn LspAdapterDelegate>; let key = key.clone(); let adapter = adapter.clone(); let this = self.weak.clone(); let pending_workspace_folders = pending_workspace_folders.clone(); let fs = self.fs.clone(); cx.spawn(move |mut cx| async move { let result = { let delegate = delegate.clone(); let adapter = adapter.clone(); let this = this.clone(); let toolchains = this .update(&mut cx, |this, cx| this.toolchain_store(cx)) .ok()?; let mut cx = cx.clone(); async move { let language_server = pending_server.await?; let workspace_config = adapter .adapter .clone() .workspace_configuration( fs.as_ref(), &delegate, toolchains.clone(), &mut cx, ) .await?; let mut initialization_options = adapter .adapter .clone() .initialization_options(fs.as_ref(), &(delegate)) .await?; match (&mut initialization_options, override_options) { (Some(initialization_options), Some(override_options)) => { merge_json_value_into(override_options, initialization_options); } (None, override_options) => initialization_options = override_options, _ => {} } let initialization_params = cx.update(|cx| { let mut params = language_server.default_initialize_params(cx); params.initialization_options = initialization_options; adapter.adapter.prepare_initialize_params(params) })??; Self::setup_lsp_messages( this.clone(), fs, &language_server, delegate, adapter, ); let did_change_configuration_params = Arc::new(lsp::DidChangeConfigurationParams { settings: workspace_config, }); let language_server = cx .update(|cx| { language_server.initialize( initialization_params, did_change_configuration_params.clone(), cx, ) })? .await .inspect_err(|_| { if let Some(this) = this.upgrade() { this.update(&mut cx, |_, cx| { cx.emit(LspStoreEvent::LanguageServerRemoved(server_id)) }) .ok(); } })?; language_server .notify::<lsp::notification::DidChangeConfiguration>( &did_change_configuration_params, ) .ok(); anyhow::Ok(language_server) } } .await; match result { Ok(server) => { this.update(&mut cx, |this, mut cx| { this.insert_newly_running_language_server( adapter, server.clone(), server_id, key, pending_workspace_folders, &mut cx, ); }) .ok(); stderr_capture.lock().take(); Some(server) } Err(err) => { let log = stderr_capture.lock().take().unwrap_or_default(); delegate.update_status( adapter.name(), LanguageServerBinaryStatus::Failed { error: format!("{err}\n-- stderr--\n{}", log), }, ); log::error!("Failed to start language server {server_name:?}: {err}"); log::error!("server stderr: {:?}", log); None } } }) }; let state = LanguageServerState::Starting { startup, pending_workspace_folders, }; self.language_servers.insert(server_id, state); self.language_server_ids .entry(key) .or_default() .insert(server_id); server_id } fn get_language_server_binary( &self, adapter: Arc<CachedLspAdapter>, delegate: Arc<dyn LspAdapterDelegate>, allow_binary_download: bool, cx: &mut App, ) -> Task<Result<LanguageServerBinary>> { let settings = ProjectSettings::get( Some(SettingsLocation { worktree_id: delegate.worktree_id(), path: Path::new(""), }), cx, ) .lsp .get(&adapter.name) .and_then(|s| s.binary.clone()); if settings.as_ref().is_some_and(|b| b.path.is_some()) { let settings = settings.unwrap(); return cx.spawn(|_| async move { Ok(LanguageServerBinary { path: PathBuf::from(&settings.path.unwrap()), env: Some(delegate.shell_env().await), arguments: settings .arguments .unwrap_or_default() .iter() .map(Into::into) .collect(), }) }); } let lsp_binary_options = LanguageServerBinaryOptions { allow_path_lookup: !settings .as_ref() .and_then(|b| b.ignore_system_version) .unwrap_or_default(), allow_binary_download, }; let toolchains = self.toolchain_store.read(cx).as_language_toolchain_store(); cx.spawn(|mut cx| async move { let binary_result = adapter .clone() .get_language_server_command( delegate.clone(), toolchains, lsp_binary_options, &mut cx, ) .await; delegate.update_status(adapter.name.clone(), LanguageServerBinaryStatus::None); let mut binary = binary_result?; if let Some(arguments) = settings.and_then(|b| b.arguments) { binary.arguments = arguments.into_iter().map(Into::into).collect(); } let mut shell_env = delegate.shell_env().await; shell_env.extend(binary.env.unwrap_or_default()); binary.env = Some(shell_env); Ok(binary) }) } fn setup_lsp_messages( this: WeakEntity<LspStore>, fs: Arc<dyn Fs>, language_server: &LanguageServer, delegate: Arc<dyn LspAdapterDelegate>, adapter: Arc<CachedLspAdapter>, ) { let name = language_server.name(); let server_id = language_server.server_id(); language_server .on_notification::<lsp::notification::PublishDiagnostics, _>({ let adapter = adapter.clone(); let this = this.clone(); move |mut params, mut cx| { let adapter = adapter.clone(); if let Some(this) = this.upgrade() { adapter.process_diagnostics(&mut params); this.update(&mut cx, |this, cx| { this.update_diagnostics( server_id, params, &adapter.disk_based_diagnostic_sources, cx, ) .log_err(); }) .ok(); } } }) .detach(); language_server .on_request::<lsp::request::WorkspaceConfiguration, _, _>({ let adapter = adapter.adapter.clone(); let delegate = delegate.clone(); let this = this.clone(); let fs = fs.clone(); move |params, mut cx| { let adapter = adapter.clone(); let delegate = delegate.clone(); let this = this.clone(); let fs = fs.clone(); async move { let toolchains = this.update(&mut cx, |this, cx| this.toolchain_store(cx))?; let workspace_config = adapter .workspace_configuration(fs.as_ref(), &delegate, toolchains, &mut cx) .await?; Ok(params .itemsLines 1–500 of 9089. Load more
use crate::{ buffer_store::{BufferStore, BufferStoreEvent}, deserialize_code_actions, environment::ProjectEnvironment, lsp_command::{self, *}, lsp_ext_command, prettier_store::{self, PrettierStore, PrettierStoreEvent}, project_settings::{LspSettings, ProjectSettings}, project_tree::{AdapterQuery, LanguageServerTree, LaunchDisposition, ProjectTree}, relativize_path, resolve_path, toolchain_store::{EmptyToolchainStore, ToolchainStoreEvent}, worktree_store::{WorktreeStore, WorktreeStoreEvent}, yarn::YarnPathStore, CodeAction, Completion, CoreCompletion, Hover, InlayHint, ProjectItem as _, ProjectPath, ProjectTransaction, ResolveState, Symbol, ToolchainStore, }; use anyhow::{anyhow, Context as _, Result}; use async_trait::async_trait; use client::{proto, TypedEnvelope}; use collections::{btree_map, BTreeMap, BTreeSet, HashMap, HashSet}; use futures::{ future::{join_all, Shared}, select, stream::FuturesUnordered, AsyncWriteExt, Future, FutureExt, StreamExt, }; use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder}; use gpui::{ App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, PromptLevel, SharedString, Task, WeakEntity, }; use http_client::HttpClient; use itertools::Itertools as _; use language::{ language_settings::{ language_settings, FormatOnSave, Formatter, LanguageSettings, SelectedFormatter, }, point_to_lsp, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, File as _, Language, LanguageRegistry, LanguageServerBinaryStatus, LanguageToolchainStore, LocalFile, LspAdapter, LspAdapterDelegate, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped, }; use lsp::{ notification::DidRenameFiles, CodeActionKind, CompletionContext, DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, Edit, FileOperationFilter, FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher, InsertTextFormat, LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId, LanguageServerName, LspRequestFuture, MessageActionItem, MessageType, OneOf, RenameFilesParams, ServerHealthStatus, ServerStatus, SymbolKind, TextEdit, WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder, }; use node_runtime::read_package_installed_version; use parking_lot::Mutex; use postage::watch; use rand::prelude::*; use rpc::{ proto::{FromProto, ToProto}, AnyProtoClient, }; use serde::Serialize; use settings::{Settings, SettingsLocation, SettingsStore}; use sha2::{Digest, Sha256}; use smol::channel::Sender; use snippet::Snippet; use std::{ any::Any, cell::RefCell, cmp::Ordering, convert::TryInto, ffi::OsStr, iter, mem, ops::{ControlFlow, Range}, path::{self, Path, PathBuf}, rc::Rc, str, sync::Arc, time::{Duration, Instant}, }; use text::{Anchor, BufferId, LineEnding, OffsetRangeExt, TransactionId}; use url::Url; use util::{ debug_panic, defer, maybe, merge_json_value_into, paths::SanitizedPath, post_inc, ResultExt, TryFutureExt as _, }; pub use fs::*; pub use language::Location; #[cfg(any(test, feature = "test-support"))] pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX; pub use worktree::{ Entry, EntryKind, File, LocalWorktree, PathChange, ProjectEntryId, UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings, FS_WATCH_LATENCY, }; const SERVER_LAUNCHING_BEFORE_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5); pub const SERVER_PROGRESS_THROTTLE_TIMEOUT: Duration = Duration::from_millis(100); #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum FormatTrigger { Save, Manual, } pub enum LspFormatTarget { Buffers, Ranges(BTreeMap<BufferId, Vec<Range<Anchor>>>), } pub type OpenLspBufferHandle = Entity<Entity<Buffer>>; // Currently, formatting operations are represented differently depending on // whether they come from a language server or an external command. #[derive(Debug)] pub enum FormatOperation { Lsp(Vec<(Range<Anchor>, Arc<str>)>), External(Diff), Prettier(Diff), } impl FormatTrigger { fn from_proto(value: i32) -> FormatTrigger { match value { 0 => FormatTrigger::Save, 1 => FormatTrigger::Manual, _ => FormatTrigger::Save, } } } pub struct LocalLspStore { weak: WeakEntity<LspStore>, worktree_store: Entity<WorktreeStore>, toolchain_store: Entity<ToolchainStore>, http_client: Arc<dyn HttpClient>, environment: Entity<ProjectEnvironment>, fs: Arc<dyn Fs>, languages: Arc<LanguageRegistry>, language_server_ids: HashMap<(WorktreeId, LanguageServerName), BTreeSet<LanguageServerId>>, yarn: Entity<YarnPathStore>, pub language_servers: HashMap<LanguageServerId, LanguageServerState>, buffers_being_formatted: HashSet<BufferId>, last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>, language_server_watched_paths: HashMap<LanguageServerId, LanguageServerWatchedPaths>, language_server_paths_watched_for_rename: HashMap<LanguageServerId, RenamePathsWatchedForServer>, language_server_watcher_registrations: HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>, supplementary_language_servers: HashMap<LanguageServerId, (LanguageServerName, Arc<LanguageServer>)>, prettier_store: Entity<PrettierStore>, next_diagnostic_group_id: usize, diagnostics: HashMap< WorktreeId, HashMap< Arc<Path>, Vec<( LanguageServerId, Vec<DiagnosticEntry<Unclipped<PointUtf16>>>, )>, >, >, buffer_snapshots: HashMap<BufferId, HashMap<LanguageServerId, Vec<LspBufferSnapshot>>>, // buffer_id -> server_id -> vec of snapshots _subscription: gpui::Subscription, lsp_tree: Entity<LanguageServerTree>, registered_buffers: HashMap<BufferId, usize>, } impl LocalLspStore { /// Returns the running language server for the given ID. Note if the language server is starting, it will not be returned. pub fn running_language_server_for_id( &self, id: LanguageServerId, ) -> Option<&Arc<LanguageServer>> { let language_server_state = self.language_servers.get(&id)?; match language_server_state { LanguageServerState::Running { server, .. } => Some(server), LanguageServerState::Starting { .. } => None, } } fn start_language_server( &mut self, worktree_handle: &Entity<Worktree>, delegate: Arc<LocalLspAdapterDelegate>, adapter: Arc<CachedLspAdapter>, settings: Arc<LspSettings>, cx: &mut App, ) -> LanguageServerId { let worktree = worktree_handle.read(cx); let worktree_id = worktree.id(); let root_path = worktree.abs_path(); let key = (worktree_id, adapter.name.clone()); let override_options = settings.initialization_options.clone(); let stderr_capture = Arc::new(Mutex::new(Some(String::new()))); let server_id = self.languages.next_language_server_id(); log::info!( "attempting to start language server {:?}, path: {root_path:?}, id: {server_id}", adapter.name.0 ); let binary = self.get_language_server_binary(adapter.clone(), delegate.clone(), true, cx); let pending_workspace_folders: Arc<Mutex<BTreeSet<Url>>> = Default::default(); let pending_server = cx.spawn({ let adapter = adapter.clone(); let server_name = adapter.name.clone(); let stderr_capture = stderr_capture.clone(); #[cfg(any(test, feature = "test-support"))] let lsp_store = self.weak.clone(); let pending_workspace_folders = pending_workspace_folders.clone(); move |cx| async move { let binary = binary.await?; #[cfg(any(test, feature = "test-support"))] if let Some(server) = lsp_store .update(&mut cx.clone(), |this, cx| { this.languages.create_fake_language_server( server_id, &server_name, binary.clone(), cx.to_async(), ) }) .ok() .flatten() { return Ok(server); } lsp::LanguageServer::new( stderr_capture, server_id, server_name, binary, &root_path, adapter.code_action_kinds(), pending_workspace_folders, cx, ) } }); let startup = { let server_name = adapter.name.0.clone(); let delegate = delegate as Arc<dyn LspAdapterDelegate>; let key = key.clone(); let adapter = adapter.clone(); let this = self.weak.clone(); let pending_workspace_folders = pending_workspace_folders.clone(); let fs = self.fs.clone(); cx.spawn(move |mut cx| async move { let result = { let delegate = delegate.clone(); let adapter = adapter.clone(); let this = this.clone(); let toolchains = this .update(&mut cx, |this, cx| this.toolchain_store(cx)) .ok()?; let mut cx = cx.clone(); async move { let language_server = pending_server.await?; let workspace_config = adapter .adapter .clone() .workspace_configuration( fs.as_ref(), &delegate, toolchains.clone(), &mut cx, ) .await?; let mut initialization_options = adapter .adapter .clone() .initialization_options(fs.as_ref(), &(delegate)) .await?; match (&mut initialization_options, override_options) { (Some(initialization_options), Some(override_options)) => { merge_json_value_into(override_options, initialization_options); } (None, override_options) => initialization_options = override_options, _ => {} } let initialization_params = cx.update(|cx| { let mut params = language_server.default_initialize_params(cx); params.initialization_options = initialization_options; adapter.adapter.prepare_initialize_params(params) })??; Self::setup_lsp_messages( this.clone(), fs, &language_server, delegate, adapter, ); let did_change_configuration_params = Arc::new(lsp::DidChangeConfigurationParams { settings: workspace_config, }); let language_server = cx .update(|cx| { language_server.initialize( initialization_params, did_change_configuration_params.clone(), cx, ) })? .await .inspect_err(|_| { if let Some(this) = this.upgrade() { this.update(&mut cx, |_, cx| { cx.emit(LspStoreEvent::LanguageServerRemoved(server_id)) }) .ok(); } })?; language_server .notify::<lsp::notification::DidChangeConfiguration>( &did_change_configuration_params, ) .ok(); anyhow::Ok(language_server) } } .await; match result { Ok(server) => { this.update(&mut cx, |this, mut cx| { this.insert_newly_running_language_server( adapter, server.clone(), server_id, key, pending_workspace_folders, &mut cx, ); }) .ok(); stderr_capture.lock().take(); Some(server) } Err(err) => { let log = stderr_capture.lock().take().unwrap_or_default(); delegate.update_status( adapter.name(), LanguageServerBinaryStatus::Failed { error: format!("{err}\n-- stderr--\n{}", log), }, ); log::error!("Failed to start language server {server_name:?}: {err}"); log::error!("server stderr: {:?}", log); None } } }) }; let state = LanguageServerState::Starting { startup, pending_workspace_folders, }; self.language_servers.insert(server_id, state); self.language_server_ids .entry(key) .or_default() .insert(server_id); server_id } fn get_language_server_binary( &self, adapter: Arc<CachedLspAdapter>, delegate: Arc<dyn LspAdapterDelegate>, allow_binary_download: bool, cx: &mut App, ) -> Task<Result<LanguageServerBinary>> { let settings = ProjectSettings::get( Some(SettingsLocation { worktree_id: delegate.worktree_id(), path: Path::new(""), }), cx, ) .lsp .get(&adapter.name) .and_then(|s| s.binary.clone()); if settings.as_ref().is_some_and(|b| b.path.is_some()) { let settings = settings.unwrap(); return cx.spawn(|_| async move { Ok(LanguageServerBinary { path: PathBuf::from(&settings.path.unwrap()), env: Some(delegate.shell_env().await), arguments: settings .arguments .unwrap_or_default() .iter() .map(Into::into) .collect(), }) }); } let lsp_binary_options = LanguageServerBinaryOptions { allow_path_lookup: !settings .as_ref() .and_then(|b| b.ignore_system_version) .unwrap_or_default(), allow_binary_download, }; let toolchains = self.toolchain_store.read(cx).as_language_toolchain_store(); cx.spawn(|mut cx| async move { let binary_result = adapter .clone() .get_language_server_command( delegate.clone(), toolchains, lsp_binary_options, &mut cx, ) .await; delegate.update_status(adapter.name.clone(), LanguageServerBinaryStatus::None); let mut binary = binary_result?; if let Some(arguments) = settings.and_then(|b| b.arguments) { binary.arguments = arguments.into_iter().map(Into::into).collect(); } let mut shell_env = delegate.shell_env().await; shell_env.extend(binary.env.unwrap_or_default()); binary.env = Some(shell_env); Ok(binary) }) } fn setup_lsp_messages( this: WeakEntity<LspStore>, fs: Arc<dyn Fs>, language_server: &LanguageServer, delegate: Arc<dyn LspAdapterDelegate>, adapter: Arc<CachedLspAdapter>, ) { let name = language_server.name(); let server_id = language_server.server_id(); language_server .on_notification::<lsp::notification::PublishDiagnostics, _>({ let adapter = adapter.clone(); let this = this.clone(); move |mut params, mut cx| { let adapter = adapter.clone(); if let Some(this) = this.upgrade() { adapter.process_diagnostics(&mut params); this.update(&mut cx, |this, cx| { this.update_diagnostics( server_id, params, &adapter.disk_based_diagnostic_sources, cx, ) .log_err(); }) .ok(); } } }) .detach(); language_server .on_request::<lsp::request::WorkspaceConfiguration, _, _>({ let adapter = adapter.adapter.clone(); let delegate = delegate.clone(); let this = this.clone(); let fs = fs.clone(); move |params, mut cx| { let adapter = adapter.clone(); let delegate = delegate.clone(); let this = this.clone(); let fs = fs.clone(); async move { let toolchains = this.update(&mut cx, |this, cx| this.toolchain_store(cx))?; let workspace_config = adapter .workspace_configuration(fs.as_ref(), &delegate, toolchains, &mut cx) .await?; Ok(params .items