9089 lines · 352.5 KB · Rust
RawDownload
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

Lines 1–500 of 9089.