Merge branch 'main' into polish-project-diagnostics

Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
Antonio Scandurra 2022-01-04 18:35:28 +01:00
commit b2f0c78924
12 changed files with 166 additions and 107 deletions

2
Cargo.lock generated
View file

@ -5683,7 +5683,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]] [[package]]
name = "zed" name = "zed"
version = "0.9.0" version = "0.11.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-recursion", "async-recursion",

View file

@ -11,7 +11,7 @@ use gpui::{
}; };
use language::{Bias, Buffer, Diagnostic, DiagnosticEntry, Point}; use language::{Bias, Buffer, Diagnostic, DiagnosticEntry, Point};
use postage::watch; use postage::watch;
use project::{Project, ProjectPath}; use project::{Project, ProjectPath, WorktreeId};
use std::{cmp::Ordering, ops::Range, path::Path, sync::Arc}; use std::{cmp::Ordering, ops::Range, path::Path, sync::Arc};
use util::TryFutureExt; use util::TryFutureExt;
use workspace::Workspace; use workspace::Workspace;
@ -45,7 +45,7 @@ struct ProjectDiagnosticsEditor {
editor: ViewHandle<Editor>, editor: ViewHandle<Editor>,
excerpts: ModelHandle<MultiBuffer>, excerpts: ModelHandle<MultiBuffer>,
path_states: Vec<(Arc<Path>, Vec<DiagnosticGroupState>)>, path_states: Vec<(Arc<Path>, Vec<DiagnosticGroupState>)>,
paths_to_update: HashMap<usize, HashSet<ProjectPath>>, paths_to_update: HashMap<WorktreeId, HashSet<ProjectPath>>,
build_settings: BuildSettings, build_settings: BuildSettings,
} }

View file

@ -7,7 +7,7 @@ use gpui::{
}; };
use language::{Diagnostic, File as _}; use language::{Diagnostic, File as _};
use postage::watch; use postage::watch;
use project::{ProjectPath, Worktree}; use project::{File, ProjectPath, Worktree};
use std::fmt::Write; use std::fmt::Write;
use std::path::Path; use std::path::Path;
use text::{Point, Selection}; use text::{Point, Selection};
@ -66,8 +66,8 @@ impl ItemHandle for BufferItemHandle {
} }
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> { fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
self.0.read(cx).file(cx).map(|f| ProjectPath { File::from_dyn(self.0.read(cx).file(cx)).map(|f| ProjectPath {
worktree_id: f.worktree_id(), worktree_id: f.worktree_id(cx),
path: f.path().clone(), path: f.path().clone(),
}) })
} }
@ -111,8 +111,8 @@ impl ItemView for Editor {
} }
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> { fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
self.buffer().read(cx).file(cx).map(|file| ProjectPath { File::from_dyn(self.buffer().read(cx).file(cx)).map(|file| ProjectPath {
worktree_id: file.worktree_id(), worktree_id: file.worktree_id(cx),
path: file.path().clone(), path: file.path().clone(),
}) })
} }

View file

@ -68,6 +68,7 @@ struct BufferState {
buffer: ModelHandle<Buffer>, buffer: ModelHandle<Buffer>,
last_version: clock::Global, last_version: clock::Global,
last_parse_count: usize, last_parse_count: usize,
last_selections_update_count: usize,
last_diagnostics_update_count: usize, last_diagnostics_update_count: usize,
excerpts: Vec<ExcerptId>, excerpts: Vec<ExcerptId>,
_subscriptions: [gpui::Subscription; 2], _subscriptions: [gpui::Subscription; 2],
@ -637,6 +638,7 @@ impl MultiBuffer {
.or_insert_with(|| BufferState { .or_insert_with(|| BufferState {
last_version: buffer_snapshot.version().clone(), last_version: buffer_snapshot.version().clone(),
last_parse_count: buffer_snapshot.parse_count(), last_parse_count: buffer_snapshot.parse_count(),
last_selections_update_count: buffer_snapshot.selections_update_count(),
last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(), last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(),
excerpts: Default::default(), excerpts: Default::default(),
_subscriptions: [ _subscriptions: [
@ -799,15 +801,23 @@ impl MultiBuffer {
let buffer = buffer_state.buffer.read(cx); let buffer = buffer_state.buffer.read(cx);
let version = buffer.version(); let version = buffer.version();
let parse_count = buffer.parse_count(); let parse_count = buffer.parse_count();
let selections_update_count = buffer.selections_update_count();
let diagnostics_update_count = buffer.diagnostics_update_count(); let diagnostics_update_count = buffer.diagnostics_update_count();
let buffer_edited = version.gt(&buffer_state.last_version); let buffer_edited = version.gt(&buffer_state.last_version);
let buffer_reparsed = parse_count > buffer_state.last_parse_count; let buffer_reparsed = parse_count > buffer_state.last_parse_count;
let buffer_selections_updated =
selections_update_count > buffer_state.last_selections_update_count;
let buffer_diagnostics_updated = let buffer_diagnostics_updated =
diagnostics_update_count > buffer_state.last_diagnostics_update_count; diagnostics_update_count > buffer_state.last_diagnostics_update_count;
if buffer_edited || buffer_reparsed || buffer_diagnostics_updated { if buffer_edited
|| buffer_reparsed
|| buffer_selections_updated
|| buffer_diagnostics_updated
{
buffer_state.last_version = version; buffer_state.last_version = version;
buffer_state.last_parse_count = parse_count; buffer_state.last_parse_count = parse_count;
buffer_state.last_selections_update_count = selections_update_count;
buffer_state.last_diagnostics_update_count = diagnostics_update_count; buffer_state.last_diagnostics_update_count = diagnostics_update_count;
excerpts_to_edit.extend( excerpts_to_edit.extend(
buffer_state buffer_state

View file

@ -12,7 +12,7 @@ use gpui::{
ViewContext, ViewHandle, WeakViewHandle, ViewContext, ViewHandle, WeakViewHandle,
}; };
use postage::watch; use postage::watch;
use project::{Project, ProjectPath}; use project::{Project, ProjectPath, WorktreeId};
use std::{ use std::{
cmp, cmp,
path::Path, path::Path,
@ -195,7 +195,7 @@ impl FileFinder {
.with_style(style.container); .with_style(style.container);
let action = Select(ProjectPath { let action = Select(ProjectPath {
worktree_id: path_match.worktree_id, worktree_id: WorktreeId::from_usize(path_match.worktree_id),
path: path_match.path.clone(), path: path_match.path.clone(),
}); });
EventHandler::new(container.boxed()) EventHandler::new(container.boxed())
@ -370,7 +370,7 @@ impl FileFinder {
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) { fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
if let Some(m) = self.matches.get(self.selected_index()) { if let Some(m) = self.matches.get(self.selected_index()) {
cx.emit(Event::Selected(ProjectPath { cx.emit(Event::Selected(ProjectPath {
worktree_id: m.worktree_id, worktree_id: WorktreeId::from_usize(m.worktree_id),
path: m.path.clone(), path: m.path.clone(),
})); }));
} }

View file

@ -67,6 +67,7 @@ pub struct Buffer {
parse_count: usize, parse_count: usize,
remote_selections: TreeMap<ReplicaId, Arc<[Selection<Anchor>]>>, remote_selections: TreeMap<ReplicaId, Arc<[Selection<Anchor>]>>,
diagnostics: DiagnosticSet, diagnostics: DiagnosticSet,
selections_update_count: usize,
diagnostics_update_count: usize, diagnostics_update_count: usize,
language_server: Option<LanguageServerState>, language_server: Option<LanguageServerState>,
deferred_ops: OperationQueue<Operation>, deferred_ops: OperationQueue<Operation>,
@ -80,6 +81,7 @@ pub struct BufferSnapshot {
diagnostics: DiagnosticSet, diagnostics: DiagnosticSet,
remote_selections: TreeMap<ReplicaId, Arc<[Selection<Anchor>]>>, remote_selections: TreeMap<ReplicaId, Arc<[Selection<Anchor>]>>,
diagnostics_update_count: usize, diagnostics_update_count: usize,
selections_update_count: usize,
is_parsing: bool, is_parsing: bool,
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
parse_count: usize, parse_count: usize,
@ -148,10 +150,6 @@ pub enum Event {
} }
pub trait File { pub trait File {
fn worktree_id(&self) -> usize;
fn entry_id(&self) -> Option<usize>;
fn mtime(&self) -> SystemTime; fn mtime(&self) -> SystemTime;
/// Returns the path of this file relative to the worktree's root directory. /// Returns the path of this file relative to the worktree's root directory.
@ -184,8 +182,6 @@ pub trait File {
fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext); fn buffer_removed(&self, buffer_id: u64, cx: &mut MutableAppContext);
fn boxed_clone(&self) -> Box<dyn File>;
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
} }
@ -366,6 +362,7 @@ impl Buffer {
pending_autoindent: Default::default(), pending_autoindent: Default::default(),
language: None, language: None,
remote_selections: Default::default(), remote_selections: Default::default(),
selections_update_count: 0,
diagnostics: Default::default(), diagnostics: Default::default(),
diagnostics_update_count: 0, diagnostics_update_count: 0,
language_server: None, language_server: None,
@ -385,6 +382,7 @@ impl Buffer {
is_parsing: self.parsing_in_background, is_parsing: self.parsing_in_background,
language: self.language.clone(), language: self.language.clone(),
parse_count: self.parse_count, parse_count: self.parse_count,
selections_update_count: self.selections_update_count,
} }
} }
@ -610,6 +608,10 @@ impl Buffer {
self.parse_count self.parse_count
} }
pub fn selections_update_count(&self) -> usize {
self.selections_update_count
}
pub fn diagnostics_update_count(&self) -> usize { pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count self.diagnostics_update_count
} }
@ -1079,8 +1081,6 @@ impl Buffer {
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
let lamport_timestamp = self.text.lamport_clock.tick(); let lamport_timestamp = self.text.lamport_clock.tick();
self.remote_selections
.insert(self.text.replica_id(), selections.clone());
self.send_operation( self.send_operation(
Operation::UpdateSelections { Operation::UpdateSelections {
replica_id: self.text.replica_id(), replica_id: self.text.replica_id(),
@ -1348,6 +1348,7 @@ impl Buffer {
} => { } => {
self.remote_selections.insert(replica_id, selections); self.remote_selections.insert(replica_id, selections);
self.text.lamport_clock.observe(lamport_timestamp); self.text.lamport_clock.observe(lamport_timestamp);
self.selections_update_count += 1;
} }
Operation::RemoveSelections { Operation::RemoveSelections {
replica_id, replica_id,
@ -1355,6 +1356,7 @@ impl Buffer {
} => { } => {
self.remote_selections.remove(&replica_id); self.remote_selections.remove(&replica_id);
self.text.lamport_clock.observe(lamport_timestamp); self.text.lamport_clock.observe(lamport_timestamp);
self.selections_update_count += 1;
} }
} }
} }
@ -1760,6 +1762,10 @@ impl BufferSnapshot {
pub fn parse_count(&self) -> usize { pub fn parse_count(&self) -> usize {
self.parse_count self.parse_count
} }
pub fn selections_update_count(&self) -> usize {
self.selections_update_count
}
} }
impl Clone for BufferSnapshot { impl Clone for BufferSnapshot {
@ -1769,6 +1775,7 @@ impl Clone for BufferSnapshot {
tree: self.tree.clone(), tree: self.tree.clone(),
remote_selections: self.remote_selections.clone(), remote_selections: self.remote_selections.clone(),
diagnostics: self.diagnostics.clone(), diagnostics: self.diagnostics.clone(),
selections_update_count: self.selections_update_count,
diagnostics_update_count: self.diagnostics_update_count, diagnostics_update_count: self.diagnostics_update_count,
is_parsing: self.is_parsing, is_parsing: self.is_parsing,
language: self.language.clone(), language: self.language.clone(),

View file

@ -59,14 +59,14 @@ pub struct Collaborator {
#[derive(Debug)] #[derive(Debug)]
pub enum Event { pub enum Event {
ActiveEntryChanged(Option<ProjectEntry>), ActiveEntryChanged(Option<ProjectEntry>),
WorktreeRemoved(usize), WorktreeRemoved(WorktreeId),
DiskBasedDiagnosticsUpdated { worktree_id: usize }, DiskBasedDiagnosticsUpdated { worktree_id: WorktreeId },
DiagnosticsUpdated(ProjectPath), DiagnosticsUpdated(ProjectPath),
} }
#[derive(Clone, Debug, Eq, PartialEq, Hash)] #[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct ProjectPath { pub struct ProjectPath {
pub worktree_id: usize, pub worktree_id: WorktreeId,
pub path: Arc<Path>, pub path: Arc<Path>,
} }
@ -105,7 +105,7 @@ impl DiagnosticSummary {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ProjectEntry { pub struct ProjectEntry {
pub worktree_id: usize, pub worktree_id: WorktreeId,
pub entry_id: usize, pub entry_id: usize,
} }
@ -321,7 +321,11 @@ impl Project {
&self.worktrees &self.worktrees
} }
pub fn worktree_for_id(&self, id: usize, cx: &AppContext) -> Option<ModelHandle<Worktree>> { pub fn worktree_for_id(
&self,
id: WorktreeId,
cx: &AppContext,
) -> Option<ModelHandle<Worktree>> {
self.worktrees self.worktrees
.iter() .iter()
.find(|worktree| worktree.read(cx).id() == id) .find(|worktree| worktree.read(cx).id() == id)
@ -479,13 +483,13 @@ impl Project {
cx.subscribe(&worktree, |_, worktree, event, cx| match event { cx.subscribe(&worktree, |_, worktree, event, cx| match event {
worktree::Event::DiagnosticsUpdated(path) => { worktree::Event::DiagnosticsUpdated(path) => {
cx.emit(Event::DiagnosticsUpdated(ProjectPath { cx.emit(Event::DiagnosticsUpdated(ProjectPath {
worktree_id: worktree.id(), worktree_id: worktree.read(cx).id(),
path: path.clone(), path: path.clone(),
})); }));
} }
worktree::Event::DiskBasedDiagnosticsUpdated => { worktree::Event::DiskBasedDiagnosticsUpdated => {
cx.emit(Event::DiskBasedDiagnosticsUpdated { cx.emit(Event::DiskBasedDiagnosticsUpdated {
worktree_id: worktree.id(), worktree_id: worktree.read(cx).id(),
}); });
} }
}) })
@ -514,9 +518,9 @@ impl Project {
cx: &'a AppContext, cx: &'a AppContext,
) -> impl Iterator<Item = (ProjectPath, DiagnosticSummary)> + 'a { ) -> impl Iterator<Item = (ProjectPath, DiagnosticSummary)> + 'a {
self.worktrees.iter().flat_map(move |worktree| { self.worktrees.iter().flat_map(move |worktree| {
let worktree = worktree.read(cx);
let worktree_id = worktree.id(); let worktree_id = worktree.id();
worktree worktree
.read(cx)
.diagnostic_summaries() .diagnostic_summaries()
.map(move |(path, summary)| (ProjectPath { worktree_id, path }, summary)) .map(move |(path, summary)| (ProjectPath { worktree_id, path }, summary))
}) })
@ -634,9 +638,9 @@ impl Project {
_: Arc<Client>, _: Arc<Client>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<()> { ) -> Result<()> {
self.worktrees.retain(|worktree| { let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
worktree.read(cx).as_remote().unwrap().remote_id() != envelope.payload.worktree_id self.worktrees
}); .retain(|worktree| worktree.read(cx).as_remote().unwrap().id() != worktree_id);
cx.notify(); cx.notify();
Ok(()) Ok(())
} }
@ -647,7 +651,8 @@ impl Project {
_: Arc<Client>, _: Arc<Client>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<()> { ) -> Result<()> {
if let Some(worktree) = self.worktree_for_id(envelope.payload.worktree_id as usize, cx) { let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
worktree.update(cx, |worktree, cx| { worktree.update(cx, |worktree, cx| {
let worktree = worktree.as_remote_mut().unwrap(); let worktree = worktree.as_remote_mut().unwrap();
worktree.update_from_remote(envelope, cx) worktree.update_from_remote(envelope, cx)
@ -662,7 +667,8 @@ impl Project {
_: Arc<Client>, _: Arc<Client>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<()> { ) -> Result<()> {
if let Some(worktree) = self.worktree_for_id(envelope.payload.worktree_id as usize, cx) { let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
worktree.update(cx, |worktree, cx| { worktree.update(cx, |worktree, cx| {
worktree.handle_update_buffer(envelope, cx) worktree.handle_update_buffer(envelope, cx)
})?; })?;
@ -676,7 +682,8 @@ impl Project {
rpc: Arc<Client>, rpc: Arc<Client>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<()> { ) -> Result<()> {
if let Some(worktree) = self.worktree_for_id(envelope.payload.worktree_id as usize, cx) { let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
worktree.update(cx, |worktree, cx| { worktree.update(cx, |worktree, cx| {
worktree.handle_save_buffer(envelope, rpc, cx) worktree.handle_save_buffer(envelope, rpc, cx)
})?; })?;
@ -690,7 +697,8 @@ impl Project {
rpc: Arc<Client>, rpc: Arc<Client>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
if let Some(worktree) = self.worktree_for_id(envelope.payload.worktree_id as usize, cx) { let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
return worktree.update(cx, |worktree, cx| { return worktree.update(cx, |worktree, cx| {
worktree.handle_open_buffer(envelope, rpc, cx) worktree.handle_open_buffer(envelope, rpc, cx)
}); });
@ -705,7 +713,8 @@ impl Project {
rpc: Arc<Client>, rpc: Arc<Client>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
if let Some(worktree) = self.worktree_for_id(envelope.payload.worktree_id as usize, cx) { let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
worktree.update(cx, |worktree, cx| { worktree.update(cx, |worktree, cx| {
worktree.handle_close_buffer(envelope, rpc, cx) worktree.handle_close_buffer(envelope, rpc, cx)
})?; })?;
@ -719,7 +728,8 @@ impl Project {
_: Arc<Client>, _: Arc<Client>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<()> { ) -> Result<()> {
if let Some(worktree) = self.worktree_for_id(envelope.payload.worktree_id as usize, cx) { let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
if let Some(worktree) = self.worktree_for_id(worktree_id, cx) {
worktree.update(cx, |worktree, cx| { worktree.update(cx, |worktree, cx| {
worktree.handle_buffer_saved(envelope, cx) worktree.handle_buffer_saved(envelope, cx)
})?; })?;
@ -772,7 +782,7 @@ impl<'a> PathMatchCandidateSet<'a> for CandidateSet {
type Candidates = CandidateSetIter<'a>; type Candidates = CandidateSetIter<'a>;
fn id(&self) -> usize { fn id(&self) -> usize {
self.snapshot.id() self.snapshot.id().to_usize()
} }
fn len(&self) -> usize { fn len(&self) -> usize {

View file

@ -59,6 +59,9 @@ enum ScanState {
Err(Arc<anyhow::Error>), Err(Arc<anyhow::Error>),
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct WorktreeId(usize);
pub enum Worktree { pub enum Worktree {
Local(LocalWorktree), Local(LocalWorktree),
Remote(RemoteWorktree), Remote(RemoteWorktree),
@ -170,7 +173,7 @@ impl Worktree {
let worktree = cx.update(|cx| { let worktree = cx.update(|cx| {
cx.add_model(|cx: &mut ModelContext<Worktree>| { cx.add_model(|cx: &mut ModelContext<Worktree>| {
let snapshot = Snapshot { let snapshot = Snapshot {
id: remote_id as usize, id: WorktreeId(remote_id as usize),
scan_id: 0, scan_id: 0,
abs_path: Path::new("").into(), abs_path: Path::new("").into(),
root_name, root_name,
@ -213,7 +216,6 @@ impl Worktree {
Worktree::Remote(RemoteWorktree { Worktree::Remote(RemoteWorktree {
project_id: project_remote_id, project_id: project_remote_id,
remote_id,
replica_id, replica_id,
snapshot, snapshot,
snapshot_rx, snapshot_rx,
@ -618,9 +620,9 @@ impl Worktree {
for (buffer_id, buffer) in open_buffers { for (buffer_id, buffer) in open_buffers {
if let Some(buffer) = buffer.upgrade(cx) { if let Some(buffer) = buffer.upgrade(cx) {
buffer.update(cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
if let Some(old_file) = buffer.file() { if let Some(old_file) = File::from_dyn(buffer.file()) {
let new_file = if let Some(entry) = old_file let new_file = if let Some(entry) = old_file
.entry_id() .entry_id
.and_then(|entry_id| self.entry_for_id(entry_id)) .and_then(|entry_id| self.entry_for_id(entry_id))
{ {
File { File {
@ -789,16 +791,13 @@ impl Worktree {
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
if let Some((project_id, worktree_id, rpc)) = match self { if let Some((project_id, worktree_id, rpc)) = match self {
Worktree::Local(worktree) => worktree.share.as_ref().map(|share| { Worktree::Local(worktree) => worktree
( .share
share.project_id, .as_ref()
worktree.id() as u64, .map(|share| (share.project_id, worktree.id(), worktree.client.clone())),
worktree.client.clone(),
)
}),
Worktree::Remote(worktree) => Some(( Worktree::Remote(worktree) => Some((
worktree.project_id, worktree.project_id,
worktree.remote_id, worktree.snapshot.id(),
worktree.client.clone(), worktree.client.clone(),
)), )),
} { } {
@ -806,7 +805,7 @@ impl Worktree {
if let Err(error) = rpc if let Err(error) = rpc
.request(proto::UpdateBuffer { .request(proto::UpdateBuffer {
project_id, project_id,
worktree_id, worktree_id: worktree_id.0 as u64,
buffer_id, buffer_id,
operations: vec![language::proto::serialize_operation(&operation)], operations: vec![language::proto::serialize_operation(&operation)],
}) })
@ -827,9 +826,33 @@ impl Worktree {
} }
} }
impl WorktreeId {
pub fn from_usize(handle_id: usize) -> Self {
Self(handle_id)
}
pub(crate) fn from_proto(id: u64) -> Self {
Self(id as usize)
}
pub fn to_proto(&self) -> u64 {
self.0 as u64
}
pub fn to_usize(&self) -> usize {
self.0
}
}
impl fmt::Display for WorktreeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct Snapshot { pub struct Snapshot {
id: usize, id: WorktreeId,
scan_id: usize, scan_id: usize,
abs_path: Arc<Path>, abs_path: Arc<Path>,
root_name: String, root_name: String,
@ -870,7 +893,6 @@ struct ShareState {
pub struct RemoteWorktree { pub struct RemoteWorktree {
project_id: u64, project_id: u64,
remote_id: u64,
snapshot: Snapshot, snapshot: Snapshot,
snapshot_rx: watch::Receiver<Snapshot>, snapshot_rx: watch::Receiver<Snapshot>,
client: Arc<Client>, client: Arc<Client>,
@ -926,7 +948,7 @@ impl LocalWorktree {
let (mut last_scan_state_tx, last_scan_state_rx) = watch::channel_with(ScanState::Scanning); let (mut last_scan_state_tx, last_scan_state_rx) = watch::channel_with(ScanState::Scanning);
let tree = cx.add_model(move |cx: &mut ModelContext<Worktree>| { let tree = cx.add_model(move |cx: &mut ModelContext<Worktree>| {
let mut snapshot = Snapshot { let mut snapshot = Snapshot {
id: cx.model_id(), id: WorktreeId::from_usize(cx.model_id()),
scan_id: 0, scan_id: 0,
abs_path, abs_path,
root_name: root_name.clone(), root_name: root_name.clone(),
@ -1114,12 +1136,12 @@ impl LocalWorktree {
path: &Path, path: &Path,
cx: &mut ModelContext<Worktree>, cx: &mut ModelContext<Worktree>,
) -> Option<ModelHandle<Buffer>> { ) -> Option<ModelHandle<Buffer>> {
let worktree_id = self.id(); let handle = cx.handle();
let mut result = None; let mut result = None;
self.open_buffers.retain(|_buffer_id, buffer| { self.open_buffers.retain(|_buffer_id, buffer| {
if let Some(buffer) = buffer.upgrade(cx.as_ref()) { if let Some(buffer) = buffer.upgrade(cx) {
if let Some(file) = buffer.read(cx.as_ref()).file() { if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
if file.worktree_id() == worktree_id && file.path().as_ref() == path { if file.worktree == handle && file.path().as_ref() == path {
result = Some(buffer); result = Some(buffer);
} }
} }
@ -1422,6 +1444,14 @@ impl Deref for LocalWorktree {
} }
} }
impl Deref for RemoteWorktree {
type Target = Snapshot;
fn deref(&self) -> &Self::Target {
&self.snapshot
}
}
impl fmt::Debug for LocalWorktree { impl fmt::Debug for LocalWorktree {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.snapshot.fmt(f) self.snapshot.fmt(f)
@ -1429,10 +1459,6 @@ impl fmt::Debug for LocalWorktree {
} }
impl RemoteWorktree { impl RemoteWorktree {
pub fn remote_id(&self) -> u64 {
self.remote_id
}
fn get_open_buffer( fn get_open_buffer(
&mut self, &mut self,
path: &Path, path: &Path,
@ -1442,8 +1468,8 @@ impl RemoteWorktree {
let mut existing_buffer = None; let mut existing_buffer = None;
self.open_buffers.retain(|_buffer_id, buffer| { self.open_buffers.retain(|_buffer_id, buffer| {
if let Some(buffer) = buffer.upgrade(cx.as_ref()) { if let Some(buffer) = buffer.upgrade(cx.as_ref()) {
if let Some(file) = buffer.read(cx.as_ref()).file() { if let Some(file) = File::from_dyn(buffer.read(cx).file()) {
if file.worktree_id() == handle.id() && file.path().as_ref() == path { if file.worktree == handle && file.path().as_ref() == path {
existing_buffer = Some(buffer); existing_buffer = Some(buffer);
} }
} }
@ -1463,7 +1489,7 @@ impl RemoteWorktree {
let rpc = self.client.clone(); let rpc = self.client.clone();
let replica_id = self.replica_id; let replica_id = self.replica_id;
let project_id = self.project_id; let project_id = self.project_id;
let remote_worktree_id = self.remote_id; let remote_worktree_id = self.id();
let root_path = self.snapshot.abs_path.clone(); let root_path = self.snapshot.abs_path.clone();
let path: Arc<Path> = Arc::from(path); let path: Arc<Path> = Arc::from(path);
let path_string = path.to_string_lossy().to_string(); let path_string = path.to_string_lossy().to_string();
@ -1476,7 +1502,7 @@ impl RemoteWorktree {
let response = rpc let response = rpc
.request(proto::OpenBuffer { .request(proto::OpenBuffer {
project_id, project_id,
worktree_id: remote_worktree_id as u64, worktree_id: remote_worktree_id.to_proto(),
path: path_string, path: path_string,
}) })
.await?; .await?;
@ -1571,14 +1597,14 @@ impl RemoteBuffer {
} }
impl Snapshot { impl Snapshot {
pub fn id(&self) -> usize { pub fn id(&self) -> WorktreeId {
self.id self.id
} }
pub fn to_proto(&self) -> proto::Worktree { pub fn to_proto(&self) -> proto::Worktree {
let root_name = self.root_name.clone(); let root_name = self.root_name.clone();
proto::Worktree { proto::Worktree {
id: self.id as u64, id: self.id.0 as u64,
root_name, root_name,
entries: self entries: self
.entries_by_path .entries_by_path
@ -1954,14 +1980,6 @@ pub struct File {
} }
impl language::File for File { impl language::File for File {
fn worktree_id(&self) -> usize {
self.worktree.id()
}
fn entry_id(&self) -> Option<usize> {
self.entry_id
}
fn mtime(&self) -> SystemTime { fn mtime(&self) -> SystemTime {
self.mtime self.mtime
} }
@ -2007,7 +2025,7 @@ impl language::File for File {
version: clock::Global, version: clock::Global,
cx: &mut MutableAppContext, cx: &mut MutableAppContext,
) -> Task<Result<(clock::Global, SystemTime)>> { ) -> Task<Result<(clock::Global, SystemTime)>> {
let worktree_id = self.worktree.read(cx).id() as u64; let worktree_id = self.worktree.read(cx).id().to_proto();
self.worktree.update(cx, |worktree, cx| match worktree { self.worktree.update(cx, |worktree, cx| match worktree {
Worktree::Local(worktree) => { Worktree::Local(worktree) => {
let rpc = worktree.client.clone(); let rpc = worktree.client.clone();
@ -2070,7 +2088,7 @@ impl language::File for File {
self.worktree.update(cx, |worktree, cx| { self.worktree.update(cx, |worktree, cx| {
if let Worktree::Remote(worktree) = worktree { if let Worktree::Remote(worktree) = worktree {
let project_id = worktree.project_id; let project_id = worktree.project_id;
let worktree_id = worktree.remote_id; let worktree_id = worktree.id().to_proto();
let rpc = worktree.client.clone(); let rpc = worktree.client.clone();
cx.background() cx.background()
.spawn(async move { .spawn(async move {
@ -2090,15 +2108,21 @@ impl language::File for File {
}); });
} }
fn boxed_clone(&self) -> Box<dyn language::File> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
} }
impl File {
pub fn from_dyn(file: Option<&dyn language::File>) -> Option<&Self> {
file.and_then(|f| f.as_any().downcast_ref())
}
pub fn worktree_id(&self, cx: &AppContext) -> WorktreeId {
self.worktree.read(cx).id()
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Entry { pub struct Entry {
pub id: usize, pub id: usize,
@ -4031,7 +4055,7 @@ mod tests {
let fs = Arc::new(RealFs); let fs = Arc::new(RealFs);
let next_entry_id = Arc::new(AtomicUsize::new(0)); let next_entry_id = Arc::new(AtomicUsize::new(0));
let mut initial_snapshot = Snapshot { let mut initial_snapshot = Snapshot {
id: 0, id: WorktreeId::from_usize(0),
scan_id: 0, scan_id: 0,
abs_path: root_dir.path().into(), abs_path: root_dir.path().into(),
entries_by_path: Default::default(), entries_by_path: Default::default(),

View file

@ -14,7 +14,7 @@ use gpui::{
ViewContext, ViewHandle, WeakViewHandle, ViewContext, ViewHandle, WeakViewHandle,
}; };
use postage::watch; use postage::watch;
use project::{Project, ProjectEntry, ProjectPath, Worktree}; use project::{Project, ProjectEntry, ProjectPath, Worktree, WorktreeId};
use std::{ use std::{
collections::{hash_map, HashMap}, collections::{hash_map, HashMap},
ffi::OsStr, ffi::OsStr,
@ -26,7 +26,7 @@ pub struct ProjectPanel {
project: ModelHandle<Project>, project: ModelHandle<Project>,
list: UniformListState, list: UniformListState,
visible_entries: Vec<Vec<usize>>, visible_entries: Vec<Vec<usize>>,
expanded_dir_ids: HashMap<usize, Vec<usize>>, expanded_dir_ids: HashMap<WorktreeId, Vec<usize>>,
selection: Option<Selection>, selection: Option<Selection>,
settings: watch::Receiver<Settings>, settings: watch::Receiver<Settings>,
handle: WeakViewHandle<Self>, handle: WeakViewHandle<Self>,
@ -34,7 +34,7 @@ pub struct ProjectPanel {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Selection { struct Selection {
worktree_id: usize, worktree_id: WorktreeId,
entry_id: usize, entry_id: usize,
index: usize, index: usize,
} }
@ -67,7 +67,10 @@ pub fn init(cx: &mut MutableAppContext) {
} }
pub enum Event { pub enum Event {
OpenedEntry { worktree_id: usize, entry_id: usize }, OpenedEntry {
worktree_id: WorktreeId,
entry_id: usize,
},
} }
impl ProjectPanel { impl ProjectPanel {
@ -114,16 +117,16 @@ impl ProjectPanel {
this this
}); });
cx.subscribe(&project_panel, move |workspace, _, event, cx| match event { cx.subscribe(&project_panel, move |workspace, _, event, cx| match event {
Event::OpenedEntry { &Event::OpenedEntry {
worktree_id, worktree_id,
entry_id, entry_id,
} => { } => {
if let Some(worktree) = project.read(cx).worktree_for_id(*worktree_id, cx) { if let Some(worktree) = project.read(cx).worktree_for_id(worktree_id, cx) {
if let Some(entry) = worktree.read(cx).entry_for_id(*entry_id) { if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) {
workspace workspace
.open_entry( .open_entry(
ProjectPath { ProjectPath {
worktree_id: worktree.id(), worktree_id,
path: entry.path.clone(), path: entry.path.clone(),
}, },
cx, cx,
@ -259,8 +262,8 @@ impl ProjectPanel {
fn select_first(&mut self, cx: &mut ViewContext<Self>) { fn select_first(&mut self, cx: &mut ViewContext<Self>) {
if let Some(worktree) = self.project.read(cx).worktrees().first() { if let Some(worktree) = self.project.read(cx).worktrees().first() {
let worktree_id = worktree.id();
let worktree = worktree.read(cx); let worktree = worktree.read(cx);
let worktree_id = worktree.id();
if let Some(root_entry) = worktree.root_entry() { if let Some(root_entry) = worktree.root_entry() {
self.selection = Some(Selection { self.selection = Some(Selection {
worktree_id, worktree_id,
@ -313,7 +316,7 @@ impl ProjectPanel {
fn update_visible_entries( fn update_visible_entries(
&mut self, &mut self,
new_selected_entry: Option<(usize, usize)>, new_selected_entry: Option<(WorktreeId, usize)>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
let worktrees = self.project.read(cx).worktrees(); let worktrees = self.project.read(cx).worktrees();
@ -322,7 +325,7 @@ impl ProjectPanel {
let mut entry_ix = 0; let mut entry_ix = 0;
for worktree in worktrees { for worktree in worktrees {
let snapshot = worktree.read(cx).snapshot(); let snapshot = worktree.read(cx).snapshot();
let worktree_id = worktree.id(); let worktree_id = snapshot.id();
let expanded_dir_ids = match self.expanded_dir_ids.entry(worktree_id) { let expanded_dir_ids = match self.expanded_dir_ids.entry(worktree_id) {
hash_map::Entry::Occupied(e) => e.into_mut(), hash_map::Entry::Occupied(e) => e.into_mut(),
@ -342,7 +345,7 @@ impl ProjectPanel {
while let Some(item) = entry_iter.entry() { while let Some(item) = entry_iter.entry() {
visible_worktree_entries.push(entry_iter.offset()); visible_worktree_entries.push(entry_iter.offset());
if let Some(new_selected_entry) = new_selected_entry { if let Some(new_selected_entry) = new_selected_entry {
if new_selected_entry == (worktree.id(), item.id) { if new_selected_entry == (worktree_id, item.id) {
self.selection = Some(Selection { self.selection = Some(Selection {
worktree_id, worktree_id,
entry_id: item.id, entry_id: item.id,
@ -371,7 +374,12 @@ impl ProjectPanel {
} }
} }
fn expand_entry(&mut self, worktree_id: usize, entry_id: usize, cx: &mut ViewContext<Self>) { fn expand_entry(
&mut self,
worktree_id: WorktreeId,
entry_id: usize,
cx: &mut ViewContext<Self>,
) {
let project = self.project.read(cx); let project = self.project.read(cx);
if let Some((worktree, expanded_dir_ids)) = project if let Some((worktree, expanded_dir_ids)) = project
.worktree_for_id(worktree_id, cx) .worktree_for_id(worktree_id, cx)
@ -417,12 +425,12 @@ impl ProjectPanel {
let end_ix = range.end.min(ix + visible_worktree_entries.len()); let end_ix = range.end.min(ix + visible_worktree_entries.len());
let worktree = &worktrees[worktree_ix]; let worktree = &worktrees[worktree_ix];
let snapshot = worktree.read(cx).snapshot();
let expanded_entry_ids = self let expanded_entry_ids = self
.expanded_dir_ids .expanded_dir_ids
.get(&worktree.id()) .get(&snapshot.id())
.map(Vec::as_slice) .map(Vec::as_slice)
.unwrap_or(&[]); .unwrap_or(&[]);
let snapshot = worktree.read(cx).snapshot();
let root_name = OsStr::new(snapshot.root_name()); let root_name = OsStr::new(snapshot.root_name());
let mut cursor = snapshot.entries(false); let mut cursor = snapshot.entries(false);
@ -439,11 +447,11 @@ impl ProjectPanel {
is_dir: entry.is_dir(), is_dir: entry.is_dir(),
is_expanded: expanded_entry_ids.binary_search(&entry.id).is_ok(), is_expanded: expanded_entry_ids.binary_search(&entry.id).is_ok(),
is_selected: self.selection.map_or(false, |e| { is_selected: self.selection.map_or(false, |e| {
e.worktree_id == worktree.id() && e.entry_id == entry.id e.worktree_id == snapshot.id() && e.entry_id == entry.id
}), }),
}; };
let entry = ProjectEntry { let entry = ProjectEntry {
worktree_id: worktree.id(), worktree_id: snapshot.id(),
entry_id: entry.id, entry_id: entry.id,
}; };
callback(entry, details, cx); callback(entry, details, cx);
@ -461,7 +469,7 @@ impl ProjectPanel {
) -> ElementBox { ) -> ElementBox {
let is_dir = details.is_dir; let is_dir = details.is_dir;
MouseEventHandler::new::<Self, _, _, _>( MouseEventHandler::new::<Self, _, _, _>(
(entry.worktree_id, entry.entry_id), (entry.worktree_id.to_usize(), entry.entry_id),
cx, cx,
|state, _| { |state, _| {
let style = match (details.is_selected, state.hovered) { let style = match (details.is_selected, state.hovered) {
@ -516,7 +524,7 @@ impl ProjectPanel {
if is_dir { if is_dir {
cx.dispatch_action(ToggleExpanded(entry)) cx.dispatch_action(ToggleExpanded(entry))
} else { } else {
cx.dispatch_action(Open(entry)) cx.dispatch_action(Open(dbg!(entry)))
} }
}) })
.with_cursor_style(CursorStyle::PointingHand) .with_cursor_style(CursorStyle::PointingHand)

View file

@ -619,10 +619,10 @@ impl Workspace {
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Task<Result<ProjectPath>> { ) -> Task<Result<ProjectPath>> {
let entry = self.worktree_for_abs_path(abs_path, cx); let entry = self.worktree_for_abs_path(abs_path, cx);
cx.spawn(|_, _| async move { cx.spawn(|_, cx| async move {
let (worktree, path) = entry.await?; let (worktree, path) = entry.await?;
Ok(ProjectPath { Ok(ProjectPath {
worktree_id: worktree.id(), worktree_id: worktree.read_with(&cx, |t, _| t.id()),
path: path.into(), path: path.into(),
}) })
}) })
@ -1252,7 +1252,7 @@ impl WorkspaceHandle for ViewHandle<Workspace> {
.worktrees(cx) .worktrees(cx)
.iter() .iter()
.flat_map(|worktree| { .flat_map(|worktree| {
let worktree_id = worktree.id(); let worktree_id = worktree.read(cx).id();
worktree.read(cx).files(true, 0).map(move |f| ProjectPath { worktree.read(cx).files(true, 0).map(move |f| ProjectPath {
worktree_id, worktree_id,
path: f.path.clone(), path: f.path.clone(),

View file

@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
description = "The fast, collaborative code editor." description = "The fast, collaborative code editor."
edition = "2018" edition = "2018"
name = "zed" name = "zed"
version = "0.9.0" version = "0.11.0"
[lib] [lib]
name = "zed" name = "zed"

View file

@ -556,7 +556,7 @@ mod tests {
workspace workspace
.open_entry( .open_entry(
ProjectPath { ProjectPath {
worktree_id: worktree.id(), worktree_id: worktree.read(cx).id(),
path: Path::new("the-new-name.rs").into(), path: Path::new("the-new-name.rs").into(),
}, },
cx, cx,