#[allow(unused)]
use super::super::*;
#[test]
fn test_empty_buffer_has_one_line() {
let editor = Editor::new("test", vec![]);
assert_eq!(editor.get_buffer().line_count(), 1);
assert_eq!(editor.get_buffer().get_line(0), Some("".to_string()));
}
#[test]
fn test_empty_buffer_cursor_starts_at_origin() {
let editor = Editor::new("test", vec![]);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 0));
}
#[test]
fn test_empty_buffer_backspace_does_nothing() {
let mut editor = Editor::new("test", vec![]);
editor.backspace();
assert_eq!(editor.get_buffer().line_count(), 1);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 0));
}
#[test]
fn test_empty_buffer_delete_does_nothing() {
let mut editor = Editor::new("test", vec![]);
editor.delete();
assert_eq!(editor.get_buffer().line_count(), 1);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 0));
}
#[test]
fn test_empty_buffer_movement_stays_at_origin() {
let mut editor = Editor::new("test", vec![]);
editor.move_up(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 0));
editor.move_down(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 0));
editor.move_left(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 0));
editor.move_right(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 0));
}
#[test]
fn test_empty_buffer_insert_character() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('A');
assert_eq!(editor.get_buffer().line_count(), 1);
assert_eq!(editor.get_buffer().get_line(0), Some("A".to_string()));
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 1));
}
#[test]
fn test_empty_buffer_select_all() {
let mut editor = Editor::new("test", vec![]);
editor.select_all();
assert_eq!(editor.get_selected_text(), "");
}
#[test]
fn test_single_char_delete_at_end_does_nothing() {
let mut editor = Editor::new("test", vec!["X".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 1));
editor.delete();
assert_eq!(editor.get_buffer().get_line(0), Some("X".to_string()));
}
#[test]
fn test_single_char_backspace_deletes_character() {
let mut editor = Editor::new("test", vec!["X".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 1));
editor.backspace();
assert_eq!(editor.get_buffer().get_line(0), Some("".to_string()));
}
#[test]
fn test_chinese_characters() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('ä½ ');
editor.insert_char('儽');
assert_eq!(editor.get_buffer().get_line(0), Some("ä½ å„½".to_string()));
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 2));
}
#[test]
fn test_arabic_text() {
let mut editor = Editor::new("test", vec![]);
let arabic = "Ł
Ų±ŲŲØŲ§";
for ch in arabic.chars() {
editor.insert_char(ch);
}
assert_eq!(editor.get_buffer().get_line(0), Some(arabic.to_string()));
}
#[test]
fn test_cursor_at_start_of_line() {
let mut editor = Editor::new("test", vec!["Hello".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 0));
editor.move_left(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 0));
editor.backspace();
assert_eq!(editor.get_buffer().get_line(0), Some("Hello".to_string()));
}
#[test]
fn test_cursor_at_end_of_line() {
let mut editor = Editor::new("test", vec!["Hello".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 5));
editor.move_right(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 5));
editor.delete();
assert_eq!(editor.get_buffer().get_line(0), Some("Hello".to_string()));
}
#[test]
fn test_cursor_wrapping_to_next_line() {
let mut editor = Editor::new("test", vec!["First".to_string(), "Second".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 5));
editor.move_right(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(1, 0));
}
#[test]
fn test_cursor_wrapping_to_previous_line() {
let mut editor = Editor::new("test", vec!["First".to_string(), "Second".to_string()]);
editor.set_cursor_position(CursorPosition::new(1, 0));
editor.move_left(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 5));
}
#[test]
fn test_select_single_character() {
let mut editor = Editor::new("test", vec!["Hello".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 0));
editor.move_right(true);
assert_eq!(editor.get_selected_text(), "H");
}
#[test]
fn test_select_word() {
let mut editor = Editor::new("test", vec!["Hello World".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 0));
for _ in 0..5 {
editor.move_right(true);
}
assert_eq!(editor.get_selected_text(), "Hello");
}
#[test]
fn test_select_across_lines() {
let mut editor = Editor::new("test", vec!["First".to_string(), "Second".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 3)); editor.move_down(true); editor.move_right(true); editor.move_right(true);
assert_eq!(editor.get_selected_text(), "st\nSecon");
}
#[test]
fn test_selection_replacement() {
let mut editor = Editor::new("test", vec!["Hello World".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 0));
for _ in 0..5 {
editor.move_right(true);
}
editor.insert_char('G');
assert_eq!(editor.get_buffer().get_line(0), Some("G World".to_string()));
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 1));
}
#[test]
fn test_select_emoji() {
let mut editor = Editor::new("test", vec!["Hello š World".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 6)); editor.move_right(true);
assert_eq!(editor.get_selected_text(), "š");
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 7));
editor.move_right(true); assert_eq!(editor.get_selected_text(), "š ");
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 8));
}
#[test]
fn test_goal_column_maintains_on_shorter_line() {
let mut editor = Editor::new(
"test",
vec![
"Long line here".to_string(),
"Short".to_string(),
"Another long line".to_string(),
],
);
editor.set_cursor_position(CursorPosition::new(0, 10));
editor.move_down(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(1, 5));
editor.move_down(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(2, 10)); }
#[test]
fn test_goal_column_resets_on_horizontal_movement() {
let mut editor = Editor::new(
"test",
vec!["Long line here".to_string(), "Short".to_string()],
);
editor.set_cursor_position(CursorPosition::new(0, 10));
editor.move_down(false);
editor.move_left(false); editor.move_up(false);
assert_eq!(editor.cursor_position().col, 4); }
#[test]
fn test_backspace_at_line_start_merges_lines() {
let mut editor = Editor::new("test", vec!["First".to_string(), "Second".to_string()]);
editor.set_cursor_position(CursorPosition::new(1, 0));
editor.backspace();
assert_eq!(editor.get_buffer().line_count(), 1);
assert_eq!(
editor.get_buffer().get_line(0),
Some("FirstSecond".to_string())
);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 5));
}
#[test]
fn test_delete_at_line_end_merges_lines() {
let mut editor = Editor::new("test", vec!["First".to_string(), "Second".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 5));
editor.delete();
assert_eq!(editor.get_buffer().line_count(), 1);
assert_eq!(
editor.get_buffer().get_line(0),
Some("FirstSecond".to_string())
);
}
#[test]
fn test_insert_newline_splits_line() {
let mut editor = Editor::new("test", vec!["HelloWorld".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 5));
editor.insert_newline();
assert_eq!(editor.get_buffer().line_count(), 2);
assert_eq!(editor.get_buffer().get_line(0), Some("Hello".to_string()));
assert_eq!(editor.get_buffer().get_line(1), Some("World".to_string()));
}
#[test]
fn test_very_long_line() {
let long_line = "x".repeat(10000);
let mut editor = Editor::new("test", vec![long_line.clone()]);
editor.set_cursor_position(CursorPosition::new(0, 10000));
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 10000));
editor.insert_char('!');
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 10001));
}
#[test]
fn test_tab_character_handling() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('\t');
assert_eq!(editor.get_buffer().get_line(0), Some("\t".to_string()));
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 1));
}
#[test]
fn test_windows_line_endings() {
let editor = Editor::new("test", vec!["Line1\r\n".to_string(), "Line2".to_string()]);
assert_eq!(editor.get_buffer().line_count(), 3);
assert_eq!(editor.get_buffer().get_line(0), Some("Line1\r".to_string()));
assert_eq!(editor.get_buffer().get_line(1), Some("".to_string()));
assert_eq!(editor.get_buffer().get_line(2), Some("Line2".to_string()));
}
#[test]
fn test_undo_after_emoji_insertion() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('A');
editor.insert_char('š');
editor.insert_char('B');
}
#[test]
fn test_zero_width_characters() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('a');
editor.insert_char('\u{200B}'); editor.insert_char('b');
let line = editor.get_buffer().get_line(0).unwrap();
assert_eq!(line, "a\u{200B}b");
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 3));
}
#[test]
fn test_surrogate_pairs() {
let mut editor = Editor::new("test", vec![]);
let surrogate = "š";
for ch in surrogate.chars() {
editor.insert_char(ch);
}
assert_eq!(editor.get_buffer().get_line(0), Some(surrogate.to_string()));
}
#[test]
fn test_right_to_left_text() {
let mut editor = Editor::new("test", vec![]);
let hebrew = "ש×××";
for ch in hebrew.chars() {
editor.insert_char(ch);
}
assert_eq!(editor.get_buffer().get_line(0), Some(hebrew.to_string()));
}
#[gpui::test]
async fn test_cursor_position_after_paste(cx: &mut gpui::TestAppContext) {
use gpui::ClipboardItem;
let mut editor = Editor::new("test", vec!["Hello".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 5));
cx.write_to_clipboard(ClipboardItem::new_string(" World".to_string()));
let clipboard_content = cx.read_from_clipboard();
if let Some(item) = clipboard_content {
if let Some(text) = item.text() {
for ch in text.chars() {
editor.insert_char(ch);
}
}
}
assert_eq!(
editor.get_buffer().get_line(0),
Some("Hello World".to_string())
);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 11));
}
#[gpui::test]
async fn test_copy_and_paste_selection(cx: &mut gpui::TestAppContext) {
use gpui::ClipboardItem;
let mut editor = Editor::new("test", vec!["Hello World".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 6));
for _ in 0..5 {
editor.move_right(true);
}
let selected_text = editor.get_selected_text();
cx.write_to_clipboard(ClipboardItem::new_string(selected_text));
editor.clear_selection();
editor.set_cursor_position(CursorPosition::new(0, 0));
let clipboard_content = cx.read_from_clipboard();
if let Some(item) = clipboard_content {
if let Some(text) = item.text() {
for ch in text.chars() {
editor.insert_char(ch);
}
}
}
assert_eq!(
editor.get_buffer().get_line(0),
Some("WorldHello World".to_string())
);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 5));
}
#[gpui::test]
async fn test_cut_and_paste(cx: &mut gpui::TestAppContext) {
use gpui::ClipboardItem;
let mut editor = Editor::new("test", vec!["Hello World".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 0));
for _ in 0..6 {
editor.move_right(true);
}
let selected_text = editor.get_selected_text();
cx.write_to_clipboard(ClipboardItem::new_string(selected_text));
editor.delete_selection();
assert_eq!(editor.get_buffer().get_line(0), Some("World".to_string()));
editor.set_cursor_position(CursorPosition::new(0, 5));
let clipboard_content = cx.read_from_clipboard();
if let Some(item) = clipboard_content {
if let Some(text) = item.text() {
for ch in text.chars() {
editor.insert_char(ch);
}
}
}
assert_eq!(
editor.get_buffer().get_line(0),
Some("WorldHello ".to_string())
);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 11));
}
#[test]
fn test_selection_with_zero_width_joiner() {
let mut editor = Editor::new("test", vec![]);
let emoji = "šØāš»"; for ch in emoji.chars() {
editor.insert_char(ch);
}
let line = editor.get_buffer().get_line(0).unwrap();
assert_eq!(line, emoji);
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 3));
editor.select_all();
let selected = editor.get_selected_text();
assert_eq!(selected, emoji);
}
#[test]
fn test_syntax_highlighting_language_detection() {
let _editor = Editor::new("test.rs", vec!["fn main() {}".to_string()]);
}
#[test]
fn test_syntax_highlighting_state_after_edit() {
let mut editor = Editor::new("test.rs", vec!["fn main() {".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 11));
editor.insert_char('}');
}
#[test]
fn test_navigate_through_empty_lines() {
let mut editor = Editor::new("test", vec!["".to_string(), "".to_string(), "".to_string()]);
assert_eq!(editor.get_buffer().line_count(), 3);
editor.move_down(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(1, 0));
editor.move_down(false);
assert_eq!(editor.cursor_position(), CursorPosition::new(2, 0));
}
#[test]
fn test_delete_empty_line() {
let mut editor = Editor::new("test", vec!["".to_string(), "".to_string(), "".to_string()]);
editor.set_cursor_position(CursorPosition::new(1, 0));
editor.backspace();
assert_eq!(editor.get_buffer().line_count(), 2);
}
#[test]
fn test_cursor_clamping_to_valid_position() {
let mut editor = Editor::new("test", vec!["Hello".to_string()]);
editor.set_cursor_position(CursorPosition::new(100, 100));
let pos = editor.cursor_position();
assert_eq!(pos, CursorPosition::new(0, 5));
}
#[test]
fn test_consecutive_emoji_deletion() {
let mut editor = Editor::new("test", vec!["ššš".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 3));
editor.backspace();
let line = editor.get_buffer().get_line(0).unwrap();
assert_eq!(line, "šš");
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 2));
}
#[test]
fn test_mixed_width_characters() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('A');
editor.insert_char('ļ¼”'); editor.insert_char('š');
editor.insert_char('B');
let line = editor.get_buffer().get_line(0).unwrap();
assert_eq!(line, "Aļ¼”šB");
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 4));
}
#[test]
fn test_consecutive_newlines() {
let mut editor = Editor::new("test", vec![]);
editor.insert_newline();
editor.insert_newline();
editor.insert_newline();
assert_eq!(editor.get_buffer().line_count(), 4);
assert_eq!(editor.cursor_position(), CursorPosition::new(3, 0));
}
#[test]
fn test_rapid_insertions() {
let mut editor = Editor::new("test", vec![]);
for c in "The quick brown fox jumps over the lazy dog".chars() {
editor.insert_char(c);
}
let line = editor.get_buffer().get_line(0).unwrap();
assert_eq!(line, "The quick brown fox jumps over the lazy dog");
}
#[test]
fn test_alternating_insert_delete() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('A');
editor.insert_char('B');
editor.backspace();
editor.insert_char('C');
editor.insert_char('D');
editor.backspace();
assert_eq!(editor.get_buffer().get_line(0), Some("AC".to_string()));
}
#[test]
fn test_selection_past_end_of_line() {
let mut editor = Editor::new("test", vec!["Hello".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 0));
for _ in 0..10 {
editor.move_right(true);
}
let selected = editor.get_selected_text();
assert_eq!(selected, "Hello");
}
#[test]
fn test_delete_across_empty_lines() {
let mut editor = Editor::new(
"test",
vec![
"First".to_string(),
"".to_string(),
"".to_string(),
"Last".to_string(),
],
);
editor.set_cursor_position(CursorPosition::new(0, 5));
editor.delete();
editor.delete();
editor.delete();
assert_eq!(editor.get_buffer().line_count(), 1);
assert_eq!(
editor.get_buffer().get_line(0),
Some("FirstLast".to_string())
);
}
#[test]
fn test_invisible_characters() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('\u{00A0}'); editor.insert_char('\u{2000}'); editor.insert_char('\u{2001}'); editor.insert_char('\u{200B}'); editor.insert_char('\u{FEFF}');
let line = editor.get_buffer().get_line(0).unwrap();
assert_eq!(line.chars().count(), 5);
}
#[test]
fn test_grapheme_cluster_navigation() {
let mut editor = Editor::new("test", vec![]);
let clusters = "Ć©"; for ch in clusters.chars() {
editor.insert_char(ch);
}
editor.insert_char(' ');
let flag = "šŗšø"; for ch in flag.chars() {
editor.insert_char(ch);
}
let line = editor.get_buffer().get_line(0).unwrap();
assert!(!line.is_empty());
}
#[test]
fn test_selection_collapse_on_typing() {
let mut editor = Editor::new("test", vec!["Hello World".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 0));
for _ in 0..5 {
editor.move_right(true);
}
assert!(editor.has_selection());
editor.insert_char('X');
assert!(!editor.has_selection());
assert_eq!(editor.get_buffer().get_line(0), Some("X World".to_string()));
}
#[test]
fn test_backspace_multiple_graphemes() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('A');
editor.insert_char('šØ');
editor.insert_char('\u{200D}'); editor.insert_char('š»'); editor.insert_char('B');
editor.backspace();
let line = editor.get_buffer().get_line(0).unwrap();
assert!(line.contains('A'));
}
#[test]
fn test_cursor_at_grapheme_boundaries() {
let mut editor = Editor::new("test", vec!["AšØāš©āš¦B".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 0));
editor.move_right(false); let pos1 = editor.cursor_position().col;
editor.move_right(false); let pos2 = editor.cursor_position().col;
assert!(pos2 > pos1);
}
#[test]
fn test_line_wrapping_behavior() {
let very_long_word = "a".repeat(200);
let mut editor = Editor::new("test", vec![very_long_word.clone()]);
editor.set_cursor_position(CursorPosition::new(0, 100));
editor.insert_newline();
assert_eq!(editor.get_buffer().line_count(), 2);
assert_eq!(editor.get_buffer().get_line(0), Some("a".repeat(100)));
assert_eq!(editor.get_buffer().get_line(1), Some("a".repeat(100)));
}
#[test]
fn test_selection_with_mixed_line_endings() {
let mut editor = Editor::new("test", vec!["Line 1".to_string(), "Line 2".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 4));
editor.move_down(true);
editor.move_right(true);
editor.move_right(true);
let selected = editor.get_selected_text();
assert_eq!(selected, " 1\nLine 2");
}
#[test]
fn test_empty_selection() {
let mut editor = Editor::new("test", vec!["Hello".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 2)); editor.move_right(true); editor.move_left(true);
let selected = editor.get_selected_text();
assert_eq!(selected, "");
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 2));
}
#[test]
fn test_normalization_inconsistencies() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('Ć©'); editor.insert_char(' ');
editor.insert_char('e');
editor.insert_char('\u{0301}');
let line = editor.get_buffer().get_line(0).unwrap();
assert_eq!(line, "Ć© e\u{0301}");
let chars: Vec<char> = line.chars().collect();
assert_eq!(chars.len(), 4); assert_eq!(chars[0], 'Ć©'); assert_eq!(chars[2], 'e'); assert_eq!(chars[3], '\u{0301}'); }
#[test]
fn test_cursor_position_after_bulk_delete() {
let mut editor = Editor::new("test", vec!["1234567890".to_string()]);
editor.set_cursor_position(CursorPosition::new(0, 3));
for _ in 0..4 {
editor.move_right(true);
}
editor.delete();
let result = editor.get_buffer().get_line(0).unwrap();
assert_eq!(result, "123890");
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 3));
}
#[test]
fn test_control_characters() {
let mut editor = Editor::new("test", vec![]);
editor.insert_char('\x00'); editor.insert_char('\x01'); editor.insert_char('\x1F'); editor.insert_char('\x7F');
let line = editor.get_buffer().get_line(0).unwrap();
assert_eq!(line.len(), 4);
assert_eq!(line.chars().next(), Some('\x00'));
assert_eq!(line.chars().nth(1), Some('\x01'));
assert_eq!(line.chars().nth(2), Some('\x1F'));
assert_eq!(line.chars().nth(3), Some('\x7F'));
assert_eq!(editor.cursor_position(), CursorPosition::new(0, 4));
}
#[cfg(test)]
fn painted_colors(editor: &mut Editor, lines: &[String]) -> Vec<Vec<Option<gpui::Hsla>>> {
lines
.iter()
.enumerate()
.map(|(index, line)| {
let runs = editor.highlight_line(line, index, "Courier".into(), 14.0);
let painted: usize = runs.iter().map(|run| run.len).sum();
assert_eq!(
painted,
line.len(),
"line {index} ({line:?}) got runs covering {painted} bytes, but \
`shape_line` is given the {} bytes of the line itself",
line.len(),
);
runs.iter()
.flat_map(|run| std::iter::repeat_n(Some(run.color), run.len))
.collect()
})
.collect()
}
#[cfg(test)]
fn block_colors_per_line(text: &str, language: &str) -> Vec<Vec<Option<gpui::Hsla>>> {
let highlighter = SyntaxHighlighter::new();
let mut colors: Vec<Option<gpui::Hsla>> = vec![None; text.len()];
for (range, style) in highlighter.highlight_block(text, language) {
for slot in &mut colors[range] {
*slot = style.color;
}
}
let mut per_line = Vec::new();
let mut offset = 0;
for line in text.split('\n') {
per_line.push(colors[offset..offset + line.len()].to_vec());
offset += line.len() + 1; }
per_line
}
#[test]
fn test_line_after_block_comment_is_highlighted_as_code() {
let lines: Vec<String> = [
"const before = 1;",
"/* opening a block comment",
"still inside the comment */",
"function after() { return 2; }",
]
.iter()
.map(|line| line.to_string())
.collect();
let mut editor = Editor::new("test_block_comment", lines.clone());
editor.set_language("JavaScript".to_string());
let painted = painted_colors(&mut editor, &lines);
assert_eq!(
painted,
block_colors_per_line(&lines.join("\n"), "JavaScript"),
"painting line by line disagreed with the stateless block pass"
);
let comment_color = painted[1][0];
let code = &painted[3];
assert!(
code.iter().all(|color| *color != comment_color),
"the line after `*/` is still painted in the comment colour"
);
let distinct: std::collections::HashSet<String> =
code.iter().map(|color| format!("{color:?}")).collect();
assert!(
distinct.len() >= 2,
"the line after `*/` collapsed to {} colour(s); `function` and `after` \
should not share the plain-text colour",
distinct.len()
);
}
#[test]
fn test_line_comment_terminates_at_end_of_line() {
let lines: Vec<String> = [
"// a line comment",
"function after() { return 2; }",
"const tail = 3;",
]
.iter()
.map(|line| line.to_string())
.collect();
let mut editor = Editor::new("test_line_comment", lines.clone());
editor.set_language("JavaScript".to_string());
let painted = painted_colors(&mut editor, &lines);
assert_eq!(
painted,
block_colors_per_line(&lines.join("\n"), "JavaScript"),
"the `//` comment leaked past its newline"
);
let comment_color = painted[0][0];
assert!(
painted[1].iter().all(|color| *color != comment_color),
"the line after a `//` comment is still painted in the comment colour"
);
}
#[test]
fn test_unterminated_python_string_ends_at_its_newline() {
let lines: Vec<String> = ["x = 'unterminated", "y = 2", "z = 3"]
.iter()
.map(|line| line.to_string())
.collect();
let mut editor = Editor::new("test_unterminated_string", lines.clone());
editor.set_language("Python".to_string());
let painted = painted_colors(&mut editor, &lines);
assert_eq!(
painted,
block_colors_per_line(&lines.join("\n"), "Python"),
"the unterminated string ran on past its newline"
);
let string_color = painted[0][4]; assert!(
painted[1].iter().all(|color| *color != string_color),
"the line after an unterminated string is still painted as string"
);
}
#[test]
fn test_last_line_is_not_given_a_separator_it_does_not_have() {
let lines: Vec<String> = ["fn main() {}", "", "// trailing"]
.iter()
.map(|line| line.to_string())
.collect();
let mut editor = Editor::new("test_last_line", lines.clone());
editor.set_language("Rust".to_string());
let painted = painted_colors(&mut editor, &lines);
assert_eq!(
painted,
block_colors_per_line(&lines.join("\n"), "Rust"),
"the last line was parsed as if it had a separator"
);
let empty_line_runs = editor.highlight_line("", 1, "Courier".into(), 14.0);
assert_eq!(
empty_line_runs.len(),
1,
"an empty line must keep exactly one zero-length run"
);
assert_eq!(empty_line_runs[0].len, 0);
}