fix: retain selections and float local review editors
This commit is contained in:
+229
-39
@@ -678,18 +678,23 @@ impl BufferViewports {
|
||||
.resolver()
|
||||
.is_some_and(|resolver| resolver.view == ResolverView::History);
|
||||
let multiple = !resolver_resolution && editor.pane_layout().len() > 1;
|
||||
let (workspace, dock) = match editor.file_picker() {
|
||||
Some(picker) if picker.presentation == FilePresentation::Docked => dock_layout(
|
||||
full_workspace,
|
||||
if editor.picker_diff().is_some() {
|
||||
DockSide::Right
|
||||
} else {
|
||||
editor.file_explorer_side()
|
||||
},
|
||||
picker.focused,
|
||||
),
|
||||
_ => (full_workspace, None),
|
||||
};
|
||||
let layout_pane = editor
|
||||
.review_panel()
|
||||
.and_then(|pane| editor.pane_origin(pane))
|
||||
.unwrap_or(focused);
|
||||
let (workspace, dock) =
|
||||
editor.with_render_pane(layout_pane, |view| match view.file_picker() {
|
||||
Some(picker) if picker.presentation == FilePresentation::Docked => dock_layout(
|
||||
full_workspace,
|
||||
if view.picker_diff().is_some() {
|
||||
DockSide::Right
|
||||
} else {
|
||||
view.file_explorer_side()
|
||||
},
|
||||
picker.focused,
|
||||
),
|
||||
_ => (full_workspace, None),
|
||||
});
|
||||
editor.set_pane_bounds(pane_rect(workspace));
|
||||
self.rects.clear();
|
||||
if resolver_history {
|
||||
@@ -737,9 +742,6 @@ impl BufferViewports {
|
||||
.retain(|id, _| view.has_buffer(*id));
|
||||
let (top, left) = self.offsets.entry(view.buffer_id()).or_default();
|
||||
let syntax = self.syntax.entry(view.buffer_id()).or_default();
|
||||
let leaf = view.review_panel_title().map_or(leaf, |title| {
|
||||
resolver_document_panel(frame, leaf, title, id == focused)
|
||||
});
|
||||
draw_document(
|
||||
frame,
|
||||
view,
|
||||
@@ -767,22 +769,25 @@ impl BufferViewports {
|
||||
self.panels.begin_frame();
|
||||
self.document.geometry = None;
|
||||
}
|
||||
if let Some(panel) = dock
|
||||
&& let Some(picker) = editor.file_picker()
|
||||
{
|
||||
self.panels.picker_scroll.distance = editor.scroll_distance();
|
||||
draw_file_list(
|
||||
frame,
|
||||
picker,
|
||||
panel,
|
||||
editor.file_navigation_hint(),
|
||||
&mut self.panels,
|
||||
editor.picker_diff().and_then(|diff| {
|
||||
diff.comparison
|
||||
.as_ref()
|
||||
.map(|comparison| (comparison.files.as_slice(), &diff.reviewed_files))
|
||||
}),
|
||||
);
|
||||
if let Some(panel) = dock {
|
||||
self.activate(layout_pane, editor);
|
||||
editor.with_render_pane(layout_pane, |view| {
|
||||
if let Some(picker) = view.file_picker() {
|
||||
self.panels.picker_scroll.distance = view.scroll_distance();
|
||||
draw_file_list(
|
||||
frame,
|
||||
picker,
|
||||
panel,
|
||||
view.file_navigation_hint(),
|
||||
&mut self.panels,
|
||||
view.picker_diff().and_then(|diff| {
|
||||
diff.comparison.as_ref().map(|comparison| {
|
||||
(comparison.files.as_slice(), &diff.reviewed_files)
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
if let Some(geometry) = &mut self.panels.diff {
|
||||
geometry.regions[0] = (panel, Focus::Files);
|
||||
editor.set_diff_pane_bounds(
|
||||
@@ -827,6 +832,44 @@ impl BufferViewports {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(panel) = editor.review_panel() {
|
||||
let source = editor.pane_origin(panel);
|
||||
let area = self
|
||||
.rects
|
||||
.iter()
|
||||
.find(|(id, _)| Some(*id) == source)
|
||||
.map_or(full_workspace, |(_, area)| *area);
|
||||
let area = floating_panel(area);
|
||||
self.rects.insert(0, (panel, area));
|
||||
self.activate(panel, editor);
|
||||
self.panels.workspace_managed = true;
|
||||
editor.with_render_pane(panel, |view| {
|
||||
frame.render_widget(Clear, area);
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Cyan))
|
||||
.title(display_text(
|
||||
view.review_panel_title().unwrap_or("Local review"),
|
||||
));
|
||||
let content = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
self.activate_document(view.buffer_id());
|
||||
let (top, left) = self.offsets.entry(view.buffer_id()).or_default();
|
||||
let syntax = self.syntax.entry(view.buffer_id()).or_default();
|
||||
draw_document(
|
||||
frame,
|
||||
view,
|
||||
content,
|
||||
full_workspace,
|
||||
top,
|
||||
left,
|
||||
syntax,
|
||||
&mut self.document,
|
||||
&mut self.panels,
|
||||
);
|
||||
});
|
||||
self.panels.workspace_managed = false;
|
||||
}
|
||||
if let Some(diff) = editor.diff().filter(|diff| diff.phase != Phase::View) {
|
||||
draw_diff(
|
||||
frame,
|
||||
@@ -1097,10 +1140,18 @@ impl BufferViewports {
|
||||
return true;
|
||||
}
|
||||
let focused = session.editor().pane_id();
|
||||
let Some(target) = self.rects.iter().find_map(|(id, area)| {
|
||||
let target = self.rects.iter().find_map(|(id, area)| {
|
||||
area.contains(Position::new(mouse.column, mouse.row))
|
||||
.then_some(*id)
|
||||
}) else {
|
||||
});
|
||||
let target = if let Some(panel) = session.editor().review_panel() {
|
||||
if target != Some(panel) {
|
||||
return true;
|
||||
}
|
||||
panel
|
||||
} else if let Some(target) = target {
|
||||
target
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
if let Some(resolver) = session
|
||||
@@ -1729,11 +1780,13 @@ fn chrome_items<'a>(
|
||||
continue;
|
||||
}
|
||||
let mut text = match name.as_str() {
|
||||
"mode" => Cow::Owned(if editor.visual_linewise() {
|
||||
"VISUAL LINE".into()
|
||||
} else {
|
||||
editor.mode().to_string()
|
||||
}),
|
||||
"mode" => Cow::Owned(
|
||||
if editor.mode() == Mode::Visual && editor.visual_linewise() {
|
||||
"VISUAL LINE".into()
|
||||
} else {
|
||||
editor.mode().to_string()
|
||||
},
|
||||
),
|
||||
"path" => Cow::Owned(chrome_title(
|
||||
"{path}{modified}",
|
||||
editor.tab_index() + 1,
|
||||
@@ -13962,7 +14015,7 @@ end
|
||||
editor.apply_diff_result(
|
||||
id,
|
||||
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
|
||||
root: std::path::PathBuf::new(),
|
||||
root: editor.project_root().to_path_buf(),
|
||||
editable_paths: Default::default(),
|
||||
before: "baseline".into(),
|
||||
after: "historic".into(),
|
||||
@@ -13983,6 +14036,143 @@ end
|
||||
terminal.get_cursor_position().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_input_keeps_characterwise_and_linewise_selection_highlights() {
|
||||
for context in ["document", "split", "unified"] {
|
||||
for selector in ['v', 'V'] {
|
||||
let mut editor = if context == "document" {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor
|
||||
.apply(vec![crate::editor::Effect::Replace(
|
||||
"alpha beta\nsecond line".into(),
|
||||
)])
|
||||
.unwrap();
|
||||
editor
|
||||
} else {
|
||||
let mut editor = readonly_diff_editor(syntax_diff_file(
|
||||
"file.txt",
|
||||
"alpha beta\nold line",
|
||||
"alpha beta\nnew line",
|
||||
));
|
||||
if context == "unified" {
|
||||
editor.execute("diff.view.toggle");
|
||||
}
|
||||
editor.handle(Input::Char('\t'));
|
||||
editor
|
||||
};
|
||||
let mut terminal =
|
||||
Terminal::new(ratatui::backend::TestBackend::new(140, 24)).unwrap();
|
||||
let mut views = BufferViewports::default();
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
let source = terminal.get_cursor_position().unwrap();
|
||||
editor.handle(Input::Char(selector));
|
||||
editor.handle(Input::Right);
|
||||
let ranges = editor.selections();
|
||||
editor.handle(Input::Char(':'));
|
||||
for ch in "diff.comment".chars() {
|
||||
editor.handle(Input::Char(ch));
|
||||
}
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(editor.mode(), Mode::Command);
|
||||
assert_eq!(editor.selections(), ranges);
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(
|
||||
buffer[(source.x, source.y)].bg,
|
||||
Color::Blue,
|
||||
"{context} {selector}"
|
||||
);
|
||||
assert_eq!(
|
||||
buffer[(source.x + if selector == 'V' { 9 } else { 1 }, source.y)].bg,
|
||||
Color::Blue,
|
||||
"{context} {selector}"
|
||||
);
|
||||
editor.handle(Input::Escape);
|
||||
assert_eq!(editor.selections(), ranges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_editor_floats_over_the_diff_without_changing_workspace_layout() {
|
||||
for unified in [false, true] {
|
||||
let mut editor = readonly_diff_editor(syntax_diff_file(
|
||||
"file.txt",
|
||||
"old first\nold last",
|
||||
"new first\nnew last",
|
||||
));
|
||||
if unified {
|
||||
editor.execute("diff.view.toggle");
|
||||
}
|
||||
editor.handle(Input::Char('\t'));
|
||||
let owner = editor.pane_id();
|
||||
let source_text = editor.context().text;
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(160, 30)).unwrap();
|
||||
let mut views = BufferViewports::default();
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
let source_area = views
|
||||
.rects
|
||||
.iter()
|
||||
.find(|(pane, _)| *pane == owner)
|
||||
.unwrap()
|
||||
.1;
|
||||
editor.execute("diff.comment");
|
||||
let panel = editor.review_panel().expect("focused floating review");
|
||||
assert_eq!(editor.pane_layout().len(), 1);
|
||||
assert!(!editor.pane_layout().contains(panel));
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(views.rects[0], (panel, floating_panel(source_area)));
|
||||
assert_eq!(
|
||||
views
|
||||
.rects
|
||||
.iter()
|
||||
.find(|(pane, _)| *pane == owner)
|
||||
.unwrap()
|
||||
.1,
|
||||
source_area
|
||||
);
|
||||
let area = views.rects[0].1;
|
||||
let cursor = terminal.get_cursor_position().unwrap();
|
||||
assert!(area.contains(cursor));
|
||||
for input in [
|
||||
Input::Char('i'),
|
||||
Input::Paste("Retained draft".into()),
|
||||
Input::Escape,
|
||||
] {
|
||||
editor.handle(input);
|
||||
}
|
||||
assert_eq!(editor.context().text, "Retained draft");
|
||||
assert_eq!(editor.review_panel(), Some(panel));
|
||||
editor.handle(Input::Escape);
|
||||
assert!(editor.review_panel().is_none());
|
||||
assert_eq!(editor.pane_id(), owner);
|
||||
assert_eq!(editor.context().text, source_text);
|
||||
editor.execute("diff.review.edit 1");
|
||||
assert_eq!(
|
||||
editor.context().text,
|
||||
"Retained draft",
|
||||
"{}",
|
||||
editor.message()
|
||||
);
|
||||
assert_eq!(editor.review_panel(), Some(panel));
|
||||
editor.execute("tab.new");
|
||||
editor.execute("tab.previous");
|
||||
editor.execute("diff.review.edit 1");
|
||||
assert_eq!(editor.review_panel(), Some(panel));
|
||||
assert_eq!(editor.context().text, "Retained draft");
|
||||
editor.execute("pane.close");
|
||||
assert!(editor.review_panel().is_none());
|
||||
assert_eq!(editor.pane_layout().len(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_projection_keeps_unified_wrapped_caret_and_selection_source_only() {
|
||||
let before = format!(
|
||||
|
||||
Reference in New Issue
Block a user