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