pub mod clangd_ext;pub mod lsp_ext_command;pub mod rust_analyzer_ext; use crate::{ CodeAction, Completion, CompletionResponse, CompletionSource, CoreCompletion, Hover, InlayHint, LspAction, ProjectItem, ProjectPath, ProjectTransaction, ResolveState, Symbol, ToolchainStore, buffer_store::{BufferStore, BufferStoreEvent}, environment::ProjectEnvironment, lsp_command::{self, *}, lsp_store, manifest_tree::{ AdapterQuery, LanguageServerTree, LanguageServerTreeNode, LaunchDisposition, ManifestQueryDelegate, ManifestTree, }, prettier_store::{self, PrettierStore, PrettierStoreEvent}, project_settings::{LspSettings, ProjectSettings}, relativize_path, resolve_path, toolchain_store::{EmptyToolchainStore, ToolchainStoreEvent}, worktree_store::{WorktreeStore, WorktreeStoreEvent}, yarn::YarnPathStore,};use anyhow::{Context as _, Result, anyhow};use async_trait::async_trait;use client::{TypedEnvelope, proto};use collections::{BTreeMap, BTreeSet, HashMap, HashSet, btree_map};use futures::{ AsyncWriteExt, Future, FutureExt, StreamExt, future::{Shared, join_all}, select, select_biased, stream::FuturesUnordered,};use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder};use gpui::{ App, AppContext, AsyncApp, Context, Entity, EventEmitter, PromptLevel, SharedString, Task, WeakEntity,};use http_client::HttpClient;use itertools::Itertools as _;use language::{ Bias, BinaryStatus, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, File as _, Language, LanguageName, LanguageRegistry, LanguageToolchainStore, LocalFile, LspAdapter, LspAdapterDelegate, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped, language_settings::{ FormatOnSave, Formatter, LanguageSettings, SelectedFormatter, language_settings, }, point_to_lsp, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, range_from_lsp, range_to_lsp,};use lsp::{ CodeActionKind, CompletionContext, DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, Edit, FileOperationFilter, FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher, LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId, LanguageServerName, LspRequestFuture, MessageActionItem, MessageType, OneOf, RenameFilesParams, SymbolKind, TextEdit, WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder, notification::DidRenameFiles,};use node_runtime::read_package_installed_version;use parking_lot::Mutex;use postage::watch;use rand::prelude::*; use rpc::{ AnyProtoClient, proto::{FromProto, ToProto},};use serde::Serialize;use settings::{Settings, SettingsLocation, SettingsStore};use sha2::{Digest, Sha256};use smol::channel::Sender;use snippet::Snippet;use std::{ any::Any, borrow::Cow, cell::RefCell, cmp::{Ordering, Reverse}, convert::TryInto, ffi::OsStr, iter, mem, ops::{ControlFlow, Range}, path::{self, Path, PathBuf}, rc::Rc, sync::Arc, time::{Duration, Instant},};use text::{Anchor, BufferId, LineEnding, OffsetRangeExt};use url::Url;use util::{ ResultExt as _, debug_panic, defer, maybe, merge_json_value_into, paths::{PathExt, SanitizedPath}, post_inc,}; 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, FS_WATCH_LATENCY, File, LocalWorktree, PathChange, ProjectEntryId, UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings,}; 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>>; 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(); async move |cx| { 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(), &mut 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(async move |cx| { let result = async { let toolchains = this.update(cx, |this, cx| this.toolchain_store(cx))?; let language_server = pending_server.await?; let workspace_config = Self::workspace_configuration_for_adapter( adapter.adapter.clone(), fs.as_ref(), &delegate, toolchains.clone(), cx, ) .await?; let mut initialization_options = Self::initialization_options_for_adapter( adapter.adapter.clone(), 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, cx) })??; Self::setup_lsp_messages( this.clone(), fs, &language_server, delegate.clone(), adapter.clone(), ); 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(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(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(), BinaryStatus::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 |_| { let mut env = delegate.shell_env().await; env.extend(settings.env.unwrap_or_default()); Ok(LanguageServerBinary { path: PathBuf::from(&settings.path.unwrap()), env: Some(env), 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(async move |cx| { let binary_result = adapter .clone() .get_language_server_command(delegate.clone(), toolchains, lsp_binary_options, cx) .await; delegate.update_status(adapter.name.clone(), BinaryStatus::None); let mut binary = binary_result?; let mut shell_env = delegate.shell_env().await; shell_env.extend(binary.env.unwrap_or_default()); if let Some(settings) = settings { if let Some(arguments) = settings.arguments { binary.arguments = arguments.into_iter().map(Into::into).collect(); } if let Some(env) = settings.env { shell_env.extend(env); } } 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, cx| { let adapter = adapter.clone(); if let Some(this) = this.upgrade() { this.update(cx, |this, cx| { { let buffer = params .uri .to_file_path() .map(|file_path| this.get_buffer(&file_path, cx)) .ok() .flatten(); adapter.process_diagnostics(&mut params, server_id, buffer); } this.merge_diagnostics( server_id, params, &adapter.disk_based_diagnostic_sources, |diagnostic, cx| adapter.retain_old_diagnostic(diagnostic, cx), 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, cx| { let adapter = adapter.clone(); let delegate = delegate.clone(); let this = this.clone(); let fs = fs.clone(); let mut cx = cx.clone(); async move {Lines 1–500 of 10587. Load more
pub mod clangd_ext; pub mod lsp_ext_command; pub mod rust_analyzer_ext; use crate::{ CodeAction, Completion, CompletionResponse, CompletionSource, CoreCompletion, Hover, InlayHint, LspAction, ProjectItem, ProjectPath, ProjectTransaction, ResolveState, Symbol, ToolchainStore, buffer_store::{BufferStore, BufferStoreEvent}, environment::ProjectEnvironment, lsp_command::{self, *}, lsp_store, manifest_tree::{ AdapterQuery, LanguageServerTree, LanguageServerTreeNode, LaunchDisposition, ManifestQueryDelegate, ManifestTree, }, prettier_store::{self, PrettierStore, PrettierStoreEvent}, project_settings::{LspSettings, ProjectSettings}, relativize_path, resolve_path, toolchain_store::{EmptyToolchainStore, ToolchainStoreEvent}, worktree_store::{WorktreeStore, WorktreeStoreEvent}, yarn::YarnPathStore, }; use anyhow::{Context as _, Result, anyhow}; use async_trait::async_trait; use client::{TypedEnvelope, proto}; use collections::{BTreeMap, BTreeSet, HashMap, HashSet, btree_map}; use futures::{ AsyncWriteExt, Future, FutureExt, StreamExt, future::{Shared, join_all}, select, select_biased, stream::FuturesUnordered, }; use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder}; use gpui::{ App, AppContext, AsyncApp, Context, Entity, EventEmitter, PromptLevel, SharedString, Task, WeakEntity, }; use http_client::HttpClient; use itertools::Itertools as _; use language::{ Bias, BinaryStatus, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, File as _, Language, LanguageName, LanguageRegistry, LanguageToolchainStore, LocalFile, LspAdapter, LspAdapterDelegate, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped, language_settings::{ FormatOnSave, Formatter, LanguageSettings, SelectedFormatter, language_settings, }, point_to_lsp, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, range_from_lsp, range_to_lsp, }; use lsp::{ CodeActionKind, CompletionContext, DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, Edit, FileOperationFilter, FileOperationPatternKind, FileOperationRegistrationOptions, FileRename, FileSystemWatcher, LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId, LanguageServerName, LspRequestFuture, MessageActionItem, MessageType, OneOf, RenameFilesParams, SymbolKind, TextEdit, WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder, notification::DidRenameFiles, }; use node_runtime::read_package_installed_version; use parking_lot::Mutex; use postage::watch; use rand::prelude::*; use rpc::{ AnyProtoClient, proto::{FromProto, ToProto}, }; use serde::Serialize; use settings::{Settings, SettingsLocation, SettingsStore}; use sha2::{Digest, Sha256}; use smol::channel::Sender; use snippet::Snippet; use std::{ any::Any, borrow::Cow, cell::RefCell, cmp::{Ordering, Reverse}, convert::TryInto, ffi::OsStr, iter, mem, ops::{ControlFlow, Range}, path::{self, Path, PathBuf}, rc::Rc, sync::Arc, time::{Duration, Instant}, }; use text::{Anchor, BufferId, LineEnding, OffsetRangeExt}; use url::Url; use util::{ ResultExt as _, debug_panic, defer, maybe, merge_json_value_into, paths::{PathExt, SanitizedPath}, post_inc, }; 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, FS_WATCH_LATENCY, File, LocalWorktree, PathChange, ProjectEntryId, UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings, }; 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>>; 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(); async move |cx| { 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(), &mut 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(async move |cx| { let result = async { let toolchains = this.update(cx, |this, cx| this.toolchain_store(cx))?; let language_server = pending_server.await?; let workspace_config = Self::workspace_configuration_for_adapter( adapter.adapter.clone(), fs.as_ref(), &delegate, toolchains.clone(), cx, ) .await?; let mut initialization_options = Self::initialization_options_for_adapter( adapter.adapter.clone(), 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, cx) })??; Self::setup_lsp_messages( this.clone(), fs, &language_server, delegate.clone(), adapter.clone(), ); 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(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(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(), BinaryStatus::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 |_| { let mut env = delegate.shell_env().await; env.extend(settings.env.unwrap_or_default()); Ok(LanguageServerBinary { path: PathBuf::from(&settings.path.unwrap()), env: Some(env), 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(async move |cx| { let binary_result = adapter .clone() .get_language_server_command(delegate.clone(), toolchains, lsp_binary_options, cx) .await; delegate.update_status(adapter.name.clone(), BinaryStatus::None); let mut binary = binary_result?; let mut shell_env = delegate.shell_env().await; shell_env.extend(binary.env.unwrap_or_default()); if let Some(settings) = settings { if let Some(arguments) = settings.arguments { binary.arguments = arguments.into_iter().map(Into::into).collect(); } if let Some(env) = settings.env { shell_env.extend(env); } } 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, cx| { let adapter = adapter.clone(); if let Some(this) = this.upgrade() { this.update(cx, |this, cx| { { let buffer = params .uri .to_file_path() .map(|file_path| this.get_buffer(&file_path, cx)) .ok() .flatten(); adapter.process_diagnostics(&mut params, server_id, buffer); } this.merge_diagnostics( server_id, params, &adapter.disk_based_diagnostic_sources, |diagnostic, cx| adapter.retain_old_diagnostic(diagnostic, cx), 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, cx| { let adapter = adapter.clone(); let delegate = delegate.clone(); let this = this.clone(); let fs = fs.clone(); let mut cx = cx.clone(); async move {