feat: support comments in ordinary source buffers

This commit is contained in:
2026-10-22 22:30:16 +02:00
parent ac5c64981d
commit e6d94dcb41
16 changed files with 1005 additions and 600 deletions
+115 -26
View File
@@ -2951,6 +2951,62 @@ fn diff_status_layouts_hide_absent_panes_and_leave_unchanged_renames_blank() {
assert!(text.contains("After · M"));
}
#[test]
fn source_review_markers_highlight_exact_text_and_reject_stale_ranges() {
use crate::editor::Effect;
use crate::plugin::TextPosition;
use crate::review::{Anchor, Comment, Review, Side};
for wrapped in [false, true] {
let directory = Directory::new();
let text = format!("aé界{}\nsecond\n", " trailing".repeat(10));
let path = directory.file("source.txt", &text);
let mut editor = Editor::open(Some(path), vec!["text".into()]).unwrap();
editor.local_review.document = Review::new(directory.0.canonicalize().unwrap());
editor.local_review.document.comments.push(Comment {
id: 1,
anchor: Anchor {
revision: "working:source-snapshot".into(),
path: "source.txt".into(),
side: Side::After,
start: TextPosition { row: 0, column: 1 },
end: TextPosition { row: 0, column: 3 },
content: text,
},
body: "Feedback".into(),
resolved: false,
});
editor.local_review.selected = Some(1);
if wrapped {
editor.execute("word_wrap.toggle");
}
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(40, 16)).unwrap();
let mut views = BufferViewports::default();
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
assert!(screen_text(&terminal).contains('●'));
assert!(
terminal
.backend()
.buffer()
.content
.iter()
.filter(|cell| cell.bg == Color::Magenta)
.count()
>= 2
);
let anchor = editor.local_review.document.comments[0].anchor.clone();
editor
.apply(vec![Effect::Replace("changed entirely\n".into())])
.unwrap();
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
assert!(!screen_text(&terminal).contains('●'));
assert_eq!(editor.local_review.document.comments[0].anchor, anchor);
}
}
fn render_diff(
terminal: &mut Terminal<ratatui::backend::TestBackend>,
diff: &DiffState,
@@ -2959,12 +3015,44 @@ fn render_diff(
terminal
.draw(|frame| {
panels.begin_frame();
draw_diff(frame, diff, frame.area(), "↑/↓", "h/j/k/l", panels, None);
draw_diff(
frame,
diff,
frame.area(),
"↑/↓",
"h/j/k/l",
panels,
None,
None,
);
})
.unwrap();
}
fn commented_diff() -> DiffState {
fn render_review_diff(
terminal: &mut Terminal<ratatui::backend::TestBackend>,
diff: &DiffState,
review: &crate::review::ReviewState,
panels: &mut WheelPanels,
) {
terminal
.draw(|frame| {
panels.begin_frame();
draw_diff(
frame,
diff,
frame.area(),
"↑/↓",
"h/j/k/l",
panels,
None,
Some(review),
);
})
.unwrap();
}
fn commented_diff() -> (DiffState, crate::review::ReviewState) {
use crate::review::{Anchor, Comment, Side};
let before = format!(
"{}\nold e\u{301}界 second\nkeep",
@@ -2980,8 +3068,9 @@ fn commented_diff() -> DiffState {
file.old_path = Some("old.txt".into());
let mut diff = diff_comparison(vec![file]);
diff.files_visible = false;
diff.local_review.document.root = diff.comparison.as_ref().unwrap().root.clone();
diff.local_review.document.comments = vec![
let mut review = crate::review::ReviewState::default();
review.document.root = diff.comparison.as_ref().unwrap().root.clone();
review.document.comments = vec![
Comment {
id: 1,
anchor: Anchor {
@@ -3009,8 +3098,8 @@ fn commented_diff() -> DiffState {
resolved: false,
},
];
diff.local_review.selected = Some(1);
diff
review.selected = Some(1);
(diff, review)
}
#[test]
@@ -3022,14 +3111,13 @@ fn local_review_highlights_exact_graphemes_newlines_clips_and_wrap_continuations
for side in [Side::Before, Side::After] {
for (wrapping, horizontal) in [(false, 0), (false, 6), (true, 0)] {
for (start, end) in [((0, 4), (0, 6)), ((0, 5), (1, 5)), ((0, 84), (1, 0))] {
let mut diff = commented_diff();
let (mut diff, mut review) = commented_diff();
diff.set_view_mode(mode);
diff.top = 0;
diff.word_wrap = wrapping;
diff.left_before = horizontal;
diff.left_after = horizontal;
let comment = diff
.local_review
let comment = review
.document
.comments
.iter_mut()
@@ -3044,11 +3132,11 @@ fn local_review_highlights_exact_graphemes_newlines_clips_and_wrap_continuations
column: end.1,
};
let anchor = comment.anchor.clone();
diff.local_review.selected = Some(comment.id);
review.selected = Some(comment.id);
let mut terminal =
Terminal::new(ratatui::backend::TestBackend::new(90, 40)).unwrap();
let mut panels = WheelPanels::default();
render_diff(&mut terminal, &diff, &mut panels);
render_review_diff(&mut terminal, &diff, &review, &mut panels);
let file = &diff.comparison.as_ref().unwrap().files[0];
let wrapped = panels
.diff_wrap
@@ -3164,13 +3252,13 @@ fn local_review_highlights_exact_graphemes_newlines_clips_and_wrap_continuations
fn local_review_markers_preserve_diff_signs_ranges_and_wrapped_geometry() {
for mode in [ViewMode::Split, ViewMode::Unified] {
for wrapping in [false, true] {
let mut diff = commented_diff();
let (mut diff, review) = commented_diff();
diff.set_view_mode(mode);
diff.top = 0;
diff.word_wrap = wrapping;
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(90, 40)).unwrap();
let mut panels = WheelPanels::default();
render_diff(&mut terminal, &diff, &mut panels);
render_review_diff(&mut terminal, &diff, &review, &mut panels);
let buffer = terminal.backend().buffer();
let markers: Vec<_> = buffer
.content
@@ -3219,20 +3307,20 @@ fn local_review_markers_preserve_diff_signs_ranges_and_wrapped_geometry() {
fn local_review_markers_reject_stale_identity_text_and_rename_paths() {
for mode in [ViewMode::Split, ViewMode::Unified] {
for mismatch in ["revision", "content", "path", "side", "root"] {
let mut diff = commented_diff();
let (mut diff, mut review) = commented_diff();
diff.set_view_mode(mode);
diff.local_review.document.comments.truncate(1);
let anchor = &mut diff.local_review.document.comments[0].anchor;
review.document.comments.truncate(1);
let anchor = &mut review.document.comments[0].anchor;
match mismatch {
"revision" => anchor.revision.push_str("-newer"),
"content" => anchor.content.replace_range(..1, "X"),
"path" => anchor.path = "new.txt".into(),
"side" => anchor.side = crate::review::Side::After,
"root" => diff.local_review.document.root = "another-repository".into(),
"root" => review.document.root = "another-repository".into(),
_ => unreachable!(),
}
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(90, 20)).unwrap();
render_diff(&mut terminal, &diff, &mut WheelPanels::default());
render_review_diff(&mut terminal, &diff, &review, &mut WheelPanels::default());
assert!(
terminal
.backend()
@@ -3258,18 +3346,18 @@ fn local_review_markers_reject_stale_identity_text_and_rename_paths() {
#[test]
fn unified_context_shows_comments_from_either_snapshot_side() {
for side in [crate::review::Side::Before, crate::review::Side::After] {
let mut diff = commented_diff();
let (mut diff, mut review) = commented_diff();
diff.set_view_mode(ViewMode::Unified);
diff.local_review
review
.document
.comments
.retain(|comment| comment.anchor.side == side);
let comment = &mut diff.local_review.document.comments[0];
let comment = &mut review.document.comments[0];
comment.anchor.start = crate::plugin::TextPosition { row: 2, column: 1 };
comment.anchor.end = crate::plugin::TextPosition { row: 2, column: 3 };
diff.local_review.selected = Some(comment.id);
review.selected = Some(comment.id);
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(90, 20)).unwrap();
render_diff(&mut terminal, &diff, &mut WheelPanels::default());
render_review_diff(&mut terminal, &diff, &review, &mut WheelPanels::default());
assert!(
screen_text(&terminal).contains("●keep"),
"{side:?}: {}",
@@ -3296,13 +3384,13 @@ fn unified_context_shows_comments_from_either_snapshot_side() {
fn local_review_markers_yield_to_text_on_narrow_surfaces() {
for mode in [ViewMode::Split, ViewMode::Unified] {
for (width, height) in [(1, 1), (2, 2), (3, 3), (8, 4), (16, 8), (30, 12)] {
let mut diff = commented_diff();
let (mut diff, review) = commented_diff();
diff.set_view_mode(mode);
diff.word_wrap = true;
let mut terminal =
Terminal::new(ratatui::backend::TestBackend::new(width, height)).unwrap();
let mut panels = WheelPanels::default();
render_diff(&mut terminal, &diff, &mut panels);
render_review_diff(&mut terminal, &diff, &review, &mut panels);
for (area, focus) in panels
.diff
.as_ref()
@@ -3702,6 +3790,7 @@ fn pull_request_review_symbols_use_terminal_cells_and_keep_the_selection_arrow_i
"h/j/k/l",
&mut panels,
with_editor.then_some(&editor),
None,
);
})
.unwrap();
@@ -4834,7 +4923,7 @@ fn unavailable_file_documents_show_the_actual_error_in_managed_workspaces() {
};
terminal
.draw(|frame| {
panels.draw_documents(frame, &diff, frame.area(), None, None);
panels.draw_documents(frame, &diff, frame.area(), None, None, None);
})
.unwrap();
let text = screen_text(&terminal);