fix: show revision-aware blame in diff buffers

Diff and snapshot sources were excluded before scheduling, and the separate diff renderer never painted attribution. Capture per-side Git provenance, preserve private object stores for historical tabs, and reuse the current-line worker and cache.
This commit is contained in:
Valdemar Lorenzen
2026-10-20 11:47:49 +02:00
parent 8f8d96ead9
commit 4fb1be7972
16 changed files with 819 additions and 91 deletions
+255 -2
View File
@@ -29,6 +29,26 @@ fn command(root: &Path, program: &str, args: &[&str]) -> String {
);
String::from_utf8(output.stdout).unwrap().trim().into()
}
fn disk_state(root: &Path) -> std::collections::BTreeMap<PathBuf, Vec<u8>> {
fn visit(root: &Path, path: &Path, state: &mut std::collections::BTreeMap<PathBuf, Vec<u8>>) {
for entry in fs::read_dir(path).unwrap() {
let path = entry.unwrap().path();
let metadata = fs::symlink_metadata(&path).unwrap();
if metadata.is_dir() {
visit(root, &path, state);
} else if metadata.is_file() {
state.insert(
path.strip_prefix(root).unwrap().to_path_buf(),
fs::read(path).unwrap(),
);
}
}
}
let mut state = std::collections::BTreeMap::new();
visit(root, root, &mut state);
state
}
fn fixture() -> (Temporary, PathBuf, String) {
let temp = Temporary::new().unwrap();
command(&temp.0, "git", &["init", "--quiet", "--template="]);
@@ -150,6 +170,239 @@ fn missing_untracked_unborn_nonrepo_and_symlink_have_no_blame() {
None
);
}
#[test]
fn historical_git_and_jj_blame_survives_renamed_and_deleted_paths() {
for is_jj in [false, true] {
let (temp, old_path, original) = fixture();
if is_jj {
command(&temp.0, "jj", &["git", "init", "--colocate", "."]);
command(&temp.0, "jj", &["new", &original]);
}
fs::write(&old_path, "historical\nsecond\n").unwrap();
let selected = if is_jj {
command(
&temp.0,
"jj",
&["describe", "-m", "Selected historical lines"],
);
let id = command(
&temp.0,
"jj",
&["log", "--no-graph", "-r", "@", "-T", "commit_id"],
);
command(&temp.0, "jj", &["new"]);
id
} else {
command(&temp.0, "git", &["add", "."]);
command(
&temp.0,
"git",
&["commit", "--quiet", "-m", "Selected historical lines"],
);
command(&temp.0, "git", &["rev-parse", "HEAD"])
};
let renamed = temp.0.join("renamed.txt");
fs::rename(&old_path, &renamed).unwrap();
let renamed_commit = if is_jj {
command(&temp.0, "jj", &["describe", "-m", "Rename"]);
let id = command(
&temp.0,
"jj",
&["log", "--no-graph", "-r", "@", "-T", "commit_id"],
);
command(&temp.0, "jj", &["new"]);
id
} else {
command(&temp.0, "git", &["add", "-A"]);
command(&temp.0, "git", &["commit", "--quiet", "-m", "Rename"]);
command(&temp.0, "git", &["rev-parse", "HEAD"])
};
fs::remove_file(&renamed).unwrap();
if is_jj {
command(&temp.0, "jj", &["describe", "-m", "Delete"]);
command(&temp.0, "jj", &["new"]);
} else {
command(&temp.0, "git", &["add", "-A"]);
command(&temp.0, "git", &["commit", "--quiet", "-m", "Delete"]);
}
let store = if is_jj {
crate::repository::store_for_jj(&temp.0).unwrap()
} else {
temp.0.join(".git")
};
let state = disk_state(&temp.0);
for (commit, path) in [
(
selected.clone(),
old_path.strip_prefix(&temp.0).unwrap().to_path_buf(),
),
(renamed_commit, PathBuf::from("renamed.txt")),
] {
let source = RevisionSource {
store: store.clone(),
commit,
path,
keepalive: None,
};
let mut worker = BlameWorker::start().unwrap();
worker
.request(
1,
old_path.clone(),
"historical\nsecond\n".into(),
Some(source),
)
.unwrap();
let started = Instant::now();
let rows = loop {
if let Some((id, result)) = worker.poll() {
assert_eq!(id, 1);
break result.unwrap().unwrap();
}
assert!(
started.elapsed() < Duration::from_secs(20),
"Historical blame stalled"
);
thread::yield_now();
};
assert_eq!(rows[0].as_ref().unwrap().commit, selected);
assert_eq!(
rows[0].as_ref().unwrap().summary,
"Selected historical lines"
);
assert!(!rows[0].as_ref().unwrap().uncommitted);
assert_eq!(rows[1].as_ref().unwrap().commit, original);
}
assert!(!old_path.exists() && !renamed.exists());
assert_eq!(disk_state(&temp.0), state);
}
}
#[test]
fn privately_fetched_git_and_noncolocated_jj_sources_keep_selected_attribution() {
use crate::diff::{Category, ComparisonMode, Revision, RevisionTarget};
let (remote, remote_path, original) = fixture();
fs::write(&remote_path, "private historical\nsecond\n").unwrap();
command(&remote.0, "git", &["add", "."]);
command(
&remote.0,
"git",
&["commit", "--quiet", "-m", "Private selected"],
);
let selected = command(&remote.0, "git", &["rev-parse", "HEAD"]);
let reference = command(&remote.0, "git", &["symbolic-ref", "HEAD"]);
for is_jj in [false, true] {
let (active, active_path, _) = fixture();
fs::remove_file(&active_path).unwrap();
if is_jj {
fs::remove_dir_all(active.0.join(".git")).unwrap();
command(&active.0, "jj", &["git", "init", "--no-colocate", "."]);
command(
&active.0,
"jj",
&["git", "remote", "add", "origin", remote.0.to_str().unwrap()],
);
} else {
command(
&active.0,
"git",
&["remote", "add", "origin", remote.0.to_str().unwrap()],
);
}
let revision = |id: &str| Revision {
target: RevisionTarget::RemoteCommit {
remote: "origin".into(),
reference: reference.clone(),
id: id.into(),
},
label: id.into(),
description: String::new(),
category: Category::Commits,
};
let state = disk_state(&active.0);
let mut repository =
crate::repository::Repository::open(&active.0, Arc::new(AtomicBool::new(false)))
.unwrap();
let comparison = repository
.compare(
1,
&revision(&original),
&revision(&selected),
ComparisonMode::Snapshots,
&[],
)
.unwrap();
let baseline = comparison.blame[1].as_ref().unwrap();
assert_eq!(baseline.commit, selected);
assert!(!baseline.store.starts_with(&active.0));
let source = RevisionSource {
store: baseline.store.clone(),
commit: baseline.commit.clone(),
path: remote_path.strip_prefix(&remote.0).unwrap().to_path_buf(),
keepalive: baseline.keepalive.clone(),
};
let store = source.store.clone();
drop(comparison);
drop(repository);
assert!(
store.exists(),
"Snapshot provenance must retain private fetched objects"
);
let rows = read_revision(&source, "private historical\nsecond\n", &|| false)
.unwrap()
.unwrap();
assert_eq!(rows[0].as_ref().unwrap().commit, selected);
assert_eq!(rows[1].as_ref().unwrap().commit, original);
assert!(!rows[0].as_ref().unwrap().uncommitted);
assert_eq!(disk_state(&active.0), state);
drop(source);
assert!(
!store.exists(),
"Last snapshot release must remove private objects"
);
}
}
#[test]
fn revision_sources_reject_unsafe_paths_and_skip_missing_or_nonregular_baselines() {
let (temp, path, commit) = fixture();
let mut source = RevisionSource {
store: temp.0.join(".git"),
commit,
path: "not-in-baseline".into(),
keepalive: None,
};
assert_eq!(read_revision(&source, "new\n", &|| false).unwrap(), None);
for path in ["", "../file", "/outside", ".git/config", ".jj/repo"] {
source.path = path.into();
assert!(read_revision(&source, "new\n", &|| false).is_err());
}
std::os::unix::fs::symlink(&path, temp.0.join("link")).unwrap();
fs::create_dir(temp.0.join("nested")).unwrap();
fs::write(temp.0.join("nested/file"), "nested\n").unwrap();
command(&temp.0, "git", &["add", "link", "nested/file"]);
command(&temp.0, "git", &["commit", "--quiet", "-m", "Link"]);
source.commit = command(&temp.0, "git", &["rev-parse", "HEAD"]);
source.path = "link".into();
assert_eq!(read_revision(&source, "target\n", &|| false).unwrap(), None);
source.path = "nested".into();
assert_eq!(read_revision(&source, "nested\n", &|| false).unwrap(), None);
source.path = "nested/file".into();
let nested = read_revision(&source, "nested\n", &|| false)
.unwrap()
.unwrap();
assert_eq!(nested[0].as_ref().unwrap().commit, source.commit);
source.path = path.strip_prefix(&temp.0).unwrap().into();
assert_eq!(
read_revision(&source, "first\n", &|| true).unwrap_err(),
"Blame cancelled"
);
assert_eq!(
read_revision(&source, &"x".repeat(LIMIT + 1), &|| false).unwrap_err(),
"Blame input exceeds 16 MiB"
);
}
#[test]
fn noncolocated_jj_uses_parent_without_snapshotting_or_changing_refs() {
let temp = Temporary::new().unwrap();
@@ -332,7 +585,7 @@ fn worker_keeps_only_latest_request_and_bounds_input() {
let mut worker = BlameWorker::start().unwrap();
for id in 0..20 {
worker
.request(id, path.clone(), "first\nsecond\n".into())
.request(id, path.clone(), "first\nsecond\n".into(), None)
.unwrap();
}
loop {
@@ -351,7 +604,7 @@ fn worker_keeps_only_latest_request_and_bounds_input() {
}
assert_eq!(
worker
.request(20, path, "x".repeat(LIMIT + 1))
.request(20, path, "x".repeat(LIMIT + 1), None)
.unwrap_err()
.kind(),
io::ErrorKind::InvalidInput
+3
View File
@@ -780,6 +780,7 @@ fn choose_pair(editor: &mut Editor) -> u64 {
fn comparison() -> Comparison {
Comparison {
blame: Default::default(),
revisions: ["abcdef123".into(), "working:snapshot".into()],
before: "abcdef123".into(),
after: "Working checkout".into(),
@@ -1774,6 +1775,7 @@ fn jujutsu_categories_empty_comparison_and_command_mode_entry_are_complete() {
assert!(editor.apply_diff_result(
id,
Ok(Outcome::Comparison(Comparison {
blame: Default::default(),
revisions: ["working:snapshot".into(), "working:snapshot".into()],
before: "Working".into(),
after: "Working".into(),
@@ -1806,6 +1808,7 @@ fn live_state(before: Vec<String>, after: Vec<String>, eof: bool) -> DiffState {
Category::Commits,
));
state.comparison = Some(Comparison {
blame: Default::default(),
revisions: ["base".into(), "working:snapshot".into()],
root: PathBuf::new(),
before: "base".into(),
+5
View File
@@ -1879,6 +1879,7 @@ fn hidden_tab_diff_results_restore_visible_insert_interaction_and_route_request_
assert!(editor.apply_diff_result(
comparison_request,
Ok(Outcome::Comparison(Comparison {
blame: Default::default(),
root: editor.project_root().to_owned(),
revisions: ["base".into(), "working-snapshot".into()],
before: "base".into(),
@@ -3068,6 +3069,7 @@ fn working_diff(editor: &mut Editor, root: &Path, paths: &[&str]) -> Box<[(PathB
assert!(editor.apply_diff_result(
id,
Ok(Outcome::Comparison(Comparison {
blame: Default::default(),
root: root.canonicalize().unwrap(),
revisions: ["base".into(), "working-snapshot".into()],
before: "base".into(),
@@ -3105,6 +3107,7 @@ fn historical_diff(editor: &mut Editor, root: &Path, before: Vec<String>, after:
editor.apply_diff_result(
id,
Ok(Outcome::Comparison(Comparison {
blame: Default::default(),
root: root.to_owned(),
revisions: ["base".into(), "head".into()],
before: "base".into(),
@@ -4108,6 +4111,7 @@ fn bookmark_refresh_reloads_paths_and_preserves_view_until_lazy_content_arrives(
assert!(editor.apply_diff_result(
refresh,
Ok(Outcome::BookmarkRefresh(Comparison {
blame: Default::default(),
root: directory.0.clone(),
revisions: ["base".into(), "topic-snapshot".into()],
before: "base".into(),
@@ -4229,6 +4233,7 @@ fn completed_working_refresh_serializes_lazy_selection_and_invalidates_pending_n
let loaded_after = target.after.clone();
target.loaded = false;
let replacement = crate::diff::Comparison {
blame: Default::default(),
root: directory.0.clone(),
revisions: ["base".into(), "ahead-snapshot".into()],
before: "base".into(),
+2
View File
@@ -588,6 +588,7 @@ fn checkout_diff_polling_uses_injected_interval_independently_of_endpoint_follow
session.editor.apply_diff_result(
id,
Ok(DiffOutcome::Comparison(Comparison {
blame: Default::default(),
revisions: ["base".into(), "after".into()],
root: fixture.0.clone(),
before: "base".into(),
@@ -3721,6 +3722,7 @@ fn async_responses_edit_only_current_bound_working_after_and_refresh_projection(
session.editor.apply_diff_result(
id,
Ok(DiffOutcome::Comparison(Comparison {
blame: Default::default(),
revisions: ["base".into(), "working:snapshot".into()],
root: fixture.0.canonicalize().unwrap(),
before: "base".into(),
+227 -5
View File
@@ -2593,6 +2593,7 @@ fn diff_comparison(files: Vec<ChangedFile>) -> DiffState {
diff.apply_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
blame: Default::default(),
root: std::path::PathBuf::new(),
editable_paths: Default::default(),
before: "old revision".into(),
@@ -4805,6 +4806,7 @@ fn immutable_diff_draw_path_preserves_document_until_explicit_close() {
editor.apply_diff_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
blame: Default::default(),
root: std::path::PathBuf::new(),
editable_paths: Default::default(),
before: "old revision".into(),
@@ -5652,6 +5654,7 @@ fn comparison_titles_follow_loaded_counts_and_each_tabs_context() {
editor.apply_diff_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
blame: Default::default(),
root: directory.0.clone(),
editable_paths: Default::default(),
before: "baseline".into(),
@@ -5745,6 +5748,7 @@ fn comparison_title_counts_follow_live_edits_and_undo_in_hidden_tabs() {
editor.apply_diff_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
blame: Default::default(),
root: directory.0.clone(),
editable_paths: ["live.rs".into()].into(),
before: "baseline".into(),
@@ -6377,6 +6381,7 @@ fn both_bars_bound_palettes_diff_selectors_docks_and_diff_prompts() {
editor.apply_diff_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
blame: Default::default(),
root: directory.0.clone(),
editable_paths: Default::default(),
before: "baseline".into(),
@@ -6455,6 +6460,218 @@ fn narrow_tab_bar_keeps_the_active_title_visible_and_handles_empty_areas() {
assert!(titles.contains("2 [No Name]"), "{titles}");
}
#[test]
fn inline_blame_is_visible_in_focused_diff_buffers() {
for unified in [false, true] {
for before in [false, true] {
let mut editor = readonly_diff_editor(deletion_file());
if unified {
editor.execute("diff.view.toggle");
}
editor.handle(Input::Char('\t'));
if editor.diff_buffer_focus() != Some(if before { Focus::Before } else { Focus::After })
{
editor.handle(Input::Char('\t'));
}
let source = editor.blame_source().unwrap();
let blame = Arc::new(crate::blame::LineBlame {
commit: "0123456789abcdef".into(),
author: "Author".into(),
summary: "Original".into(),
uncommitted: false,
});
editor.set_line_blame(source.clone(), blame.clone());
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(180, 12)).unwrap();
let mut views = BufferViewports::default();
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
let screen = screen_text(&terminal);
assert!(screen.contains("Author · Original (01234567)"), "{screen}");
let caret = terminal.get_cursor_position().unwrap();
assert!(pane_screen(&terminal, Rect::new(0, caret.y, 180, 1)).contains("Author"));
editor.handle(Input::Char('\t'));
assert!(!editor.set_line_blame(source, blame));
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
assert!(!screen_text(&terminal).contains("Author · Original"));
}
}
}
#[test]
fn diff_blame_uses_final_wrap_and_survives_a_historical_tab() {
for unified in [false, true] {
let text = "x".repeat(200);
let mut editor = readonly_diff_editor(syntax_diff_file("file.txt", &text, "after"));
if unified {
editor.execute("diff.view.toggle");
}
editor.handle(Input::Char('\t'));
editor.execute("word_wrap.toggle");
let source = editor.blame_source().unwrap();
let blame = Arc::new(crate::blame::LineBlame {
commit: String::new(),
author: String::new(),
summary: String::new(),
uncommitted: true,
});
editor.set_line_blame(source.clone(), blame.clone());
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(180, 14)).unwrap();
let mut views = BufferViewports::default();
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
let caret = terminal.get_cursor_position().unwrap();
let screen = screen_text(&terminal);
assert_eq!(screen.matches("Uncommitted").count(), 1, "{screen}");
assert!(!pane_screen(&terminal, Rect::new(0, caret.y, 180, 1)).contains("Uncommitted"));
let mut narrow = Terminal::new(ratatui::backend::TestBackend::new(12, 14)).unwrap();
narrow.draw(|frame| views.draw(frame, &mut editor)).unwrap();
assert!(!screen_text(&narrow).contains("Uncommitted"));
editor.execute("tab.new");
assert!(editor.diff().is_none());
if !editor.word_wrap() {
editor.execute("word_wrap.toggle");
}
editor.set_cached_line_blame(&source, blame);
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
assert!(screen_text(&terminal).contains("Uncommitted"));
editor.handle(Input::Char('i'));
editor.handle(Input::Char('z'));
assert_eq!(editor.lines().join("\n"), text);
}
}
#[test]
fn inline_blame_loads_real_historical_and_working_diff_sources() {
let directory = Directory::new();
let git = |args: &[&str]| {
let output = std::process::Command::new("git")
.current_dir(&directory.0)
.args(args)
.env("GIT_CONFIG_GLOBAL", "/dev/null")
.env("GIT_CONFIG_NOSYSTEM", "1")
.env("GIT_AUTHOR_NAME", "Author")
.env("GIT_AUTHOR_EMAIL", "author@example.test")
.env("GIT_COMMITTER_NAME", "Author")
.env("GIT_COMMITTER_EMAIL", "author@example.test")
.env("GIT_AUTHOR_DATE", "2001-01-01T00:00:00Z")
.env("GIT_COMMITTER_DATE", "2001-01-01T00:00:00Z")
.output()
.unwrap();
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout).unwrap().trim().to_owned()
};
git(&["init", "-q", "--template="]);
let path = directory.file("file.txt", "original\n");
git(&["add", "file.txt"]);
git(&["commit", "-qm", "Original"]);
let original = git(&["rev-parse", "HEAD"]);
directory.file("file.txt", "historical\n");
git(&["commit", "-qam", "Historical"]);
let historical = git(&["rev-parse", "HEAD"]);
git(&["rm", "-q", "file.txt"]);
git(&["commit", "-qm", "Deleted"]);
let config = directory.file(
".git/vamp-config.rb",
"Vamp.configure { |c| c.git_gutter = 'none' }",
);
let mut session = EditorSession::open(Some(path.clone()), vec![], Some(&config)).unwrap();
let ready = |session: &mut EditorSession, predicate: &dyn Fn(&Editor) -> bool| {
let deadline = std::time::Instant::now() + Duration::from_secs(10);
while !predicate(session.editor()) {
assert!(
std::time::Instant::now() < deadline,
"message={:?}, state={:?}, blame={:?}, text={:?}",
session.editor().message(),
session.editor().diff().map(|diff| (
diff.phase,
diff.focus,
diff.file,
&diff.error
)),
session.editor().line_blame(),
session.editor().lines()
);
session.poll();
std::thread::sleep(Duration::from_millis(1));
}
};
session.execute("diff");
ready(&mut session, &|editor| {
editor.diff().is_some_and(|diff| diff.catalog.is_some())
});
for revision in [&original, &historical] {
for ch in revision.chars() {
session.handle(Input::Char(ch));
}
session.handle(Input::Down);
session.handle(Input::Enter);
}
ready(&mut session, &|editor| {
editor
.diff()
.is_some_and(|diff| diff.selected_file_loaded())
});
session.handle(Input::Char('\t'));
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(180, 12)).unwrap();
let mut views = BufferViewports::default();
for expected in ["Original", "Historical"] {
ready(&mut session, &|editor| {
editor
.line_blame()
.is_some_and(|label| label.contains(expected))
});
terminal
.draw(|frame| views.draw_session(frame, &mut session))
.unwrap();
let screen = screen_text(&terminal);
assert!(screen.contains(&format!("Author · {expected}")), "{screen}");
assert!(!screen.contains("Uncommitted"), "{screen}");
session.handle(Input::Char('\t'));
}
assert!(!path.exists());
session.execute("diff.close");
directory.file("file.txt", "restored\n");
git(&["add", "file.txt"]);
git(&["commit", "-qm", "Restored"]);
directory.file("file.txt", "unsaved baseline\n");
session.editor_mut().refresh_files();
session.execute("diff");
ready(&mut session, &|editor| {
editor.diff().is_some_and(|diff| diff.catalog.is_some())
});
for ch in git(&["rev-parse", "HEAD"]).chars() {
session.handle(Input::Char(ch));
}
session.handle(Input::Down);
session.handle(Input::Enter);
session.handle(Input::Enter);
ready(&mut session, &|editor| {
editor
.diff()
.is_some_and(|diff| diff.selected_file_loaded())
});
session.handle(Input::Char('\t'));
session.handle(Input::Char('\t'));
ready(&mut session, &|editor| {
editor.line_blame() == Some("Uncommitted")
});
terminal
.draw(|frame| views.draw_session(frame, &mut session))
.unwrap();
assert!(screen_text(&terminal).contains("Uncommitted"));
assert_eq!(fs::read_to_string(path).unwrap(), "unsaved baseline\n");
}
#[test]
fn inline_blame_sanitizes_metadata_preserves_text_and_hides_stale_labels() {
let directory = Directory::new();
@@ -6545,11 +6762,6 @@ fn inline_blame_uses_last_wrap_continuation_and_never_overflows_narrow_panes() {
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
assert!(!screen_text(&terminal).contains("Uncommitted"));
let mut historical = readonly_diff_editor(deletion_file());
assert!(historical.blame_source().is_none());
historical.execute("tab.new");
assert!(historical.diff().is_none());
assert!(historical.blame_source().is_none());
}
#[test]
@@ -7782,6 +7994,7 @@ fn deferred_lsp_definition_centers_only_successful_current_diff_results() {
editor.apply_diff_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
blame: Default::default(),
root: directory.0.clone(),
editable_paths: Default::default(),
before: "baseline".into(),
@@ -7880,6 +8093,13 @@ fn install_readonly_diff(editor: &mut Editor, file: ChangedFile) {
editor.apply_diff_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
blame: std::array::from_fn(|_| {
Some(crate::diff::BlameRevision {
store: editor.project_root().join(".git"),
commit: "0123456789abcdef0123456789abcdef01234567".into(),
keepalive: None,
})
}),
root: editor.project_root().to_path_buf(),
editable_paths: Default::default(),
before: "baseline".into(),
@@ -8732,6 +8952,7 @@ fn diff_semantic_colors_stay_buffer_local_across_focus_changes() {
editor.apply_diff_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
blame: Default::default(),
root: directory.0.clone(),
editable_paths: if writable {
[std::path::PathBuf::from("live.rs")].into()
@@ -8862,6 +9083,7 @@ fn editable_after_renders_live_syntax_caret_visual_selection_and_command_prompt(
editor.apply_diff_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
blame: Default::default(),
root: directory.0.clone(),
editable_paths: [std::path::PathBuf::from("live.rs")].into(),
before: "baseline".into(),