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