fix: render theme-aware software cursors and mode banners
This commit is contained in:
+217
-102
@@ -323,7 +323,11 @@ fn historical_buffer_markers_retain_real_final_newlines_and_scope() {
|
||||
for (x, y, symbol) in [(3, 0, "↵"), (0, 1, "·"), (1, 1, "·"), (7, 1, "↵")] {
|
||||
let cell = &buffer[(area.x + x, area.y + y)];
|
||||
assert_eq!(cell.symbol(), symbol);
|
||||
assert!(cell.modifier.contains(Modifier::DIM));
|
||||
assert_eq!(
|
||||
cell.modifier.contains(Modifier::DIM),
|
||||
views.panels.cursor != Some(Position::new(area.x + x, area.y + y)),
|
||||
"only the primary cursor removes marker dim styling",
|
||||
);
|
||||
}
|
||||
assert_eq!(buffer[(area.x + 6, area.y + 1)].symbol(), " ");
|
||||
assert_eq!(buffer[(area.x + 6, area.y + 1)].bg, Color::Red);
|
||||
@@ -847,6 +851,7 @@ fn whitespace_characters_preserve_source_geometry_and_overlay_styles() {
|
||||
session.handle(Input::End);
|
||||
session.handle(Input::Char('v'));
|
||||
session.handle(Input::Left);
|
||||
session.handle(Input::Left);
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, session.editor_mut()))
|
||||
.unwrap();
|
||||
@@ -905,6 +910,7 @@ fn trailing_whitespace_is_opt_in_and_preserves_wrapping_and_selection() {
|
||||
session.handle(Input::End);
|
||||
session.handle(Input::Char('v'));
|
||||
session.handle(Input::Left);
|
||||
session.handle(Input::Left);
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, session.editor_mut()))
|
||||
.unwrap();
|
||||
@@ -1290,7 +1296,7 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
);
|
||||
assert_eq!(buffer[(x, content.y + 1)].symbol(), "v");
|
||||
assert_eq!(
|
||||
buffer[(x, content.y + 1)].bg,
|
||||
buffer[(x + 1, content.y + 1)].bg,
|
||||
views.panels.diff_colors.row[1]
|
||||
);
|
||||
}
|
||||
@@ -1464,7 +1470,7 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
let searched_caret = render(&mut terminal, &mut views, &mut session);
|
||||
assert!(screen_text(&terminal).contains("context 30"));
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(searched_caret.x, searched_caret.y)].bg,
|
||||
terminal.backend().buffer()[(searched_caret.x + 1, searched_caret.y)].bg,
|
||||
Color::Yellow,
|
||||
"source {:?} ({:?}), terminal {searched_caret:?}, caret {:?}, viewport {:?}, focus {:?}",
|
||||
session.editor().cursor(),
|
||||
@@ -5383,9 +5389,10 @@ fn floating_fields_use_border_titles_and_inner_horizontal_padding() {
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(80, 12)).unwrap();
|
||||
let workspace = Rect::new(3, 2, 74, 8);
|
||||
let mut panel = Rect::default();
|
||||
let mut cursor = Position::default();
|
||||
terminal
|
||||
.draw(|frame| {
|
||||
panel = draw_floating_input(frame, &editor, workspace).unwrap();
|
||||
(panel, cursor) = draw_floating_input(frame, &editor, workspace).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(panel.x, workspace.x);
|
||||
@@ -5397,10 +5404,7 @@ fn floating_fields_use_border_titles_and_inner_horizontal_padding() {
|
||||
assert_eq!(buffer[(panel.x + 1, panel.y + 1)].symbol(), " ");
|
||||
assert_eq!(buffer[(panel.x + 2, panel.y + 1)].symbol(), "n");
|
||||
assert_eq!(buffer[(panel.right() - 2, panel.y + 1)].symbol(), " ");
|
||||
assert_eq!(
|
||||
terminal.get_cursor_position().unwrap(),
|
||||
Position::new(panel.x + 8, panel.y + 1)
|
||||
);
|
||||
assert_eq!(cursor, Position::new(panel.x + 8, panel.y + 1));
|
||||
assert_eq!(
|
||||
pane_screen(
|
||||
&terminal,
|
||||
@@ -8558,7 +8562,7 @@ fn native_visual_selection_overrides_selected_review_range_background() {
|
||||
let mut views = BufferViewports::default();
|
||||
let source = render_native_diff(&mut terminal, &mut views, &mut editor);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(source.x, source.y)].bg,
|
||||
terminal.backend().buffer()[(source.x + 1, source.y)].bg,
|
||||
Color::Magenta
|
||||
);
|
||||
editor.handle(Input::Char('v'));
|
||||
@@ -9064,10 +9068,6 @@ fn wrapped_diff_aligns_continuations_and_preserves_native_selection_and_caret()
|
||||
terminal.backend().buffer()[(caret.x, caret.y)].symbol(),
|
||||
"D"
|
||||
);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(caret.x, caret.y)].fg,
|
||||
Color::DarkGray
|
||||
);
|
||||
editor.handle(Input::Char('v'));
|
||||
editor.handle(Input::Home);
|
||||
render_native_diff(&mut terminal, &mut views, &mut editor);
|
||||
@@ -9123,10 +9123,6 @@ fn readonly_diff_panes_render_native_caret_visual_search_and_paired_focus() {
|
||||
terminal.backend().buffer()[(caret.x, caret.y)].symbol(),
|
||||
"f"
|
||||
);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(caret.x, caret.y)].fg,
|
||||
Color::Magenta
|
||||
);
|
||||
editor.handle(Input::Char('v'));
|
||||
editor.handle(Input::Right);
|
||||
render_native_diff(&mut terminal, &mut views, &mut editor);
|
||||
@@ -9140,7 +9136,7 @@ fn readonly_diff_panes_render_native_caret_visual_search_and_paired_focus() {
|
||||
editor.handle(Input::Char('V'));
|
||||
render_native_diff(&mut terminal, &mut views, &mut editor);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(caret.x, caret.y + 1)].bg,
|
||||
terminal.backend().buffer()[(caret.x + 1, caret.y + 1)].bg,
|
||||
Color::Blue
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -9157,11 +9153,11 @@ fn readonly_diff_panes_render_native_caret_visual_search_and_paired_focus() {
|
||||
let searched = render_native_diff(&mut terminal, &mut views, &mut editor);
|
||||
assert!(before.contains(searched));
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(searched.x, searched.y)].bg,
|
||||
terminal.backend().buffer()[(searched.x + 1, searched.y)].bg,
|
||||
Color::Yellow
|
||||
);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(searched.x, searched.y)].fg,
|
||||
terminal.backend().buffer()[(searched.x + 1, searched.y)].fg,
|
||||
Color::Black
|
||||
);
|
||||
let before_row = editor
|
||||
@@ -9926,7 +9922,7 @@ fn snippet_highlight_tracks_argument_graphemes_without_filling_the_row() {
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
// A wide glyph's leading cell owns its terminal style.
|
||||
for x in (0..28).filter(|x| *x != 8) {
|
||||
for x in (0..28).filter(|x| *x != 8 && *x != caret.x) {
|
||||
assert_eq!(
|
||||
buffer[(x, 0)].bg == Color::Blue,
|
||||
selected.contains(&x),
|
||||
@@ -9935,6 +9931,11 @@ fn snippet_highlight_tracks_argument_graphemes_without_filling_the_row() {
|
||||
screen_text(&terminal),
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
buffer[caret].bg,
|
||||
Color::Green,
|
||||
"cursor overrides the snippet selection"
|
||||
);
|
||||
assert_eq!(terminal.get_cursor_position().unwrap(), caret);
|
||||
}
|
||||
editor.handle(Input::Escape);
|
||||
@@ -10504,7 +10505,7 @@ end
|
||||
"T"
|
||||
);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(cursor.x, cursor.y)].fg,
|
||||
terminal.backend().buffer()[(cursor.x - 1, cursor.y)].fg,
|
||||
Color::DarkGray
|
||||
);
|
||||
assert_eq!(fs::read_to_string(target).unwrap(), target_text);
|
||||
@@ -10522,7 +10523,7 @@ fn syntax_tokens_refresh_after_native_edits_and_undo_without_changing_text_on_dr
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(terminal.backend().buffer()[(2, 0)].fg, Color::Yellow);
|
||||
assert_eq!(terminal.backend().buffer()[(3, 0)].fg, Color::Yellow);
|
||||
let revision = editor.text_revision();
|
||||
editor.handle(Input::Right);
|
||||
terminal
|
||||
@@ -10571,7 +10572,7 @@ fn syntax_tokens_refresh_after_native_edits_and_undo_without_changing_text_on_dr
|
||||
("42", Color::Yellow),
|
||||
("\"ok\"", Color::Green),
|
||||
] {
|
||||
let x = 2 + source.find(token).unwrap() as u16;
|
||||
let x = 2 + (source.find(token).unwrap() + token.len() - 1) as u16;
|
||||
assert_eq!(buffer[(x, 0)].fg, color, "{token}");
|
||||
assert_eq!(buffer[(x, 0)].bg, Color::Reset);
|
||||
}
|
||||
@@ -10611,7 +10612,7 @@ fn syntax_continuations_above_viewport_refresh_when_the_opening_line_changes() {
|
||||
"v"
|
||||
);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(caret.x, caret.y)].fg,
|
||||
terminal.backend().buffer()[(caret.x + 1, caret.y)].fg,
|
||||
color,
|
||||
"{name}"
|
||||
);
|
||||
@@ -10626,7 +10627,7 @@ fn syntax_continuations_above_viewport_refresh_when_the_opening_line_changes() {
|
||||
"v"
|
||||
);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(caret.x, caret.y)].fg,
|
||||
terminal.backend().buffer()[(caret.x + 1, caret.y)].fg,
|
||||
Color::Reset,
|
||||
"{name}"
|
||||
);
|
||||
@@ -10635,7 +10636,7 @@ fn syntax_continuations_above_viewport_refresh_when_the_opening_line_changes() {
|
||||
.draw(|frame| viewports.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(caret.x, caret.y)].fg,
|
||||
terminal.backend().buffer()[(caret.x + 1, caret.y)].fg,
|
||||
color,
|
||||
"{name}"
|
||||
);
|
||||
@@ -10895,7 +10896,7 @@ fn search_visual_selection_and_secondary_carets_override_semantic_foreground() {
|
||||
.unwrap();
|
||||
let found = editor.search_match().unwrap();
|
||||
let y = found.start.row as u16;
|
||||
for x in 4..8 {
|
||||
for x in 5..8 {
|
||||
assert_eq!(terminal.backend().buffer()[(x, y)].fg, Color::Black);
|
||||
assert_eq!(terminal.backend().buffer()[(x, y)].bg, Color::Yellow);
|
||||
assert!(
|
||||
@@ -10956,7 +10957,7 @@ fn syntax_redetects_saved_paths_without_text_changes_and_plain_text_keeps_defaul
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(terminal.backend().buffer()[(2, 0)].fg, Color::Magenta);
|
||||
assert_eq!(terminal.backend().buffer()[(3, 0)].fg, Color::Magenta);
|
||||
let revision = editor.text_revision();
|
||||
for name in ["source.vampunknown", "source.txt", "again.rs"] {
|
||||
editor.execute(&format!("w {}", directory.0.join(name).display()));
|
||||
@@ -10965,13 +10966,13 @@ fn syntax_redetects_saved_paths_without_text_changes_and_plain_text_keeps_defaul
|
||||
.draw(|frame| viewports.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
if name.ends_with(".rs") {
|
||||
assert_eq!(terminal.backend().buffer()[(2, 0)].fg, Color::Magenta);
|
||||
assert_eq!(terminal.backend().buffer()[(3, 0)].fg, Color::Magenta);
|
||||
assert_eq!(
|
||||
viewports.syntax[&editor.buffer_id()].language(),
|
||||
Some("Rust")
|
||||
);
|
||||
} else {
|
||||
assert_eq!(terminal.backend().buffer()[(2, 0)].fg, Color::Reset);
|
||||
assert_eq!(terminal.backend().buffer()[(3, 0)].fg, Color::Reset);
|
||||
assert!(viewports.syntax[&editor.buffer_id()].line(0).is_empty());
|
||||
assert!(viewports.syntax[&editor.buffer_id()].language().is_none());
|
||||
}
|
||||
@@ -10990,7 +10991,7 @@ fn syntax_caches_stay_independent_across_buffer_edits_and_retire_closed_ids() {
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(terminal.backend().buffer()[(2, 0)].fg, Color::Yellow);
|
||||
assert_eq!(terminal.backend().buffer()[(3, 0)].fg, Color::Yellow);
|
||||
editor.execute(&format!(
|
||||
"edit {}",
|
||||
directory.file("b.py", "# note").display()
|
||||
@@ -10999,19 +11000,19 @@ fn syntax_caches_stay_independent_across_buffer_edits_and_retire_closed_ids() {
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(terminal.backend().buffer()[(2, 0)].fg, Color::DarkGray);
|
||||
assert_eq!(terminal.backend().buffer()[(3, 0)].fg, Color::DarkGray);
|
||||
editor
|
||||
.apply(vec![crate::editor::Effect::Replace("\"ok\"".into())])
|
||||
.unwrap();
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(terminal.backend().buffer()[(2, 0)].fg, Color::Green);
|
||||
assert_eq!(terminal.backend().buffer()[(3, 0)].fg, Color::Green);
|
||||
editor.execute(&format!("buffer {a_id}"));
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(terminal.backend().buffer()[(2, 0)].fg, Color::Yellow);
|
||||
assert_eq!(terminal.backend().buffer()[(3, 0)].fg, Color::Yellow);
|
||||
assert_eq!(viewports.syntax[&a_id].language(), Some("Rust"));
|
||||
assert_eq!(viewports.syntax[&b_id].language(), Some("Python"));
|
||||
editor.execute(&format!("buffer {b_id}"));
|
||||
@@ -11019,7 +11020,7 @@ fn syntax_caches_stay_independent_across_buffer_edits_and_retire_closed_ids() {
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(terminal.backend().buffer()[(2, 0)].fg, Color::Green);
|
||||
assert_eq!(terminal.backend().buffer()[(3, 0)].fg, Color::Green);
|
||||
assert!(!viewports.syntax.contains_key(&a_id));
|
||||
assert!(!viewports.offsets.contains_key(&a_id));
|
||||
assert_eq!(viewports.syntax.len(), 1);
|
||||
@@ -15217,7 +15218,7 @@ fn secondary_carets_render_on_empty_lines_and_at_line_end() {
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(48, 5)).unwrap();
|
||||
let (mut top, mut left) = (0, 0);
|
||||
let mut syntax = SyntaxCache::new();
|
||||
for (enabled, expected) in [(true, Color::Rgb(135, 215, 135)), (false, Color::Magenta)] {
|
||||
for (enabled, expected) in [(true, Color::Green), (false, Color::Magenta)] {
|
||||
editor.cursor_config.mode_colors = enabled;
|
||||
terminal
|
||||
.draw(|frame| {
|
||||
@@ -15597,84 +15598,118 @@ fn active_search_highlight_uses_display_cells_and_clips_with_horizontal_scroll()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cursor_appearance_tracks_editing_and_prompt_modes_without_replacing_text() {
|
||||
use crate::cursor::CursorShape;
|
||||
|
||||
fn software_cursor_tracks_mode_palette_and_preserves_document_text() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor
|
||||
.apply(vec![crate::editor::Effect::Replace("hello\n".into())])
|
||||
.unwrap();
|
||||
editor.cursor_config.insert = CursorShape::Bar;
|
||||
editor.cursor_config.visual = CursorShape::Underline;
|
||||
let mut terminal = Terminal::with_options(
|
||||
CrosstermBackend::new(Vec::new()),
|
||||
ratatui::TerminalOptions {
|
||||
viewport: ratatui::Viewport::Fixed(Rect::new(0, 0, 60, 12)),
|
||||
},
|
||||
)
|
||||
let mut terminal = Terminal::new(SoftwareCursorBackend(ratatui::backend::TestBackend::new(
|
||||
60, 12,
|
||||
)))
|
||||
.unwrap();
|
||||
let mut views = BufferViewports::default();
|
||||
for (inputs, mode, shape, color) in [
|
||||
(vec![], Mode::Normal, "\x1b[2 q", "#5fd7ff"),
|
||||
(vec![Input::Char('i')], Mode::Insert, "\x1b[6 q", "#87d787"),
|
||||
for (inputs, mode, expected) in [
|
||||
(vec![], Mode::Normal, Color::Cyan),
|
||||
(vec![Input::Char('i')], Mode::Insert, Color::Green),
|
||||
(
|
||||
vec![Input::Escape, Input::Char('v')],
|
||||
Mode::Visual,
|
||||
"\x1b[4 q",
|
||||
"#d787ff",
|
||||
Color::Magenta,
|
||||
),
|
||||
(
|
||||
vec![Input::Escape, Input::Char(':')],
|
||||
Mode::Command,
|
||||
"\x1b[2 q",
|
||||
"#ffd75f",
|
||||
Color::Yellow,
|
||||
),
|
||||
(
|
||||
vec![Input::Escape, Input::Char('/')],
|
||||
Mode::Search,
|
||||
"\x1b[2 q",
|
||||
"#ffaf5f",
|
||||
Color::Blue,
|
||||
),
|
||||
(vec![Input::Escape], Mode::Normal, "\x1b[2 q", "#5fd7ff"),
|
||||
(vec![Input::Escape], Mode::Normal, Color::Cyan),
|
||||
] {
|
||||
for input in inputs {
|
||||
editor.handle(input);
|
||||
}
|
||||
assert_eq!(editor.mode(), mode);
|
||||
let appearance = CursorAppearance::for_editor(&editor);
|
||||
let mut output = Vec::new();
|
||||
draw_frame(
|
||||
&mut terminal,
|
||||
&mut output,
|
||||
false,
|
||||
Some(appearance),
|
||||
|frame| views.draw(frame, &mut editor),
|
||||
)
|
||||
.unwrap();
|
||||
let output = String::from_utf8(output).unwrap();
|
||||
assert!(output.contains(shape), "{mode}: {output:?}");
|
||||
assert!(
|
||||
output.contains(&format!("\x1b]12;{color}\x1b\\")),
|
||||
"{mode}: {output:?}"
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
let position = if matches!(mode, Mode::Command | Mode::Search) {
|
||||
let panel = views.panels.input.unwrap();
|
||||
Position::new(panel.x + 2, panel.y + 1)
|
||||
} else {
|
||||
views.panels.caret.unwrap().1
|
||||
};
|
||||
assert_eq!(
|
||||
terminal.backend().0.buffer()[position].bg,
|
||||
expected,
|
||||
"{mode}"
|
||||
);
|
||||
assert!(
|
||||
!terminal.backend().0.cursor_visible(),
|
||||
"hardware cursor must stay hidden"
|
||||
);
|
||||
assert!(output.find(shape).unwrap() < output.find("\x1b[?25h").unwrap());
|
||||
assert_eq!(editor.lines(), &["hello", ""]);
|
||||
}
|
||||
editor.cursor_config.mode_colors = false;
|
||||
editor.handle(Input::Char('i'));
|
||||
let mut output = Vec::new();
|
||||
draw_frame(
|
||||
&mut terminal,
|
||||
&mut output,
|
||||
false,
|
||||
Some(CursorAppearance::for_editor(&editor)),
|
||||
|frame| views.draw(frame, &mut editor),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn software_cursor_shapes_preserve_wide_text_and_restore_cells_after_motion() {
|
||||
use crate::cursor::CursorShape;
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor
|
||||
.apply(vec![crate::editor::Effect::Replace("界e\u{301}\n".into())])
|
||||
.unwrap();
|
||||
let mut terminal = Terminal::new(SoftwareCursorBackend(ratatui::backend::TestBackend::new(
|
||||
40, 8,
|
||||
)))
|
||||
.unwrap();
|
||||
let output = String::from_utf8(output).unwrap();
|
||||
assert!(output.contains("\x1b[6 q"));
|
||||
assert!(output.contains("\x1b]112\x1b\\"));
|
||||
assert!(!output.contains("\x1b]12;"));
|
||||
let mut views = BufferViewports::default();
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
let position = views.panels.cursor.unwrap();
|
||||
let buffer = terminal.backend().0.buffer();
|
||||
assert_eq!(buffer[position].symbol(), "界");
|
||||
assert_eq!(buffer[position].bg, Color::Cyan);
|
||||
|
||||
editor.cursor_config.normal = CursorShape::Bar;
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(terminal.backend().0.buffer()[position].symbol(), "▏");
|
||||
assert_eq!(terminal.backend().0.buffer()[position].fg, Color::Cyan);
|
||||
editor.handle(Input::Right);
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
assert_eq!(terminal.backend().0.buffer()[position].symbol(), "界");
|
||||
let next = views.panels.cursor.unwrap();
|
||||
assert_eq!(next.x, position.x + 2);
|
||||
assert_eq!(terminal.backend().0.buffer()[next].symbol(), "▏");
|
||||
|
||||
editor.cursor_config.normal = CursorShape::Underline;
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().0.buffer();
|
||||
assert_eq!(buffer[next].symbol(), "e\u{301}");
|
||||
assert!(buffer[next].modifier.contains(Modifier::UNDERLINED));
|
||||
assert_eq!(buffer[next].underline_color, Color::Cyan);
|
||||
assert_eq!(buffer[next].fg, Color::Cyan);
|
||||
|
||||
editor.cursor_config.normal = CursorShape::Block;
|
||||
editor.cursor_config.mode_colors = false;
|
||||
terminal
|
||||
.draw(|frame| views.draw(frame, &mut editor))
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().0.buffer();
|
||||
assert_eq!(buffer[next].symbol(), "e\u{301}");
|
||||
assert!(buffer[next].modifier.contains(Modifier::REVERSED));
|
||||
assert_eq!(buffer[next].bg, Color::Reset);
|
||||
assert_eq!(editor.lines(), &["界e\u{301}", ""]);
|
||||
assert!(!terminal.backend().0.cursor_visible());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -15708,14 +15743,14 @@ fn terminal_can_present_previous_frame_while_next_frame_is_computed() {
|
||||
synchronized: Rc::clone(&synchronized),
|
||||
};
|
||||
let mut terminal = Terminal::with_options(
|
||||
CrosstermBackend::new(Vec::new()),
|
||||
SoftwareCursorBackend(CrosstermBackend::new(Vec::new())),
|
||||
ratatui::TerminalOptions {
|
||||
viewport: ratatui::Viewport::Fixed(Rect::new(0, 0, 20, 5)),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
for text in ["first frame", "second frame", "third frame"] {
|
||||
draw_frame(&mut terminal, &mut output, false, None, |frame| {
|
||||
draw_frame(&mut terminal, &mut output, false, |frame| {
|
||||
assert!(
|
||||
!synchronized.get(),
|
||||
"terminal presentation suspended during frame computation"
|
||||
@@ -15737,7 +15772,7 @@ fn scrolling_never_paints_with_an_exposed_cursor_even_without_synchronized_updat
|
||||
.unwrap();
|
||||
let mut output = Vec::new();
|
||||
let mut terminal = Terminal::with_options(
|
||||
CrosstermBackend::new(Vec::new()),
|
||||
SoftwareCursorBackend(CrosstermBackend::new(Vec::new())),
|
||||
ratatui::TerminalOptions {
|
||||
viewport: ratatui::Viewport::Fixed(Rect::new(0, 0, 20, 5)),
|
||||
},
|
||||
@@ -15746,7 +15781,7 @@ fn scrolling_never_paints_with_an_exposed_cursor_even_without_synchronized_updat
|
||||
let mut viewports = BufferViewports::default();
|
||||
for key in std::iter::repeat_n('j', 20).chain(std::iter::repeat_n('k', 20)) {
|
||||
editor.handle(Input::Char(key));
|
||||
draw_frame(&mut terminal, &mut output, false, None, |frame| {
|
||||
draw_frame(&mut terminal, &mut output, false, |frame| {
|
||||
viewports.draw(frame, &mut editor)
|
||||
})
|
||||
.unwrap();
|
||||
@@ -15788,7 +15823,10 @@ fn scrolling_never_paints_with_an_exposed_cursor_even_without_synchronized_updat
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
assert!(visible);
|
||||
assert!(
|
||||
!visible,
|
||||
"hardware cursor must stay hidden behind the software cursor"
|
||||
);
|
||||
assert!(!synchronized);
|
||||
for (row, expected) in [(0, "row 0"), (1, "row 1"), (2, "row 2")] {
|
||||
let line = std::str::from_utf8(&screen[row]).unwrap().trim_end();
|
||||
@@ -15821,12 +15859,12 @@ fn terminal_session_scopes_the_callers_panic_hook() {
|
||||
"error" => Err(io::Error::from(io::ErrorKind::BrokenPipe)),
|
||||
"panic" => {
|
||||
let mut terminal = Terminal::with_options(
|
||||
CrosstermBackend::new(Vec::new()),
|
||||
SoftwareCursorBackend(CrosstermBackend::new(Vec::new())),
|
||||
ratatui::TerminalOptions {
|
||||
viewport: ratatui::Viewport::Fixed(Rect::new(0, 0, 20, 5)),
|
||||
},
|
||||
)?;
|
||||
draw_frame(&mut terminal, &mut io::stdout(), false, None, |_| {
|
||||
draw_frame(&mut terminal, &mut io::stdout(), false, |_| {
|
||||
panic::panic_any(73_u64)
|
||||
})
|
||||
}
|
||||
@@ -15953,13 +15991,6 @@ fn terminal_session_scopes_the_callers_panic_hook() {
|
||||
"{mode}: {before:?}"
|
||||
);
|
||||
}
|
||||
for sequence in ["\x1b[0 q", "\x1b]112\x1b\\"] {
|
||||
assert_eq!(
|
||||
before.matches(sequence).count(),
|
||||
entered,
|
||||
"{mode}: {before:?}"
|
||||
);
|
||||
}
|
||||
for protocol in [1000, 1002, 1003, 1015, 1006] {
|
||||
assert_eq!(
|
||||
before.matches(&format!("\x1b[?{protocol}h")).count(),
|
||||
@@ -16046,6 +16077,8 @@ end
|
||||
}
|
||||
}
|
||||
|
||||
// Document-layer checks exclude the final primary-cursor overlay. Complete-frame
|
||||
// cursor and banner checks render through BufferViewports instead.
|
||||
#[cfg(test)]
|
||||
fn draw(
|
||||
frame: &mut Frame,
|
||||
@@ -16257,3 +16290,85 @@ fn shared_shell_status_is_a_separate_segment_left_of_the_battery() {
|
||||
assert!(!top.contains("sle..."));
|
||||
assert!(top.trim_end().ends_with("bat 27%"), "{top}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn both_banners_follow_mode_with_neutral_gaps_even_when_cursor_colors_are_disabled() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor.execute("tab.new");
|
||||
editor.cursor_config.mode_colors = false;
|
||||
configure_chrome(&mut editor, |top, bottom| {
|
||||
top.left = vec!["tabs".into()];
|
||||
top.center = vec!["path".into()];
|
||||
top.right = vec!["badge".into()];
|
||||
for name in ["tabs", "badge", "path"] {
|
||||
top.segments.insert(
|
||||
name.into(),
|
||||
SegmentConfig {
|
||||
title: match name {
|
||||
"tabs" => "{index}".into(),
|
||||
"path" => "N".into(),
|
||||
_ => String::new(),
|
||||
},
|
||||
padding: usize::from(name != "path"),
|
||||
margin: [1; 2],
|
||||
leading_edge: if name == "path" { "" } else { "<" }.into(),
|
||||
trailing_edge: if name == "path" { "" } else { ">" }.into(),
|
||||
first_leading_edge: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
*bottom = top.clone();
|
||||
});
|
||||
editor.set_label("badge".into(), "B".into()).unwrap();
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(60, 6)).unwrap();
|
||||
let mut syntax = SyntaxCache::new();
|
||||
for (input, mode, accent, foreground) in [
|
||||
(None, Mode::Normal, Color::Cyan, Color::Black),
|
||||
(Some('i'), Mode::Insert, Color::Green, Color::Black),
|
||||
(Some('v'), Mode::Visual, Color::Magenta, Color::Black),
|
||||
(Some(':'), Mode::Command, Color::Yellow, Color::Black),
|
||||
(Some('/'), Mode::Search, Color::Blue, Color::White),
|
||||
(None, Mode::Normal, Color::Cyan, Color::Black),
|
||||
] {
|
||||
editor.handle(Input::Escape);
|
||||
if let Some(ch) = input {
|
||||
editor.handle(Input::Char(ch));
|
||||
}
|
||||
assert_eq!(editor.mode(), mode);
|
||||
terminal
|
||||
.draw(|frame| {
|
||||
let area = frame.area();
|
||||
assert_eq!(
|
||||
draw_bars(frame, &editor, area, &mut syntax),
|
||||
Rect::new(0, 1, 60, 4)
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
for y in [0, 5] {
|
||||
for (x, symbol) in [(9, "2"), (56, "B")] {
|
||||
assert_eq!(buffer[(x, y)].symbol(), symbol, "{mode:?}");
|
||||
}
|
||||
for x in [8, 9, 10, 55, 56, 57] {
|
||||
assert_eq!(buffer[(x, y)].bg, accent, "{mode:?} at {x},{y}");
|
||||
assert_eq!(buffer[(x, y)].fg, foreground, "{mode:?} at {x},{y}");
|
||||
}
|
||||
for (x, symbol) in [(7, "<"), (11, ">"), (54, "<"), (58, ">")] {
|
||||
assert_eq!(buffer[(x, y)].symbol(), symbol);
|
||||
assert_eq!(buffer[(x, y)].fg, accent, "{mode:?} at {x},{y}");
|
||||
assert_eq!(buffer[(x, y)].bg, Color::Reset);
|
||||
}
|
||||
assert_eq!(buffer[(2, y)].symbol(), "1");
|
||||
assert_eq!(buffer[(2, y)].fg, Color::Gray);
|
||||
assert_eq!(buffer[(2, y)].bg, Color::Reset);
|
||||
assert_eq!(buffer[(29, y)].symbol(), "N");
|
||||
assert_eq!(buffer[(29, y)].fg, Color::Reset);
|
||||
assert_eq!(buffer[(29, y)].bg, Color::Reset);
|
||||
for x in [0, 4, 5, 6, 12, 13, 28, 30, 52, 53, 59] {
|
||||
assert_eq!(buffer[(x, y)].symbol(), " ");
|
||||
assert_eq!(buffer[(x, y)].fg, Color::Reset, "{mode:?} at {x},{y}");
|
||||
assert_eq!(buffer[(x, y)].bg, Color::Reset, "{mode:?} at {x},{y}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user