project.rs

   1pub mod buffer_store;
   2mod color_extractor;
   3pub mod connection_manager;
   4pub mod debounced_delay;
   5pub mod lsp_command;
   6pub mod lsp_ext_command;
   7pub mod lsp_store;
   8pub mod prettier_store;
   9pub mod project_settings;
  10pub mod search;
  11mod task_inventory;
  12pub mod task_store;
  13pub mod terminals;
  14pub mod worktree_store;
  15
  16#[cfg(test)]
  17mod project_tests;
  18
  19mod direnv;
  20mod environment;
  21pub use environment::EnvironmentErrorMessage;
  22pub mod search_history;
  23mod yarn;
  24
  25use anyhow::{anyhow, Result};
  26use buffer_store::{BufferStore, BufferStoreEvent};
  27use client::{
  28    proto, Client, Collaborator, DevServerProjectId, PendingEntitySubscription, ProjectId,
  29    TypedEnvelope, UserStore,
  30};
  31use clock::ReplicaId;
  32use collections::{BTreeSet, HashMap, HashSet};
  33use debounced_delay::DebouncedDelay;
  34pub use environment::ProjectEnvironment;
  35use futures::{
  36    channel::mpsc::{self, UnboundedReceiver},
  37    future::try_join_all,
  38    StreamExt,
  39};
  40
  41use git::{blame::Blame, repository::GitRepository};
  42use gpui::{
  43    AnyModel, AppContext, AsyncAppContext, BorrowAppContext, Context, EventEmitter, Hsla, Model,
  44    ModelContext, SharedString, Task, WeakModel, WindowContext,
  45};
  46use itertools::Itertools;
  47use language::{
  48    language_settings::InlayHintKind, proto::split_operations, Buffer, BufferEvent,
  49    CachedLspAdapter, Capability, CodeLabel, DiagnosticEntry, Documentation, File as _, Language,
  50    LanguageRegistry, LanguageServerName, PointUtf16, ToOffset, ToPointUtf16, Transaction,
  51    Unclipped,
  52};
  53use lsp::{
  54    CompletionContext, CompletionItemKind, DocumentHighlightKind, LanguageServer, LanguageServerId,
  55};
  56use lsp_command::*;
  57use node_runtime::NodeRuntime;
  58use parking_lot::{Mutex, RwLock};
  59pub use prettier_store::PrettierStore;
  60use project_settings::{ProjectSettings, SettingsObserver, SettingsObserverEvent};
  61use remote::{SshConnectionOptions, SshRemoteClient};
  62use rpc::{proto::SSH_PROJECT_ID, AnyProtoClient, ErrorCode};
  63use search::{SearchInputKind, SearchQuery, SearchResult};
  64use search_history::SearchHistory;
  65use settings::{InvalidSettingsError, Settings, SettingsLocation, SettingsStore};
  66use smol::channel::Receiver;
  67use snippet::Snippet;
  68use snippet_provider::SnippetProvider;
  69use std::{
  70    borrow::Cow,
  71    ops::Range,
  72    path::{Component, Path, PathBuf},
  73    str,
  74    sync::Arc,
  75    time::Duration,
  76};
  77use task_store::TaskStore;
  78use terminals::Terminals;
  79use text::{Anchor, BufferId};
  80use util::{paths::compare_paths, ResultExt as _};
  81use worktree::{CreatedEntry, Snapshot, Traversal};
  82use worktree_store::{WorktreeStore, WorktreeStoreEvent};
  83
  84pub use fs::*;
  85pub use language::Location;
  86#[cfg(any(test, feature = "test-support"))]
  87pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
  88pub use task_inventory::{
  89    BasicContextProvider, ContextProviderWithTasks, Inventory, TaskSourceKind,
  90};
  91pub use worktree::{
  92    Entry, EntryKind, File, LocalWorktree, PathChange, ProjectEntryId, RepositoryEntry,
  93    UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings,
  94    FS_WATCH_LATENCY,
  95};
  96
  97pub use buffer_store::ProjectTransaction;
  98pub use lsp_store::{
  99    DiagnosticSummary, LanguageServerLogType, LanguageServerProgress, LanguageServerPromptRequest,
 100    LanguageServerStatus, LanguageServerToQuery, LspStore, LspStoreEvent,
 101    SERVER_PROGRESS_THROTTLE_TIMEOUT,
 102};
 103
 104const MAX_PROJECT_SEARCH_HISTORY_SIZE: usize = 500;
 105const MAX_SEARCH_RESULT_FILES: usize = 5_000;
 106const MAX_SEARCH_RESULT_RANGES: usize = 10_000;
 107
 108pub trait Item {
 109    fn try_open(
 110        project: &Model<Project>,
 111        path: &ProjectPath,
 112        cx: &mut AppContext,
 113    ) -> Option<Task<Result<Model<Self>>>>
 114    where
 115        Self: Sized;
 116    fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
 117    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
 118}
 119
 120#[derive(Clone)]
 121pub enum OpenedBufferEvent {
 122    Disconnected,
 123    Ok(BufferId),
 124    Err(BufferId, Arc<anyhow::Error>),
 125}
 126
 127/// Semantics-aware entity that is relevant to one or more [`Worktree`] with the files.
 128/// `Project` is responsible for tasks, LSP and collab queries, synchronizing worktree states accordingly.
 129/// Maps [`Worktree`] entries with its own logic using [`ProjectEntryId`] and [`ProjectPath`] structs.
 130///
 131/// Can be either local (for the project opened on the same host) or remote.(for collab projects, browsed by multiple remote users).
 132pub struct Project {
 133    active_entry: Option<ProjectEntryId>,
 134    buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,
 135    languages: Arc<LanguageRegistry>,
 136    client: Arc<client::Client>,
 137    join_project_response_message_id: u32,
 138    task_store: Model<TaskStore>,
 139    user_store: Model<UserStore>,
 140    fs: Arc<dyn Fs>,
 141    ssh_client: Option<Model<SshRemoteClient>>,
 142    client_state: ProjectClientState,
 143    collaborators: HashMap<proto::PeerId, Collaborator>,
 144    client_subscriptions: Vec<client::Subscription>,
 145    worktree_store: Model<WorktreeStore>,
 146    buffer_store: Model<BufferStore>,
 147    lsp_store: Model<LspStore>,
 148    _subscriptions: Vec<gpui::Subscription>,
 149    buffers_needing_diff: HashSet<WeakModel<Buffer>>,
 150    git_diff_debouncer: DebouncedDelay<Self>,
 151    remotely_created_models: Arc<Mutex<RemotelyCreatedModels>>,
 152    terminals: Terminals,
 153    node: Option<NodeRuntime>,
 154    hosted_project_id: Option<ProjectId>,
 155    dev_server_project_id: Option<client::DevServerProjectId>,
 156    search_history: SearchHistory,
 157    search_included_history: SearchHistory,
 158    search_excluded_history: SearchHistory,
 159    snippets: Model<SnippetProvider>,
 160    environment: Model<ProjectEnvironment>,
 161    settings_observer: Model<SettingsObserver>,
 162}
 163
 164#[derive(Default)]
 165struct RemotelyCreatedModels {
 166    worktrees: Vec<Model<Worktree>>,
 167    buffers: Vec<Model<Buffer>>,
 168    retain_count: usize,
 169}
 170
 171struct RemotelyCreatedModelGuard {
 172    remote_models: std::sync::Weak<Mutex<RemotelyCreatedModels>>,
 173}
 174
 175impl Drop for RemotelyCreatedModelGuard {
 176    fn drop(&mut self) {
 177        if let Some(remote_models) = self.remote_models.upgrade() {
 178            let mut remote_models = remote_models.lock();
 179            assert!(
 180                remote_models.retain_count > 0,
 181                "RemotelyCreatedModelGuard dropped too many times"
 182            );
 183            remote_models.retain_count -= 1;
 184            if remote_models.retain_count == 0 {
 185                remote_models.buffers.clear();
 186                remote_models.worktrees.clear();
 187            }
 188        }
 189    }
 190}
 191/// Message ordered with respect to buffer operations
 192#[derive(Debug)]
 193enum BufferOrderedMessage {
 194    Operation {
 195        buffer_id: BufferId,
 196        operation: proto::Operation,
 197    },
 198    LanguageServerUpdate {
 199        language_server_id: LanguageServerId,
 200        message: proto::update_language_server::Variant,
 201    },
 202    Resync,
 203}
 204
 205#[derive(Debug)]
 206enum ProjectClientState {
 207    Local,
 208    Shared {
 209        remote_id: u64,
 210    },
 211    Remote {
 212        sharing_has_stopped: bool,
 213        capability: Capability,
 214        remote_id: u64,
 215        replica_id: ReplicaId,
 216        in_room: bool,
 217    },
 218}
 219
 220#[derive(Clone, Debug, PartialEq)]
 221pub enum Event {
 222    LanguageServerAdded(LanguageServerId),
 223    LanguageServerRemoved(LanguageServerId),
 224    LanguageServerLog(LanguageServerId, LanguageServerLogType, String),
 225    Notification(String),
 226    LocalSettingsUpdated(Result<(), InvalidSettingsError>),
 227    LanguageServerPrompt(LanguageServerPromptRequest),
 228    LanguageNotFound(Model<Buffer>),
 229    ActiveEntryChanged(Option<ProjectEntryId>),
 230    ActivateProjectPanel,
 231    WorktreeAdded,
 232    WorktreeOrderChanged,
 233    WorktreeRemoved(WorktreeId),
 234    WorktreeUpdatedEntries(WorktreeId, UpdatedEntriesSet),
 235    WorktreeUpdatedGitRepositories,
 236    DiskBasedDiagnosticsStarted {
 237        language_server_id: LanguageServerId,
 238    },
 239    DiskBasedDiagnosticsFinished {
 240        language_server_id: LanguageServerId,
 241    },
 242    DiagnosticsUpdated {
 243        path: ProjectPath,
 244        language_server_id: LanguageServerId,
 245    },
 246    RemoteIdChanged(Option<u64>),
 247    DisconnectedFromHost,
 248    DisconnectedFromSshRemote,
 249    Closed,
 250    DeletedEntry(ProjectEntryId),
 251    CollaboratorUpdated {
 252        old_peer_id: proto::PeerId,
 253        new_peer_id: proto::PeerId,
 254    },
 255    CollaboratorJoined(proto::PeerId),
 256    CollaboratorLeft(proto::PeerId),
 257    HostReshared,
 258    Reshared,
 259    Rejoined,
 260    RefreshInlayHints,
 261    RevealInProjectPanel(ProjectEntryId),
 262    SnippetEdit(BufferId, Vec<(lsp::Range, Snippet)>),
 263}
 264
 265#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
 266pub struct ProjectPath {
 267    pub worktree_id: WorktreeId,
 268    pub path: Arc<Path>,
 269}
 270
 271impl ProjectPath {
 272    pub fn from_proto(p: proto::ProjectPath) -> Self {
 273        Self {
 274            worktree_id: WorktreeId::from_proto(p.worktree_id),
 275            path: Arc::from(PathBuf::from(p.path)),
 276        }
 277    }
 278
 279    pub fn to_proto(&self) -> proto::ProjectPath {
 280        proto::ProjectPath {
 281            worktree_id: self.worktree_id.to_proto(),
 282            path: self.path.to_string_lossy().to_string(),
 283        }
 284    }
 285}
 286
 287#[derive(Debug, Clone, PartialEq, Eq)]
 288pub struct InlayHint {
 289    pub position: language::Anchor,
 290    pub label: InlayHintLabel,
 291    pub kind: Option<InlayHintKind>,
 292    pub padding_left: bool,
 293    pub padding_right: bool,
 294    pub tooltip: Option<InlayHintTooltip>,
 295    pub resolve_state: ResolveState,
 296}
 297
 298/// The user's intent behind a given completion confirmation
 299#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
 300pub enum CompletionIntent {
 301    /// The user intends to 'commit' this result, if possible
 302    /// completion confirmations should run side effects
 303    Complete,
 304    /// The user intends to continue 'composing' this completion
 305    /// completion confirmations should not run side effects and
 306    /// let the user continue composing their action
 307    Compose,
 308}
 309
 310impl CompletionIntent {
 311    pub fn is_complete(&self) -> bool {
 312        self == &Self::Complete
 313    }
 314
 315    pub fn is_compose(&self) -> bool {
 316        self == &Self::Compose
 317    }
 318}
 319
 320/// A completion provided by a language server
 321#[derive(Clone)]
 322pub struct Completion {
 323    /// The range of the buffer that will be replaced.
 324    pub old_range: Range<Anchor>,
 325    /// The new text that will be inserted.
 326    pub new_text: String,
 327    /// A label for this completion that is shown in the menu.
 328    pub label: CodeLabel,
 329    /// The id of the language server that produced this completion.
 330    pub server_id: LanguageServerId,
 331    /// The documentation for this completion.
 332    pub documentation: Option<Documentation>,
 333    /// The raw completion provided by the language server.
 334    pub lsp_completion: lsp::CompletionItem,
 335    /// An optional callback to invoke when this completion is confirmed.
 336    /// Returns, whether new completions should be retriggered after the current one.
 337    /// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.
 338    /// if no confirmation is provided or `false` is returned, the completion will be committed.
 339    pub confirm: Option<Arc<dyn Send + Sync + Fn(CompletionIntent, &mut WindowContext) -> bool>>,
 340}
 341
 342impl std::fmt::Debug for Completion {
 343    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 344        f.debug_struct("Completion")
 345            .field("old_range", &self.old_range)
 346            .field("new_text", &self.new_text)
 347            .field("label", &self.label)
 348            .field("server_id", &self.server_id)
 349            .field("documentation", &self.documentation)
 350            .field("lsp_completion", &self.lsp_completion)
 351            .finish()
 352    }
 353}
 354
 355/// A completion provided by a language server
 356#[derive(Clone, Debug)]
 357pub(crate) struct CoreCompletion {
 358    old_range: Range<Anchor>,
 359    new_text: String,
 360    server_id: LanguageServerId,
 361    lsp_completion: lsp::CompletionItem,
 362}
 363
 364/// A code action provided by a language server.
 365#[derive(Clone, Debug)]
 366pub struct CodeAction {
 367    /// The id of the language server that produced this code action.
 368    pub server_id: LanguageServerId,
 369    /// The range of the buffer where this code action is applicable.
 370    pub range: Range<Anchor>,
 371    /// The raw code action provided by the language server.
 372    pub lsp_action: lsp::CodeAction,
 373}
 374
 375#[derive(Debug, Clone, PartialEq, Eq)]
 376pub enum ResolveState {
 377    Resolved,
 378    CanResolve(LanguageServerId, Option<lsp::LSPAny>),
 379    Resolving,
 380}
 381
 382impl InlayHint {
 383    pub fn text(&self) -> String {
 384        match &self.label {
 385            InlayHintLabel::String(s) => s.to_owned(),
 386            InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""),
 387        }
 388    }
 389}
 390
 391#[derive(Debug, Clone, PartialEq, Eq)]
 392pub enum InlayHintLabel {
 393    String(String),
 394    LabelParts(Vec<InlayHintLabelPart>),
 395}
 396
 397#[derive(Debug, Clone, PartialEq, Eq)]
 398pub struct InlayHintLabelPart {
 399    pub value: String,
 400    pub tooltip: Option<InlayHintLabelPartTooltip>,
 401    pub location: Option<(LanguageServerId, lsp::Location)>,
 402}
 403
 404#[derive(Debug, Clone, PartialEq, Eq)]
 405pub enum InlayHintTooltip {
 406    String(String),
 407    MarkupContent(MarkupContent),
 408}
 409
 410#[derive(Debug, Clone, PartialEq, Eq)]
 411pub enum InlayHintLabelPartTooltip {
 412    String(String),
 413    MarkupContent(MarkupContent),
 414}
 415
 416#[derive(Debug, Clone, PartialEq, Eq)]
 417pub struct MarkupContent {
 418    pub kind: HoverBlockKind,
 419    pub value: String,
 420}
 421
 422#[derive(Debug, Clone)]
 423pub struct LocationLink {
 424    pub origin: Option<Location>,
 425    pub target: Location,
 426}
 427
 428#[derive(Debug)]
 429pub struct DocumentHighlight {
 430    pub range: Range<language::Anchor>,
 431    pub kind: DocumentHighlightKind,
 432}
 433
 434#[derive(Clone, Debug)]
 435pub struct Symbol {
 436    pub language_server_name: LanguageServerName,
 437    pub source_worktree_id: WorktreeId,
 438    pub path: ProjectPath,
 439    pub label: CodeLabel,
 440    pub name: String,
 441    pub kind: lsp::SymbolKind,
 442    pub range: Range<Unclipped<PointUtf16>>,
 443    pub signature: [u8; 32],
 444}
 445
 446#[derive(Clone, Debug, PartialEq)]
 447pub struct HoverBlock {
 448    pub text: String,
 449    pub kind: HoverBlockKind,
 450}
 451
 452#[derive(Clone, Debug, PartialEq, Eq)]
 453pub enum HoverBlockKind {
 454    PlainText,
 455    Markdown,
 456    Code { language: String },
 457}
 458
 459#[derive(Debug, Clone)]
 460pub struct Hover {
 461    pub contents: Vec<HoverBlock>,
 462    pub range: Option<Range<language::Anchor>>,
 463    pub language: Option<Arc<Language>>,
 464}
 465
 466impl Hover {
 467    pub fn is_empty(&self) -> bool {
 468        self.contents.iter().all(|block| block.text.is_empty())
 469    }
 470}
 471
 472enum EntitySubscription {
 473    Project(PendingEntitySubscription<Project>),
 474    BufferStore(PendingEntitySubscription<BufferStore>),
 475    WorktreeStore(PendingEntitySubscription<WorktreeStore>),
 476    LspStore(PendingEntitySubscription<LspStore>),
 477    SettingsObserver(PendingEntitySubscription<SettingsObserver>),
 478}
 479
 480#[derive(Clone)]
 481pub enum DirectoryLister {
 482    Project(Model<Project>),
 483    Local(Arc<dyn Fs>),
 484}
 485
 486impl DirectoryLister {
 487    pub fn is_local(&self, cx: &AppContext) -> bool {
 488        match self {
 489            DirectoryLister::Local(_) => true,
 490            DirectoryLister::Project(project) => project.read(cx).is_local(),
 491        }
 492    }
 493
 494    pub fn resolve_tilde<'a>(&self, path: &'a String, cx: &AppContext) -> Cow<'a, str> {
 495        if self.is_local(cx) {
 496            shellexpand::tilde(path)
 497        } else {
 498            Cow::from(path)
 499        }
 500    }
 501
 502    pub fn default_query(&self, cx: &mut AppContext) -> String {
 503        if let DirectoryLister::Project(project) = self {
 504            if let Some(worktree) = project.read(cx).visible_worktrees(cx).next() {
 505                return worktree.read(cx).abs_path().to_string_lossy().to_string();
 506            }
 507        };
 508        "~/".to_string()
 509    }
 510
 511    pub fn list_directory(&self, path: String, cx: &mut AppContext) -> Task<Result<Vec<PathBuf>>> {
 512        match self {
 513            DirectoryLister::Project(project) => {
 514                project.update(cx, |project, cx| project.list_directory(path, cx))
 515            }
 516            DirectoryLister::Local(fs) => {
 517                let fs = fs.clone();
 518                cx.background_executor().spawn(async move {
 519                    let mut results = vec![];
 520                    let expanded = shellexpand::tilde(&path);
 521                    let query = Path::new(expanded.as_ref());
 522                    let mut response = fs.read_dir(query).await?;
 523                    while let Some(path) = response.next().await {
 524                        if let Some(file_name) = path?.file_name() {
 525                            results.push(PathBuf::from(file_name.to_os_string()));
 526                        }
 527                    }
 528                    Ok(results)
 529                })
 530            }
 531        }
 532    }
 533}
 534
 535#[cfg(any(test, feature = "test-support"))]
 536pub const DEFAULT_COMPLETION_CONTEXT: CompletionContext = CompletionContext {
 537    trigger_kind: lsp::CompletionTriggerKind::INVOKED,
 538    trigger_character: None,
 539};
 540
 541impl Project {
 542    pub fn init_settings(cx: &mut AppContext) {
 543        WorktreeSettings::register(cx);
 544        ProjectSettings::register(cx);
 545    }
 546
 547    pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
 548        connection_manager::init(client.clone(), cx);
 549        Self::init_settings(cx);
 550
 551        let client: AnyProtoClient = client.clone().into();
 552        client.add_model_message_handler(Self::handle_add_collaborator);
 553        client.add_model_message_handler(Self::handle_update_project_collaborator);
 554        client.add_model_message_handler(Self::handle_remove_collaborator);
 555        client.add_model_message_handler(Self::handle_update_project);
 556        client.add_model_message_handler(Self::handle_unshare_project);
 557        client.add_model_request_handler(Self::handle_update_buffer);
 558        client.add_model_message_handler(Self::handle_update_worktree);
 559        client.add_model_request_handler(Self::handle_synchronize_buffers);
 560
 561        client.add_model_request_handler(Self::handle_search_candidate_buffers);
 562        client.add_model_request_handler(Self::handle_open_buffer_by_id);
 563        client.add_model_request_handler(Self::handle_open_buffer_by_path);
 564        client.add_model_request_handler(Self::handle_open_new_buffer);
 565        client.add_model_message_handler(Self::handle_create_buffer_for_peer);
 566
 567        WorktreeStore::init(&client);
 568        BufferStore::init(&client);
 569        LspStore::init(&client);
 570        SettingsObserver::init(&client);
 571        TaskStore::init(Some(&client));
 572    }
 573
 574    pub fn local(
 575        client: Arc<Client>,
 576        node: NodeRuntime,
 577        user_store: Model<UserStore>,
 578        languages: Arc<LanguageRegistry>,
 579        fs: Arc<dyn Fs>,
 580        env: Option<HashMap<String, String>>,
 581        cx: &mut AppContext,
 582    ) -> Model<Self> {
 583        cx.new_model(|cx: &mut ModelContext<Self>| {
 584            let (tx, rx) = mpsc::unbounded();
 585            cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
 586                .detach();
 587            let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
 588            let worktree_store = cx.new_model(|_| WorktreeStore::local(false, fs.clone()));
 589            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
 590                .detach();
 591
 592            let buffer_store = cx.new_model(|cx| BufferStore::local(worktree_store.clone(), cx));
 593            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
 594                .detach();
 595
 596            let prettier_store = cx.new_model(|cx| {
 597                PrettierStore::new(
 598                    node.clone(),
 599                    fs.clone(),
 600                    languages.clone(),
 601                    worktree_store.clone(),
 602                    cx,
 603                )
 604            });
 605
 606            let environment = ProjectEnvironment::new(&worktree_store, env, cx);
 607
 608            let task_store = cx.new_model(|cx| {
 609                TaskStore::local(
 610                    fs.clone(),
 611                    buffer_store.downgrade(),
 612                    worktree_store.clone(),
 613                    environment.clone(),
 614                    cx,
 615                )
 616            });
 617
 618            let settings_observer = cx.new_model(|cx| {
 619                SettingsObserver::new_local(
 620                    fs.clone(),
 621                    worktree_store.clone(),
 622                    task_store.clone(),
 623                    cx,
 624                )
 625            });
 626            cx.subscribe(&settings_observer, Self::on_settings_observer_event)
 627                .detach();
 628
 629            let lsp_store = cx.new_model(|cx| {
 630                LspStore::new_local(
 631                    buffer_store.clone(),
 632                    worktree_store.clone(),
 633                    prettier_store.clone(),
 634                    environment.clone(),
 635                    languages.clone(),
 636                    Some(client.http_client()),
 637                    fs.clone(),
 638                    cx,
 639                )
 640            });
 641            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
 642
 643            Self {
 644                buffer_ordered_messages_tx: tx,
 645                collaborators: Default::default(),
 646                worktree_store,
 647                buffer_store,
 648                lsp_store,
 649                join_project_response_message_id: 0,
 650                client_state: ProjectClientState::Local,
 651                client_subscriptions: Vec::new(),
 652                _subscriptions: vec![cx.on_release(Self::release)],
 653                active_entry: None,
 654                snippets,
 655                languages,
 656                client,
 657                task_store,
 658                user_store,
 659                settings_observer,
 660                fs,
 661                ssh_client: None,
 662                buffers_needing_diff: Default::default(),
 663                git_diff_debouncer: DebouncedDelay::new(),
 664                terminals: Terminals {
 665                    local_handles: Vec::new(),
 666                },
 667                node: Some(node),
 668                hosted_project_id: None,
 669                dev_server_project_id: None,
 670                search_history: Self::new_search_history(),
 671                environment,
 672                remotely_created_models: Default::default(),
 673
 674                search_included_history: Self::new_search_history(),
 675                search_excluded_history: Self::new_search_history(),
 676            }
 677        })
 678    }
 679
 680    pub fn ssh(
 681        ssh: Model<SshRemoteClient>,
 682        client: Arc<Client>,
 683        node: NodeRuntime,
 684        user_store: Model<UserStore>,
 685        languages: Arc<LanguageRegistry>,
 686        fs: Arc<dyn Fs>,
 687        cx: &mut AppContext,
 688    ) -> Model<Self> {
 689        cx.new_model(|cx: &mut ModelContext<Self>| {
 690            let (tx, rx) = mpsc::unbounded();
 691            cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
 692                .detach();
 693            let global_snippets_dir = paths::config_dir().join("snippets");
 694            let snippets =
 695                SnippetProvider::new(fs.clone(), BTreeSet::from_iter([global_snippets_dir]), cx);
 696
 697            let ssh_proto = ssh.read(cx).to_proto_client();
 698            let worktree_store =
 699                cx.new_model(|_| WorktreeStore::remote(false, ssh_proto.clone(), 0, None));
 700            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
 701                .detach();
 702
 703            let buffer_store = cx.new_model(|cx| {
 704                BufferStore::remote(
 705                    worktree_store.clone(),
 706                    ssh.read(cx).to_proto_client(),
 707                    SSH_PROJECT_ID,
 708                    cx,
 709                )
 710            });
 711            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
 712                .detach();
 713
 714            let task_store = cx.new_model(|cx| {
 715                TaskStore::remote(
 716                    fs.clone(),
 717                    buffer_store.downgrade(),
 718                    worktree_store.clone(),
 719                    ssh.read(cx).to_proto_client(),
 720                    SSH_PROJECT_ID,
 721                    cx,
 722                )
 723            });
 724
 725            let settings_observer = cx.new_model(|cx| {
 726                SettingsObserver::new_ssh(
 727                    ssh_proto.clone(),
 728                    worktree_store.clone(),
 729                    task_store.clone(),
 730                    cx,
 731                )
 732            });
 733            cx.subscribe(&settings_observer, Self::on_settings_observer_event)
 734                .detach();
 735
 736            let environment = ProjectEnvironment::new(&worktree_store, None, cx);
 737            let lsp_store = cx.new_model(|cx| {
 738                LspStore::new_remote(
 739                    buffer_store.clone(),
 740                    worktree_store.clone(),
 741                    languages.clone(),
 742                    ssh_proto.clone(),
 743                    SSH_PROJECT_ID,
 744                    cx,
 745                )
 746            });
 747            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
 748
 749            cx.on_release(|this, cx| {
 750                if let Some(ssh_client) = this.ssh_client.as_ref() {
 751                    ssh_client
 752                        .read(cx)
 753                        .to_proto_client()
 754                        .send(proto::ShutdownRemoteServer {})
 755                        .log_err();
 756                }
 757            })
 758            .detach();
 759
 760            cx.subscribe(&ssh, Self::on_ssh_event).detach();
 761            cx.observe(&ssh, |_, _, cx| cx.notify()).detach();
 762
 763            let this = Self {
 764                buffer_ordered_messages_tx: tx,
 765                collaborators: Default::default(),
 766                worktree_store,
 767                buffer_store,
 768                lsp_store,
 769                join_project_response_message_id: 0,
 770                client_state: ProjectClientState::Local,
 771                client_subscriptions: Vec::new(),
 772                _subscriptions: vec![cx.on_release(Self::release)],
 773                active_entry: None,
 774                snippets,
 775                languages,
 776                client,
 777                task_store,
 778                user_store,
 779                settings_observer,
 780                fs,
 781                ssh_client: Some(ssh.clone()),
 782                buffers_needing_diff: Default::default(),
 783                git_diff_debouncer: DebouncedDelay::new(),
 784                terminals: Terminals {
 785                    local_handles: Vec::new(),
 786                },
 787                node: Some(node),
 788                hosted_project_id: None,
 789                dev_server_project_id: None,
 790                search_history: Self::new_search_history(),
 791                environment,
 792                remotely_created_models: Default::default(),
 793
 794                search_included_history: Self::new_search_history(),
 795                search_excluded_history: Self::new_search_history(),
 796            };
 797
 798            let ssh = ssh.read(cx);
 799            ssh.subscribe_to_entity(SSH_PROJECT_ID, &cx.handle());
 800            ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.buffer_store);
 801            ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.worktree_store);
 802            ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.lsp_store);
 803            ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.settings_observer);
 804
 805            ssh_proto.add_model_message_handler(Self::handle_create_buffer_for_peer);
 806            ssh_proto.add_model_message_handler(Self::handle_update_worktree);
 807            ssh_proto.add_model_message_handler(Self::handle_update_project);
 808            ssh_proto.add_model_request_handler(BufferStore::handle_update_buffer);
 809            BufferStore::init(&ssh_proto);
 810            LspStore::init(&ssh_proto);
 811            SettingsObserver::init(&ssh_proto);
 812            TaskStore::init(Some(&ssh_proto));
 813
 814            this
 815        })
 816    }
 817
 818    pub async fn remote(
 819        remote_id: u64,
 820        client: Arc<Client>,
 821        user_store: Model<UserStore>,
 822        languages: Arc<LanguageRegistry>,
 823        fs: Arc<dyn Fs>,
 824        cx: AsyncAppContext,
 825    ) -> Result<Model<Self>> {
 826        let project =
 827            Self::in_room(remote_id, client, user_store, languages, fs, cx.clone()).await?;
 828        cx.update(|cx| {
 829            connection_manager::Manager::global(cx).update(cx, |manager, cx| {
 830                manager.maintain_project_connection(&project, cx)
 831            })
 832        })?;
 833        Ok(project)
 834    }
 835
 836    pub async fn in_room(
 837        remote_id: u64,
 838        client: Arc<Client>,
 839        user_store: Model<UserStore>,
 840        languages: Arc<LanguageRegistry>,
 841        fs: Arc<dyn Fs>,
 842        cx: AsyncAppContext,
 843    ) -> Result<Model<Self>> {
 844        client.authenticate_and_connect(true, &cx).await?;
 845
 846        let subscriptions = [
 847            EntitySubscription::Project(client.subscribe_to_entity::<Self>(remote_id)?),
 848            EntitySubscription::BufferStore(client.subscribe_to_entity::<BufferStore>(remote_id)?),
 849            EntitySubscription::WorktreeStore(
 850                client.subscribe_to_entity::<WorktreeStore>(remote_id)?,
 851            ),
 852            EntitySubscription::LspStore(client.subscribe_to_entity::<LspStore>(remote_id)?),
 853            EntitySubscription::SettingsObserver(
 854                client.subscribe_to_entity::<SettingsObserver>(remote_id)?,
 855            ),
 856        ];
 857        let response = client
 858            .request_envelope(proto::JoinProject {
 859                project_id: remote_id,
 860            })
 861            .await?;
 862        Self::from_join_project_response(
 863            response,
 864            subscriptions,
 865            client,
 866            false,
 867            user_store,
 868            languages,
 869            fs,
 870            cx,
 871        )
 872        .await
 873    }
 874
 875    #[allow(clippy::too_many_arguments)]
 876    async fn from_join_project_response(
 877        response: TypedEnvelope<proto::JoinProjectResponse>,
 878        subscriptions: [EntitySubscription; 5],
 879        client: Arc<Client>,
 880        run_tasks: bool,
 881        user_store: Model<UserStore>,
 882        languages: Arc<LanguageRegistry>,
 883        fs: Arc<dyn Fs>,
 884        mut cx: AsyncAppContext,
 885    ) -> Result<Model<Self>> {
 886        let remote_id = response.payload.project_id;
 887        let role = response.payload.role();
 888
 889        let worktree_store = cx.new_model(|_| {
 890            WorktreeStore::remote(
 891                true,
 892                client.clone().into(),
 893                response.payload.project_id,
 894                response
 895                    .payload
 896                    .dev_server_project_id
 897                    .map(DevServerProjectId),
 898            )
 899        })?;
 900        let buffer_store = cx.new_model(|cx| {
 901            BufferStore::remote(worktree_store.clone(), client.clone().into(), remote_id, cx)
 902        })?;
 903
 904        let lsp_store = cx.new_model(|cx| {
 905            let mut lsp_store = LspStore::new_remote(
 906                buffer_store.clone(),
 907                worktree_store.clone(),
 908                languages.clone(),
 909                client.clone().into(),
 910                remote_id,
 911                cx,
 912            );
 913            lsp_store.set_language_server_statuses_from_proto(response.payload.language_servers);
 914            lsp_store
 915        })?;
 916
 917        let task_store = cx.new_model(|cx| {
 918            if run_tasks {
 919                TaskStore::remote(
 920                    fs.clone(),
 921                    buffer_store.downgrade(),
 922                    worktree_store.clone(),
 923                    client.clone().into(),
 924                    remote_id,
 925                    cx,
 926                )
 927            } else {
 928                TaskStore::Noop
 929            }
 930        })?;
 931
 932        let settings_observer = cx.new_model(|cx| {
 933            SettingsObserver::new_remote(worktree_store.clone(), task_store.clone(), cx)
 934        })?;
 935
 936        let this = cx.new_model(|cx| {
 937            let replica_id = response.payload.replica_id as ReplicaId;
 938
 939            let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
 940
 941            let mut worktrees = Vec::new();
 942            for worktree in response.payload.worktrees {
 943                let worktree =
 944                    Worktree::remote(remote_id, replica_id, worktree, client.clone().into(), cx);
 945                worktrees.push(worktree);
 946            }
 947
 948            let (tx, rx) = mpsc::unbounded();
 949            cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
 950                .detach();
 951
 952            cx.subscribe(&worktree_store, Self::on_worktree_store_event)
 953                .detach();
 954
 955            cx.subscribe(&buffer_store, Self::on_buffer_store_event)
 956                .detach();
 957            cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
 958            cx.subscribe(&settings_observer, Self::on_settings_observer_event)
 959                .detach();
 960
 961            let mut this = Self {
 962                buffer_ordered_messages_tx: tx,
 963                buffer_store: buffer_store.clone(),
 964                worktree_store: worktree_store.clone(),
 965                lsp_store: lsp_store.clone(),
 966                active_entry: None,
 967                collaborators: Default::default(),
 968                join_project_response_message_id: response.message_id,
 969                languages,
 970                user_store: user_store.clone(),
 971                task_store,
 972                snippets,
 973                fs,
 974                ssh_client: None,
 975                settings_observer: settings_observer.clone(),
 976                client_subscriptions: Default::default(),
 977                _subscriptions: vec![cx.on_release(Self::release)],
 978                client: client.clone(),
 979                client_state: ProjectClientState::Remote {
 980                    sharing_has_stopped: false,
 981                    capability: Capability::ReadWrite,
 982                    remote_id,
 983                    replica_id,
 984                    in_room: response.payload.dev_server_project_id.is_none(),
 985                },
 986                buffers_needing_diff: Default::default(),
 987                git_diff_debouncer: DebouncedDelay::new(),
 988                terminals: Terminals {
 989                    local_handles: Vec::new(),
 990                },
 991                node: None,
 992                hosted_project_id: None,
 993                dev_server_project_id: response
 994                    .payload
 995                    .dev_server_project_id
 996                    .map(DevServerProjectId),
 997                search_history: Self::new_search_history(),
 998                search_included_history: Self::new_search_history(),
 999                search_excluded_history: Self::new_search_history(),
1000                environment: ProjectEnvironment::new(&worktree_store, None, cx),
1001                remotely_created_models: Arc::new(Mutex::new(RemotelyCreatedModels::default())),
1002            };
1003            this.set_role(role, cx);
1004            for worktree in worktrees {
1005                this.add_worktree(&worktree, cx);
1006            }
1007            this
1008        })?;
1009
1010        let subscriptions = subscriptions
1011            .into_iter()
1012            .map(|s| match s {
1013                EntitySubscription::BufferStore(subscription) => {
1014                    subscription.set_model(&buffer_store, &mut cx)
1015                }
1016                EntitySubscription::WorktreeStore(subscription) => {
1017                    subscription.set_model(&worktree_store, &mut cx)
1018                }
1019                EntitySubscription::SettingsObserver(subscription) => {
1020                    subscription.set_model(&settings_observer, &mut cx)
1021                }
1022                EntitySubscription::Project(subscription) => subscription.set_model(&this, &mut cx),
1023                EntitySubscription::LspStore(subscription) => {
1024                    subscription.set_model(&lsp_store, &mut cx)
1025                }
1026            })
1027            .collect::<Vec<_>>();
1028
1029        let user_ids = response
1030            .payload
1031            .collaborators
1032            .iter()
1033            .map(|peer| peer.user_id)
1034            .collect();
1035        user_store
1036            .update(&mut cx, |user_store, cx| user_store.get_users(user_ids, cx))?
1037            .await?;
1038
1039        this.update(&mut cx, |this, cx| {
1040            this.set_collaborators_from_proto(response.payload.collaborators, cx)?;
1041            this.client_subscriptions.extend(subscriptions);
1042            anyhow::Ok(())
1043        })??;
1044
1045        Ok(this)
1046    }
1047
1048    pub async fn hosted(
1049        remote_id: ProjectId,
1050        user_store: Model<UserStore>,
1051        client: Arc<Client>,
1052        languages: Arc<LanguageRegistry>,
1053        fs: Arc<dyn Fs>,
1054        cx: AsyncAppContext,
1055    ) -> Result<Model<Self>> {
1056        client.authenticate_and_connect(true, &cx).await?;
1057
1058        let subscriptions = [
1059            EntitySubscription::Project(client.subscribe_to_entity::<Self>(remote_id.0)?),
1060            EntitySubscription::BufferStore(
1061                client.subscribe_to_entity::<BufferStore>(remote_id.0)?,
1062            ),
1063            EntitySubscription::WorktreeStore(
1064                client.subscribe_to_entity::<WorktreeStore>(remote_id.0)?,
1065            ),
1066            EntitySubscription::LspStore(client.subscribe_to_entity::<LspStore>(remote_id.0)?),
1067            EntitySubscription::SettingsObserver(
1068                client.subscribe_to_entity::<SettingsObserver>(remote_id.0)?,
1069            ),
1070        ];
1071        let response = client
1072            .request_envelope(proto::JoinHostedProject {
1073                project_id: remote_id.0,
1074            })
1075            .await?;
1076        Self::from_join_project_response(
1077            response,
1078            subscriptions,
1079            client,
1080            true,
1081            user_store,
1082            languages,
1083            fs,
1084            cx,
1085        )
1086        .await
1087    }
1088
1089    fn new_search_history() -> SearchHistory {
1090        SearchHistory::new(
1091            Some(MAX_PROJECT_SEARCH_HISTORY_SIZE),
1092            search_history::QueryInsertionBehavior::AlwaysInsert,
1093        )
1094    }
1095
1096    fn release(&mut self, cx: &mut AppContext) {
1097        match &self.client_state {
1098            ProjectClientState::Local => {}
1099            ProjectClientState::Shared { .. } => {
1100                let _ = self.unshare_internal(cx);
1101            }
1102            ProjectClientState::Remote { remote_id, .. } => {
1103                let _ = self.client.send(proto::LeaveProject {
1104                    project_id: *remote_id,
1105                });
1106                self.disconnected_from_host_internal(cx);
1107            }
1108        }
1109    }
1110
1111    #[cfg(any(test, feature = "test-support"))]
1112    pub async fn example(
1113        root_paths: impl IntoIterator<Item = &Path>,
1114        cx: &mut AsyncAppContext,
1115    ) -> Model<Project> {
1116        use clock::FakeSystemClock;
1117
1118        let fs = Arc::new(RealFs::default());
1119        let languages = LanguageRegistry::test(cx.background_executor().clone());
1120        let clock = Arc::new(FakeSystemClock::default());
1121        let http_client = http_client::FakeHttpClient::with_404_response();
1122        let client = cx
1123            .update(|cx| client::Client::new(clock, http_client.clone(), cx))
1124            .unwrap();
1125        let user_store = cx
1126            .new_model(|cx| UserStore::new(client.clone(), cx))
1127            .unwrap();
1128        let project = cx
1129            .update(|cx| {
1130                Project::local(
1131                    client,
1132                    node_runtime::NodeRuntime::unavailable(),
1133                    user_store,
1134                    Arc::new(languages),
1135                    fs,
1136                    None,
1137                    cx,
1138                )
1139            })
1140            .unwrap();
1141        for path in root_paths {
1142            let (tree, _) = project
1143                .update(cx, |project, cx| {
1144                    project.find_or_create_worktree(path, true, cx)
1145                })
1146                .unwrap()
1147                .await
1148                .unwrap();
1149            tree.update(cx, |tree, _| tree.as_local().unwrap().scan_complete())
1150                .unwrap()
1151                .await;
1152        }
1153        project
1154    }
1155
1156    #[cfg(any(test, feature = "test-support"))]
1157    pub async fn test(
1158        fs: Arc<dyn Fs>,
1159        root_paths: impl IntoIterator<Item = &Path>,
1160        cx: &mut gpui::TestAppContext,
1161    ) -> Model<Project> {
1162        use clock::FakeSystemClock;
1163
1164        let languages = LanguageRegistry::test(cx.executor());
1165        let clock = Arc::new(FakeSystemClock::default());
1166        let http_client = http_client::FakeHttpClient::with_404_response();
1167        let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx));
1168        let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
1169        let project = cx.update(|cx| {
1170            Project::local(
1171                client,
1172                node_runtime::NodeRuntime::unavailable(),
1173                user_store,
1174                Arc::new(languages),
1175                fs,
1176                None,
1177                cx,
1178            )
1179        });
1180        for path in root_paths {
1181            let (tree, _) = project
1182                .update(cx, |project, cx| {
1183                    project.find_or_create_worktree(path, true, cx)
1184                })
1185                .await
1186                .unwrap();
1187
1188            project.update(cx, |project, cx| {
1189                let tree_id = tree.read(cx).id();
1190                project.environment.update(cx, |environment, _| {
1191                    environment.set_cached(&[(tree_id, HashMap::default())])
1192                });
1193            });
1194
1195            tree.update(cx, |tree, _| tree.as_local().unwrap().scan_complete())
1196                .await;
1197        }
1198        project
1199    }
1200
1201    pub fn lsp_store(&self) -> Model<LspStore> {
1202        self.lsp_store.clone()
1203    }
1204
1205    pub fn worktree_store(&self) -> Model<WorktreeStore> {
1206        self.worktree_store.clone()
1207    }
1208
1209    pub fn buffer_for_id(&self, remote_id: BufferId, cx: &AppContext) -> Option<Model<Buffer>> {
1210        self.buffer_store.read(cx).get(remote_id)
1211    }
1212
1213    pub fn languages(&self) -> &Arc<LanguageRegistry> {
1214        &self.languages
1215    }
1216
1217    pub fn client(&self) -> Arc<Client> {
1218        self.client.clone()
1219    }
1220
1221    pub fn user_store(&self) -> Model<UserStore> {
1222        self.user_store.clone()
1223    }
1224
1225    pub fn node_runtime(&self) -> Option<&NodeRuntime> {
1226        self.node.as_ref()
1227    }
1228
1229    pub fn opened_buffers(&self, cx: &AppContext) -> Vec<Model<Buffer>> {
1230        self.buffer_store.read(cx).buffers().collect()
1231    }
1232
1233    pub fn cli_environment(&self, cx: &AppContext) -> Option<HashMap<String, String>> {
1234        self.environment.read(cx).get_cli_environment()
1235    }
1236
1237    pub fn shell_environment_errors<'a>(
1238        &'a self,
1239        cx: &'a AppContext,
1240    ) -> impl Iterator<Item = (&'a WorktreeId, &'a EnvironmentErrorMessage)> {
1241        self.environment.read(cx).environment_errors()
1242    }
1243
1244    pub fn remove_environment_error(
1245        &mut self,
1246        cx: &mut ModelContext<Self>,
1247        worktree_id: WorktreeId,
1248    ) {
1249        self.environment.update(cx, |environment, _| {
1250            environment.remove_environment_error(worktree_id);
1251        });
1252    }
1253
1254    #[cfg(any(test, feature = "test-support"))]
1255    pub fn has_open_buffer(&self, path: impl Into<ProjectPath>, cx: &AppContext) -> bool {
1256        self.buffer_store
1257            .read(cx)
1258            .get_by_path(&path.into(), cx)
1259            .is_some()
1260    }
1261
1262    pub fn fs(&self) -> &Arc<dyn Fs> {
1263        &self.fs
1264    }
1265
1266    pub fn remote_id(&self) -> Option<u64> {
1267        match self.client_state {
1268            ProjectClientState::Local => None,
1269            ProjectClientState::Shared { remote_id, .. }
1270            | ProjectClientState::Remote { remote_id, .. } => Some(remote_id),
1271        }
1272    }
1273
1274    pub fn hosted_project_id(&self) -> Option<ProjectId> {
1275        self.hosted_project_id
1276    }
1277
1278    pub fn dev_server_project_id(&self) -> Option<DevServerProjectId> {
1279        self.dev_server_project_id
1280    }
1281
1282    pub fn supports_terminal(&self, cx: &AppContext) -> bool {
1283        if self.is_local() {
1284            return true;
1285        }
1286        if self.is_via_ssh() {
1287            return true;
1288        }
1289        let Some(id) = self.dev_server_project_id else {
1290            return false;
1291        };
1292        let Some(server) = dev_server_projects::Store::global(cx)
1293            .read(cx)
1294            .dev_server_for_project(id)
1295        else {
1296            return false;
1297        };
1298        server.ssh_connection_string.is_some()
1299    }
1300
1301    pub fn ssh_connection_string(&self, cx: &AppContext) -> Option<SharedString> {
1302        if let Some(ssh_state) = &self.ssh_client {
1303            return Some(ssh_state.read(cx).connection_string().into());
1304        }
1305        let dev_server_id = self.dev_server_project_id()?;
1306        dev_server_projects::Store::global(cx)
1307            .read(cx)
1308            .dev_server_for_project(dev_server_id)?
1309            .ssh_connection_string
1310            .clone()
1311    }
1312
1313    pub fn ssh_connection_state(&self, cx: &AppContext) -> Option<remote::ConnectionState> {
1314        self.ssh_client
1315            .as_ref()
1316            .map(|ssh| ssh.read(cx).connection_state())
1317    }
1318
1319    pub fn ssh_connection_options(&self, cx: &AppContext) -> Option<SshConnectionOptions> {
1320        self.ssh_client
1321            .as_ref()
1322            .map(|ssh| ssh.read(cx).connection_options())
1323    }
1324
1325    pub fn replica_id(&self) -> ReplicaId {
1326        match self.client_state {
1327            ProjectClientState::Remote { replica_id, .. } => replica_id,
1328            _ => {
1329                if self.ssh_client.is_some() {
1330                    1
1331                } else {
1332                    0
1333                }
1334            }
1335        }
1336    }
1337
1338    pub fn task_store(&self) -> &Model<TaskStore> {
1339        &self.task_store
1340    }
1341
1342    pub fn snippets(&self) -> &Model<SnippetProvider> {
1343        &self.snippets
1344    }
1345
1346    pub fn search_history(&self, kind: SearchInputKind) -> &SearchHistory {
1347        match kind {
1348            SearchInputKind::Query => &self.search_history,
1349            SearchInputKind::Include => &self.search_included_history,
1350            SearchInputKind::Exclude => &self.search_excluded_history,
1351        }
1352    }
1353
1354    pub fn search_history_mut(&mut self, kind: SearchInputKind) -> &mut SearchHistory {
1355        match kind {
1356            SearchInputKind::Query => &mut self.search_history,
1357            SearchInputKind::Include => &mut self.search_included_history,
1358            SearchInputKind::Exclude => &mut self.search_excluded_history,
1359        }
1360    }
1361
1362    pub fn collaborators(&self) -> &HashMap<proto::PeerId, Collaborator> {
1363        &self.collaborators
1364    }
1365
1366    pub fn host(&self) -> Option<&Collaborator> {
1367        self.collaborators.values().find(|c| c.replica_id == 0)
1368    }
1369
1370    pub fn set_worktrees_reordered(&mut self, worktrees_reordered: bool, cx: &mut AppContext) {
1371        self.worktree_store.update(cx, |store, _| {
1372            store.set_worktrees_reordered(worktrees_reordered);
1373        });
1374    }
1375
1376    /// Collect all worktrees, including ones that don't appear in the project panel
1377    pub fn worktrees<'a>(
1378        &self,
1379        cx: &'a AppContext,
1380    ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
1381        self.worktree_store.read(cx).worktrees()
1382    }
1383
1384    /// Collect all user-visible worktrees, the ones that appear in the project panel.
1385    pub fn visible_worktrees<'a>(
1386        &'a self,
1387        cx: &'a AppContext,
1388    ) -> impl 'a + DoubleEndedIterator<Item = Model<Worktree>> {
1389        self.worktree_store.read(cx).visible_worktrees(cx)
1390    }
1391
1392    pub fn worktree_root_names<'a>(&'a self, cx: &'a AppContext) -> impl Iterator<Item = &'a str> {
1393        self.visible_worktrees(cx)
1394            .map(|tree| tree.read(cx).root_name())
1395    }
1396
1397    pub fn worktree_for_id(&self, id: WorktreeId, cx: &AppContext) -> Option<Model<Worktree>> {
1398        self.worktree_store.read(cx).worktree_for_id(id, cx)
1399    }
1400
1401    pub fn worktree_for_entry(
1402        &self,
1403        entry_id: ProjectEntryId,
1404        cx: &AppContext,
1405    ) -> Option<Model<Worktree>> {
1406        self.worktree_store
1407            .read(cx)
1408            .worktree_for_entry(entry_id, cx)
1409    }
1410
1411    pub fn worktree_id_for_entry(
1412        &self,
1413        entry_id: ProjectEntryId,
1414        cx: &AppContext,
1415    ) -> Option<WorktreeId> {
1416        self.worktree_for_entry(entry_id, cx)
1417            .map(|worktree| worktree.read(cx).id())
1418    }
1419
1420    /// Checks if the entry is the root of a worktree.
1421    pub fn entry_is_worktree_root(&self, entry_id: ProjectEntryId, cx: &AppContext) -> bool {
1422        self.worktree_for_entry(entry_id, cx)
1423            .map(|worktree| {
1424                worktree
1425                    .read(cx)
1426                    .root_entry()
1427                    .is_some_and(|e| e.id == entry_id)
1428            })
1429            .unwrap_or(false)
1430    }
1431
1432    pub fn visibility_for_paths(&self, paths: &[PathBuf], cx: &AppContext) -> Option<bool> {
1433        paths
1434            .iter()
1435            .map(|path| self.visibility_for_path(path, cx))
1436            .max()
1437            .flatten()
1438    }
1439
1440    pub fn visibility_for_path(&self, path: &Path, cx: &AppContext) -> Option<bool> {
1441        self.worktrees(cx)
1442            .filter_map(|worktree| {
1443                let worktree = worktree.read(cx);
1444                worktree
1445                    .as_local()?
1446                    .contains_abs_path(path)
1447                    .then(|| worktree.is_visible())
1448            })
1449            .max()
1450    }
1451
1452    pub fn create_entry(
1453        &mut self,
1454        project_path: impl Into<ProjectPath>,
1455        is_directory: bool,
1456        cx: &mut ModelContext<Self>,
1457    ) -> Task<Result<CreatedEntry>> {
1458        let project_path = project_path.into();
1459        let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
1460            return Task::ready(Err(anyhow!(format!(
1461                "No worktree for path {project_path:?}"
1462            ))));
1463        };
1464        worktree.update(cx, |worktree, cx| {
1465            worktree.create_entry(project_path.path, is_directory, cx)
1466        })
1467    }
1468
1469    pub fn copy_entry(
1470        &mut self,
1471        entry_id: ProjectEntryId,
1472        relative_worktree_source_path: Option<PathBuf>,
1473        new_path: impl Into<Arc<Path>>,
1474        cx: &mut ModelContext<Self>,
1475    ) -> Task<Result<Option<Entry>>> {
1476        let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
1477            return Task::ready(Ok(None));
1478        };
1479        worktree.update(cx, |worktree, cx| {
1480            worktree.copy_entry(entry_id, relative_worktree_source_path, new_path, cx)
1481        })
1482    }
1483
1484    pub fn rename_entry(
1485        &mut self,
1486        entry_id: ProjectEntryId,
1487        new_path: impl Into<Arc<Path>>,
1488        cx: &mut ModelContext<Self>,
1489    ) -> Task<Result<CreatedEntry>> {
1490        let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
1491            return Task::ready(Err(anyhow!(format!("No worktree for entry {entry_id:?}"))));
1492        };
1493        worktree.update(cx, |worktree, cx| {
1494            worktree.rename_entry(entry_id, new_path, cx)
1495        })
1496    }
1497
1498    pub fn delete_entry(
1499        &mut self,
1500        entry_id: ProjectEntryId,
1501        trash: bool,
1502        cx: &mut ModelContext<Self>,
1503    ) -> Option<Task<Result<()>>> {
1504        let worktree = self.worktree_for_entry(entry_id, cx)?;
1505        worktree.update(cx, |worktree, cx| {
1506            worktree.delete_entry(entry_id, trash, cx)
1507        })
1508    }
1509
1510    pub fn expand_entry(
1511        &mut self,
1512        worktree_id: WorktreeId,
1513        entry_id: ProjectEntryId,
1514        cx: &mut ModelContext<Self>,
1515    ) -> Option<Task<Result<()>>> {
1516        let worktree = self.worktree_for_id(worktree_id, cx)?;
1517        worktree.update(cx, |worktree, cx| worktree.expand_entry(entry_id, cx))
1518    }
1519
1520    pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext<Self>) -> Result<()> {
1521        if !matches!(self.client_state, ProjectClientState::Local) {
1522            if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state {
1523                if *in_room || self.dev_server_project_id.is_none() {
1524                    return Err(anyhow!("project was already shared"));
1525                } else {
1526                    *in_room = true;
1527                    return Ok(());
1528                }
1529            } else {
1530                return Err(anyhow!("project was already shared"));
1531            }
1532        }
1533        self.client_subscriptions.extend([
1534            self.client
1535                .subscribe_to_entity(project_id)?
1536                .set_model(&cx.handle(), &mut cx.to_async()),
1537            self.client
1538                .subscribe_to_entity(project_id)?
1539                .set_model(&self.worktree_store, &mut cx.to_async()),
1540            self.client
1541                .subscribe_to_entity(project_id)?
1542                .set_model(&self.buffer_store, &mut cx.to_async()),
1543            self.client
1544                .subscribe_to_entity(project_id)?
1545                .set_model(&self.lsp_store, &mut cx.to_async()),
1546            self.client
1547                .subscribe_to_entity(project_id)?
1548                .set_model(&self.settings_observer, &mut cx.to_async()),
1549        ]);
1550
1551        self.buffer_store.update(cx, |buffer_store, cx| {
1552            buffer_store.shared(project_id, self.client.clone().into(), cx)
1553        });
1554        self.worktree_store.update(cx, |worktree_store, cx| {
1555            worktree_store.shared(project_id, self.client.clone().into(), cx);
1556        });
1557        self.lsp_store.update(cx, |lsp_store, cx| {
1558            lsp_store.shared(project_id, self.client.clone().into(), cx)
1559        });
1560        self.task_store.update(cx, |task_store, cx| {
1561            task_store.shared(project_id, self.client.clone().into(), cx);
1562        });
1563        self.settings_observer.update(cx, |settings_observer, cx| {
1564            settings_observer.shared(project_id, self.client.clone().into(), cx)
1565        });
1566
1567        self.client_state = ProjectClientState::Shared {
1568            remote_id: project_id,
1569        };
1570
1571        cx.emit(Event::RemoteIdChanged(Some(project_id)));
1572        cx.notify();
1573        Ok(())
1574    }
1575
1576    pub fn reshared(
1577        &mut self,
1578        message: proto::ResharedProject,
1579        cx: &mut ModelContext<Self>,
1580    ) -> Result<()> {
1581        self.buffer_store
1582            .update(cx, |buffer_store, _| buffer_store.forget_shared_buffers());
1583        self.set_collaborators_from_proto(message.collaborators, cx)?;
1584
1585        self.worktree_store.update(cx, |worktree_store, cx| {
1586            worktree_store.send_project_updates(cx);
1587        });
1588        cx.notify();
1589        cx.emit(Event::Reshared);
1590        Ok(())
1591    }
1592
1593    pub fn rejoined(
1594        &mut self,
1595        message: proto::RejoinedProject,
1596        message_id: u32,
1597        cx: &mut ModelContext<Self>,
1598    ) -> Result<()> {
1599        cx.update_global::<SettingsStore, _>(|store, cx| {
1600            self.worktree_store.update(cx, |worktree_store, cx| {
1601                for worktree in worktree_store.worktrees() {
1602                    store
1603                        .clear_local_settings(worktree.read(cx).id(), cx)
1604                        .log_err();
1605                }
1606            });
1607        });
1608
1609        self.join_project_response_message_id = message_id;
1610        self.set_worktrees_from_proto(message.worktrees, cx)?;
1611        self.set_collaborators_from_proto(message.collaborators, cx)?;
1612        self.lsp_store.update(cx, |lsp_store, _| {
1613            lsp_store.set_language_server_statuses_from_proto(message.language_servers)
1614        });
1615        self.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
1616            .unwrap();
1617        cx.emit(Event::Rejoined);
1618        cx.notify();
1619        Ok(())
1620    }
1621
1622    pub fn unshare(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
1623        self.unshare_internal(cx)?;
1624        cx.notify();
1625        Ok(())
1626    }
1627
1628    fn unshare_internal(&mut self, cx: &mut AppContext) -> Result<()> {
1629        if self.is_via_collab() {
1630            if self.dev_server_project_id().is_some() {
1631                if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state {
1632                    *in_room = false
1633                }
1634                return Ok(());
1635            } else {
1636                return Err(anyhow!("attempted to unshare a remote project"));
1637            }
1638        }
1639
1640        if let ProjectClientState::Shared { remote_id, .. } = self.client_state {
1641            self.client_state = ProjectClientState::Local;
1642            self.collaborators.clear();
1643            self.client_subscriptions.clear();
1644            self.worktree_store.update(cx, |store, cx| {
1645                store.unshared(cx);
1646            });
1647            self.buffer_store.update(cx, |buffer_store, cx| {
1648                buffer_store.forget_shared_buffers();
1649                buffer_store.unshared(cx)
1650            });
1651            self.task_store.update(cx, |task_store, cx| {
1652                task_store.unshared(cx);
1653            });
1654            self.settings_observer.update(cx, |settings_observer, cx| {
1655                settings_observer.unshared(cx);
1656            });
1657
1658            self.client
1659                .send(proto::UnshareProject {
1660                    project_id: remote_id,
1661                })
1662                .ok();
1663            Ok(())
1664        } else {
1665            Err(anyhow!("attempted to unshare an unshared project"))
1666        }
1667    }
1668
1669    pub fn disconnected_from_host(&mut self, cx: &mut ModelContext<Self>) {
1670        if self.is_disconnected(cx) {
1671            return;
1672        }
1673        self.disconnected_from_host_internal(cx);
1674        cx.emit(Event::DisconnectedFromHost);
1675        cx.notify();
1676    }
1677
1678    pub fn set_role(&mut self, role: proto::ChannelRole, cx: &mut ModelContext<Self>) {
1679        let new_capability =
1680            if role == proto::ChannelRole::Member || role == proto::ChannelRole::Admin {
1681                Capability::ReadWrite
1682            } else {
1683                Capability::ReadOnly
1684            };
1685        if let ProjectClientState::Remote { capability, .. } = &mut self.client_state {
1686            if *capability == new_capability {
1687                return;
1688            }
1689
1690            *capability = new_capability;
1691            for buffer in self.opened_buffers(cx) {
1692                buffer.update(cx, |buffer, cx| buffer.set_capability(new_capability, cx));
1693            }
1694        }
1695    }
1696
1697    fn disconnected_from_host_internal(&mut self, cx: &mut AppContext) {
1698        if let ProjectClientState::Remote {
1699            sharing_has_stopped,
1700            ..
1701        } = &mut self.client_state
1702        {
1703            *sharing_has_stopped = true;
1704            self.collaborators.clear();
1705            self.worktree_store.update(cx, |store, cx| {
1706                store.disconnected_from_host(cx);
1707            });
1708            self.buffer_store.update(cx, |buffer_store, cx| {
1709                buffer_store.disconnected_from_host(cx)
1710            });
1711            self.lsp_store
1712                .update(cx, |lsp_store, _cx| lsp_store.disconnected_from_host());
1713        }
1714    }
1715
1716    pub fn close(&mut self, cx: &mut ModelContext<Self>) {
1717        cx.emit(Event::Closed);
1718    }
1719
1720    pub fn is_disconnected(&self, cx: &AppContext) -> bool {
1721        match &self.client_state {
1722            ProjectClientState::Remote {
1723                sharing_has_stopped,
1724                ..
1725            } => *sharing_has_stopped,
1726            ProjectClientState::Local if self.is_via_ssh() => self.ssh_is_disconnected(cx),
1727            _ => false,
1728        }
1729    }
1730
1731    fn ssh_is_disconnected(&self, cx: &AppContext) -> bool {
1732        self.ssh_client
1733            .as_ref()
1734            .map(|ssh| ssh.read(cx).is_disconnected())
1735            .unwrap_or(false)
1736    }
1737
1738    pub fn capability(&self) -> Capability {
1739        match &self.client_state {
1740            ProjectClientState::Remote { capability, .. } => *capability,
1741            ProjectClientState::Shared { .. } | ProjectClientState::Local => Capability::ReadWrite,
1742        }
1743    }
1744
1745    pub fn is_read_only(&self, cx: &AppContext) -> bool {
1746        self.is_disconnected(cx) || self.capability() == Capability::ReadOnly
1747    }
1748
1749    pub fn is_local(&self) -> bool {
1750        match &self.client_state {
1751            ProjectClientState::Local | ProjectClientState::Shared { .. } => {
1752                self.ssh_client.is_none()
1753            }
1754            ProjectClientState::Remote { .. } => false,
1755        }
1756    }
1757
1758    pub fn is_via_ssh(&self) -> bool {
1759        match &self.client_state {
1760            ProjectClientState::Local | ProjectClientState::Shared { .. } => {
1761                self.ssh_client.is_some()
1762            }
1763            ProjectClientState::Remote { .. } => false,
1764        }
1765    }
1766
1767    pub fn is_via_collab(&self) -> bool {
1768        match &self.client_state {
1769            ProjectClientState::Local | ProjectClientState::Shared { .. } => false,
1770            ProjectClientState::Remote { .. } => true,
1771        }
1772    }
1773
1774    pub fn create_buffer(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<Model<Buffer>>> {
1775        self.buffer_store
1776            .update(cx, |buffer_store, cx| buffer_store.create_buffer(cx))
1777    }
1778
1779    pub fn create_local_buffer(
1780        &mut self,
1781        text: &str,
1782        language: Option<Arc<Language>>,
1783        cx: &mut ModelContext<Self>,
1784    ) -> Model<Buffer> {
1785        if self.is_via_collab() || self.is_via_ssh() {
1786            panic!("called create_local_buffer on a remote project")
1787        }
1788        self.buffer_store.update(cx, |buffer_store, cx| {
1789            buffer_store.create_local_buffer(text, language, cx)
1790        })
1791    }
1792
1793    pub fn open_path(
1794        &mut self,
1795        path: ProjectPath,
1796        cx: &mut ModelContext<Self>,
1797    ) -> Task<Result<(Option<ProjectEntryId>, AnyModel)>> {
1798        let task = self.open_buffer(path.clone(), cx);
1799        cx.spawn(move |_, cx| async move {
1800            let buffer = task.await?;
1801            let project_entry_id = buffer.read_with(&cx, |buffer, cx| {
1802                File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
1803            })?;
1804
1805            let buffer: &AnyModel = &buffer;
1806            Ok((project_entry_id, buffer.clone()))
1807        })
1808    }
1809
1810    pub fn open_local_buffer(
1811        &mut self,
1812        abs_path: impl AsRef<Path>,
1813        cx: &mut ModelContext<Self>,
1814    ) -> Task<Result<Model<Buffer>>> {
1815        if let Some((worktree, relative_path)) = self.find_worktree(abs_path.as_ref(), cx) {
1816            self.open_buffer((worktree.read(cx).id(), relative_path), cx)
1817        } else {
1818            Task::ready(Err(anyhow!("no such path")))
1819        }
1820    }
1821
1822    pub fn open_buffer(
1823        &mut self,
1824        path: impl Into<ProjectPath>,
1825        cx: &mut ModelContext<Self>,
1826    ) -> Task<Result<Model<Buffer>>> {
1827        if (self.is_via_collab() || self.is_via_ssh()) && self.is_disconnected(cx) {
1828            return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
1829        }
1830
1831        self.buffer_store.update(cx, |buffer_store, cx| {
1832            buffer_store.open_buffer(path.into(), cx)
1833        })
1834    }
1835
1836    pub fn open_buffer_by_id(
1837        &mut self,
1838        id: BufferId,
1839        cx: &mut ModelContext<Self>,
1840    ) -> Task<Result<Model<Buffer>>> {
1841        if let Some(buffer) = self.buffer_for_id(id, cx) {
1842            Task::ready(Ok(buffer))
1843        } else if self.is_local() || self.is_via_ssh() {
1844            Task::ready(Err(anyhow!("buffer {} does not exist", id)))
1845        } else if let Some(project_id) = self.remote_id() {
1846            let request = self.client.request(proto::OpenBufferById {
1847                project_id,
1848                id: id.into(),
1849            });
1850            cx.spawn(move |this, mut cx| async move {
1851                let buffer_id = BufferId::new(request.await?.buffer_id)?;
1852                this.update(&mut cx, |this, cx| {
1853                    this.wait_for_remote_buffer(buffer_id, cx)
1854                })?
1855                .await
1856            })
1857        } else {
1858            Task::ready(Err(anyhow!("cannot open buffer while disconnected")))
1859        }
1860    }
1861
1862    pub fn save_buffers(
1863        &self,
1864        buffers: HashSet<Model<Buffer>>,
1865        cx: &mut ModelContext<Self>,
1866    ) -> Task<Result<()>> {
1867        cx.spawn(move |this, mut cx| async move {
1868            let save_tasks = buffers.into_iter().filter_map(|buffer| {
1869                this.update(&mut cx, |this, cx| this.save_buffer(buffer, cx))
1870                    .ok()
1871            });
1872            try_join_all(save_tasks).await?;
1873            Ok(())
1874        })
1875    }
1876
1877    pub fn save_buffer(
1878        &self,
1879        buffer: Model<Buffer>,
1880        cx: &mut ModelContext<Self>,
1881    ) -> Task<Result<()>> {
1882        self.buffer_store
1883            .update(cx, |buffer_store, cx| buffer_store.save_buffer(buffer, cx))
1884    }
1885
1886    pub fn save_buffer_as(
1887        &mut self,
1888        buffer: Model<Buffer>,
1889        path: ProjectPath,
1890        cx: &mut ModelContext<Self>,
1891    ) -> Task<Result<()>> {
1892        self.buffer_store.update(cx, |buffer_store, cx| {
1893            buffer_store.save_buffer_as(buffer.clone(), path, cx)
1894        })
1895    }
1896
1897    pub fn get_open_buffer(
1898        &mut self,
1899        path: &ProjectPath,
1900        cx: &mut ModelContext<Self>,
1901    ) -> Option<Model<Buffer>> {
1902        self.buffer_store.read(cx).get_by_path(path, cx)
1903    }
1904
1905    fn register_buffer(
1906        &mut self,
1907        buffer: &Model<Buffer>,
1908        cx: &mut ModelContext<Self>,
1909    ) -> Result<()> {
1910        {
1911            let mut remotely_created_models = self.remotely_created_models.lock();
1912            if remotely_created_models.retain_count > 0 {
1913                remotely_created_models.buffers.push(buffer.clone())
1914            }
1915        }
1916
1917        self.request_buffer_diff_recalculation(buffer, cx);
1918
1919        cx.subscribe(buffer, |this, buffer, event, cx| {
1920            this.on_buffer_event(buffer, event, cx);
1921        })
1922        .detach();
1923
1924        Ok(())
1925    }
1926
1927    async fn send_buffer_ordered_messages(
1928        this: WeakModel<Self>,
1929        rx: UnboundedReceiver<BufferOrderedMessage>,
1930        mut cx: AsyncAppContext,
1931    ) -> Result<()> {
1932        const MAX_BATCH_SIZE: usize = 128;
1933
1934        let mut operations_by_buffer_id = HashMap::default();
1935        async fn flush_operations(
1936            this: &WeakModel<Project>,
1937            operations_by_buffer_id: &mut HashMap<BufferId, Vec<proto::Operation>>,
1938            needs_resync_with_host: &mut bool,
1939            is_local: bool,
1940            cx: &mut AsyncAppContext,
1941        ) -> Result<()> {
1942            for (buffer_id, operations) in operations_by_buffer_id.drain() {
1943                let request = this.update(cx, |this, _| {
1944                    let project_id = this.remote_id()?;
1945                    Some(this.client.request(proto::UpdateBuffer {
1946                        buffer_id: buffer_id.into(),
1947                        project_id,
1948                        operations,
1949                    }))
1950                })?;
1951                if let Some(request) = request {
1952                    if request.await.is_err() && !is_local {
1953                        *needs_resync_with_host = true;
1954                        break;
1955                    }
1956                }
1957            }
1958            Ok(())
1959        }
1960
1961        let mut needs_resync_with_host = false;
1962        let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
1963
1964        while let Some(changes) = changes.next().await {
1965            let is_local = this.update(&mut cx, |this, _| this.is_local())?;
1966
1967            for change in changes {
1968                match change {
1969                    BufferOrderedMessage::Operation {
1970                        buffer_id,
1971                        operation,
1972                    } => {
1973                        if needs_resync_with_host {
1974                            continue;
1975                        }
1976
1977                        operations_by_buffer_id
1978                            .entry(buffer_id)
1979                            .or_insert(Vec::new())
1980                            .push(operation);
1981                    }
1982
1983                    BufferOrderedMessage::Resync => {
1984                        operations_by_buffer_id.clear();
1985                        if this
1986                            .update(&mut cx, |this, cx| this.synchronize_remote_buffers(cx))?
1987                            .await
1988                            .is_ok()
1989                        {
1990                            needs_resync_with_host = false;
1991                        }
1992                    }
1993
1994                    BufferOrderedMessage::LanguageServerUpdate {
1995                        language_server_id,
1996                        message,
1997                    } => {
1998                        flush_operations(
1999                            &this,
2000                            &mut operations_by_buffer_id,
2001                            &mut needs_resync_with_host,
2002                            is_local,
2003                            &mut cx,
2004                        )
2005                        .await?;
2006
2007                        this.update(&mut cx, |this, _| {
2008                            if let Some(project_id) = this.remote_id() {
2009                                this.client
2010                                    .send(proto::UpdateLanguageServer {
2011                                        project_id,
2012                                        language_server_id: language_server_id.0 as u64,
2013                                        variant: Some(message),
2014                                    })
2015                                    .log_err();
2016                            }
2017                        })?;
2018                    }
2019                }
2020            }
2021
2022            flush_operations(
2023                &this,
2024                &mut operations_by_buffer_id,
2025                &mut needs_resync_with_host,
2026                is_local,
2027                &mut cx,
2028            )
2029            .await?;
2030        }
2031
2032        Ok(())
2033    }
2034
2035    fn on_buffer_store_event(
2036        &mut self,
2037        _: Model<BufferStore>,
2038        event: &BufferStoreEvent,
2039        cx: &mut ModelContext<Self>,
2040    ) {
2041        match event {
2042            BufferStoreEvent::BufferAdded(buffer) => {
2043                self.register_buffer(buffer, cx).log_err();
2044            }
2045            BufferStoreEvent::BufferChangedFilePath { .. } => {}
2046            BufferStoreEvent::BufferDropped(buffer_id) => {
2047                if let Some(ref ssh_client) = self.ssh_client {
2048                    ssh_client
2049                        .read(cx)
2050                        .to_proto_client()
2051                        .send(proto::CloseBuffer {
2052                            project_id: 0,
2053                            buffer_id: buffer_id.to_proto(),
2054                        })
2055                        .log_err();
2056                }
2057            }
2058        }
2059    }
2060
2061    fn on_lsp_store_event(
2062        &mut self,
2063        _: Model<LspStore>,
2064        event: &LspStoreEvent,
2065        cx: &mut ModelContext<Self>,
2066    ) {
2067        match event {
2068            LspStoreEvent::DiagnosticsUpdated {
2069                language_server_id,
2070                path,
2071            } => cx.emit(Event::DiagnosticsUpdated {
2072                path: path.clone(),
2073                language_server_id: *language_server_id,
2074            }),
2075            LspStoreEvent::LanguageServerAdded(language_server_id) => {
2076                cx.emit(Event::LanguageServerAdded(*language_server_id))
2077            }
2078            LspStoreEvent::LanguageServerRemoved(language_server_id) => {
2079                cx.emit(Event::LanguageServerRemoved(*language_server_id))
2080            }
2081            LspStoreEvent::LanguageServerLog(server_id, log_type, string) => cx.emit(
2082                Event::LanguageServerLog(*server_id, log_type.clone(), string.clone()),
2083            ),
2084            LspStoreEvent::LanguageDetected {
2085                buffer,
2086                new_language,
2087            } => {
2088                let Some(_) = new_language else {
2089                    cx.emit(Event::LanguageNotFound(buffer.clone()));
2090                    return;
2091                };
2092            }
2093            LspStoreEvent::RefreshInlayHints => cx.emit(Event::RefreshInlayHints),
2094            LspStoreEvent::LanguageServerPrompt(prompt) => {
2095                cx.emit(Event::LanguageServerPrompt(prompt.clone()))
2096            }
2097            LspStoreEvent::DiskBasedDiagnosticsStarted { language_server_id } => {
2098                cx.emit(Event::DiskBasedDiagnosticsStarted {
2099                    language_server_id: *language_server_id,
2100                });
2101            }
2102            LspStoreEvent::DiskBasedDiagnosticsFinished { language_server_id } => {
2103                cx.emit(Event::DiskBasedDiagnosticsFinished {
2104                    language_server_id: *language_server_id,
2105                });
2106            }
2107            LspStoreEvent::LanguageServerUpdate {
2108                language_server_id,
2109                message,
2110            } => {
2111                if self.is_local() {
2112                    self.enqueue_buffer_ordered_message(
2113                        BufferOrderedMessage::LanguageServerUpdate {
2114                            language_server_id: *language_server_id,
2115                            message: message.clone(),
2116                        },
2117                    )
2118                    .ok();
2119                }
2120            }
2121            LspStoreEvent::Notification(message) => cx.emit(Event::Notification(message.clone())),
2122            LspStoreEvent::SnippetEdit {
2123                buffer_id,
2124                edits,
2125                most_recent_edit,
2126            } => {
2127                if most_recent_edit.replica_id == self.replica_id() {
2128                    cx.emit(Event::SnippetEdit(*buffer_id, edits.clone()))
2129                }
2130            }
2131        }
2132    }
2133
2134    fn on_ssh_event(
2135        &mut self,
2136        _: Model<SshRemoteClient>,
2137        event: &remote::SshRemoteEvent,
2138        cx: &mut ModelContext<Self>,
2139    ) {
2140        match event {
2141            remote::SshRemoteEvent::Disconnected => {
2142                // if self.is_via_ssh() {
2143                // self.collaborators.clear();
2144                self.worktree_store.update(cx, |store, cx| {
2145                    store.disconnected_from_host(cx);
2146                });
2147                self.buffer_store.update(cx, |buffer_store, cx| {
2148                    buffer_store.disconnected_from_host(cx)
2149                });
2150                self.lsp_store.update(cx, |lsp_store, _cx| {
2151                    lsp_store.disconnected_from_ssh_remote()
2152                });
2153                cx.emit(Event::DisconnectedFromSshRemote);
2154            }
2155        }
2156    }
2157
2158    fn on_settings_observer_event(
2159        &mut self,
2160        _: Model<SettingsObserver>,
2161        event: &SettingsObserverEvent,
2162        cx: &mut ModelContext<Self>,
2163    ) {
2164        match event {
2165            SettingsObserverEvent::LocalSettingsUpdated(error) => {
2166                cx.emit(Event::LocalSettingsUpdated(error.clone()))
2167            }
2168        }
2169    }
2170
2171    fn on_worktree_store_event(
2172        &mut self,
2173        _: Model<WorktreeStore>,
2174        event: &WorktreeStoreEvent,
2175        cx: &mut ModelContext<Self>,
2176    ) {
2177        match event {
2178            WorktreeStoreEvent::WorktreeAdded(worktree) => {
2179                self.on_worktree_added(worktree, cx);
2180                cx.emit(Event::WorktreeAdded);
2181            }
2182            WorktreeStoreEvent::WorktreeRemoved(_, id) => {
2183                self.on_worktree_removed(*id, cx);
2184                cx.emit(Event::WorktreeRemoved(*id));
2185            }
2186            WorktreeStoreEvent::WorktreeOrderChanged => cx.emit(Event::WorktreeOrderChanged),
2187            WorktreeStoreEvent::WorktreeUpdateSent(_) => {}
2188        }
2189    }
2190
2191    fn on_worktree_added(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
2192        {
2193            let mut remotely_created_models = self.remotely_created_models.lock();
2194            if remotely_created_models.retain_count > 0 {
2195                remotely_created_models.worktrees.push(worktree.clone())
2196            }
2197        }
2198        cx.observe(worktree, |_, _, cx| cx.notify()).detach();
2199        cx.subscribe(worktree, |project, worktree, event, cx| match event {
2200            worktree::Event::UpdatedEntries(changes) => {
2201                cx.emit(Event::WorktreeUpdatedEntries(
2202                    worktree.read(cx).id(),
2203                    changes.clone(),
2204                ));
2205
2206                let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
2207                project
2208                    .client()
2209                    .telemetry()
2210                    .report_discovered_project_events(worktree_id, changes);
2211            }
2212            worktree::Event::UpdatedGitRepositories(_) => {
2213                cx.emit(Event::WorktreeUpdatedGitRepositories);
2214            }
2215            worktree::Event::DeletedEntry(id) => cx.emit(Event::DeletedEntry(*id)),
2216        })
2217        .detach();
2218        cx.notify();
2219    }
2220
2221    fn on_worktree_removed(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
2222        if let Some(dev_server_project_id) = self.dev_server_project_id {
2223            let paths: Vec<String> = self
2224                .visible_worktrees(cx)
2225                .filter_map(|worktree| {
2226                    if worktree.read(cx).id() == id_to_remove {
2227                        None
2228                    } else {
2229                        Some(worktree.read(cx).abs_path().to_string_lossy().to_string())
2230                    }
2231                })
2232                .collect();
2233            if !paths.is_empty() {
2234                let request = self.client.request(proto::UpdateDevServerProject {
2235                    dev_server_project_id: dev_server_project_id.0,
2236                    paths,
2237                });
2238                cx.background_executor()
2239                    .spawn(request)
2240                    .detach_and_log_err(cx);
2241            }
2242            return;
2243        }
2244
2245        if let Some(ssh) = &self.ssh_client {
2246            ssh.read(cx)
2247                .to_proto_client()
2248                .send(proto::RemoveWorktree {
2249                    worktree_id: id_to_remove.to_proto(),
2250                })
2251                .log_err();
2252        }
2253
2254        cx.notify();
2255    }
2256
2257    fn on_buffer_event(
2258        &mut self,
2259        buffer: Model<Buffer>,
2260        event: &BufferEvent,
2261        cx: &mut ModelContext<Self>,
2262    ) -> Option<()> {
2263        if matches!(
2264            event,
2265            BufferEvent::Edited { .. } | BufferEvent::Reloaded | BufferEvent::DiffBaseChanged
2266        ) {
2267            self.request_buffer_diff_recalculation(&buffer, cx);
2268        }
2269
2270        let buffer_id = buffer.read(cx).remote_id();
2271        match event {
2272            BufferEvent::Operation {
2273                operation,
2274                is_local: true,
2275            } => {
2276                let operation = language::proto::serialize_operation(operation);
2277
2278                if let Some(ssh) = &self.ssh_client {
2279                    ssh.read(cx)
2280                        .to_proto_client()
2281                        .send(proto::UpdateBuffer {
2282                            project_id: 0,
2283                            buffer_id: buffer_id.to_proto(),
2284                            operations: vec![operation.clone()],
2285                        })
2286                        .ok();
2287                }
2288
2289                self.enqueue_buffer_ordered_message(BufferOrderedMessage::Operation {
2290                    buffer_id,
2291                    operation,
2292                })
2293                .ok();
2294            }
2295
2296            _ => {}
2297        }
2298
2299        None
2300    }
2301
2302    fn request_buffer_diff_recalculation(
2303        &mut self,
2304        buffer: &Model<Buffer>,
2305        cx: &mut ModelContext<Self>,
2306    ) {
2307        self.buffers_needing_diff.insert(buffer.downgrade());
2308        let first_insertion = self.buffers_needing_diff.len() == 1;
2309
2310        let settings = ProjectSettings::get_global(cx);
2311        let delay = if let Some(delay) = settings.git.gutter_debounce {
2312            delay
2313        } else {
2314            if first_insertion {
2315                let this = cx.weak_model();
2316                cx.defer(move |cx| {
2317                    if let Some(this) = this.upgrade() {
2318                        this.update(cx, |this, cx| {
2319                            this.recalculate_buffer_diffs(cx).detach();
2320                        });
2321                    }
2322                });
2323            }
2324            return;
2325        };
2326
2327        const MIN_DELAY: u64 = 50;
2328        let delay = delay.max(MIN_DELAY);
2329        let duration = Duration::from_millis(delay);
2330
2331        self.git_diff_debouncer
2332            .fire_new(duration, cx, move |this, cx| {
2333                this.recalculate_buffer_diffs(cx)
2334            });
2335    }
2336
2337    fn recalculate_buffer_diffs(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
2338        let buffers = self.buffers_needing_diff.drain().collect::<Vec<_>>();
2339        cx.spawn(move |this, mut cx| async move {
2340            let tasks: Vec<_> = buffers
2341                .iter()
2342                .filter_map(|buffer| {
2343                    let buffer = buffer.upgrade()?;
2344                    buffer
2345                        .update(&mut cx, |buffer, cx| buffer.recalculate_diff(cx))
2346                        .ok()
2347                        .flatten()
2348                })
2349                .collect();
2350
2351            futures::future::join_all(tasks).await;
2352
2353            this.update(&mut cx, |this, cx| {
2354                if this.buffers_needing_diff.is_empty() {
2355                    // TODO: Would a `ModelContext<Project>.notify()` suffice here?
2356                    for buffer in buffers {
2357                        if let Some(buffer) = buffer.upgrade() {
2358                            buffer.update(cx, |_, cx| cx.notify());
2359                        }
2360                    }
2361                } else {
2362                    this.recalculate_buffer_diffs(cx).detach();
2363                }
2364            })
2365            .ok();
2366        })
2367    }
2368
2369    pub fn set_language_for_buffer(
2370        &mut self,
2371        buffer: &Model<Buffer>,
2372        new_language: Arc<Language>,
2373        cx: &mut ModelContext<Self>,
2374    ) {
2375        self.lsp_store.update(cx, |lsp_store, cx| {
2376            lsp_store.set_language_for_buffer(buffer, new_language, cx)
2377        })
2378    }
2379
2380    pub fn restart_language_servers_for_buffers(
2381        &mut self,
2382        buffers: impl IntoIterator<Item = Model<Buffer>>,
2383        cx: &mut ModelContext<Self>,
2384    ) {
2385        self.lsp_store.update(cx, |lsp_store, cx| {
2386            lsp_store.restart_language_servers_for_buffers(buffers, cx)
2387        })
2388    }
2389
2390    pub fn cancel_language_server_work_for_buffers(
2391        &mut self,
2392        buffers: impl IntoIterator<Item = Model<Buffer>>,
2393        cx: &mut ModelContext<Self>,
2394    ) {
2395        self.lsp_store.update(cx, |lsp_store, cx| {
2396            lsp_store.cancel_language_server_work_for_buffers(buffers, cx)
2397        })
2398    }
2399
2400    pub fn cancel_language_server_work(
2401        &mut self,
2402        server_id: LanguageServerId,
2403        token_to_cancel: Option<String>,
2404        cx: &mut ModelContext<Self>,
2405    ) {
2406        self.lsp_store.update(cx, |lsp_store, cx| {
2407            lsp_store.cancel_language_server_work(server_id, token_to_cancel, cx)
2408        })
2409    }
2410
2411    fn enqueue_buffer_ordered_message(&mut self, message: BufferOrderedMessage) -> Result<()> {
2412        self.buffer_ordered_messages_tx
2413            .unbounded_send(message)
2414            .map_err(|e| anyhow!(e))
2415    }
2416
2417    pub fn language_server_statuses<'a>(
2418        &'a self,
2419        cx: &'a AppContext,
2420    ) -> impl DoubleEndedIterator<Item = (LanguageServerId, &'a LanguageServerStatus)> {
2421        self.lsp_store.read(cx).language_server_statuses()
2422    }
2423
2424    pub fn last_formatting_failure<'a>(&self, cx: &'a AppContext) -> Option<&'a str> {
2425        self.lsp_store.read(cx).last_formatting_failure()
2426    }
2427
2428    pub fn update_diagnostics(
2429        &mut self,
2430        language_server_id: LanguageServerId,
2431        params: lsp::PublishDiagnosticsParams,
2432        disk_based_sources: &[String],
2433        cx: &mut ModelContext<Self>,
2434    ) -> Result<()> {
2435        self.lsp_store.update(cx, |lsp_store, cx| {
2436            lsp_store.update_diagnostics(language_server_id, params, disk_based_sources, cx)
2437        })
2438    }
2439
2440    pub fn update_diagnostic_entries(
2441        &mut self,
2442        server_id: LanguageServerId,
2443        abs_path: PathBuf,
2444        version: Option<i32>,
2445        diagnostics: Vec<DiagnosticEntry<Unclipped<PointUtf16>>>,
2446        cx: &mut ModelContext<Project>,
2447    ) -> Result<(), anyhow::Error> {
2448        self.lsp_store.update(cx, |lsp_store, cx| {
2449            lsp_store.update_diagnostic_entries(server_id, abs_path, version, diagnostics, cx)
2450        })
2451    }
2452
2453    pub fn reload_buffers(
2454        &self,
2455        buffers: HashSet<Model<Buffer>>,
2456        push_to_history: bool,
2457        cx: &mut ModelContext<Self>,
2458    ) -> Task<Result<ProjectTransaction>> {
2459        self.buffer_store.update(cx, |buffer_store, cx| {
2460            buffer_store.reload_buffers(buffers, push_to_history, cx)
2461        })
2462    }
2463
2464    pub fn format(
2465        &mut self,
2466        buffers: HashSet<Model<Buffer>>,
2467        push_to_history: bool,
2468        trigger: lsp_store::FormatTrigger,
2469        cx: &mut ModelContext<Project>,
2470    ) -> Task<anyhow::Result<ProjectTransaction>> {
2471        self.lsp_store.update(cx, |lsp_store, cx| {
2472            lsp_store.format(buffers, push_to_history, trigger, cx)
2473        })
2474    }
2475
2476    #[inline(never)]
2477    fn definition_impl(
2478        &mut self,
2479        buffer: &Model<Buffer>,
2480        position: PointUtf16,
2481        cx: &mut ModelContext<Self>,
2482    ) -> Task<Result<Vec<LocationLink>>> {
2483        self.request_lsp(
2484            buffer.clone(),
2485            LanguageServerToQuery::Primary,
2486            GetDefinition { position },
2487            cx,
2488        )
2489    }
2490    pub fn definition<T: ToPointUtf16>(
2491        &mut self,
2492        buffer: &Model<Buffer>,
2493        position: T,
2494        cx: &mut ModelContext<Self>,
2495    ) -> Task<Result<Vec<LocationLink>>> {
2496        let position = position.to_point_utf16(buffer.read(cx));
2497        self.definition_impl(buffer, position, cx)
2498    }
2499
2500    fn declaration_impl(
2501        &mut self,
2502        buffer: &Model<Buffer>,
2503        position: PointUtf16,
2504        cx: &mut ModelContext<Self>,
2505    ) -> Task<Result<Vec<LocationLink>>> {
2506        self.request_lsp(
2507            buffer.clone(),
2508            LanguageServerToQuery::Primary,
2509            GetDeclaration { position },
2510            cx,
2511        )
2512    }
2513
2514    pub fn declaration<T: ToPointUtf16>(
2515        &mut self,
2516        buffer: &Model<Buffer>,
2517        position: T,
2518        cx: &mut ModelContext<Self>,
2519    ) -> Task<Result<Vec<LocationLink>>> {
2520        let position = position.to_point_utf16(buffer.read(cx));
2521        self.declaration_impl(buffer, position, cx)
2522    }
2523
2524    fn type_definition_impl(
2525        &mut self,
2526        buffer: &Model<Buffer>,
2527        position: PointUtf16,
2528        cx: &mut ModelContext<Self>,
2529    ) -> Task<Result<Vec<LocationLink>>> {
2530        self.request_lsp(
2531            buffer.clone(),
2532            LanguageServerToQuery::Primary,
2533            GetTypeDefinition { position },
2534            cx,
2535        )
2536    }
2537
2538    pub fn type_definition<T: ToPointUtf16>(
2539        &mut self,
2540        buffer: &Model<Buffer>,
2541        position: T,
2542        cx: &mut ModelContext<Self>,
2543    ) -> Task<Result<Vec<LocationLink>>> {
2544        let position = position.to_point_utf16(buffer.read(cx));
2545        self.type_definition_impl(buffer, position, cx)
2546    }
2547
2548    pub fn implementation<T: ToPointUtf16>(
2549        &mut self,
2550        buffer: &Model<Buffer>,
2551        position: T,
2552        cx: &mut ModelContext<Self>,
2553    ) -> Task<Result<Vec<LocationLink>>> {
2554        let position = position.to_point_utf16(buffer.read(cx));
2555        self.request_lsp(
2556            buffer.clone(),
2557            LanguageServerToQuery::Primary,
2558            GetImplementation { position },
2559            cx,
2560        )
2561    }
2562
2563    pub fn references<T: ToPointUtf16>(
2564        &mut self,
2565        buffer: &Model<Buffer>,
2566        position: T,
2567        cx: &mut ModelContext<Self>,
2568    ) -> Task<Result<Vec<Location>>> {
2569        let position = position.to_point_utf16(buffer.read(cx));
2570        self.request_lsp(
2571            buffer.clone(),
2572            LanguageServerToQuery::Primary,
2573            GetReferences { position },
2574            cx,
2575        )
2576    }
2577
2578    fn document_highlights_impl(
2579        &mut self,
2580        buffer: &Model<Buffer>,
2581        position: PointUtf16,
2582        cx: &mut ModelContext<Self>,
2583    ) -> Task<Result<Vec<DocumentHighlight>>> {
2584        self.request_lsp(
2585            buffer.clone(),
2586            LanguageServerToQuery::Primary,
2587            GetDocumentHighlights { position },
2588            cx,
2589        )
2590    }
2591
2592    pub fn document_highlights<T: ToPointUtf16>(
2593        &mut self,
2594        buffer: &Model<Buffer>,
2595        position: T,
2596        cx: &mut ModelContext<Self>,
2597    ) -> Task<Result<Vec<DocumentHighlight>>> {
2598        let position = position.to_point_utf16(buffer.read(cx));
2599        self.document_highlights_impl(buffer, position, cx)
2600    }
2601
2602    pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
2603        self.lsp_store
2604            .update(cx, |lsp_store, cx| lsp_store.symbols(query, cx))
2605    }
2606
2607    pub fn open_buffer_for_symbol(
2608        &mut self,
2609        symbol: &Symbol,
2610        cx: &mut ModelContext<Self>,
2611    ) -> Task<Result<Model<Buffer>>> {
2612        self.lsp_store.update(cx, |lsp_store, cx| {
2613            lsp_store.open_buffer_for_symbol(symbol, cx)
2614        })
2615    }
2616
2617    pub fn open_local_buffer_via_lsp(
2618        &mut self,
2619        abs_path: lsp::Url,
2620        language_server_id: LanguageServerId,
2621        language_server_name: LanguageServerName,
2622        cx: &mut ModelContext<Self>,
2623    ) -> Task<Result<Model<Buffer>>> {
2624        self.lsp_store.update(cx, |lsp_store, cx| {
2625            lsp_store.open_local_buffer_via_lsp(
2626                abs_path,
2627                language_server_id,
2628                language_server_name,
2629                cx,
2630            )
2631        })
2632    }
2633
2634    pub fn signature_help<T: ToPointUtf16>(
2635        &self,
2636        buffer: &Model<Buffer>,
2637        position: T,
2638        cx: &mut ModelContext<Self>,
2639    ) -> Task<Vec<SignatureHelp>> {
2640        self.lsp_store.update(cx, |lsp_store, cx| {
2641            lsp_store.signature_help(buffer, position, cx)
2642        })
2643    }
2644
2645    pub fn hover<T: ToPointUtf16>(
2646        &self,
2647        buffer: &Model<Buffer>,
2648        position: T,
2649        cx: &mut ModelContext<Self>,
2650    ) -> Task<Vec<Hover>> {
2651        let position = position.to_point_utf16(buffer.read(cx));
2652        self.lsp_store
2653            .update(cx, |lsp_store, cx| lsp_store.hover(buffer, position, cx))
2654    }
2655
2656    pub fn linked_edit(
2657        &self,
2658        buffer: &Model<Buffer>,
2659        position: Anchor,
2660        cx: &mut ModelContext<Self>,
2661    ) -> Task<Result<Vec<Range<Anchor>>>> {
2662        self.lsp_store.update(cx, |lsp_store, cx| {
2663            lsp_store.linked_edit(buffer, position, cx)
2664        })
2665    }
2666
2667    pub fn completions<T: ToOffset + ToPointUtf16>(
2668        &self,
2669        buffer: &Model<Buffer>,
2670        position: T,
2671        context: CompletionContext,
2672        cx: &mut ModelContext<Self>,
2673    ) -> Task<Result<Vec<Completion>>> {
2674        let position = position.to_point_utf16(buffer.read(cx));
2675        self.lsp_store.update(cx, |lsp_store, cx| {
2676            lsp_store.completions(buffer, position, context, cx)
2677        })
2678    }
2679
2680    pub fn resolve_completions(
2681        &self,
2682        buffer: Model<Buffer>,
2683        completion_indices: Vec<usize>,
2684        completions: Arc<RwLock<Box<[Completion]>>>,
2685        cx: &mut ModelContext<Self>,
2686    ) -> Task<Result<bool>> {
2687        self.lsp_store.update(cx, |lsp_store, cx| {
2688            lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
2689        })
2690    }
2691
2692    pub fn apply_additional_edits_for_completion(
2693        &self,
2694        buffer_handle: Model<Buffer>,
2695        completion: Completion,
2696        push_to_history: bool,
2697        cx: &mut ModelContext<Self>,
2698    ) -> Task<Result<Option<Transaction>>> {
2699        self.lsp_store.update(cx, |lsp_store, cx| {
2700            lsp_store.apply_additional_edits_for_completion(
2701                buffer_handle,
2702                completion,
2703                push_to_history,
2704                cx,
2705            )
2706        })
2707    }
2708
2709    pub fn code_actions<T: Clone + ToOffset>(
2710        &mut self,
2711        buffer_handle: &Model<Buffer>,
2712        range: Range<T>,
2713        cx: &mut ModelContext<Self>,
2714    ) -> Task<Result<Vec<CodeAction>>> {
2715        let buffer = buffer_handle.read(cx);
2716        let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
2717        self.lsp_store.update(cx, |lsp_store, cx| {
2718            lsp_store.code_actions(buffer_handle, range, cx)
2719        })
2720    }
2721
2722    pub fn apply_code_action(
2723        &self,
2724        buffer_handle: Model<Buffer>,
2725        action: CodeAction,
2726        push_to_history: bool,
2727        cx: &mut ModelContext<Self>,
2728    ) -> Task<Result<ProjectTransaction>> {
2729        self.lsp_store.update(cx, |lsp_store, cx| {
2730            lsp_store.apply_code_action(buffer_handle, action, push_to_history, cx)
2731        })
2732    }
2733
2734    fn prepare_rename_impl(
2735        &mut self,
2736        buffer: Model<Buffer>,
2737        position: PointUtf16,
2738        cx: &mut ModelContext<Self>,
2739    ) -> Task<Result<Option<Range<Anchor>>>> {
2740        self.request_lsp(
2741            buffer,
2742            LanguageServerToQuery::Primary,
2743            PrepareRename { position },
2744            cx,
2745        )
2746    }
2747    pub fn prepare_rename<T: ToPointUtf16>(
2748        &mut self,
2749        buffer: Model<Buffer>,
2750        position: T,
2751        cx: &mut ModelContext<Self>,
2752    ) -> Task<Result<Option<Range<Anchor>>>> {
2753        let position = position.to_point_utf16(buffer.read(cx));
2754        self.prepare_rename_impl(buffer, position, cx)
2755    }
2756
2757    fn perform_rename_impl(
2758        &mut self,
2759        buffer: Model<Buffer>,
2760        position: PointUtf16,
2761        new_name: String,
2762        push_to_history: bool,
2763        cx: &mut ModelContext<Self>,
2764    ) -> Task<Result<ProjectTransaction>> {
2765        let position = position.to_point_utf16(buffer.read(cx));
2766        self.request_lsp(
2767            buffer,
2768            LanguageServerToQuery::Primary,
2769            PerformRename {
2770                position,
2771                new_name,
2772                push_to_history,
2773            },
2774            cx,
2775        )
2776    }
2777
2778    pub fn perform_rename<T: ToPointUtf16>(
2779        &mut self,
2780        buffer: Model<Buffer>,
2781        position: T,
2782        new_name: String,
2783        cx: &mut ModelContext<Self>,
2784    ) -> Task<Result<ProjectTransaction>> {
2785        let position = position.to_point_utf16(buffer.read(cx));
2786        self.perform_rename_impl(buffer, position, new_name, true, cx)
2787    }
2788
2789    pub fn on_type_format<T: ToPointUtf16>(
2790        &mut self,
2791        buffer: Model<Buffer>,
2792        position: T,
2793        trigger: String,
2794        push_to_history: bool,
2795        cx: &mut ModelContext<Self>,
2796    ) -> Task<Result<Option<Transaction>>> {
2797        self.lsp_store.update(cx, |lsp_store, cx| {
2798            lsp_store.on_type_format(buffer, position, trigger, push_to_history, cx)
2799        })
2800    }
2801
2802    pub fn inlay_hints<T: ToOffset>(
2803        &mut self,
2804        buffer_handle: Model<Buffer>,
2805        range: Range<T>,
2806        cx: &mut ModelContext<Self>,
2807    ) -> Task<anyhow::Result<Vec<InlayHint>>> {
2808        let buffer = buffer_handle.read(cx);
2809        let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
2810        self.lsp_store.update(cx, |lsp_store, cx| {
2811            lsp_store.inlay_hints(buffer_handle, range, cx)
2812        })
2813    }
2814
2815    pub fn resolve_inlay_hint(
2816        &self,
2817        hint: InlayHint,
2818        buffer_handle: Model<Buffer>,
2819        server_id: LanguageServerId,
2820        cx: &mut ModelContext<Self>,
2821    ) -> Task<anyhow::Result<InlayHint>> {
2822        self.lsp_store.update(cx, |lsp_store, cx| {
2823            lsp_store.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
2824        })
2825    }
2826
2827    pub fn search(
2828        &mut self,
2829        query: SearchQuery,
2830        cx: &mut ModelContext<Self>,
2831    ) -> Receiver<SearchResult> {
2832        let (result_tx, result_rx) = smol::channel::unbounded();
2833
2834        let matching_buffers_rx = if query.is_opened_only() {
2835            self.sort_search_candidates(&query, cx)
2836        } else {
2837            self.find_search_candidate_buffers(&query, MAX_SEARCH_RESULT_FILES + 1, cx)
2838        };
2839
2840        cx.spawn(|_, cx| async move {
2841            let mut range_count = 0;
2842            let mut buffer_count = 0;
2843            let mut limit_reached = false;
2844            let query = Arc::new(query);
2845            let mut chunks = matching_buffers_rx.ready_chunks(64);
2846
2847            // Now that we know what paths match the query, we will load at most
2848            // 64 buffers at a time to avoid overwhelming the main thread. For each
2849            // opened buffer, we will spawn a background task that retrieves all the
2850            // ranges in the buffer matched by the query.
2851            'outer: while let Some(matching_buffer_chunk) = chunks.next().await {
2852                let mut chunk_results = Vec::new();
2853                for buffer in matching_buffer_chunk {
2854                    let buffer = buffer.clone();
2855                    let query = query.clone();
2856                    let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
2857                    chunk_results.push(cx.background_executor().spawn(async move {
2858                        let ranges = query
2859                            .search(&snapshot, None)
2860                            .await
2861                            .iter()
2862                            .map(|range| {
2863                                snapshot.anchor_before(range.start)
2864                                    ..snapshot.anchor_after(range.end)
2865                            })
2866                            .collect::<Vec<_>>();
2867                        anyhow::Ok((buffer, ranges))
2868                    }));
2869                }
2870
2871                let chunk_results = futures::future::join_all(chunk_results).await;
2872                for result in chunk_results {
2873                    if let Some((buffer, ranges)) = result.log_err() {
2874                        range_count += ranges.len();
2875                        buffer_count += 1;
2876                        result_tx
2877                            .send(SearchResult::Buffer { buffer, ranges })
2878                            .await?;
2879                        if buffer_count > MAX_SEARCH_RESULT_FILES
2880                            || range_count > MAX_SEARCH_RESULT_RANGES
2881                        {
2882                            limit_reached = true;
2883                            break 'outer;
2884                        }
2885                    }
2886                }
2887            }
2888
2889            if limit_reached {
2890                result_tx.send(SearchResult::LimitReached).await?;
2891            }
2892
2893            anyhow::Ok(())
2894        })
2895        .detach();
2896
2897        result_rx
2898    }
2899
2900    fn find_search_candidate_buffers(
2901        &mut self,
2902        query: &SearchQuery,
2903        limit: usize,
2904        cx: &mut ModelContext<Project>,
2905    ) -> Receiver<Model<Buffer>> {
2906        if self.is_local() {
2907            let fs = self.fs.clone();
2908            self.buffer_store.update(cx, |buffer_store, cx| {
2909                buffer_store.find_search_candidates(query, limit, fs, cx)
2910            })
2911        } else {
2912            self.find_search_candidates_remote(query, limit, cx)
2913        }
2914    }
2915
2916    fn sort_search_candidates(
2917        &mut self,
2918        search_query: &SearchQuery,
2919        cx: &mut ModelContext<Project>,
2920    ) -> Receiver<Model<Buffer>> {
2921        let worktree_store = self.worktree_store.read(cx);
2922        let mut buffers = search_query
2923            .buffers()
2924            .into_iter()
2925            .flatten()
2926            .filter(|buffer| {
2927                let b = buffer.read(cx);
2928                if let Some(file) = b.file() {
2929                    if !search_query.file_matches(file.path()) {
2930                        return false;
2931                    }
2932                    if let Some(entry) = b
2933                        .entry_id(cx)
2934                        .and_then(|entry_id| worktree_store.entry_for_id(entry_id, cx))
2935                    {
2936                        if entry.is_ignored && !search_query.include_ignored() {
2937                            return false;
2938                        }
2939                    }
2940                }
2941                true
2942            })
2943            .collect::<Vec<_>>();
2944        let (tx, rx) = smol::channel::unbounded();
2945        buffers.sort_by(|a, b| match (a.read(cx).file(), b.read(cx).file()) {
2946            (None, None) => a.read(cx).remote_id().cmp(&b.read(cx).remote_id()),
2947            (None, Some(_)) => std::cmp::Ordering::Less,
2948            (Some(_), None) => std::cmp::Ordering::Greater,
2949            (Some(a), Some(b)) => compare_paths((a.path(), true), (b.path(), true)),
2950        });
2951        for buffer in buffers {
2952            tx.send_blocking(buffer.clone()).unwrap()
2953        }
2954
2955        rx
2956    }
2957
2958    fn find_search_candidates_remote(
2959        &mut self,
2960        query: &SearchQuery,
2961        limit: usize,
2962        cx: &mut ModelContext<Project>,
2963    ) -> Receiver<Model<Buffer>> {
2964        let (tx, rx) = smol::channel::unbounded();
2965
2966        let (client, remote_id): (AnyProtoClient, _) = if let Some(ssh_client) = &self.ssh_client {
2967            (ssh_client.read(cx).to_proto_client(), 0)
2968        } else if let Some(remote_id) = self.remote_id() {
2969            (self.client.clone().into(), remote_id)
2970        } else {
2971            return rx;
2972        };
2973
2974        let request = client.request(proto::FindSearchCandidates {
2975            project_id: remote_id,
2976            query: Some(query.to_proto()),
2977            limit: limit as _,
2978        });
2979        let guard = self.retain_remotely_created_models(cx);
2980
2981        cx.spawn(move |this, mut cx| async move {
2982            let response = request.await?;
2983            for buffer_id in response.buffer_ids {
2984                let buffer_id = BufferId::new(buffer_id)?;
2985                let buffer = this
2986                    .update(&mut cx, |this, cx| {
2987                        this.wait_for_remote_buffer(buffer_id, cx)
2988                    })?
2989                    .await?;
2990                let _ = tx.send(buffer).await;
2991            }
2992
2993            drop(guard);
2994            anyhow::Ok(())
2995        })
2996        .detach_and_log_err(cx);
2997        rx
2998    }
2999
3000    pub fn request_lsp<R: LspCommand>(
3001        &mut self,
3002        buffer_handle: Model<Buffer>,
3003        server: LanguageServerToQuery,
3004        request: R,
3005        cx: &mut ModelContext<Self>,
3006    ) -> Task<Result<R::Response>>
3007    where
3008        <R::LspRequest as lsp::request::Request>::Result: Send,
3009        <R::LspRequest as lsp::request::Request>::Params: Send,
3010    {
3011        let guard = self.retain_remotely_created_models(cx);
3012        let task = self.lsp_store.update(cx, |lsp_store, cx| {
3013            lsp_store.request_lsp(buffer_handle, server, request, cx)
3014        });
3015        cx.spawn(|_, _| async move {
3016            let result = task.await;
3017            drop(guard);
3018            result
3019        })
3020    }
3021
3022    /// Move a worktree to a new position in the worktree order.
3023    ///
3024    /// The worktree will moved to the opposite side of the destination worktree.
3025    ///
3026    /// # Example
3027    ///
3028    /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `33`,
3029    /// worktree_order will be updated to produce the indexes `[11, 33, 22]`.
3030    ///
3031    /// Given the worktree order `[11, 22, 33]` and a call to move worktree `22` to `11`,
3032    /// worktree_order will be updated to produce the indexes `[22, 11, 33]`.
3033    ///
3034    /// # Errors
3035    ///
3036    /// An error will be returned if the worktree or destination worktree are not found.
3037    pub fn move_worktree(
3038        &mut self,
3039        source: WorktreeId,
3040        destination: WorktreeId,
3041        cx: &mut ModelContext<'_, Self>,
3042    ) -> Result<()> {
3043        self.worktree_store.update(cx, |worktree_store, cx| {
3044            worktree_store.move_worktree(source, destination, cx)
3045        })
3046    }
3047
3048    pub fn find_or_create_worktree(
3049        &mut self,
3050        abs_path: impl AsRef<Path>,
3051        visible: bool,
3052        cx: &mut ModelContext<Self>,
3053    ) -> Task<Result<(Model<Worktree>, PathBuf)>> {
3054        let abs_path = abs_path.as_ref();
3055        if let Some((tree, relative_path)) = self.find_worktree(abs_path, cx) {
3056            Task::ready(Ok((tree, relative_path)))
3057        } else {
3058            let worktree = self.create_worktree(abs_path, visible, cx);
3059            cx.background_executor()
3060                .spawn(async move { Ok((worktree.await?, PathBuf::new())) })
3061        }
3062    }
3063
3064    pub fn find_worktree(
3065        &self,
3066        abs_path: &Path,
3067        cx: &AppContext,
3068    ) -> Option<(Model<Worktree>, PathBuf)> {
3069        self.worktree_store.read_with(cx, |worktree_store, cx| {
3070            worktree_store.find_worktree(abs_path, cx)
3071        })
3072    }
3073
3074    pub fn is_shared(&self) -> bool {
3075        match &self.client_state {
3076            ProjectClientState::Shared { .. } => true,
3077            ProjectClientState::Local => false,
3078            ProjectClientState::Remote { in_room, .. } => *in_room,
3079        }
3080    }
3081
3082    /// Returns the resolved version of `path`, that was found in `buffer`, if it exists.
3083    pub fn resolve_existing_file_path(
3084        &self,
3085        path: &str,
3086        buffer: &Model<Buffer>,
3087        cx: &mut ModelContext<Self>,
3088    ) -> Task<Option<ResolvedPath>> {
3089        let path_buf = PathBuf::from(path);
3090        if path_buf.is_absolute() || path.starts_with("~") {
3091            self.resolve_abs_file_path(path, cx)
3092        } else {
3093            self.resolve_path_in_worktrees(path_buf, buffer, cx)
3094        }
3095    }
3096
3097    pub fn abs_file_path_exists(&self, path: &str, cx: &mut ModelContext<Self>) -> Task<bool> {
3098        let resolve_task = self.resolve_abs_file_path(path, cx);
3099        cx.background_executor().spawn(async move {
3100            let resolved_path = resolve_task.await;
3101            resolved_path.is_some()
3102        })
3103    }
3104
3105    fn resolve_abs_file_path(
3106        &self,
3107        path: &str,
3108        cx: &mut ModelContext<Self>,
3109    ) -> Task<Option<ResolvedPath>> {
3110        if self.is_local() {
3111            let expanded = PathBuf::from(shellexpand::tilde(&path).into_owned());
3112
3113            let fs = self.fs.clone();
3114            cx.background_executor().spawn(async move {
3115                let path = expanded.as_path();
3116                let exists = fs.is_file(path).await;
3117
3118                exists.then(|| ResolvedPath::AbsPath(expanded))
3119            })
3120        } else if let Some(ssh_client) = self.ssh_client.as_ref() {
3121            let request = ssh_client
3122                .read(cx)
3123                .to_proto_client()
3124                .request(proto::CheckFileExists {
3125                    project_id: SSH_PROJECT_ID,
3126                    path: path.to_string(),
3127                });
3128            cx.background_executor().spawn(async move {
3129                let response = request.await.log_err()?;
3130                if response.exists {
3131                    Some(ResolvedPath::AbsPath(PathBuf::from(response.path)))
3132                } else {
3133                    None
3134                }
3135            })
3136        } else {
3137            return Task::ready(None);
3138        }
3139    }
3140
3141    fn resolve_path_in_worktrees(
3142        &self,
3143        path: PathBuf,
3144        buffer: &Model<Buffer>,
3145        cx: &mut ModelContext<Self>,
3146    ) -> Task<Option<ResolvedPath>> {
3147        let mut candidates = vec![path.clone()];
3148
3149        if let Some(file) = buffer.read(cx).file() {
3150            if let Some(dir) = file.path().parent() {
3151                let joined = dir.to_path_buf().join(path);
3152                candidates.push(joined);
3153            }
3154        }
3155
3156        let worktrees = self.worktrees(cx).collect::<Vec<_>>();
3157        cx.spawn(|_, mut cx| async move {
3158            for worktree in worktrees {
3159                for candidate in candidates.iter() {
3160                    let path = worktree
3161                        .update(&mut cx, |worktree, _| {
3162                            let root_entry_path = &worktree.root_entry()?.path;
3163
3164                            let resolved = resolve_path(root_entry_path, candidate);
3165
3166                            let stripped =
3167                                resolved.strip_prefix(root_entry_path).unwrap_or(&resolved);
3168
3169                            worktree.entry_for_path(stripped).map(|entry| {
3170                                ResolvedPath::ProjectPath(ProjectPath {
3171                                    worktree_id: worktree.id(),
3172                                    path: entry.path.clone(),
3173                                })
3174                            })
3175                        })
3176                        .ok()?;
3177
3178                    if path.is_some() {
3179                        return path;
3180                    }
3181                }
3182            }
3183            None
3184        })
3185    }
3186
3187    pub fn list_directory(
3188        &self,
3189        query: String,
3190        cx: &mut ModelContext<Self>,
3191    ) -> Task<Result<Vec<PathBuf>>> {
3192        if self.is_local() {
3193            DirectoryLister::Local(self.fs.clone()).list_directory(query, cx)
3194        } else if let Some(session) = self.ssh_client.as_ref() {
3195            let request = proto::ListRemoteDirectory {
3196                dev_server_id: SSH_PROJECT_ID,
3197                path: query,
3198            };
3199
3200            let response = session.read(cx).to_proto_client().request(request);
3201            cx.background_executor().spawn(async move {
3202                let response = response.await?;
3203                Ok(response.entries.into_iter().map(PathBuf::from).collect())
3204            })
3205        } else if let Some(dev_server) = self.dev_server_project_id().and_then(|id| {
3206            dev_server_projects::Store::global(cx)
3207                .read(cx)
3208                .dev_server_for_project(id)
3209        }) {
3210            let request = proto::ListRemoteDirectory {
3211                dev_server_id: dev_server.id.0,
3212                path: query,
3213            };
3214            let response = self.client.request(request);
3215            cx.background_executor().spawn(async move {
3216                let response = response.await?;
3217                Ok(response.entries.into_iter().map(PathBuf::from).collect())
3218            })
3219        } else {
3220            Task::ready(Err(anyhow!("cannot list directory in remote project")))
3221        }
3222    }
3223
3224    fn create_worktree(
3225        &mut self,
3226        abs_path: impl AsRef<Path>,
3227        visible: bool,
3228        cx: &mut ModelContext<Self>,
3229    ) -> Task<Result<Model<Worktree>>> {
3230        self.worktree_store.update(cx, |worktree_store, cx| {
3231            worktree_store.create_worktree(abs_path, visible, cx)
3232        })
3233    }
3234
3235    pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
3236        self.worktree_store.update(cx, |worktree_store, cx| {
3237            worktree_store.remove_worktree(id_to_remove, cx);
3238        });
3239    }
3240
3241    fn add_worktree(&mut self, worktree: &Model<Worktree>, cx: &mut ModelContext<Self>) {
3242        self.worktree_store.update(cx, |worktree_store, cx| {
3243            worktree_store.add(worktree, cx);
3244        });
3245    }
3246
3247    pub fn set_active_path(&mut self, entry: Option<ProjectPath>, cx: &mut ModelContext<Self>) {
3248        let new_active_entry = entry.and_then(|project_path| {
3249            let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
3250            let entry = worktree.read(cx).entry_for_path(project_path.path)?;
3251            Some(entry.id)
3252        });
3253        if new_active_entry != self.active_entry {
3254            self.active_entry = new_active_entry;
3255            self.lsp_store.update(cx, |lsp_store, _| {
3256                lsp_store.set_active_entry(new_active_entry);
3257            });
3258            cx.emit(Event::ActiveEntryChanged(new_active_entry));
3259        }
3260    }
3261
3262    pub fn language_servers_running_disk_based_diagnostics<'a>(
3263        &'a self,
3264        cx: &'a AppContext,
3265    ) -> impl Iterator<Item = LanguageServerId> + 'a {
3266        self.lsp_store
3267            .read(cx)
3268            .language_servers_running_disk_based_diagnostics()
3269    }
3270
3271    pub fn diagnostic_summary(&self, include_ignored: bool, cx: &AppContext) -> DiagnosticSummary {
3272        let mut summary = DiagnosticSummary::default();
3273        for (_, _, path_summary) in self.diagnostic_summaries(include_ignored, cx) {
3274            summary.error_count += path_summary.error_count;
3275            summary.warning_count += path_summary.warning_count;
3276        }
3277        summary
3278    }
3279
3280    pub fn diagnostic_summaries<'a>(
3281        &'a self,
3282        include_ignored: bool,
3283        cx: &'a AppContext,
3284    ) -> impl Iterator<Item = (ProjectPath, LanguageServerId, DiagnosticSummary)> + 'a {
3285        self.lsp_store
3286            .read(cx)
3287            .diagnostic_summaries(include_ignored, cx)
3288    }
3289
3290    pub fn active_entry(&self) -> Option<ProjectEntryId> {
3291        self.active_entry
3292    }
3293
3294    pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Entry> {
3295        self.worktree_store.read(cx).entry_for_path(path, cx)
3296    }
3297
3298    pub fn path_for_entry(&self, entry_id: ProjectEntryId, cx: &AppContext) -> Option<ProjectPath> {
3299        let worktree = self.worktree_for_entry(entry_id, cx)?;
3300        let worktree = worktree.read(cx);
3301        let worktree_id = worktree.id();
3302        let path = worktree.entry_for_id(entry_id)?.path.clone();
3303        Some(ProjectPath { worktree_id, path })
3304    }
3305
3306    pub fn absolute_path(&self, project_path: &ProjectPath, cx: &AppContext) -> Option<PathBuf> {
3307        let workspace_root = self
3308            .worktree_for_id(project_path.worktree_id, cx)?
3309            .read(cx)
3310            .abs_path();
3311        let project_path = project_path.path.as_ref();
3312
3313        Some(if project_path == Path::new("") {
3314            workspace_root.to_path_buf()
3315        } else {
3316            workspace_root.join(project_path)
3317        })
3318    }
3319
3320    /// Attempts to find a `ProjectPath` corresponding to the given path. If the path
3321    /// is a *full path*, meaning it starts with the root name of a worktree, we'll locate
3322    /// it in that worktree. Otherwise, we'll attempt to find it as a relative path in
3323    /// the first visible worktree that has an entry for that relative path.
3324    ///
3325    /// We use this to resolve edit steps, when there's a chance an LLM may omit the workree
3326    /// root name from paths.
3327    ///
3328    /// # Arguments
3329    ///
3330    /// * `path` - A full path that starts with a worktree root name, or alternatively a
3331    ///            relative path within a visible worktree.
3332    /// * `cx` - A reference to the `AppContext`.
3333    ///
3334    /// # Returns
3335    ///
3336    /// Returns `Some(ProjectPath)` if a matching worktree is found, otherwise `None`.
3337    pub fn find_project_path(&self, path: &Path, cx: &AppContext) -> Option<ProjectPath> {
3338        let worktree_store = self.worktree_store.read(cx);
3339
3340        for worktree in worktree_store.visible_worktrees(cx) {
3341            let worktree_root_name = worktree.read(cx).root_name();
3342            if let Ok(relative_path) = path.strip_prefix(worktree_root_name) {
3343                return Some(ProjectPath {
3344                    worktree_id: worktree.read(cx).id(),
3345                    path: relative_path.into(),
3346                });
3347            }
3348        }
3349
3350        for worktree in worktree_store.visible_worktrees(cx) {
3351            let worktree = worktree.read(cx);
3352            if let Some(entry) = worktree.entry_for_path(path) {
3353                return Some(ProjectPath {
3354                    worktree_id: worktree.id(),
3355                    path: entry.path.clone(),
3356                });
3357            }
3358        }
3359
3360        None
3361    }
3362
3363    pub fn get_workspace_root(
3364        &self,
3365        project_path: &ProjectPath,
3366        cx: &AppContext,
3367    ) -> Option<PathBuf> {
3368        Some(
3369            self.worktree_for_id(project_path.worktree_id, cx)?
3370                .read(cx)
3371                .abs_path()
3372                .to_path_buf(),
3373        )
3374    }
3375
3376    pub fn get_repo(
3377        &self,
3378        project_path: &ProjectPath,
3379        cx: &AppContext,
3380    ) -> Option<Arc<dyn GitRepository>> {
3381        self.worktree_for_id(project_path.worktree_id, cx)?
3382            .read(cx)
3383            .as_local()?
3384            .local_git_repo(&project_path.path)
3385    }
3386
3387    pub fn get_first_worktree_root_repo(&self, cx: &AppContext) -> Option<Arc<dyn GitRepository>> {
3388        let worktree = self.visible_worktrees(cx).next()?.read(cx).as_local()?;
3389        let root_entry = worktree.root_git_entry()?;
3390        worktree.get_local_repo(&root_entry)?.repo().clone().into()
3391    }
3392
3393    pub fn blame_buffer(
3394        &self,
3395        buffer: &Model<Buffer>,
3396        version: Option<clock::Global>,
3397        cx: &AppContext,
3398    ) -> Task<Result<Blame>> {
3399        self.buffer_store.read(cx).blame_buffer(buffer, version, cx)
3400    }
3401
3402    // RPC message handlers
3403
3404    async fn handle_unshare_project(
3405        this: Model<Self>,
3406        _: TypedEnvelope<proto::UnshareProject>,
3407        mut cx: AsyncAppContext,
3408    ) -> Result<()> {
3409        this.update(&mut cx, |this, cx| {
3410            if this.is_local() || this.is_via_ssh() {
3411                this.unshare(cx)?;
3412            } else {
3413                this.disconnected_from_host(cx);
3414            }
3415            Ok(())
3416        })?
3417    }
3418
3419    async fn handle_add_collaborator(
3420        this: Model<Self>,
3421        mut envelope: TypedEnvelope<proto::AddProjectCollaborator>,
3422        mut cx: AsyncAppContext,
3423    ) -> Result<()> {
3424        let collaborator = envelope
3425            .payload
3426            .collaborator
3427            .take()
3428            .ok_or_else(|| anyhow!("empty collaborator"))?;
3429
3430        let collaborator = Collaborator::from_proto(collaborator)?;
3431        this.update(&mut cx, |this, cx| {
3432            this.buffer_store.update(cx, |buffer_store, _| {
3433                buffer_store.forget_shared_buffers_for(&collaborator.peer_id);
3434            });
3435            cx.emit(Event::CollaboratorJoined(collaborator.peer_id));
3436            this.collaborators
3437                .insert(collaborator.peer_id, collaborator);
3438            cx.notify();
3439        })?;
3440
3441        Ok(())
3442    }
3443
3444    async fn handle_update_project_collaborator(
3445        this: Model<Self>,
3446        envelope: TypedEnvelope<proto::UpdateProjectCollaborator>,
3447        mut cx: AsyncAppContext,
3448    ) -> Result<()> {
3449        let old_peer_id = envelope
3450            .payload
3451            .old_peer_id
3452            .ok_or_else(|| anyhow!("missing old peer id"))?;
3453        let new_peer_id = envelope
3454            .payload
3455            .new_peer_id
3456            .ok_or_else(|| anyhow!("missing new peer id"))?;
3457        this.update(&mut cx, |this, cx| {
3458            let collaborator = this
3459                .collaborators
3460                .remove(&old_peer_id)
3461                .ok_or_else(|| anyhow!("received UpdateProjectCollaborator for unknown peer"))?;
3462            let is_host = collaborator.replica_id == 0;
3463            this.collaborators.insert(new_peer_id, collaborator);
3464
3465            log::info!("peer {} became {}", old_peer_id, new_peer_id,);
3466            this.buffer_store.update(cx, |buffer_store, _| {
3467                buffer_store.update_peer_id(&old_peer_id, new_peer_id)
3468            });
3469
3470            if is_host {
3471                this.buffer_store
3472                    .update(cx, |buffer_store, _| buffer_store.discard_incomplete());
3473                this.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync)
3474                    .unwrap();
3475                cx.emit(Event::HostReshared);
3476            }
3477
3478            cx.emit(Event::CollaboratorUpdated {
3479                old_peer_id,
3480                new_peer_id,
3481            });
3482            cx.notify();
3483            Ok(())
3484        })?
3485    }
3486
3487    async fn handle_remove_collaborator(
3488        this: Model<Self>,
3489        envelope: TypedEnvelope<proto::RemoveProjectCollaborator>,
3490        mut cx: AsyncAppContext,
3491    ) -> Result<()> {
3492        this.update(&mut cx, |this, cx| {
3493            let peer_id = envelope
3494                .payload
3495                .peer_id
3496                .ok_or_else(|| anyhow!("invalid peer id"))?;
3497            let replica_id = this
3498                .collaborators
3499                .remove(&peer_id)
3500                .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
3501                .replica_id;
3502            this.buffer_store.update(cx, |buffer_store, cx| {
3503                buffer_store.forget_shared_buffers_for(&peer_id);
3504                for buffer in buffer_store.buffers() {
3505                    buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
3506                }
3507            });
3508
3509            cx.emit(Event::CollaboratorLeft(peer_id));
3510            cx.notify();
3511            Ok(())
3512        })?
3513    }
3514
3515    async fn handle_update_project(
3516        this: Model<Self>,
3517        envelope: TypedEnvelope<proto::UpdateProject>,
3518        mut cx: AsyncAppContext,
3519    ) -> Result<()> {
3520        this.update(&mut cx, |this, cx| {
3521            // Don't handle messages that were sent before the response to us joining the project
3522            if envelope.message_id > this.join_project_response_message_id {
3523                this.set_worktrees_from_proto(envelope.payload.worktrees, cx)?;
3524            }
3525            Ok(())
3526        })?
3527    }
3528
3529    // Collab sends UpdateWorktree protos as messages
3530    async fn handle_update_worktree(
3531        this: Model<Self>,
3532        envelope: TypedEnvelope<proto::UpdateWorktree>,
3533        mut cx: AsyncAppContext,
3534    ) -> Result<()> {
3535        this.update(&mut cx, |this, cx| {
3536            let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
3537            if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
3538                worktree.update(cx, |worktree, _| {
3539                    let worktree = worktree.as_remote_mut().unwrap();
3540                    worktree.update_from_remote(envelope.payload);
3541                });
3542            }
3543            Ok(())
3544        })?
3545    }
3546
3547    async fn handle_update_buffer(
3548        this: Model<Self>,
3549        envelope: TypedEnvelope<proto::UpdateBuffer>,
3550        cx: AsyncAppContext,
3551    ) -> Result<proto::Ack> {
3552        let buffer_store = this.read_with(&cx, |this, cx| {
3553            if let Some(ssh) = &this.ssh_client {
3554                let mut payload = envelope.payload.clone();
3555                payload.project_id = SSH_PROJECT_ID;
3556                cx.background_executor()
3557                    .spawn(ssh.read(cx).to_proto_client().request(payload))
3558                    .detach_and_log_err(cx);
3559            }
3560            this.buffer_store.clone()
3561        })?;
3562        BufferStore::handle_update_buffer(buffer_store, envelope, cx).await
3563    }
3564
3565    fn retain_remotely_created_models(
3566        &mut self,
3567        cx: &mut ModelContext<Self>,
3568    ) -> RemotelyCreatedModelGuard {
3569        {
3570            let mut remotely_create_models = self.remotely_created_models.lock();
3571            if remotely_create_models.retain_count == 0 {
3572                remotely_create_models.buffers = self.buffer_store.read(cx).buffers().collect();
3573                remotely_create_models.worktrees =
3574                    self.worktree_store.read(cx).worktrees().collect();
3575            }
3576            remotely_create_models.retain_count += 1;
3577        }
3578        RemotelyCreatedModelGuard {
3579            remote_models: Arc::downgrade(&self.remotely_created_models),
3580        }
3581    }
3582
3583    async fn handle_create_buffer_for_peer(
3584        this: Model<Self>,
3585        envelope: TypedEnvelope<proto::CreateBufferForPeer>,
3586        mut cx: AsyncAppContext,
3587    ) -> Result<()> {
3588        this.update(&mut cx, |this, cx| {
3589            this.buffer_store.update(cx, |buffer_store, cx| {
3590                buffer_store.handle_create_buffer_for_peer(
3591                    envelope,
3592                    this.replica_id(),
3593                    this.capability(),
3594                    cx,
3595                )
3596            })
3597        })?
3598    }
3599
3600    async fn handle_synchronize_buffers(
3601        this: Model<Self>,
3602        envelope: TypedEnvelope<proto::SynchronizeBuffers>,
3603        mut cx: AsyncAppContext,
3604    ) -> Result<proto::SynchronizeBuffersResponse> {
3605        let response = this.update(&mut cx, |this, cx| {
3606            let client = this.client.clone();
3607            this.buffer_store.update(cx, |this, cx| {
3608                this.handle_synchronize_buffers(envelope, cx, client)
3609            })
3610        })??;
3611
3612        Ok(response)
3613    }
3614
3615    async fn handle_search_candidate_buffers(
3616        this: Model<Self>,
3617        envelope: TypedEnvelope<proto::FindSearchCandidates>,
3618        mut cx: AsyncAppContext,
3619    ) -> Result<proto::FindSearchCandidatesResponse> {
3620        let peer_id = envelope.original_sender_id()?;
3621        let message = envelope.payload;
3622        let query = SearchQuery::from_proto(
3623            message
3624                .query
3625                .ok_or_else(|| anyhow!("missing query field"))?,
3626        )?;
3627        let mut results = this.update(&mut cx, |this, cx| {
3628            this.find_search_candidate_buffers(&query, message.limit as _, cx)
3629        })?;
3630
3631        let mut response = proto::FindSearchCandidatesResponse {
3632            buffer_ids: Vec::new(),
3633        };
3634
3635        while let Some(buffer) = results.next().await {
3636            this.update(&mut cx, |this, cx| {
3637                let buffer_id = this.create_buffer_for_peer(&buffer, peer_id, cx);
3638                response.buffer_ids.push(buffer_id.to_proto());
3639            })?;
3640        }
3641
3642        Ok(response)
3643    }
3644
3645    async fn handle_open_buffer_by_id(
3646        this: Model<Self>,
3647        envelope: TypedEnvelope<proto::OpenBufferById>,
3648        mut cx: AsyncAppContext,
3649    ) -> Result<proto::OpenBufferResponse> {
3650        let peer_id = envelope.original_sender_id()?;
3651        let buffer_id = BufferId::new(envelope.payload.id)?;
3652        let buffer = this
3653            .update(&mut cx, |this, cx| this.open_buffer_by_id(buffer_id, cx))?
3654            .await?;
3655        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
3656    }
3657
3658    async fn handle_open_buffer_by_path(
3659        this: Model<Self>,
3660        envelope: TypedEnvelope<proto::OpenBufferByPath>,
3661        mut cx: AsyncAppContext,
3662    ) -> Result<proto::OpenBufferResponse> {
3663        let peer_id = envelope.original_sender_id()?;
3664        let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
3665        let open_buffer = this.update(&mut cx, |this, cx| {
3666            this.open_buffer(
3667                ProjectPath {
3668                    worktree_id,
3669                    path: PathBuf::from(envelope.payload.path).into(),
3670                },
3671                cx,
3672            )
3673        })?;
3674
3675        let buffer = open_buffer.await?;
3676        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
3677    }
3678
3679    async fn handle_open_new_buffer(
3680        this: Model<Self>,
3681        envelope: TypedEnvelope<proto::OpenNewBuffer>,
3682        mut cx: AsyncAppContext,
3683    ) -> Result<proto::OpenBufferResponse> {
3684        let buffer = this
3685            .update(&mut cx, |this, cx| this.create_buffer(cx))?
3686            .await?;
3687        let peer_id = envelope.original_sender_id()?;
3688
3689        Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
3690    }
3691
3692    fn respond_to_open_buffer_request(
3693        this: Model<Self>,
3694        buffer: Model<Buffer>,
3695        peer_id: proto::PeerId,
3696        cx: &mut AsyncAppContext,
3697    ) -> Result<proto::OpenBufferResponse> {
3698        this.update(cx, |this, cx| {
3699            let is_private = buffer
3700                .read(cx)
3701                .file()
3702                .map(|f| f.is_private())
3703                .unwrap_or_default();
3704            if is_private {
3705                Err(anyhow!(ErrorCode::UnsharedItem))
3706            } else {
3707                Ok(proto::OpenBufferResponse {
3708                    buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(),
3709                })
3710            }
3711        })?
3712    }
3713
3714    fn create_buffer_for_peer(
3715        &mut self,
3716        buffer: &Model<Buffer>,
3717        peer_id: proto::PeerId,
3718        cx: &mut AppContext,
3719    ) -> BufferId {
3720        self.buffer_store
3721            .update(cx, |buffer_store, cx| {
3722                buffer_store.create_buffer_for_peer(buffer, peer_id, cx)
3723            })
3724            .detach_and_log_err(cx);
3725        buffer.read(cx).remote_id()
3726    }
3727
3728    fn wait_for_remote_buffer(
3729        &mut self,
3730        id: BufferId,
3731        cx: &mut ModelContext<Self>,
3732    ) -> Task<Result<Model<Buffer>>> {
3733        self.buffer_store.update(cx, |buffer_store, cx| {
3734            buffer_store.wait_for_remote_buffer(id, cx)
3735        })
3736    }
3737
3738    fn synchronize_remote_buffers(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
3739        let project_id = match self.client_state {
3740            ProjectClientState::Remote {
3741                sharing_has_stopped,
3742                remote_id,
3743                ..
3744            } => {
3745                if sharing_has_stopped {
3746                    return Task::ready(Err(anyhow!(
3747                        "can't synchronize remote buffers on a readonly project"
3748                    )));
3749                } else {
3750                    remote_id
3751                }
3752            }
3753            ProjectClientState::Shared { .. } | ProjectClientState::Local => {
3754                return Task::ready(Err(anyhow!(
3755                    "can't synchronize remote buffers on a local project"
3756                )))
3757            }
3758        };
3759
3760        let client = self.client.clone();
3761        cx.spawn(move |this, mut cx| async move {
3762            let (buffers, incomplete_buffer_ids) = this.update(&mut cx, |this, cx| {
3763                this.buffer_store.read(cx).buffer_version_info(cx)
3764            })?;
3765            let response = client
3766                .request(proto::SynchronizeBuffers {
3767                    project_id,
3768                    buffers,
3769                })
3770                .await?;
3771
3772            let send_updates_for_buffers = this.update(&mut cx, |this, cx| {
3773                response
3774                    .buffers
3775                    .into_iter()
3776                    .map(|buffer| {
3777                        let client = client.clone();
3778                        let buffer_id = match BufferId::new(buffer.id) {
3779                            Ok(id) => id,
3780                            Err(e) => {
3781                                return Task::ready(Err(e));
3782                            }
3783                        };
3784                        let remote_version = language::proto::deserialize_version(&buffer.version);
3785                        if let Some(buffer) = this.buffer_for_id(buffer_id, cx) {
3786                            let operations =
3787                                buffer.read(cx).serialize_ops(Some(remote_version), cx);
3788                            cx.background_executor().spawn(async move {
3789                                let operations = operations.await;
3790                                for chunk in split_operations(operations) {
3791                                    client
3792                                        .request(proto::UpdateBuffer {
3793                                            project_id,
3794                                            buffer_id: buffer_id.into(),
3795                                            operations: chunk,
3796                                        })
3797                                        .await?;
3798                                }
3799                                anyhow::Ok(())
3800                            })
3801                        } else {
3802                            Task::ready(Ok(()))
3803                        }
3804                    })
3805                    .collect::<Vec<_>>()
3806            })?;
3807
3808            // Any incomplete buffers have open requests waiting. Request that the host sends
3809            // creates these buffers for us again to unblock any waiting futures.
3810            for id in incomplete_buffer_ids {
3811                cx.background_executor()
3812                    .spawn(client.request(proto::OpenBufferById {
3813                        project_id,
3814                        id: id.into(),
3815                    }))
3816                    .detach();
3817            }
3818
3819            futures::future::join_all(send_updates_for_buffers)
3820                .await
3821                .into_iter()
3822                .collect()
3823        })
3824    }
3825
3826    pub fn worktree_metadata_protos(&self, cx: &AppContext) -> Vec<proto::WorktreeMetadata> {
3827        self.worktree_store.read(cx).worktree_metadata_protos(cx)
3828    }
3829
3830    fn set_worktrees_from_proto(
3831        &mut self,
3832        worktrees: Vec<proto::WorktreeMetadata>,
3833        cx: &mut ModelContext<Project>,
3834    ) -> Result<()> {
3835        cx.notify();
3836        self.worktree_store.update(cx, |worktree_store, cx| {
3837            worktree_store.set_worktrees_from_proto(worktrees, self.replica_id(), cx)
3838        })
3839    }
3840
3841    fn set_collaborators_from_proto(
3842        &mut self,
3843        messages: Vec<proto::Collaborator>,
3844        cx: &mut ModelContext<Self>,
3845    ) -> Result<()> {
3846        let mut collaborators = HashMap::default();
3847        for message in messages {
3848            let collaborator = Collaborator::from_proto(message)?;
3849            collaborators.insert(collaborator.peer_id, collaborator);
3850        }
3851        for old_peer_id in self.collaborators.keys() {
3852            if !collaborators.contains_key(old_peer_id) {
3853                cx.emit(Event::CollaboratorLeft(*old_peer_id));
3854            }
3855        }
3856        self.collaborators = collaborators;
3857        Ok(())
3858    }
3859
3860    pub fn language_servers<'a>(
3861        &'a self,
3862        cx: &'a AppContext,
3863    ) -> impl 'a + Iterator<Item = (LanguageServerId, LanguageServerName, WorktreeId)> {
3864        self.lsp_store.read(cx).language_servers()
3865    }
3866
3867    pub fn supplementary_language_servers<'a>(
3868        &'a self,
3869        cx: &'a AppContext,
3870    ) -> impl 'a + Iterator<Item = (LanguageServerId, LanguageServerName)> {
3871        self.lsp_store.read(cx).supplementary_language_servers()
3872    }
3873
3874    pub fn language_server_for_id(
3875        &self,
3876        id: LanguageServerId,
3877        cx: &AppContext,
3878    ) -> Option<Arc<LanguageServer>> {
3879        self.lsp_store.read(cx).language_server_for_id(id)
3880    }
3881
3882    pub fn language_servers_for_buffer<'a>(
3883        &'a self,
3884        buffer: &'a Buffer,
3885        cx: &'a AppContext,
3886    ) -> impl Iterator<Item = (&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
3887        self.lsp_store
3888            .read(cx)
3889            .language_servers_for_buffer(buffer, cx)
3890    }
3891
3892    pub fn language_server_for_buffer<'a>(
3893        &'a self,
3894        buffer: &'a Buffer,
3895        server_id: LanguageServerId,
3896        cx: &'a AppContext,
3897    ) -> Option<(&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
3898        self.lsp_store
3899            .read(cx)
3900            .language_server_for_buffer(buffer, server_id, cx)
3901    }
3902}
3903
3904fn deserialize_code_actions(code_actions: &HashMap<String, bool>) -> Vec<lsp::CodeActionKind> {
3905    code_actions
3906        .iter()
3907        .flat_map(|(kind, enabled)| {
3908            if *enabled {
3909                Some(kind.clone().into())
3910            } else {
3911                None
3912            }
3913        })
3914        .collect()
3915}
3916
3917pub struct PathMatchCandidateSet {
3918    pub snapshot: Snapshot,
3919    pub include_ignored: bool,
3920    pub include_root_name: bool,
3921    pub candidates: Candidates,
3922}
3923
3924pub enum Candidates {
3925    /// Only consider directories.
3926    Directories,
3927    /// Only consider files.
3928    Files,
3929    /// Consider directories and files.
3930    Entries,
3931}
3932
3933impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet {
3934    type Candidates = PathMatchCandidateSetIter<'a>;
3935
3936    fn id(&self) -> usize {
3937        self.snapshot.id().to_usize()
3938    }
3939
3940    fn len(&self) -> usize {
3941        match self.candidates {
3942            Candidates::Files => {
3943                if self.include_ignored {
3944                    self.snapshot.file_count()
3945                } else {
3946                    self.snapshot.visible_file_count()
3947                }
3948            }
3949
3950            Candidates::Directories => {
3951                if self.include_ignored {
3952                    self.snapshot.dir_count()
3953                } else {
3954                    self.snapshot.visible_dir_count()
3955                }
3956            }
3957
3958            Candidates::Entries => {
3959                if self.include_ignored {
3960                    self.snapshot.entry_count()
3961                } else {
3962                    self.snapshot.visible_entry_count()
3963                }
3964            }
3965        }
3966    }
3967
3968    fn prefix(&self) -> Arc<str> {
3969        if self.snapshot.root_entry().map_or(false, |e| e.is_file()) {
3970            self.snapshot.root_name().into()
3971        } else if self.include_root_name {
3972            format!("{}{}", self.snapshot.root_name(), std::path::MAIN_SEPARATOR).into()
3973        } else {
3974            Arc::default()
3975        }
3976    }
3977
3978    fn candidates(&'a self, start: usize) -> Self::Candidates {
3979        PathMatchCandidateSetIter {
3980            traversal: match self.candidates {
3981                Candidates::Directories => self.snapshot.directories(self.include_ignored, start),
3982                Candidates::Files => self.snapshot.files(self.include_ignored, start),
3983                Candidates::Entries => self.snapshot.entries(self.include_ignored, start),
3984            },
3985        }
3986    }
3987}
3988
3989pub struct PathMatchCandidateSetIter<'a> {
3990    traversal: Traversal<'a>,
3991}
3992
3993impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
3994    type Item = fuzzy::PathMatchCandidate<'a>;
3995
3996    fn next(&mut self) -> Option<Self::Item> {
3997        self.traversal
3998            .next()
3999            .map(|entry| fuzzy::PathMatchCandidate {
4000                is_dir: entry.kind.is_dir(),
4001                path: &entry.path,
4002                char_bag: entry.char_bag,
4003            })
4004    }
4005}
4006
4007impl EventEmitter<Event> for Project {}
4008
4009impl<'a> From<&'a ProjectPath> for SettingsLocation<'a> {
4010    fn from(val: &'a ProjectPath) -> Self {
4011        SettingsLocation {
4012            worktree_id: val.worktree_id,
4013            path: val.path.as_ref(),
4014        }
4015    }
4016}
4017
4018impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
4019    fn from((worktree_id, path): (WorktreeId, P)) -> Self {
4020        Self {
4021            worktree_id,
4022            path: path.as_ref().into(),
4023        }
4024    }
4025}
4026
4027pub fn relativize_path(base: &Path, path: &Path) -> PathBuf {
4028    let mut path_components = path.components();
4029    let mut base_components = base.components();
4030    let mut components: Vec<Component> = Vec::new();
4031    loop {
4032        match (path_components.next(), base_components.next()) {
4033            (None, None) => break,
4034            (Some(a), None) => {
4035                components.push(a);
4036                components.extend(path_components.by_ref());
4037                break;
4038            }
4039            (None, _) => components.push(Component::ParentDir),
4040            (Some(a), Some(b)) if components.is_empty() && a == b => (),
4041            (Some(a), Some(Component::CurDir)) => components.push(a),
4042            (Some(a), Some(_)) => {
4043                components.push(Component::ParentDir);
4044                for _ in base_components {
4045                    components.push(Component::ParentDir);
4046                }
4047                components.push(a);
4048                components.extend(path_components.by_ref());
4049                break;
4050            }
4051        }
4052    }
4053    components.iter().map(|c| c.as_os_str()).collect()
4054}
4055
4056fn resolve_path(base: &Path, path: &Path) -> PathBuf {
4057    let mut result = base.to_path_buf();
4058    for component in path.components() {
4059        match component {
4060            Component::ParentDir => {
4061                result.pop();
4062            }
4063            Component::CurDir => (),
4064            _ => result.push(component),
4065        }
4066    }
4067    result
4068}
4069
4070/// ResolvedPath is a path that has been resolved to either a ProjectPath
4071/// or an AbsPath and that *exists*.
4072#[derive(Debug, Clone)]
4073pub enum ResolvedPath {
4074    ProjectPath(ProjectPath),
4075    AbsPath(PathBuf),
4076}
4077
4078impl ResolvedPath {
4079    pub fn abs_path(&self) -> Option<&Path> {
4080        match self {
4081            Self::AbsPath(path) => Some(path.as_path()),
4082            _ => None,
4083        }
4084    }
4085
4086    pub fn project_path(&self) -> Option<&ProjectPath> {
4087        match self {
4088            Self::ProjectPath(path) => Some(&path),
4089            _ => None,
4090        }
4091    }
4092}
4093
4094impl Item for Buffer {
4095    fn try_open(
4096        project: &Model<Project>,
4097        path: &ProjectPath,
4098        cx: &mut AppContext,
4099    ) -> Option<Task<Result<Model<Self>>>> {
4100        Some(project.update(cx, |project, cx| project.open_buffer(path.clone(), cx)))
4101    }
4102
4103    fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
4104        File::from_dyn(self.file()).and_then(|file| file.project_entry_id(cx))
4105    }
4106
4107    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
4108        File::from_dyn(self.file()).map(|file| ProjectPath {
4109            worktree_id: file.worktree_id(cx),
4110            path: file.path().clone(),
4111        })
4112    }
4113}
4114
4115impl Completion {
4116    /// A key that can be used to sort completions when displaying
4117    /// them to the user.
4118    pub fn sort_key(&self) -> (usize, &str) {
4119        let kind_key = match self.lsp_completion.kind {
4120            Some(lsp::CompletionItemKind::KEYWORD) => 0,
4121            Some(lsp::CompletionItemKind::VARIABLE) => 1,
4122            _ => 2,
4123        };
4124        (kind_key, &self.label.text[self.label.filter_range.clone()])
4125    }
4126
4127    /// Whether this completion is a snippet.
4128    pub fn is_snippet(&self) -> bool {
4129        self.lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET)
4130    }
4131
4132    /// Returns the corresponding color for this completion.
4133    ///
4134    /// Will return `None` if this completion's kind is not [`CompletionItemKind::COLOR`].
4135    pub fn color(&self) -> Option<Hsla> {
4136        match self.lsp_completion.kind {
4137            Some(CompletionItemKind::COLOR) => color_extractor::extract_color(&self.lsp_completion),
4138            _ => None,
4139        }
4140    }
4141}
4142
4143#[derive(Debug)]
4144pub struct NoRepositoryError {}
4145
4146impl std::fmt::Display for NoRepositoryError {
4147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4148        write!(f, "no git repository for worktree found")
4149    }
4150}
4151
4152impl std::error::Error for NoRepositoryError {}
4153
4154pub fn sort_worktree_entries(entries: &mut [Entry]) {
4155    entries.sort_by(|entry_a, entry_b| {
4156        compare_paths(
4157            (&entry_a.path, entry_a.is_file()),
4158            (&entry_b.path, entry_b.is_file()),
4159        )
4160    });
4161}