Identify Worktree entries by their inode
This will allow us to re-parent elements when re-scanning when the file system changes.
This commit is contained in:
parent
3c0bbe5eb5
commit
24cdfd2471
8 changed files with 95 additions and 89 deletions
|
@ -433,7 +433,7 @@ impl Buffer {
|
||||||
self.file.as_ref().map(|file| file.path(app))
|
self.file.as_ref().map(|file| file.path(app))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entry_id(&self) -> Option<(usize, usize)> {
|
pub fn entry_id(&self) -> Option<(usize, u64)> {
|
||||||
self.file.as_ref().map(|file| file.entry_id())
|
self.file.as_ref().map(|file| file.entry_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1225,7 +1225,7 @@ impl workspace::ItemView for BufferView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)> {
|
fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)> {
|
||||||
self.buffer.read(app).entry_id()
|
self.buffer.read(app).entry_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub fn init(app: &mut MutableAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Selected(usize, usize),
|
Selected(usize, u64),
|
||||||
Dismissed,
|
Dismissed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,7 +339,7 @@ impl FileFinder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select(&mut self, entry: &(usize, usize), ctx: &mut ViewContext<Self>) {
|
fn select(&mut self, entry: &(usize, u64), ctx: &mut ViewContext<Self>) {
|
||||||
let (tree_id, entry_id) = *entry;
|
let (tree_id, entry_id) = *entry;
|
||||||
ctx.emit(Event::Selected(tree_id, entry_id));
|
ctx.emit(Event::Selected(tree_id, entry_id));
|
||||||
}
|
}
|
||||||
|
@ -347,7 +347,7 @@ impl FileFinder {
|
||||||
fn spawn_search(&mut self, query: String, ctx: &mut ViewContext<Self>) {
|
fn spawn_search(&mut self, query: String, ctx: &mut ViewContext<Self>) {
|
||||||
let worktrees = self.worktrees(ctx.as_ref());
|
let worktrees = self.worktrees(ctx.as_ref());
|
||||||
let search_id = util::post_inc(&mut self.search_count);
|
let search_id = util::post_inc(&mut self.search_count);
|
||||||
let pool = ctx.app().scoped_pool().clone();
|
let pool = ctx.as_ref().scoped_pool().clone();
|
||||||
let task = ctx.background_executor().spawn(async move {
|
let task = ctx.background_executor().spawn(async move {
|
||||||
let matches = match_paths(worktrees.as_slice(), &query, false, false, 100, pool);
|
let matches = match_paths(worktrees.as_slice(), &query, false, false, 100, pool);
|
||||||
(search_id, matches)
|
(search_id, matches)
|
||||||
|
|
|
@ -105,15 +105,12 @@ impl Pane {
|
||||||
self.items.get(self.active_item).cloned()
|
self.items.get(self.active_item).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_entry(
|
pub fn activate_entry(&mut self, entry_id: (usize, u64), ctx: &mut ViewContext<Self>) -> bool {
|
||||||
&mut self,
|
if let Some(index) = self
|
||||||
entry_id: (usize, usize),
|
.items
|
||||||
ctx: &mut ViewContext<Self>,
|
.iter()
|
||||||
) -> bool {
|
.position(|item| item.entry_id(ctx.as_ref()).map_or(false, |id| id == entry_id))
|
||||||
if let Some(index) = self.items.iter().position(|item| {
|
{
|
||||||
item.entry_id(ctx.as_ref())
|
|
||||||
.map_or(false, |id| id == entry_id)
|
|
||||||
}) {
|
|
||||||
self.activate_item(index, ctx);
|
self.activate_item(index, ctx);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -76,7 +76,7 @@ enum OpenedItem {
|
||||||
pub struct Workspace {
|
pub struct Workspace {
|
||||||
replica_id: ReplicaId,
|
replica_id: ReplicaId,
|
||||||
worktrees: HashSet<ModelHandle<Worktree>>,
|
worktrees: HashSet<ModelHandle<Worktree>>,
|
||||||
items: HashMap<(usize, usize), OpenedItem>,
|
items: HashMap<(usize, u64), OpenedItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
|
@ -125,7 +125,7 @@ impl Workspace {
|
||||||
|
|
||||||
pub fn open_entry(
|
pub fn open_entry(
|
||||||
&mut self,
|
&mut self,
|
||||||
entry: (usize, usize),
|
entry: (usize, u64),
|
||||||
ctx: &mut ModelContext<'_, Self>,
|
ctx: &mut ModelContext<'_, Self>,
|
||||||
) -> anyhow::Result<Pin<Box<dyn Future<Output = OpenResult> + Send>>> {
|
) -> anyhow::Result<Pin<Box<dyn Future<Output = OpenResult> + Send>>> {
|
||||||
if let Some(item) = self.items.get(&entry).cloned() {
|
if let Some(item) = self.items.get(&entry).cloned() {
|
||||||
|
@ -200,12 +200,12 @@ impl Entity for Workspace {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub trait WorkspaceHandle {
|
pub trait WorkspaceHandle {
|
||||||
fn file_entries(&self, app: &AppContext) -> Vec<(usize, usize)>;
|
fn file_entries(&self, app: &AppContext) -> Vec<(usize, u64)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl WorkspaceHandle for ModelHandle<Workspace> {
|
impl WorkspaceHandle for ModelHandle<Workspace> {
|
||||||
fn file_entries(&self, app: &AppContext) -> Vec<(usize, usize)> {
|
fn file_entries(&self, app: &AppContext) -> Vec<(usize, u64)> {
|
||||||
self.read(app)
|
self.read(app)
|
||||||
.worktrees()
|
.worktrees()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub fn init(app: &mut MutableAppContext) {
|
||||||
|
|
||||||
pub trait ItemView: View {
|
pub trait ItemView: View {
|
||||||
fn title(&self, app: &AppContext) -> String;
|
fn title(&self, app: &AppContext) -> String;
|
||||||
fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)>;
|
fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)>;
|
||||||
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
|
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
|
@ -42,7 +42,7 @@ pub trait ItemView: View {
|
||||||
|
|
||||||
pub trait ItemViewHandle: Send + Sync {
|
pub trait ItemViewHandle: Send + Sync {
|
||||||
fn title(&self, app: &AppContext) -> String;
|
fn title(&self, app: &AppContext) -> String;
|
||||||
fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)>;
|
fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)>;
|
||||||
fn boxed_clone(&self) -> Box<dyn ItemViewHandle>;
|
fn boxed_clone(&self) -> Box<dyn ItemViewHandle>;
|
||||||
fn clone_on_split(&self, app: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
|
fn clone_on_split(&self, app: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
|
||||||
fn set_parent_pane(&self, pane: &ViewHandle<Pane>, app: &mut MutableAppContext);
|
fn set_parent_pane(&self, pane: &ViewHandle<Pane>, app: &mut MutableAppContext);
|
||||||
|
@ -57,7 +57,7 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
||||||
self.read(app).title(app)
|
self.read(app).title(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)> {
|
fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)> {
|
||||||
self.read(app).entry_id(app)
|
self.read(app).entry_id(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ pub struct WorkspaceView {
|
||||||
center: PaneGroup,
|
center: PaneGroup,
|
||||||
panes: Vec<ViewHandle<Pane>>,
|
panes: Vec<ViewHandle<Pane>>,
|
||||||
active_pane: ViewHandle<Pane>,
|
active_pane: ViewHandle<Pane>,
|
||||||
loading_entries: HashSet<(usize, usize)>,
|
loading_entries: HashSet<(usize, u64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkspaceView {
|
impl WorkspaceView {
|
||||||
|
@ -189,7 +189,7 @@ impl WorkspaceView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_entry(&mut self, entry: (usize, usize), ctx: &mut ViewContext<Self>) {
|
pub fn open_entry(&mut self, entry: (usize, u64), ctx: &mut ViewContext<Self>) {
|
||||||
if self.loading_entries.contains(&entry) {
|
if self.loading_entries.contains(&entry) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ const ADDITIONAL_DISTANCE_PENALTY: f64 = 0.05;
|
||||||
const MIN_DISTANCE_PENALTY: f64 = 0.2;
|
const MIN_DISTANCE_PENALTY: f64 = 0.2;
|
||||||
|
|
||||||
pub struct PathEntry {
|
pub struct PathEntry {
|
||||||
pub entry_id: usize,
|
pub ino: u64,
|
||||||
pub path_chars: CharBag,
|
pub path_chars: CharBag,
|
||||||
pub path: Vec<char>,
|
pub path: Vec<char>,
|
||||||
pub lowercase_path: Vec<char>,
|
pub lowercase_path: Vec<char>,
|
||||||
|
@ -24,7 +24,7 @@ pub struct PathMatch {
|
||||||
pub score: f64,
|
pub score: f64,
|
||||||
pub positions: Vec<usize>,
|
pub positions: Vec<usize>,
|
||||||
pub tree_id: usize,
|
pub tree_id: usize,
|
||||||
pub entry_id: usize,
|
pub entry_id: u64,
|
||||||
pub skipped_prefix_len: usize,
|
pub skipped_prefix_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ fn match_single_tree_paths(
|
||||||
if score > 0.0 {
|
if score > 0.0 {
|
||||||
results.push(Reverse(PathMatch {
|
results.push(Reverse(PathMatch {
|
||||||
tree_id,
|
tree_id,
|
||||||
entry_id: path_entry.entry_id,
|
entry_id: path_entry.ino,
|
||||||
score,
|
score,
|
||||||
positions: match_positions.clone(),
|
positions: match_positions.clone(),
|
||||||
skipped_prefix_len,
|
skipped_prefix_len,
|
||||||
|
@ -453,7 +453,7 @@ mod tests {
|
||||||
let path_chars = CharBag::from(&lowercase_path[..]);
|
let path_chars = CharBag::from(&lowercase_path[..]);
|
||||||
let path = path.chars().collect();
|
let path = path.chars().collect();
|
||||||
path_entries.push(PathEntry {
|
path_entries.push(PathEntry {
|
||||||
entry_id: i,
|
ino: i as u64,
|
||||||
path_chars,
|
path_chars,
|
||||||
path,
|
path,
|
||||||
lowercase_path,
|
lowercase_path,
|
||||||
|
@ -490,7 +490,12 @@ mod tests {
|
||||||
results
|
results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|result| (paths[result.0.entry_id].clone(), result.0.positions))
|
.map(|result| {
|
||||||
|
(
|
||||||
|
paths[result.0.entry_id as usize].clone(),
|
||||||
|
result.0.positions,
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,15 +33,15 @@ pub struct Worktree(Arc<RwLock<WorktreeState>>);
|
||||||
struct WorktreeState {
|
struct WorktreeState {
|
||||||
id: usize,
|
id: usize,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
root_ino: Option<usize>,
|
root_ino: Option<u64>,
|
||||||
entries: HashMap<usize, Entry>,
|
entries: HashMap<u64, Entry>,
|
||||||
file_paths: Vec<PathEntry>,
|
file_paths: Vec<PathEntry>,
|
||||||
histories: HashMap<usize, History>,
|
histories: HashMap<u64, History>,
|
||||||
scanning: bool,
|
scanning: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DirToScan {
|
struct DirToScan {
|
||||||
id: usize,
|
ino: u64,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
relative_path: PathBuf,
|
relative_path: PathBuf,
|
||||||
ignore: Option<Ignore>,
|
ignore: Option<Ignore>,
|
||||||
|
@ -102,14 +102,13 @@ impl Worktree {
|
||||||
}
|
}
|
||||||
let is_ignored = ignore.matched(&path, metadata.is_dir()).is_ignore();
|
let is_ignored = ignore.matched(&path, metadata.is_dir()).is_ignore();
|
||||||
|
|
||||||
self.0.write().root_ino = Some(0);
|
|
||||||
if metadata.file_type().is_dir() {
|
if metadata.file_type().is_dir() {
|
||||||
let is_ignored = is_ignored || name == ".git";
|
let is_ignored = is_ignored || name == ".git";
|
||||||
let id = self.insert_dir(None, name, ino, is_symlink, is_ignored);
|
self.insert_dir(None, name, ino, is_symlink, is_ignored);
|
||||||
let (tx, rx) = channel::unbounded();
|
let (tx, rx) = channel::unbounded();
|
||||||
|
|
||||||
tx.send(Ok(DirToScan {
|
tx.send(Ok(DirToScan {
|
||||||
id,
|
ino,
|
||||||
path,
|
path,
|
||||||
relative_path,
|
relative_path,
|
||||||
ignore: Some(ignore),
|
ignore: Some(ignore),
|
||||||
|
@ -131,6 +130,7 @@ impl Worktree {
|
||||||
} else {
|
} else {
|
||||||
self.insert_file(None, name, ino, is_symlink, is_ignored, relative_path);
|
self.insert_file(None, name, ino, is_symlink, is_ignored, relative_path);
|
||||||
}
|
}
|
||||||
|
self.0.write().root_ino = Some(ino);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -159,12 +159,12 @@ impl Worktree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = self.insert_dir(Some(to_scan.id), name, ino, is_symlink, is_ignored);
|
self.insert_dir(Some(to_scan.ino), name, ino, is_symlink, is_ignored);
|
||||||
new_children.push(id);
|
new_children.push(ino);
|
||||||
|
|
||||||
let dirs_to_scan = to_scan.dirs_to_scan.clone();
|
let dirs_to_scan = to_scan.dirs_to_scan.clone();
|
||||||
let _ = to_scan.dirs_to_scan.send(Ok(DirToScan {
|
let _ = to_scan.dirs_to_scan.send(Ok(DirToScan {
|
||||||
id,
|
ino,
|
||||||
path,
|
path,
|
||||||
relative_path,
|
relative_path,
|
||||||
ignore,
|
ignore,
|
||||||
|
@ -175,18 +175,19 @@ impl Worktree {
|
||||||
i.matched(to_scan.path.join(&name), false).is_ignore()
|
i.matched(to_scan.path.join(&name), false).is_ignore()
|
||||||
});
|
});
|
||||||
|
|
||||||
new_children.push(self.insert_file(
|
self.insert_file(
|
||||||
Some(to_scan.id),
|
Some(to_scan.ino),
|
||||||
name,
|
name,
|
||||||
ino,
|
ino,
|
||||||
is_symlink,
|
is_symlink,
|
||||||
is_ignored,
|
is_ignored,
|
||||||
relative_path,
|
relative_path,
|
||||||
));
|
);
|
||||||
|
new_children.push(ino);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Entry::Dir { children, .. }) = &mut self.0.write().entries.get_mut(&to_scan.id)
|
if let Some(Entry::Dir { children, .. }) = &mut self.0.write().entries.get_mut(&to_scan.ino)
|
||||||
{
|
{
|
||||||
*children = new_children.clone();
|
*children = new_children.clone();
|
||||||
}
|
}
|
||||||
|
@ -196,16 +197,15 @@ impl Worktree {
|
||||||
|
|
||||||
fn insert_dir(
|
fn insert_dir(
|
||||||
&self,
|
&self,
|
||||||
parent: Option<usize>,
|
parent: Option<u64>,
|
||||||
name: OsString,
|
name: OsString,
|
||||||
ino: u64,
|
ino: u64,
|
||||||
is_symlink: bool,
|
is_symlink: bool,
|
||||||
is_ignored: bool,
|
is_ignored: bool,
|
||||||
) -> usize {
|
) {
|
||||||
let entries = &mut self.0.write().entries;
|
let entries = &mut self.0.write().entries;
|
||||||
let dir_id = entries.len();
|
|
||||||
entries.insert(
|
entries.insert(
|
||||||
dir_id,
|
ino,
|
||||||
Entry::Dir {
|
Entry::Dir {
|
||||||
parent,
|
parent,
|
||||||
name,
|
name,
|
||||||
|
@ -215,27 +215,25 @@ impl Worktree {
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
dir_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_file(
|
fn insert_file(
|
||||||
&self,
|
&self,
|
||||||
parent: Option<usize>,
|
parent: Option<u64>,
|
||||||
name: OsString,
|
name: OsString,
|
||||||
ino: u64,
|
ino: u64,
|
||||||
is_symlink: bool,
|
is_symlink: bool,
|
||||||
is_ignored: bool,
|
is_ignored: bool,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
) -> usize {
|
) {
|
||||||
let path = path.to_string_lossy();
|
let path = path.to_string_lossy();
|
||||||
let lowercase_path = path.to_lowercase().chars().collect::<Vec<_>>();
|
let lowercase_path = path.to_lowercase().chars().collect::<Vec<_>>();
|
||||||
let path = path.chars().collect::<Vec<_>>();
|
let path = path.chars().collect::<Vec<_>>();
|
||||||
let path_chars = CharBag::from(&path[..]);
|
let path_chars = CharBag::from(&path[..]);
|
||||||
|
|
||||||
let mut state = self.0.write();
|
let mut state = self.0.write();
|
||||||
let entry_id = state.entries.len();
|
|
||||||
state.entries.insert(
|
state.entries.insert(
|
||||||
entry_id,
|
ino,
|
||||||
Entry::File {
|
Entry::File {
|
||||||
parent,
|
parent,
|
||||||
name,
|
name,
|
||||||
|
@ -245,25 +243,23 @@ impl Worktree {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
state.file_paths.push(PathEntry {
|
state.file_paths.push(PathEntry {
|
||||||
entry_id,
|
ino,
|
||||||
path_chars,
|
path_chars,
|
||||||
path,
|
path,
|
||||||
lowercase_path,
|
lowercase_path,
|
||||||
is_ignored,
|
is_ignored,
|
||||||
});
|
});
|
||||||
entry_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entry_path(&self, mut entry_id: usize) -> Result<PathBuf> {
|
pub fn entry_path(&self, mut entry_id: u64) -> Result<PathBuf> {
|
||||||
let state = self.0.read();
|
let state = self.0.read();
|
||||||
|
|
||||||
if entry_id >= state.entries.len() {
|
|
||||||
return Err(anyhow!("Entry does not exist in tree"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut entries = Vec::new();
|
let mut entries = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let entry = &state.entries[&entry_id];
|
let entry = state
|
||||||
|
.entries
|
||||||
|
.get(&entry_id)
|
||||||
|
.ok_or_else(|| anyhow!("entry does not exist in worktree"))?;
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
if let Some(parent_id) = entry.parent() {
|
if let Some(parent_id) = entry.parent() {
|
||||||
entry_id = parent_id;
|
entry_id = parent_id;
|
||||||
|
@ -279,13 +275,13 @@ impl Worktree {
|
||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn abs_entry_path(&self, entry_id: usize) -> Result<PathBuf> {
|
pub fn abs_entry_path(&self, entry_id: u64) -> Result<PathBuf> {
|
||||||
let mut path = self.0.read().path.clone();
|
let mut path = self.0.read().path.clone();
|
||||||
path.pop();
|
path.pop();
|
||||||
Ok(path.join(self.entry_path(entry_id)?))
|
Ok(path.join(self.entry_path(entry_id)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_entry(&self, f: &mut fmt::Formatter<'_>, entry_id: usize, indent: usize) -> fmt::Result {
|
fn fmt_entry(&self, f: &mut fmt::Formatter<'_>, entry_id: u64, indent: usize) -> fmt::Result {
|
||||||
match &self.0.read().entries[&entry_id] {
|
match &self.0.read().entries[&entry_id] {
|
||||||
Entry::Dir { name, children, .. } => {
|
Entry::Dir { name, children, .. } => {
|
||||||
write!(
|
write!(
|
||||||
|
@ -333,6 +329,10 @@ impl Worktree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_entry(&self, entry_id: u64) -> bool {
|
||||||
|
self.0.read().entries.contains_key(&entry_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn entry_count(&self) -> usize {
|
pub fn entry_count(&self) -> usize {
|
||||||
self.0.read().entries.len()
|
self.0.read().entries.len()
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,7 @@ impl Worktree {
|
||||||
self.0.read().file_paths.len()
|
self.0.read().file_paths.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_history(&self, entry_id: usize) -> impl Future<Output = Result<History>> {
|
pub fn load_history(&self, entry_id: u64) -> impl Future<Output = Result<History>> {
|
||||||
let tree = self.clone();
|
let tree = self.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
|
@ -360,12 +360,7 @@ impl Worktree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save<'a>(
|
pub fn save<'a>(&self, entry_id: u64, content: Snapshot, ctx: &AppContext) -> Task<Result<()>> {
|
||||||
&self,
|
|
||||||
entry_id: usize,
|
|
||||||
content: Snapshot,
|
|
||||||
ctx: &AppContext,
|
|
||||||
) -> Task<Result<()>> {
|
|
||||||
let path = self.abs_entry_path(entry_id);
|
let path = self.abs_entry_path(entry_id);
|
||||||
ctx.background_executor().spawn(async move {
|
ctx.background_executor().spawn(async move {
|
||||||
let buffer_size = content.text_summary().bytes.min(10 * 1024);
|
let buffer_size = content.text_summary().bytes.min(10 * 1024);
|
||||||
|
@ -420,34 +415,34 @@ impl WorktreeState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WorktreeHandle {
|
pub trait WorktreeHandle {
|
||||||
fn file(&self, entry_id: usize, app: &AppContext) -> Result<FileHandle>;
|
fn file(&self, entry_id: u64, app: &AppContext) -> Result<FileHandle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorktreeHandle for ModelHandle<Worktree> {
|
impl WorktreeHandle for ModelHandle<Worktree> {
|
||||||
fn file(&self, entry_id: usize, app: &AppContext) -> Result<FileHandle> {
|
fn file(&self, entry_id: u64, app: &AppContext) -> Result<FileHandle> {
|
||||||
if entry_id >= self.read(app).entry_count() {
|
if self.read(app).has_entry(entry_id) {
|
||||||
return Err(anyhow!("Entry does not exist in tree"));
|
Err(anyhow!("entry does not exist in tree"))
|
||||||
|
} else {
|
||||||
|
Ok(FileHandle {
|
||||||
|
worktree: self.clone(),
|
||||||
|
entry_id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(FileHandle {
|
|
||||||
worktree: self.clone(),
|
|
||||||
entry_id,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Entry {
|
pub enum Entry {
|
||||||
Dir {
|
Dir {
|
||||||
parent: Option<usize>,
|
parent: Option<u64>,
|
||||||
name: OsString,
|
name: OsString,
|
||||||
ino: u64,
|
ino: u64,
|
||||||
is_symlink: bool,
|
is_symlink: bool,
|
||||||
is_ignored: bool,
|
is_ignored: bool,
|
||||||
children: Vec<usize>,
|
children: Vec<u64>,
|
||||||
},
|
},
|
||||||
File {
|
File {
|
||||||
parent: Option<usize>,
|
parent: Option<u64>,
|
||||||
name: OsString,
|
name: OsString,
|
||||||
ino: u64,
|
ino: u64,
|
||||||
is_symlink: bool,
|
is_symlink: bool,
|
||||||
|
@ -456,12 +451,18 @@ pub enum Entry {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
fn parent(&self) -> Option<usize> {
|
fn parent(&self) -> Option<u64> {
|
||||||
match self {
|
match self {
|
||||||
Entry::Dir { parent, .. } | Entry::File { parent, .. } => *parent,
|
Entry::Dir { parent, .. } | Entry::File { parent, .. } => *parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ino(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Entry::Dir { ino, .. } | Entry::File { ino, .. } => *ino,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn name(&self) -> &OsStr {
|
fn name(&self) -> &OsStr {
|
||||||
match self {
|
match self {
|
||||||
Entry::Dir { name, .. } | Entry::File { name, .. } => name,
|
Entry::Dir { name, .. } | Entry::File { name, .. } => name,
|
||||||
|
@ -472,7 +473,7 @@ impl Entry {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FileHandle {
|
pub struct FileHandle {
|
||||||
worktree: ModelHandle<Worktree>,
|
worktree: ModelHandle<Worktree>,
|
||||||
entry_id: usize,
|
entry_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileHandle {
|
impl FileHandle {
|
||||||
|
@ -489,13 +490,13 @@ impl FileHandle {
|
||||||
worktree.save(self.entry_id, content, ctx)
|
worktree.save(self.entry_id, content, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entry_id(&self) -> (usize, usize) {
|
pub fn entry_id(&self) -> (usize, u64) {
|
||||||
(self.worktree.id(), self.entry_id)
|
(self.worktree.id(), self.entry_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IterStackEntry {
|
struct IterStackEntry {
|
||||||
entry_id: usize,
|
entry_id: u64,
|
||||||
child_idx: usize,
|
child_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,18 +517,21 @@ impl Iterator for Iter {
|
||||||
|
|
||||||
return if let Some(entry) = state.root_entry().cloned() {
|
return if let Some(entry) = state.root_entry().cloned() {
|
||||||
self.stack.push(IterStackEntry {
|
self.stack.push(IterStackEntry {
|
||||||
entry_id: 0,
|
entry_id: entry.ino(),
|
||||||
child_idx: 0,
|
child_idx: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(Traversal::Push { entry_id: 0, entry })
|
Some(Traversal::Push {
|
||||||
|
entry_id: entry.ino(),
|
||||||
|
entry,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(parent) = self.stack.last_mut() {
|
while let Some(parent) = self.stack.last_mut() {
|
||||||
if let Entry::Dir { children, .. } = &state.entries[&parent.entry_id] {
|
if let Some(Entry::Dir { children, .. }) = &state.entries.get(&parent.entry_id) {
|
||||||
if parent.child_idx < children.len() {
|
if parent.child_idx < children.len() {
|
||||||
let child_id = children[post_inc(&mut parent.child_idx)];
|
let child_id = children[post_inc(&mut parent.child_idx)];
|
||||||
|
|
||||||
|
@ -558,7 +562,7 @@ impl Iterator for Iter {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Traversal {
|
pub enum Traversal {
|
||||||
Push { entry_id: usize, entry: Entry },
|
Push { entry_id: u64, entry: Entry },
|
||||||
Pop,
|
Pop,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,7 +572,7 @@ pub struct FilesIter {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FilesIterItem {
|
pub struct FilesIterItem {
|
||||||
pub entry_id: usize,
|
pub entry_id: u64,
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue