5098 lines · 171.4 KB · Rust
RawDownload
pub mod buffer_store;mod color_extractor;pub mod connection_manager;pub mod debounced_delay;pub mod debugger;pub mod git_store;pub mod image_store;pub mod lsp_command;pub mod lsp_store;mod manifest_tree;pub mod prettier_store;pub mod project_settings;pub mod search;mod task_inventory;pub mod task_store;pub mod terminals;pub mod toolchain_store;pub mod worktree_store; #[cfg(test)]mod project_tests; mod direnv;mod environment;use buffer_diff::BufferDiff;pub use environment::{EnvironmentErrorMessage, ProjectEnvironmentEvent};use git_store::{Repository, RepositoryId};pub mod search_history;mod yarn; use crate::git_store::GitStore;pub use git_store::git_traversal::{ChildEntriesGitIter, GitEntry, GitEntryRef, GitTraversal}; use anyhow::{Context as _, Result, anyhow};use buffer_store::{BufferStore, BufferStoreEvent};use client::{    Client, Collaborator, PendingEntitySubscription, ProjectId, TypedEnvelope, UserStore, proto,};use clock::ReplicaId; use dap::{DapRegistry, DebugAdapterConfig, client::DebugAdapterClient}; use collections::{BTreeSet, HashMap, HashSet};use debounced_delay::DebouncedDelay;use debugger::{    breakpoint_store::BreakpointStore,    dap_store::{DapStore, DapStoreEvent},    session::Session,};pub use environment::ProjectEnvironment;use futures::{    StreamExt,    channel::mpsc::{self, UnboundedReceiver},    future::try_join_all,};pub use image_store::{ImageItem, ImageStore};use image_store::{ImageItemEvent, ImageStoreEvent}; use ::git::{blame::Blame, status::FileStatus};use gpui::{    AnyEntity, App, AppContext, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Hsla,    SharedString, Task, WeakEntity, Window,};use itertools::Itertools;use language::{    Buffer, BufferEvent, Capability, CodeLabel, Language, LanguageName, LanguageRegistry,    PointUtf16, ToOffset, ToPointUtf16, Toolchain, ToolchainList, Transaction, Unclipped,    language_settings::InlayHintKind, proto::split_operations,};use lsp::{    CodeActionKind, CompletionContext, CompletionItemKind, DocumentHighlightKind, InsertTextMode,    LanguageServerId, LanguageServerName, MessageActionItem,};use lsp_command::*;use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle};pub use manifest_tree::ManifestProviders;use node_runtime::NodeRuntime;use parking_lot::Mutex;pub use prettier_store::PrettierStore;use project_settings::{ProjectSettings, SettingsObserver, SettingsObserverEvent};use remote::{SshConnectionOptions, SshRemoteClient};use rpc::{    AnyProtoClient, ErrorCode,    proto::{FromProto, LanguageServerPromptResponse, SSH_PROJECT_ID, ToProto},};use search::{SearchInputKind, SearchQuery, SearchResult};use search_history::SearchHistory;use settings::{InvalidSettingsError, Settings, SettingsLocation, SettingsStore};use smol::channel::Receiver;use snippet::Snippet;use snippet_provider::SnippetProvider;use std::{    borrow::Cow,    ops::Range,    path::{Component, Path, PathBuf},    pin::pin,    str,    sync::Arc,    time::Duration,}; use task_store::TaskStore;use terminals::Terminals;use text::{Anchor, BufferId};use toolchain_store::EmptyToolchainStore;use util::{    ResultExt as _, maybe,    paths::{SanitizedPath, compare_paths},};use worktree::{CreatedEntry, Snapshot, Traversal};pub use worktree::{    Entry, EntryKind, FS_WATCH_LATENCY, File, LocalWorktree, PathChange, ProjectEntryId,    UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings,};use worktree_store::{WorktreeStore, WorktreeStoreEvent}; 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 task_inventory::{    BasicContextProvider, ContextProviderWithTasks, Inventory, TaskContexts, TaskSourceKind,}; pub use buffer_store::ProjectTransaction;pub use lsp_store::{    DiagnosticSummary, LanguageServerLogType, LanguageServerProgress, LanguageServerPromptRequest,    LanguageServerStatus, LanguageServerToQuery, LspStore, LspStoreEvent,    SERVER_PROGRESS_THROTTLE_TIMEOUT,};pub use toolchain_store::ToolchainStore;const MAX_PROJECT_SEARCH_HISTORY_SIZE: usize = 500;const MAX_SEARCH_RESULT_FILES: usize = 5_000;const MAX_SEARCH_RESULT_RANGES: usize = 10_000; pub trait ProjectItem {    fn try_open(        project: &Entity<Project>,        path: &ProjectPath,        cx: &mut App,    ) -> Option<Task<Result<Entity<Self>>>>    where        Self: Sized;    fn entry_id(&self, cx: &App) -> Option<ProjectEntryId>;    fn project_path(&self, cx: &App) -> Option<ProjectPath>;    fn is_dirty(&self) -> bool;} #[derive(Clone)]pub enum OpenedBufferEvent {    Disconnected,    Ok(BufferId),    Err(BufferId, Arc<anyhow::Error>),} /// Semantics-aware entity that is relevant to one or more [`Worktree`] with the files./// `Project` is responsible for tasks, LSP and collab queries, synchronizing worktree states accordingly./// Maps [`Worktree`] entries with its own logic using [`ProjectEntryId`] and [`ProjectPath`] structs.////// Can be either local (for the project opened on the same host) or remote.(for collab projects, browsed by multiple remote users).pub struct Project {    active_entry: Option<ProjectEntryId>,    buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,    languages: Arc<LanguageRegistry>,    debug_adapters: Arc<DapRegistry>,    dap_store: Entity<DapStore>,     breakpoint_store: Entity<BreakpointStore>,    client: Arc<client::Client>,    join_project_response_message_id: u32,    task_store: Entity<TaskStore>,    user_store: Entity<UserStore>,    fs: Arc<dyn Fs>,    ssh_client: Option<Entity<SshRemoteClient>>,    client_state: ProjectClientState,    git_store: Entity<GitStore>,    collaborators: HashMap<proto::PeerId, Collaborator>,    client_subscriptions: Vec<client::Subscription>,    worktree_store: Entity<WorktreeStore>,    buffer_store: Entity<BufferStore>,    image_store: Entity<ImageStore>,    lsp_store: Entity<LspStore>,    _subscriptions: Vec<gpui::Subscription>,    buffers_needing_diff: HashSet<WeakEntity<Buffer>>,    git_diff_debouncer: DebouncedDelay<Self>,    remotely_created_models: Arc<Mutex<RemotelyCreatedModels>>,    terminals: Terminals,    node: Option<NodeRuntime>,    search_history: SearchHistory,    search_included_history: SearchHistory,    search_excluded_history: SearchHistory,    snippets: Entity<SnippetProvider>,    environment: Entity<ProjectEnvironment>,    settings_observer: Entity<SettingsObserver>,    toolchain_store: Option<Entity<ToolchainStore>>,} #[derive(Default)]struct RemotelyCreatedModels {    worktrees: Vec<Entity<Worktree>>,    buffers: Vec<Entity<Buffer>>,    retain_count: usize,} struct RemotelyCreatedModelGuard {    remote_models: std::sync::Weak<Mutex<RemotelyCreatedModels>>,} impl Drop for RemotelyCreatedModelGuard {    fn drop(&mut self) {        if let Some(remote_models) = self.remote_models.upgrade() {            let mut remote_models = remote_models.lock();            assert!(                remote_models.retain_count > 0,                "RemotelyCreatedModelGuard dropped too many times"            );            remote_models.retain_count -= 1;            if remote_models.retain_count == 0 {                remote_models.buffers.clear();                remote_models.worktrees.clear();            }        }    }}/// Message ordered with respect to buffer operations#[derive(Debug)]enum BufferOrderedMessage {    Operation {        buffer_id: BufferId,        operation: proto::Operation,    },    LanguageServerUpdate {        language_server_id: LanguageServerId,        message: proto::update_language_server::Variant,    },    Resync,} #[derive(Debug)]enum ProjectClientState {    /// Single-player mode.    Local,    /// Multi-player mode but still a local project.    Shared { remote_id: u64 },    /// Multi-player mode but working on a remote project.    Remote {        sharing_has_stopped: bool,        capability: Capability,        remote_id: u64,        replica_id: ReplicaId,    },} #[derive(Clone, Debug, PartialEq)]pub enum Event {    LanguageServerAdded(LanguageServerId, LanguageServerName, Option<WorktreeId>),    LanguageServerRemoved(LanguageServerId),    LanguageServerLog(LanguageServerId, LanguageServerLogType, String),    Toast {        notification_id: SharedString,        message: String,    },    HideToast {        notification_id: SharedString,    },    LanguageServerPrompt(LanguageServerPromptRequest),    LanguageNotFound(Entity<Buffer>),    ActiveEntryChanged(Option<ProjectEntryId>),    ActivateProjectPanel,    WorktreeAdded(WorktreeId),    WorktreeOrderChanged,    WorktreeRemoved(WorktreeId),    WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),    DiskBasedDiagnosticsStarted {        language_server_id: LanguageServerId,    },    DiskBasedDiagnosticsFinished {        language_server_id: LanguageServerId,    },    DiagnosticsUpdated {        path: ProjectPath,        language_server_id: LanguageServerId,    },    RemoteIdChanged(Option<u64>),    DisconnectedFromHost,    DisconnectedFromSshRemote,    Closed,    DeletedEntry(WorktreeId, ProjectEntryId),    CollaboratorUpdated {        old_peer_id: proto::PeerId,        new_peer_id: proto::PeerId,    },    CollaboratorJoined(proto::PeerId),    CollaboratorLeft(proto::PeerId),    HostReshared,    Reshared,    Rejoined,    RefreshInlayHints,    RefreshCodeLens,    RevealInProjectPanel(ProjectEntryId),    SnippetEdit(BufferId, Vec<(lsp::Range, Snippet)>),    ExpandedAllForEntry(WorktreeId, ProjectEntryId),} pub enum DebugAdapterClientState {    Starting(Task<Option<Arc<DebugAdapterClient>>>),    Running(Arc<DebugAdapterClient>),} #[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]pub struct ProjectPath {    pub worktree_id: WorktreeId,    pub path: Arc<Path>,} impl ProjectPath {    pub fn from_proto(p: proto::ProjectPath) -> Self {        Self {            worktree_id: WorktreeId::from_proto(p.worktree_id),            path: Arc::<Path>::from_proto(p.path),        }    }     pub fn to_proto(&self) -> proto::ProjectPath {        proto::ProjectPath {            worktree_id: self.worktree_id.to_proto(),            path: self.path.as_ref().to_proto(),        }    }     pub fn root_path(worktree_id: WorktreeId) -> Self {        Self {            worktree_id,            path: Path::new("").into(),        }    }} #[derive(Debug, Default)]pub enum PrepareRenameResponse {    Success(Range<Anchor>),    OnlyUnpreparedRenameSupported,    #[default]    InvalidPosition,} #[derive(Debug, Clone, PartialEq, Eq)]pub struct InlayHint {    pub position: language::Anchor,    pub label: InlayHintLabel,    pub kind: Option<InlayHintKind>,    pub padding_left: bool,    pub padding_right: bool,    pub tooltip: Option<InlayHintTooltip>,    pub resolve_state: ResolveState,} /// The user's intent behind a given completion confirmation#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]pub enum CompletionIntent {    /// The user intends to 'commit' this result, if possible    /// completion confirmations should run side effects.    ///    /// For LSP completions, will respect the setting `completions.lsp_insert_mode`.    Complete,    /// Similar to [Self::Complete], but behaves like `lsp_insert_mode` is set to `insert`.    CompleteWithInsert,    /// Similar to [Self::Complete], but behaves like `lsp_insert_mode` is set to `replace`.    CompleteWithReplace,    /// The user intends to continue 'composing' this completion    /// completion confirmations should not run side effects and    /// let the user continue composing their action    Compose,} impl CompletionIntent {    pub fn is_complete(&self) -> bool {        self == &Self::Complete    }     pub fn is_compose(&self) -> bool {        self == &Self::Compose    }} /// Similar to `CoreCompletion`, but with extra metadata attached.#[derive(Clone)]pub struct Completion {    /// The range of text that will be replaced by this completion.    pub replace_range: Range<Anchor>,    /// The new text that will be inserted.    pub new_text: String,    /// A label for this completion that is shown in the menu.    pub label: CodeLabel,    /// The documentation for this completion.    pub documentation: Option<CompletionDocumentation>,    /// Completion data source which it was constructed from.    pub source: CompletionSource,    /// A path to an icon for this completion that is shown in the menu.    pub icon_path: Option<SharedString>,    /// Whether to adjust indentation (the default) or not.    pub insert_text_mode: Option<InsertTextMode>,    /// An optional callback to invoke when this completion is confirmed.    /// Returns, whether new completions should be retriggered after the current one.    /// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.    /// if no confirmation is provided or `false` is returned, the completion will be committed.    pub confirm: Option<Arc<dyn Send + Sync + Fn(CompletionIntent, &mut Window, &mut App) -> bool>>,} #[derive(Debug, Clone)]pub enum CompletionSource {    Lsp {        /// The alternate `insert` range, if provided by the LSP server.        insert_range: Option<Range<Anchor>>,        /// The id of the language server that produced this completion.        server_id: LanguageServerId,        /// The raw completion provided by the language server.        lsp_completion: Box<lsp::CompletionItem>,        /// A set of defaults for this completion item.        lsp_defaults: Option<Arc<lsp::CompletionListItemDefaults>>,        /// Whether this completion has been resolved, to ensure it happens once per completion.        resolved: bool,    },    Custom,    BufferWord {        word_range: Range<Anchor>,        resolved: bool,    },} impl CompletionSource {    pub fn server_id(&self) -> Option<LanguageServerId> {        if let CompletionSource::Lsp { server_id, .. } = self {            Some(*server_id)        } else {            None        }    }     pub fn lsp_completion(&self, apply_defaults: bool) -> Option<Cow<lsp::CompletionItem>> {        if let Self::Lsp {            lsp_completion,            lsp_defaults,            ..        } = self        {            if apply_defaults {                if let Some(lsp_defaults) = lsp_defaults {                    let mut completion_with_defaults = *lsp_completion.clone();                    let default_commit_characters = lsp_defaults.commit_characters.as_ref();                    let default_edit_range = lsp_defaults.edit_range.as_ref();                    let default_insert_text_format = lsp_defaults.insert_text_format.as_ref();                    let default_insert_text_mode = lsp_defaults.insert_text_mode.as_ref();                     if default_commit_characters.is_some()                        || default_edit_range.is_some()                        || default_insert_text_format.is_some()                        || default_insert_text_mode.is_some()                    {                        if completion_with_defaults.commit_characters.is_none()                            && default_commit_characters.is_some()                        {                            completion_with_defaults.commit_characters =                                default_commit_characters.cloned()                        }                        if completion_with_defaults.text_edit.is_none() {                            match default_edit_range {                                Some(lsp::CompletionListItemDefaultsEditRange::Range(range)) => {                                    completion_with_defaults.text_edit =                                        Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {                                            range: *range,                                            new_text: completion_with_defaults.label.clone(),                                        }))                                }                                Some(                                    lsp::CompletionListItemDefaultsEditRange::InsertAndReplace {                                        insert,                                        replace,                                    },                                ) => {                                    completion_with_defaults.text_edit =                                        Some(lsp::CompletionTextEdit::InsertAndReplace(                                            lsp::InsertReplaceEdit {                                                new_text: completion_with_defaults.label.clone(),                                                insert: *insert,                                                replace: *replace,                                            },                                        ))                                }                                None => {}                            }                        }                        if completion_with_defaults.insert_text_format.is_none()                            && default_insert_text_format.is_some()                        {                            completion_with_defaults.insert_text_format =                                default_insert_text_format.cloned()                        }                        if completion_with_defaults.insert_text_mode.is_none()                            && default_insert_text_mode.is_some()

Lines 1–500 of 5098.