feat: implement selection-first multi-target editing
This commit is contained in:
+87
-3
@@ -242,7 +242,7 @@ fn draw(frame: &mut Frame, editor: &Editor, top: &mut usize, left: &mut usize) {
|
||||
buffer[(area.x + (column - *left) as u16, y)].set_style(style);
|
||||
}
|
||||
}
|
||||
if let Some(selection) = editor.selection() {
|
||||
for selection in editor.selections() {
|
||||
let first = selection.start.row.max(*top);
|
||||
let last = selection.end.row.min(top.saturating_add(height - 1));
|
||||
let style = Style::default().fg(Color::White).bg(Color::Blue);
|
||||
@@ -267,6 +267,19 @@ fn draw(frame: &mut Frame, editor: &Editor, top: &mut usize, left: &mut usize) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for caret in editor.carets().into_iter().filter(|caret| *caret != cursor) {
|
||||
if caret.row < *top || caret.row >= top.saturating_add(height) {
|
||||
continue;
|
||||
}
|
||||
let column = display_column(&editor.lines()[caret.row], caret.column);
|
||||
if column >= *left && column < left.saturating_add(width) {
|
||||
frame.buffer_mut()[(
|
||||
area.x + (column - *left) as u16,
|
||||
area.y + (caret.row - *top) as u16,
|
||||
)]
|
||||
.set_style(Style::default().fg(Color::Black).bg(Color::Magenta));
|
||||
}
|
||||
}
|
||||
let path = editor
|
||||
.path()
|
||||
.map(|p| p.to_string_lossy().into_owned())
|
||||
@@ -276,12 +289,17 @@ fn draw(frame: &mut Frame, editor: &Editor, top: &mut usize, left: &mut usize) {
|
||||
.map(|c| if c.is_control() { '�' } else { c })
|
||||
.collect();
|
||||
let status = format!(
|
||||
" {} {}{} {}:{}",
|
||||
" {} {}{} {}:{}{}",
|
||||
editor.mode(),
|
||||
path,
|
||||
if editor.dirty() { " [+]" } else { "" },
|
||||
cursor.row + 1,
|
||||
cursor.column + 1
|
||||
cursor.column + 1,
|
||||
if editor.selection_count() > 1 {
|
||||
format!(" {} selections", editor.selection_count())
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
);
|
||||
frame.render_widget(
|
||||
Paragraph::new(status).style(Style::default().fg(Color::Black).bg(Color::Cyan)),
|
||||
@@ -326,6 +344,72 @@ mod tests {
|
||||
assert_eq!(visible_line(text, 5, 2), " e\u{301}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selection_set_renders_all_ranges_and_counts_offscreen_targets() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor
|
||||
.apply(vec![crate::editor::Effect::Replace(
|
||||
"界\n界\n界\n界\n界".into(),
|
||||
)])
|
||||
.unwrap();
|
||||
editor.handle(Input::Home);
|
||||
editor.handle(Input::Char('v'));
|
||||
editor.handle(Input::Ctrl('a'));
|
||||
assert_eq!(editor.selection_count(), 5);
|
||||
assert_eq!(editor.selections().len(), 5);
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(48, 4)).unwrap();
|
||||
let (mut top, mut left) = (0, 0);
|
||||
terminal
|
||||
.draw(|frame| draw(frame, &editor, &mut top, &mut left))
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
for selection in editor.selections() {
|
||||
if selection.start.row >= top && selection.start.row < top + 2 {
|
||||
let y = (selection.start.row - top) as u16;
|
||||
assert!(matches!(buffer[(0, y)].bg, Color::Blue | Color::Magenta));
|
||||
}
|
||||
}
|
||||
let status: String = (0..48).map(|x| buffer[(x, 2)].symbol()).collect();
|
||||
assert!(status.contains("5 selections"), "{status}");
|
||||
assert_eq!(
|
||||
terminal.get_cursor_position().unwrap(),
|
||||
ratatui::layout::Position::new(0, (editor.cursor().row - top) as u16)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secondary_carets_render_on_empty_lines_and_at_line_end() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor
|
||||
.apply(vec![crate::editor::Effect::Replace("a\n\n界".into())])
|
||||
.unwrap();
|
||||
editor.handle(Input::Home);
|
||||
editor.handle(Input::Ctrl('e'));
|
||||
editor.handle(Input::Ctrl('e'));
|
||||
assert_eq!(editor.selection_count(), 3);
|
||||
editor.handle(Input::Char('A'));
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(48, 5)).unwrap();
|
||||
let (mut top, mut left) = (0, 0);
|
||||
terminal
|
||||
.draw(|frame| draw(frame, &editor, &mut top, &mut left))
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
for caret in editor.carets() {
|
||||
let x = display_column(&editor.lines()[caret.row], caret.column) as u16;
|
||||
assert_eq!(buffer[(x, caret.row as u16)].symbol(), " ");
|
||||
if caret != editor.cursor() {
|
||||
assert_eq!(buffer[(x, caret.row as u16)].bg, Color::Magenta);
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
terminal.get_cursor_position().unwrap(),
|
||||
ratatui::layout::Position::new(
|
||||
display_column(&editor.lines()[editor.cursor().row], editor.cursor().column) as u16,
|
||||
editor.cursor().row as u16
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visual_selection_highlights_graphemes_newlines_and_clipped_ranges_over_search() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user