Skip to content

Commit b382448

Browse files
committed
feat: Diff source caching and autodetection
1 parent 3fcf168 commit b382448

File tree

9 files changed

+268
-92
lines changed

9 files changed

+268
-92
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

helix-term/src/commands.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ use std::{
7171
future::Future,
7272
io::Read,
7373
num::NonZeroUsize,
74+
sync::Arc,
7475
};
7576

7677
use std::{
@@ -3027,15 +3028,15 @@ fn jumplist_picker(cx: &mut Context) {
30273028

30283029
fn changed_file_picker(cx: &mut Context) {
30293030
pub struct FileChangeData {
3030-
cwd: PathBuf,
3031+
cwd: Arc<Path>,
30313032
style_untracked: Style,
30323033
style_modified: Style,
30333034
style_conflict: Style,
30343035
style_deleted: Style,
30353036
style_renamed: Style,
30363037
}
30373038

3038-
let cwd = helix_stdx::env::current_working_dir();
3039+
let cwd: Arc<Path> = Arc::from(helix_stdx::env::current_working_dir().as_path());
30393040
if !cwd.exists() {
30403041
cx.editor
30413042
.set_error("Current working directory does not exist");
@@ -3106,17 +3107,24 @@ fn changed_file_picker(cx: &mut Context) {
31063107
.with_preview(|_editor, meta| Some((meta.path().into(), None)));
31073108
let injector = picker.injector();
31083109

3109-
cx.editor
3110-
.diff_providers
3111-
.clone()
3112-
.for_each_changed_file(cwd, move |change| match change {
3110+
// Helix can be launched without arguments, in which case no diff provider will be loaded since
3111+
// there is no file to provide infos for.
3112+
//
3113+
// This ensures we have one to work with for cwd (and as a bonus it means any file opened
3114+
// from this picker will have its diff provider already in cache).
3115+
cx.editor.diff_providers.add(&cwd);
3116+
cx.editor.diff_providers.clone().for_each_changed_file(
3117+
cwd.clone(),
3118+
move |change| match change {
31133119
Ok(change) => injector.push(change).is_ok(),
31143120
Err(err) => {
31153121
status::report_blocking(err);
31163122
true
31173123
}
3118-
});
3124+
},
3125+
);
31193126
cx.push_layer(Box::new(overlaid(picker)));
3127+
cx.editor.diff_providers.remove(&cwd);
31203128
}
31213129

31223130
pub fn command_palette(cx: &mut Context) {

helix-term/src/commands/typed.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,7 @@ fn reload(
12791279

12801280
let scrolloff = cx.editor.config().scrolloff;
12811281
let (view, doc) = current!(cx.editor);
1282-
doc.reload(view, &cx.editor.diff_providers).map(|_| {
1282+
doc.reload(view, &mut cx.editor.diff_providers).map(|_| {
12831283
view.ensure_cursor_in_view(doc, scrolloff);
12841284
})?;
12851285
if let Some(path) = doc.path() {
@@ -1318,6 +1318,8 @@ fn reload_all(
13181318
})
13191319
.collect();
13201320

1321+
cx.editor.diff_providers.reset();
1322+
13211323
for (doc_id, view_ids) in docs_view_ids {
13221324
let doc = doc_mut!(cx.editor, &doc_id);
13231325

@@ -1327,7 +1329,7 @@ fn reload_all(
13271329
// Ensure that the view is synced with the document's history.
13281330
view.sync_changes(doc);
13291331

1330-
if let Err(error) = doc.reload(view, &cx.editor.diff_providers) {
1332+
if let Err(error) = doc.reload(view, &mut cx.editor.diff_providers) {
13311333
cx.editor.set_error(format!("{}", error));
13321334
continue;
13331335
}

helix-vcs/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p
1919
parking_lot = "0.12"
2020
arc-swap = { version = "1.7.1" }
2121

22-
gix = { version = "0.64.0", features = ["attributes", "status"], default-features = false, optional = true }
22+
gix = { version = "0.64.0", features = ["attributes", "parallel", "status"], default-features = false, optional = true }
2323
imara-diff = "0.1.7"
2424
anyhow = "1"
2525

helix-vcs/src/git.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,11 @@ use crate::FileChange;
2222
#[cfg(test)]
2323
mod test;
2424

25-
pub fn get_diff_base(file: &Path) -> Result<Vec<u8>> {
25+
pub fn get_diff_base(repo: &ThreadSafeRepository, file: &Path) -> Result<Vec<u8>> {
2626
debug_assert!(!file.exists() || file.is_file());
2727
debug_assert!(file.is_absolute());
2828

29-
// TODO cache repository lookup
30-
31-
let repo_dir = file.parent().context("file has no parent directory")?;
32-
let repo = open_repo(repo_dir)
33-
.context("failed to open git repo")?
34-
.to_thread_local();
29+
let repo = repo.to_thread_local();
3530
let head = repo.head_commit()?;
3631
let file_oid = find_file_in_commit(&repo, &head, file)?;
3732

@@ -53,13 +48,14 @@ pub fn get_diff_base(file: &Path) -> Result<Vec<u8>> {
5348
}
5449
}
5550

56-
pub fn get_current_head_name(file: &Path) -> Result<Arc<ArcSwap<Box<str>>>> {
51+
pub fn get_current_head_name(
52+
repo: &ThreadSafeRepository,
53+
file: &Path,
54+
) -> Result<Arc<ArcSwap<Box<str>>>> {
5755
debug_assert!(!file.exists() || file.is_file());
5856
debug_assert!(file.is_absolute());
59-
let repo_dir = file.parent().context("file has no parent directory")?;
60-
let repo = open_repo(repo_dir)
61-
.context("failed to open git repo")?
62-
.to_thread_local();
57+
58+
let repo = repo.to_thread_local();
6359
let head_ref = repo.head_ref()?;
6460
let head_commit = repo.head_commit()?;
6561

@@ -71,11 +67,14 @@ pub fn get_current_head_name(file: &Path) -> Result<Arc<ArcSwap<Box<str>>>> {
7167
Ok(Arc::new(ArcSwap::from_pointee(name.into_boxed_str())))
7268
}
7369

74-
pub fn for_each_changed_file(cwd: &Path, f: impl Fn(Result<FileChange>) -> bool) -> Result<()> {
75-
status(&open_repo(cwd)?.to_thread_local(), f)
70+
pub fn for_each_changed_file(
71+
repo: &ThreadSafeRepository,
72+
f: impl Fn(Result<FileChange>) -> bool,
73+
) -> Result<()> {
74+
status(&repo.to_thread_local(), f)
7675
}
7776

78-
fn open_repo(path: &Path) -> Result<ThreadSafeRepository> {
77+
pub(super) fn open_repo(path: &Path) -> Result<ThreadSafeRepository> {
7978
// custom open options
8079
let mut git_open_opts_map = gix::sec::trust::Mapping::<gix::open::Options>::default();
8180

helix-vcs/src/git/test.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ fn missing_file() {
5454
let file = temp_git.path().join("file.txt");
5555
File::create(&file).unwrap().write_all(b"foo").unwrap();
5656

57-
assert!(git::get_diff_base(&file).is_err());
57+
let repo = git::open_repo(temp_git.path()).unwrap();
58+
assert!(git::get_diff_base(&repo, &file).is_err());
5859
}
5960

6061
#[test]
@@ -64,7 +65,12 @@ fn unmodified_file() {
6465
let contents = b"foo".as_slice();
6566
File::create(&file).unwrap().write_all(contents).unwrap();
6667
create_commit(temp_git.path(), true);
67-
assert_eq!(git::get_diff_base(&file).unwrap(), Vec::from(contents));
68+
69+
let repo = git::open_repo(temp_git.path()).unwrap();
70+
assert_eq!(
71+
git::get_diff_base(&repo, &file).unwrap(),
72+
Vec::from(contents)
73+
);
6874
}
6975

7076
#[test]
@@ -76,7 +82,11 @@ fn modified_file() {
7682
create_commit(temp_git.path(), true);
7783
File::create(&file).unwrap().write_all(b"bar").unwrap();
7884

79-
assert_eq!(git::get_diff_base(&file).unwrap(), Vec::from(contents));
85+
let repo = git::open_repo(temp_git.path()).unwrap();
86+
assert_eq!(
87+
git::get_diff_base(&repo, &file).unwrap(),
88+
Vec::from(contents)
89+
);
8090
}
8191

8292
/// Test that `get_file_head` does not return content for a directory.
@@ -95,7 +105,9 @@ fn directory() {
95105

96106
std::fs::remove_dir_all(&dir).unwrap();
97107
File::create(&dir).unwrap().write_all(b"bar").unwrap();
98-
assert!(git::get_diff_base(&dir).is_err());
108+
109+
let repo = git::open_repo(temp_git.path()).unwrap();
110+
assert!(git::get_diff_base(&repo, &dir).is_err());
99111
}
100112

101113
/// Test that `get_file_head` does not return content for a symlink.
@@ -116,6 +128,11 @@ fn symlink() {
116128
symlink("file.txt", &file_link).unwrap();
117129

118130
create_commit(temp_git.path(), true);
119-
assert!(git::get_diff_base(&file_link).is_err());
120-
assert_eq!(git::get_diff_base(&file).unwrap(), Vec::from(contents));
131+
132+
let repo = git::open_repo(temp_git.path()).unwrap();
133+
assert!(git::get_diff_base(&repo, &file_link).is_err());
134+
assert_eq!(
135+
git::get_diff_base(&repo, &file).unwrap(),
136+
Vec::from(contents)
137+
);
121138
}

0 commit comments

Comments
 (0)