8620 lines · 335.3 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},    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, HashMap, HashSet};use futures::{    future::{join_all, Shared},    select,    stream::FuturesUnordered,    AsyncWriteExt, Future, FutureExt, StreamExt,};use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder};use gpui::{    AppContext, AsyncAppContext, Context, Entity, EventEmitter, Model, ModelContext, PromptLevel,    Task, WeakModel,};use http_client::HttpClient;use itertools::Itertools as _;use language::{    language_settings::{        language_settings, FormatOnSave, Formatter, LanguageSettings, SelectedFormatter,    },    markdown, point_to_lsp, prepare_completion_documentation,    proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},    range_from_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel, Diagnostic,    DiagnosticEntry, DiagnosticSet, Diff, Documentation, File as _, Language, LanguageName,    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, Url,    WillRenameFiles, WorkDoneProgressCancelParams, WorkspaceFolder,};use node_runtime::read_package_installed_version;use parking_lot::{Mutex, RwLock};use postage::watch;use rand::prelude::*; use rpc::AnyProtoClient;use serde::Serialize;use settings::{Settings, SettingsLocation, SettingsStore};use sha2::{Digest, Sha256};use similar::{ChangeTag, TextDiff};use smol::channel::Sender;use snippet::Snippet;use std::{    any::Any,    cmp::Ordering,    convert::TryInto,    ffi::OsStr,    iter, mem,    ops::{ControlFlow, Range},    path::{self, Path, PathBuf},    str,    sync::Arc,    time::{Duration, Instant},};use text::{Anchor, BufferId, LineEnding, Point, Selection};use util::{    debug_panic, defer, maybe, merge_json_value_into, 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, RepositoryEntry,    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 FormatTarget {    Buffer,    Ranges(Vec<Selection<Point>>),} impl FormatTarget {    pub fn as_selections(&self) -> Option<&[Selection<Point>]> {        match self {            FormatTarget::Buffer => None,            FormatTarget::Ranges(selections) => Some(selections.as_slice()),        }    }} // proto::RegisterBufferWithLanguageServer {} pub type OpenLspBufferHandle = Model<Model<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>, String)>),    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 {    worktree_store: Model<WorktreeStore>,    toolchain_store: Model<ToolchainStore>,    http_client: Arc<dyn HttpClient>,    environment: Model<ProjectEnvironment>,    fs: Arc<dyn Fs>,    languages: Arc<LanguageRegistry>,    language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>,    yarn: Model<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: Model<PrettierStore>,    current_lsp_settings: HashMap<LanguageServerName, LspSettings>,    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,    registered_buffers: HashMap<BufferId, usize>,} impl LocalLspStore {    fn start_language_server(        &mut self,        worktree_handle: &Model<Worktree>,        delegate: Arc<LocalLspAdapterDelegate>,        adapter: Arc<CachedLspAdapter>,        language: LanguageName,        cx: &mut ModelContext<LspStore>,    ) {        let worktree = worktree_handle.read(cx);        let worktree_id = worktree.id();        let root_path = worktree.abs_path();        let key = (worktree_id, adapter.name.clone());         if self.language_server_ids.contains_key(&key) {            return;        }         let project_settings = ProjectSettings::get(            Some(SettingsLocation {                worktree_id,                path: Path::new(""),            }),            cx,        );        let lsp = project_settings.lsp.get(&adapter.name);        let override_options = lsp.and_then(|s| s.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_server = cx.spawn({            let adapter = adapter.clone();            let server_name = adapter.name.clone();            let stderr_capture = stderr_capture.clone();             move |_lsp_store, 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(),                    cx,                )            }        });         let state = LanguageServerState::Starting({            let server_name = adapter.name.0.clone();            let delegate = delegate as Arc<dyn LspAdapterDelegate>;            let language = language.clone();            let key = key.clone();            let adapter = adapter.clone();             cx.spawn(move |this, 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(&delegate, toolchains.clone(), &mut cx)                            .await?;                         let mut initialization_options = adapter                            .adapter                            .clone()                            .initialization_options(&(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(), &language_server, delegate, adapter);                         let language_server = cx                            .update(|cx| {                                language_server.initialize(Some(initialization_params), 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>(                                lsp::DidChangeConfigurationParams {                                    settings: workspace_config,                                },                            )                            .ok();                         anyhow::Ok(language_server)                    }                }                .await;                 match result {                    Ok(server) => {                        this.update(&mut cx, |this, mut cx| {                            this.insert_newly_running_language_server(                                language,                                adapter,                                server.clone(),                                server_id,                                key,                                &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                    }                }            })        });         self.language_servers.insert(server_id, state);        self.language_server_ids.insert(key, server_id);    }     pub fn start_language_servers(        &mut self,        worktree: &Model<Worktree>,        language: LanguageName,        cx: &mut ModelContext<LspStore>,    ) {        let root_file = worktree            .update(cx, |tree, cx| tree.root_file(cx))            .map(|f| f as _);        let settings = language_settings(Some(language.clone()), root_file.as_ref(), cx);        if !settings.enable_language_server {            return;        }         let available_lsp_adapters = self.languages.clone().lsp_adapters(&language);        let available_language_servers = available_lsp_adapters            .iter()            .map(|lsp_adapter| lsp_adapter.name.clone())            .collect::<Vec<_>>();         let desired_language_servers =            settings.customized_language_servers(&available_language_servers);         let mut enabled_lsp_adapters: Vec<Arc<CachedLspAdapter>> = Vec::new();        for desired_language_server in desired_language_servers {            if let Some(adapter) = available_lsp_adapters                .iter()                .find(|adapter| adapter.name == desired_language_server)            {                enabled_lsp_adapters.push(adapter.clone());                continue;            }             if let Some(adapter) = self                .languages                .load_available_lsp_adapter(&desired_language_server)            {                self.languages                    .register_lsp_adapter(language.clone(), adapter.adapter.clone());                enabled_lsp_adapters.push(adapter);                continue;            }             log::warn!(                "no language server found matching '{}'",                desired_language_server.0            );        }         for adapter in &enabled_lsp_adapters {            let delegate = LocalLspAdapterDelegate::new(                self.languages.clone(),                &self.environment,                cx.weak_model(),                &worktree,                self.http_client.clone(),                self.fs.clone(),                cx,            );            self.start_language_server(worktree, delegate, adapter.clone(), language.clone(), cx);        }         // After starting all the language servers, reorder them to reflect the desired order        // based on the settings.        //        // This is done, in part, to ensure that language servers loaded at different points        // (e.g., native vs extension) still end up in the right order at the end, rather than        // it being based on which language server happened to be loaded in first.        self.languages            .reorder_language_servers(&language, enabled_lsp_adapters);    }     fn get_language_server_binary(        &self,        adapter: Arc<CachedLspAdapter>,        delegate: Arc<dyn LspAdapterDelegate>,        allow_binary_download: bool,        cx: &mut ModelContext<LspStore>,    ) -> 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: WeakModel<LspStore>,

Lines 1–500 of 8620.