feat: follow forward working-checkout changes in diffs
This commit is contained in:
+113
-21
@@ -84,6 +84,12 @@ impl DiffWorker {
|
||||
} => repository
|
||||
.compare(id, &before, &after, mode, &buffers)
|
||||
.map(DiffOutcome::Comparison),
|
||||
DiffOperation::RefreshWorking {
|
||||
comparison_id,
|
||||
buffers,
|
||||
} => repository
|
||||
.refresh_working_after(comparison_id, id, &buffers)
|
||||
.map(DiffOutcome::WorkingRefresh),
|
||||
DiffOperation::LoadFile {
|
||||
comparison_id,
|
||||
file_index,
|
||||
@@ -181,6 +187,8 @@ pub struct EditorSession {
|
||||
battery: Option<crate::battery::Battery>,
|
||||
battery_enabled: bool,
|
||||
last_file_refresh: Instant,
|
||||
last_diff_refresh: Instant,
|
||||
pub(crate) diff_follow_working_copy: bool,
|
||||
}
|
||||
|
||||
impl EditorSession {
|
||||
@@ -287,6 +295,8 @@ impl EditorSession {
|
||||
battery: None,
|
||||
battery_enabled: false,
|
||||
last_file_refresh: Instant::now(),
|
||||
last_diff_refresh: Instant::now(),
|
||||
diff_follow_working_copy: config.diff_follow_working_copy,
|
||||
};
|
||||
session.synchronize_documents();
|
||||
session.poll_battery();
|
||||
@@ -352,6 +362,7 @@ impl EditorSession {
|
||||
return false;
|
||||
};
|
||||
if !diff.loading
|
||||
&& !diff.refresh_pending()
|
||||
&& let Some(id) = worker.pending.take()
|
||||
{
|
||||
worker.cancelled_request.store(id, Ordering::Release);
|
||||
@@ -359,29 +370,28 @@ impl EditorSession {
|
||||
}
|
||||
true
|
||||
});
|
||||
let Some((id, operation)) = self.editor.take_diff_request() else {
|
||||
return;
|
||||
};
|
||||
let tab = self.editor.tab_id();
|
||||
if let std::collections::btree_map::Entry::Vacant(entry) = self.diff_workers.entry(tab) {
|
||||
match DiffWorker::start(self.editor.diff_root()) {
|
||||
Ok(worker) => {
|
||||
entry.insert(worker);
|
||||
}
|
||||
Err(error) => {
|
||||
self.editor.apply_diff_result(id, Err(error));
|
||||
return;
|
||||
while let Some((tab, id, operation)) = self.editor.take_next_diff_request() {
|
||||
if let std::collections::btree_map::Entry::Vacant(entry) = self.diff_workers.entry(tab)
|
||||
{
|
||||
match DiffWorker::start(self.editor.diff_root(tab)) {
|
||||
Ok(worker) => {
|
||||
entry.insert(worker);
|
||||
}
|
||||
Err(error) => {
|
||||
self.editor.apply_diff_result(id, Err(error));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(error) = self
|
||||
.diff_workers
|
||||
.get_mut(&tab)
|
||||
.expect("diff worker started")
|
||||
.request(id, operation)
|
||||
{
|
||||
self.editor.apply_diff_result(id, Err(error));
|
||||
self.diff_workers.remove(&tab);
|
||||
if let Err(error) = self
|
||||
.diff_workers
|
||||
.get_mut(&tab)
|
||||
.expect("diff worker started")
|
||||
.request(id, operation)
|
||||
{
|
||||
self.editor.apply_diff_result(id, Err(error));
|
||||
self.diff_workers.remove(&tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1141,6 +1151,16 @@ impl EditorSession {
|
||||
self.editor.refresh_files()
|
||||
}
|
||||
|
||||
fn refresh_working_diffs_at(&mut self, now: Instant) {
|
||||
if !self.diff_follow_working_copy
|
||||
|| now.saturating_duration_since(self.last_diff_refresh) < Duration::from_secs(1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.last_diff_refresh = now;
|
||||
self.editor.queue_working_refreshes();
|
||||
}
|
||||
|
||||
/// Drains ready plugin work without waiting for command or server responses.
|
||||
pub fn poll(&mut self) -> bool {
|
||||
self.poll_at(Instant::now())
|
||||
@@ -1150,6 +1170,7 @@ impl EditorSession {
|
||||
let mut changed = self.refresh_files_at(now);
|
||||
changed |= self.synchronize_documents();
|
||||
changed |= self.poll_battery();
|
||||
self.refresh_working_diffs_at(now);
|
||||
changed |= self.poll_diff();
|
||||
for update in self.lsp.poll() {
|
||||
match self.apply_update("core", update) {
|
||||
@@ -1212,6 +1233,77 @@ mod tests {
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn working_diff_polling_uses_injected_interval_and_never_queues_historical_or_disabled_views() {
|
||||
use crate::diff::{Catalog, Category, Comparison, RepoKind, Revision, RevisionTarget};
|
||||
let fixture = Fixture::new();
|
||||
fixture.file("buffer.txt", "base");
|
||||
for working in [true, false] {
|
||||
let mut session = fixture.open("");
|
||||
session.editor.execute("diff");
|
||||
let (id, _) = session.editor.take_diff_request().unwrap();
|
||||
session.editor.apply_diff_result(
|
||||
id,
|
||||
Ok(DiffOutcome::Catalog(Catalog {
|
||||
kind: RepoKind::Git,
|
||||
notice: None,
|
||||
entries: [
|
||||
RevisionTarget::Working,
|
||||
RevisionTarget::Commit("base".into()),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|target| Revision {
|
||||
target,
|
||||
label: "revision".into(),
|
||||
description: String::new(),
|
||||
category: Category::Commits,
|
||||
})
|
||||
.collect(),
|
||||
})),
|
||||
);
|
||||
session.editor.handle(Input::Down);
|
||||
session.editor.handle(Input::Enter);
|
||||
if !working {
|
||||
session.editor.handle(Input::Down);
|
||||
}
|
||||
session.editor.handle(Input::Enter);
|
||||
let (id, _) = session.editor.take_diff_request().unwrap();
|
||||
session.editor.apply_diff_result(
|
||||
id,
|
||||
Ok(DiffOutcome::Comparison(Comparison {
|
||||
root: fixture.0.clone(),
|
||||
before: "base".into(),
|
||||
after: "after".into(),
|
||||
files: Vec::new(),
|
||||
editable_paths: Default::default(),
|
||||
})),
|
||||
);
|
||||
let now = session.last_diff_refresh;
|
||||
session.diff_follow_working_copy = false;
|
||||
session.refresh_working_diffs_at(now + Duration::from_secs(1));
|
||||
assert!(session.editor.take_next_diff_request().is_none());
|
||||
session.diff_follow_working_copy = true;
|
||||
session.refresh_working_diffs_at(now + Duration::from_millis(999));
|
||||
assert!(session.editor.take_next_diff_request().is_none());
|
||||
session.refresh_working_diffs_at(now + Duration::from_secs(1));
|
||||
let request = session.editor.take_next_diff_request();
|
||||
assert_eq!(request.is_some(), working);
|
||||
if let Some((_, refresh, operation)) = request {
|
||||
assert!(
|
||||
matches!(operation, DiffOperation::RefreshWorking { comparison_id, .. }
|
||||
if comparison_id == id)
|
||||
);
|
||||
session.refresh_working_diffs_at(now + Duration::from_secs(2));
|
||||
assert!(session.editor.take_next_diff_request().is_none());
|
||||
assert!(
|
||||
!session
|
||||
.editor
|
||||
.apply_diff_result(refresh, Ok(DiffOutcome::WorkingRefresh(None)))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blame_sources_are_deduplicated_and_stale_results_cannot_reappear() {
|
||||
let fixture = Fixture::new();
|
||||
|
||||
Reference in New Issue
Block a user