feat: add literal buffer search and match navigation
This commit is contained in:
+73
-3
@@ -228,6 +228,20 @@ fn draw(frame: &mut Frame, editor: &Editor, top: &mut usize, left: &mut usize) {
|
||||
Paragraph::new(lines),
|
||||
Rect::new(area.x, area.y, area.width, area.height - 2),
|
||||
);
|
||||
if let Some(found) = editor.search_match()
|
||||
&& found.start.row >= *top
|
||||
&& found.start.row < top.saturating_add(height)
|
||||
{
|
||||
let line = &editor.lines()[found.start.row];
|
||||
let start = display_column(line, found.start.column).max(*left);
|
||||
let end = display_column(line, found.end_column).min(left.saturating_add(width));
|
||||
let y = area.y + (found.start.row - *top) as u16;
|
||||
let style = Style::default().fg(Color::Black).bg(Color::Yellow);
|
||||
let buffer = frame.buffer_mut();
|
||||
for column in start..end {
|
||||
buffer[(area.x + (column - *left) as u16, y)].set_style(style);
|
||||
}
|
||||
}
|
||||
let path = editor
|
||||
.path()
|
||||
.map(|p| p.to_string_lossy().into_owned())
|
||||
@@ -249,9 +263,14 @@ fn draw(frame: &mut Frame, editor: &Editor, top: &mut usize, left: &mut usize) {
|
||||
Rect::new(area.x, area.y + area.height - 2, area.width, 1),
|
||||
);
|
||||
let bottom = area.y + area.height - 1;
|
||||
if editor.mode() == Mode::Command {
|
||||
let command = format!(":{}", editor.command_line());
|
||||
let command_width = UnicodeWidthStr::width(command.as_str());
|
||||
if matches!(editor.mode(), Mode::Command | Mode::Search) {
|
||||
let prefix = if editor.mode() == Mode::Search {
|
||||
'/'
|
||||
} else {
|
||||
':'
|
||||
};
|
||||
let command = format!("{prefix}{}", editor.command_line());
|
||||
let command_width = display_column(&command, usize::MAX);
|
||||
let offset = command_width.saturating_sub(width - 1);
|
||||
frame.render_widget(
|
||||
Paragraph::new(visible_line(&command, offset, width)),
|
||||
@@ -282,6 +301,57 @@ mod tests {
|
||||
assert_eq!(visible_line(text, 5, 2), " e\u{301}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn active_search_highlight_uses_display_cells_and_clips_with_horizontal_scroll() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor
|
||||
.apply(vec![crate::editor::Effect::Replace(
|
||||
"a\t界e\u{301}zz".into(),
|
||||
)])
|
||||
.unwrap();
|
||||
editor.handle(Input::Char('/'));
|
||||
for ch in "\t界e\u{301}".chars() {
|
||||
editor.handle(Input::Char(ch));
|
||||
}
|
||||
editor.handle(Input::Enter);
|
||||
editor.handle(Input::End);
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(5, 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();
|
||||
assert_eq!(left, 4);
|
||||
assert_eq!(buffer[(0, 0)].symbol(), "界");
|
||||
assert_eq!(buffer[(2, 0)].symbol(), "e\u{301}");
|
||||
for x in [0, 2] {
|
||||
assert_eq!(buffer[(x, 0)].bg, Color::Yellow);
|
||||
}
|
||||
for x in 3..5 {
|
||||
assert_ne!(buffer[(x, 0)].bg, Color::Yellow);
|
||||
}
|
||||
editor.handle(Input::Char('/'));
|
||||
for ch in "界界".chars() {
|
||||
editor.handle(Input::Char(ch));
|
||||
}
|
||||
terminal
|
||||
.draw(|frame| draw(frame, &editor, &mut top, &mut left))
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(buffer[(0, 4)].symbol(), "界");
|
||||
assert_eq!(buffer[(2, 4)].symbol(), "界");
|
||||
editor.handle(Input::Backspace);
|
||||
editor.handle(Input::Backspace);
|
||||
editor.handle(Input::Char('\t'));
|
||||
terminal
|
||||
.draw(|frame| draw(frame, &editor, &mut top, &mut left))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
terminal.get_cursor_position().unwrap(),
|
||||
ratatui::layout::Position::new(4, 4)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn terminal_session_scopes_the_callers_panic_hook() {
|
||||
|
||||
Reference in New Issue
Block a user