diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 27d27a5947e30c1a60d290748cb0a7c459d7d615..f30f7337a1af10f21ad11e4203f401b548604372 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -3227,448 +3227,457 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 { (delta.pow(1.2) / 300.0).into() } -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::{ -// display_map::{BlockDisposition, BlockProperties}, -// editor_tests::{init_test, update_test_language_settings}, -// Editor, MultiBuffer, -// }; -// use gpui::TestAppContext; -// use language::language_settings; -// use log::info; -// use std::{num::NonZeroU32, sync::Arc}; -// use util::test::sample_text; - -// #[gpui::test] -// fn test_layout_line_numbers(cx: &mut TestAppContext) { -// init_test(cx, |_| {}); -// let editor = cx -// .add_window(|cx| { -// let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); -// Editor::new(EditorMode::Full, buffer, None, None, cx) -// }) -// .root(cx); -// let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); - -// let layouts = editor.update(cx, |editor, cx| { -// let snapshot = editor.snapshot(cx); -// element -// .layout_line_numbers( -// 0..6, -// &Default::default(), -// DisplayPoint::new(0, 0), -// false, -// &snapshot, -// cx, -// ) -// .0 -// }); -// assert_eq!(layouts.len(), 6); - -// let relative_rows = editor.update(cx, |editor, cx| { -// let snapshot = editor.snapshot(cx); -// element.calculate_relative_line_numbers(&snapshot, &(0..6), Some(3)) -// }); -// assert_eq!(relative_rows[&0], 3); -// assert_eq!(relative_rows[&1], 2); -// assert_eq!(relative_rows[&2], 1); -// // current line has no relative number -// assert_eq!(relative_rows[&4], 1); -// assert_eq!(relative_rows[&5], 2); - -// // works if cursor is before screen -// let relative_rows = editor.update(cx, |editor, cx| { -// let snapshot = editor.snapshot(cx); - -// element.calculate_relative_line_numbers(&snapshot, &(3..6), Some(1)) -// }); -// assert_eq!(relative_rows.len(), 3); -// assert_eq!(relative_rows[&3], 2); -// assert_eq!(relative_rows[&4], 3); -// assert_eq!(relative_rows[&5], 4); - -// // works if cursor is after screen -// let relative_rows = editor.update(cx, |editor, cx| { -// let snapshot = editor.snapshot(cx); - -// element.calculate_relative_line_numbers(&snapshot, &(0..3), Some(6)) -// }); -// assert_eq!(relative_rows.len(), 3); -// assert_eq!(relative_rows[&0], 5); -// assert_eq!(relative_rows[&1], 4); -// assert_eq!(relative_rows[&2], 3); -// } - -// #[gpui::test] -// async fn test_vim_visual_selections(cx: &mut TestAppContext) { -// init_test(cx, |_| {}); - -// let editor = cx -// .add_window(|cx| { -// let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx); -// Editor::new(EditorMode::Full, buffer, None, None, cx) -// }) -// .root(cx); -// let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); -// let (_, state) = editor.update(cx, |editor, cx| { -// editor.cursor_shape = CursorShape::Block; -// editor.change_selections(None, cx, |s| { -// s.select_ranges([ -// Point::new(0, 0)..Point::new(1, 0), -// Point::new(3, 2)..Point::new(3, 3), -// Point::new(5, 6)..Point::new(6, 0), -// ]); -// }); -// element.layout( -// SizeConstraint::new(point(500., 500.), point(500., 500.)), -// editor, -// cx, -// ) -// }); -// assert_eq!(state.selections.len(), 1); -// let local_selections = &state.selections[0].1; -// assert_eq!(local_selections.len(), 3); -// // moves cursor back one line -// assert_eq!(local_selections[0].head, DisplayPoint::new(0, 6)); -// assert_eq!( -// local_selections[0].range, -// DisplayPoint::new(0, 0)..DisplayPoint::new(1, 0) -// ); - -// // moves cursor back one column -// assert_eq!( -// local_selections[1].range, -// DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3) -// ); -// assert_eq!(local_selections[1].head, DisplayPoint::new(3, 2)); - -// // leaves cursor on the max point -// assert_eq!( -// local_selections[2].range, -// DisplayPoint::new(5, 6)..DisplayPoint::new(6, 0) -// ); -// assert_eq!(local_selections[2].head, DisplayPoint::new(6, 0)); - -// // active lines does not include 1 (even though the range of the selection does) -// assert_eq!( -// state.active_rows.keys().cloned().collect::>(), -// vec![0, 3, 5, 6] -// ); - -// // multi-buffer support -// // in DisplayPoint co-ordinates, this is what we're dealing with: -// // 0: [[file -// // 1: header]] -// // 2: aaaaaa -// // 3: bbbbbb -// // 4: cccccc -// // 5: -// // 6: ... -// // 7: ffffff -// // 8: gggggg -// // 9: hhhhhh -// // 10: -// // 11: [[file -// // 12: header]] -// // 13: bbbbbb -// // 14: cccccc -// // 15: dddddd -// let editor = cx -// .add_window(|cx| { -// let buffer = MultiBuffer::build_multi( -// [ -// ( -// &(sample_text(8, 6, 'a') + "\n"), -// vec![ -// Point::new(0, 0)..Point::new(3, 0), -// Point::new(4, 0)..Point::new(7, 0), -// ], -// ), -// ( -// &(sample_text(8, 6, 'a') + "\n"), -// vec![Point::new(1, 0)..Point::new(3, 0)], -// ), -// ], -// cx, -// ); -// Editor::new(EditorMode::Full, buffer, None, None, cx) -// }) -// .root(cx); -// let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); -// let (_, state) = editor.update(cx, |editor, cx| { -// editor.cursor_shape = CursorShape::Block; -// editor.change_selections(None, cx, |s| { -// s.select_display_ranges([ -// DisplayPoint::new(4, 0)..DisplayPoint::new(7, 0), -// DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0), -// ]); -// }); -// element.layout( -// SizeConstraint::new(point(500., 500.), point(500., 500.)), -// editor, -// cx, -// ) -// }); - -// assert_eq!(state.selections.len(), 1); -// let local_selections = &state.selections[0].1; -// assert_eq!(local_selections.len(), 2); - -// // moves cursor on excerpt boundary back a line -// // and doesn't allow selection to bleed through -// assert_eq!( -// local_selections[0].range, -// DisplayPoint::new(4, 0)..DisplayPoint::new(6, 0) -// ); -// assert_eq!(local_selections[0].head, DisplayPoint::new(5, 0)); - -// // moves cursor on buffer boundary back two lines -// // and doesn't allow selection to bleed through -// assert_eq!( -// local_selections[1].range, -// DisplayPoint::new(10, 0)..DisplayPoint::new(11, 0) -// ); -// assert_eq!(local_selections[1].head, DisplayPoint::new(10, 0)); -// } - -// #[gpui::test] -// fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) { -// init_test(cx, |_| {}); - -// let editor = cx -// .add_window(|cx| { -// let buffer = MultiBuffer::build_simple("", cx); -// Editor::new(EditorMode::Full, buffer, None, None, cx) -// }) -// .root(cx); - -// editor.update(cx, |editor, cx| { -// editor.set_placeholder_text("hello", cx); -// editor.insert_blocks( -// [BlockProperties { -// style: BlockStyle::Fixed, -// disposition: BlockDisposition::Above, -// height: 3, -// position: Anchor::min(), -// render: Arc::new(|_| Empty::new().into_any), -// }], -// None, -// cx, -// ); - -// // Blur the editor so that it displays placeholder text. -// cx.blur(); -// }); - -// let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); -// let (size, mut state) = editor.update(cx, |editor, cx| { -// element.layout( -// SizeConstraint::new(point(500., 500.), point(500., 500.)), -// editor, -// cx, -// ) -// }); - -// assert_eq!(state.position_map.line_layouts.len(), 4); -// assert_eq!( -// state -// .line_number_layouts -// .iter() -// .map(Option::is_some) -// .collect::>(), -// &[false, false, false, true] -// ); - -// // Don't panic. -// let bounds = Bounds::::new(Default::default(), size); -// editor.update(cx, |editor, cx| { -// element.paint(bounds, bounds, &mut state, editor, cx); -// }); -// } - -// #[gpui::test] -// fn test_all_invisibles_drawing(cx: &mut TestAppContext) { -// const TAB_SIZE: u32 = 4; - -// let input_text = "\t \t|\t| a b"; -// let expected_invisibles = vec![ -// Invisible::Tab { -// line_start_offset: 0, -// }, -// Invisible::Whitespace { -// line_offset: TAB_SIZE as usize, -// }, -// Invisible::Tab { -// line_start_offset: TAB_SIZE as usize + 1, -// }, -// Invisible::Tab { -// line_start_offset: TAB_SIZE as usize * 2 + 1, -// }, -// Invisible::Whitespace { -// line_offset: TAB_SIZE as usize * 3 + 1, -// }, -// Invisible::Whitespace { -// line_offset: TAB_SIZE as usize * 3 + 3, -// }, -// ]; -// assert_eq!( -// expected_invisibles.len(), -// input_text -// .chars() -// .filter(|initial_char| initial_char.is_whitespace()) -// .count(), -// "Hardcoded expected invisibles differ from the actual ones in '{input_text}'" -// ); - -// init_test(cx, |s| { -// s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); -// s.defaults.tab_size = NonZeroU32::new(TAB_SIZE); -// }); - -// let actual_invisibles = -// collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0); - -// assert_eq!(expected_invisibles, actual_invisibles); -// } - -// #[gpui::test] -// fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) { -// init_test(cx, |s| { -// s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); -// s.defaults.tab_size = NonZeroU32::new(4); -// }); - -// for editor_mode_without_invisibles in [ -// EditorMode::SingleLine, -// EditorMode::AutoHeight { max_lines: 100 }, -// ] { -// let invisibles = collect_invisibles_from_new_editor( -// cx, -// editor_mode_without_invisibles, -// "\t\t\t| | a b", -// 500.0, -// ); -// assert!(invisibles.is_empty, -// "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}"); -// } -// } - -// #[gpui::test] -// fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) { -// let tab_size = 4; -// let input_text = "a\tbcd ".repeat(9); -// let repeated_invisibles = [ -// Invisible::Tab { -// line_start_offset: 1, -// }, -// Invisible::Whitespace { -// line_offset: tab_size as usize + 3, -// }, -// Invisible::Whitespace { -// line_offset: tab_size as usize + 4, -// }, -// Invisible::Whitespace { -// line_offset: tab_size as usize + 5, -// }, -// ]; -// let expected_invisibles = std::iter::once(repeated_invisibles) -// .cycle() -// .take(9) -// .flatten() -// .collect::>(); -// assert_eq!( -// expected_invisibles.len(), -// input_text -// .chars() -// .filter(|initial_char| initial_char.is_whitespace()) -// .count(), -// "Hardcoded expected invisibles differ from the actual ones in '{input_text}'" -// ); -// info!("Expected invisibles: {expected_invisibles:?}"); - -// init_test(cx, |_| {}); - -// // Put the same string with repeating whitespace pattern into editors of various size, -// // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point. -// let resize_step = 10.0; -// let mut editor_width = 200.0; -// while editor_width <= 1000.0 { -// update_test_language_settings(cx, |s| { -// s.defaults.tab_size = NonZeroU32::new(tab_size); -// s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); -// s.defaults.preferred_line_length = Some(editor_width as u32); -// s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength); -// }); - -// let actual_invisibles = -// collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, editor_width); - -// // Whatever the editor size is, ensure it has the same invisible kinds in the same order -// // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets). -// let mut i = 0; -// for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() { -// i = actual_index; -// match expected_invisibles.get(i) { -// Some(expected_invisible) => match (expected_invisible, actual_invisible) { -// (Invisible::Whitespace { .. }, Invisible::Whitespace { .. }) -// | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {} -// _ => { -// panic!("At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}") -// } -// }, -// None => panic!("Unexpected extra invisible {actual_invisible:?} at index {i}"), -// } -// } -// let missing_expected_invisibles = &expected_invisibles[i + 1..]; -// assert!( -// missing_expected_invisibles.is_empty, -// "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}" -// ); - -// editor_width += resize_step; -// } -// } - -// fn collect_invisibles_from_new_editor( -// cx: &mut TestAppContext, -// editor_mode: EditorMode, -// input_text: &str, -// editor_width: f32, -// ) -> Vec { -// info!( -// "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'" -// ); -// let editor = cx -// .add_window(|cx| { -// let buffer = MultiBuffer::build_simple(&input_text, cx); -// Editor::new(editor_mode, buffer, None, None, cx) -// }) -// .root(cx); - -// let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); -// let (_, layout_state) = editor.update(cx, |editor, cx| { -// editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx); -// editor.set_wrap_width(Some(editor_width), cx); - -// element.layout( -// SizeConstraint::new(point(editor_width, 500.), point(editor_width, 500.)), -// editor, -// cx, -// ) -// }); - -// layout_state -// .position_map -// .line_layouts -// .iter() -// .map(|line_with_invisibles| &line_with_invisibles.invisibles) -// .flatten() -// .cloned() -// .collect() -// } -// } +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + display_map::{BlockDisposition, BlockProperties}, + editor_tests::{init_test, update_test_language_settings}, + Editor, MultiBuffer, + }; + use gpui::TestAppContext; + use language::language_settings; + use log::info; + use std::{num::NonZeroU32, sync::Arc}; + use util::test::sample_text; + + #[gpui::test] + fn test_shape_line_numbers(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + let window = cx.add_window(|cx| { + let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); + Editor::new(EditorMode::Full, buffer, None, cx) + }); + + let editor = window.root(cx).unwrap(); + let style = cx.update(|cx| editor.read(cx).style().unwrap().clone()); + let element = EditorElement::new(&editor, style); + + let layouts = window + .update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + element + .shape_line_numbers( + 0..6, + &Default::default(), + DisplayPoint::new(0, 0), + false, + &snapshot, + cx, + ) + .0 + }) + .unwrap(); + assert_eq!(layouts.len(), 6); + + let relative_rows = window + .update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + element.calculate_relative_line_numbers(&snapshot, &(0..6), Some(3)) + }) + .unwrap(); + assert_eq!(relative_rows[&0], 3); + assert_eq!(relative_rows[&1], 2); + assert_eq!(relative_rows[&2], 1); + // current line has no relative number + assert_eq!(relative_rows[&4], 1); + assert_eq!(relative_rows[&5], 2); + + // works if cursor is before screen + let relative_rows = window + .update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + + element.calculate_relative_line_numbers(&snapshot, &(3..6), Some(1)) + }) + .unwrap(); + assert_eq!(relative_rows.len(), 3); + assert_eq!(relative_rows[&3], 2); + assert_eq!(relative_rows[&4], 3); + assert_eq!(relative_rows[&5], 4); + + // works if cursor is after screen + let relative_rows = window + .update(cx, |editor, cx| { + let snapshot = editor.snapshot(cx); + + element.calculate_relative_line_numbers(&snapshot, &(0..3), Some(6)) + }) + .unwrap(); + assert_eq!(relative_rows.len(), 3); + assert_eq!(relative_rows[&0], 5); + assert_eq!(relative_rows[&1], 4); + assert_eq!(relative_rows[&2], 3); + } + + // #[gpui::test] + // async fn test_vim_visual_selections(cx: &mut TestAppContext) { + // init_test(cx, |_| {}); + + // let editor = cx + // .add_window(|cx| { + // let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx); + // Editor::new(EditorMode::Full, buffer, None, None, cx) + // }) + // .root(cx); + // let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); + // let (_, state) = editor.update(cx, |editor, cx| { + // editor.cursor_shape = CursorShape::Block; + // editor.change_selections(None, cx, |s| { + // s.select_ranges([ + // Point::new(0, 0)..Point::new(1, 0), + // Point::new(3, 2)..Point::new(3, 3), + // Point::new(5, 6)..Point::new(6, 0), + // ]); + // }); + // element.layout( + // SizeConstraint::new(point(500., 500.), point(500., 500.)), + // editor, + // cx, + // ) + // }); + // assert_eq!(state.selections.len(), 1); + // let local_selections = &state.selections[0].1; + // assert_eq!(local_selections.len(), 3); + // // moves cursor back one line + // assert_eq!(local_selections[0].head, DisplayPoint::new(0, 6)); + // assert_eq!( + // local_selections[0].range, + // DisplayPoint::new(0, 0)..DisplayPoint::new(1, 0) + // ); + + // // moves cursor back one column + // assert_eq!( + // local_selections[1].range, + // DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3) + // ); + // assert_eq!(local_selections[1].head, DisplayPoint::new(3, 2)); + + // // leaves cursor on the max point + // assert_eq!( + // local_selections[2].range, + // DisplayPoint::new(5, 6)..DisplayPoint::new(6, 0) + // ); + // assert_eq!(local_selections[2].head, DisplayPoint::new(6, 0)); + + // // active lines does not include 1 (even though the range of the selection does) + // assert_eq!( + // state.active_rows.keys().cloned().collect::>(), + // vec![0, 3, 5, 6] + // ); + + // // multi-buffer support + // // in DisplayPoint co-ordinates, this is what we're dealing with: + // // 0: [[file + // // 1: header]] + // // 2: aaaaaa + // // 3: bbbbbb + // // 4: cccccc + // // 5: + // // 6: ... + // // 7: ffffff + // // 8: gggggg + // // 9: hhhhhh + // // 10: + // // 11: [[file + // // 12: header]] + // // 13: bbbbbb + // // 14: cccccc + // // 15: dddddd + // let editor = cx + // .add_window(|cx| { + // let buffer = MultiBuffer::build_multi( + // [ + // ( + // &(sample_text(8, 6, 'a') + "\n"), + // vec![ + // Point::new(0, 0)..Point::new(3, 0), + // Point::new(4, 0)..Point::new(7, 0), + // ], + // ), + // ( + // &(sample_text(8, 6, 'a') + "\n"), + // vec![Point::new(1, 0)..Point::new(3, 0)], + // ), + // ], + // cx, + // ); + // Editor::new(EditorMode::Full, buffer, None, None, cx) + // }) + // .root(cx); + // let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); + // let (_, state) = editor.update(cx, |editor, cx| { + // editor.cursor_shape = CursorShape::Block; + // editor.change_selections(None, cx, |s| { + // s.select_display_ranges([ + // DisplayPoint::new(4, 0)..DisplayPoint::new(7, 0), + // DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0), + // ]); + // }); + // element.layout( + // SizeConstraint::new(point(500., 500.), point(500., 500.)), + // editor, + // cx, + // ) + // }); + + // assert_eq!(state.selections.len(), 1); + // let local_selections = &state.selections[0].1; + // assert_eq!(local_selections.len(), 2); + + // // moves cursor on excerpt boundary back a line + // // and doesn't allow selection to bleed through + // assert_eq!( + // local_selections[0].range, + // DisplayPoint::new(4, 0)..DisplayPoint::new(6, 0) + // ); + // assert_eq!(local_selections[0].head, DisplayPoint::new(5, 0)); + + // // moves cursor on buffer boundary back two lines + // // and doesn't allow selection to bleed through + // assert_eq!( + // local_selections[1].range, + // DisplayPoint::new(10, 0)..DisplayPoint::new(11, 0) + // ); + // assert_eq!(local_selections[1].head, DisplayPoint::new(10, 0)); + // } + + // #[gpui::test] + // fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) { + // init_test(cx, |_| {}); + + // let editor = cx + // .add_window(|cx| { + // let buffer = MultiBuffer::build_simple("", cx); + // Editor::new(EditorMode::Full, buffer, None, None, cx) + // }) + // .root(cx); + + // editor.update(cx, |editor, cx| { + // editor.set_placeholder_text("hello", cx); + // editor.insert_blocks( + // [BlockProperties { + // style: BlockStyle::Fixed, + // disposition: BlockDisposition::Above, + // height: 3, + // position: Anchor::min(), + // render: Arc::new(|_| Empty::new().into_any), + // }], + // None, + // cx, + // ); + + // // Blur the editor so that it displays placeholder text. + // cx.blur(); + // }); + + // let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); + // let (size, mut state) = editor.update(cx, |editor, cx| { + // element.layout( + // SizeConstraint::new(point(500., 500.), point(500., 500.)), + // editor, + // cx, + // ) + // }); + + // assert_eq!(state.position_map.line_layouts.len(), 4); + // assert_eq!( + // state + // .line_number_layouts + // .iter() + // .map(Option::is_some) + // .collect::>(), + // &[false, false, false, true] + // ); + + // // Don't panic. + // let bounds = Bounds::::new(Default::default(), size); + // editor.update(cx, |editor, cx| { + // element.paint(bounds, bounds, &mut state, editor, cx); + // }); + // } + + // #[gpui::test] + // fn test_all_invisibles_drawing(cx: &mut TestAppContext) { + // const TAB_SIZE: u32 = 4; + + // let input_text = "\t \t|\t| a b"; + // let expected_invisibles = vec![ + // Invisible::Tab { + // line_start_offset: 0, + // }, + // Invisible::Whitespace { + // line_offset: TAB_SIZE as usize, + // }, + // Invisible::Tab { + // line_start_offset: TAB_SIZE as usize + 1, + // }, + // Invisible::Tab { + // line_start_offset: TAB_SIZE as usize * 2 + 1, + // }, + // Invisible::Whitespace { + // line_offset: TAB_SIZE as usize * 3 + 1, + // }, + // Invisible::Whitespace { + // line_offset: TAB_SIZE as usize * 3 + 3, + // }, + // ]; + // assert_eq!( + // expected_invisibles.len(), + // input_text + // .chars() + // .filter(|initial_char| initial_char.is_whitespace()) + // .count(), + // "Hardcoded expected invisibles differ from the actual ones in '{input_text}'" + // ); + + // init_test(cx, |s| { + // s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); + // s.defaults.tab_size = NonZeroU32::new(TAB_SIZE); + // }); + + // let actual_invisibles = + // collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0); + + // assert_eq!(expected_invisibles, actual_invisibles); + // } + + // #[gpui::test] + // fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) { + // init_test(cx, |s| { + // s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); + // s.defaults.tab_size = NonZeroU32::new(4); + // }); + + // for editor_mode_without_invisibles in [ + // EditorMode::SingleLine, + // EditorMode::AutoHeight { max_lines: 100 }, + // ] { + // let invisibles = collect_invisibles_from_new_editor( + // cx, + // editor_mode_without_invisibles, + // "\t\t\t| | a b", + // 500.0, + // ); + // assert!(invisibles.is_empty, + // "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}"); + // } + // } + + // #[gpui::test] + // fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) { + // let tab_size = 4; + // let input_text = "a\tbcd ".repeat(9); + // let repeated_invisibles = [ + // Invisible::Tab { + // line_start_offset: 1, + // }, + // Invisible::Whitespace { + // line_offset: tab_size as usize + 3, + // }, + // Invisible::Whitespace { + // line_offset: tab_size as usize + 4, + // }, + // Invisible::Whitespace { + // line_offset: tab_size as usize + 5, + // }, + // ]; + // let expected_invisibles = std::iter::once(repeated_invisibles) + // .cycle() + // .take(9) + // .flatten() + // .collect::>(); + // assert_eq!( + // expected_invisibles.len(), + // input_text + // .chars() + // .filter(|initial_char| initial_char.is_whitespace()) + // .count(), + // "Hardcoded expected invisibles differ from the actual ones in '{input_text}'" + // ); + // info!("Expected invisibles: {expected_invisibles:?}"); + + // init_test(cx, |_| {}); + + // // Put the same string with repeating whitespace pattern into editors of various size, + // // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point. + // let resize_step = 10.0; + // let mut editor_width = 200.0; + // while editor_width <= 1000.0 { + // update_test_language_settings(cx, |s| { + // s.defaults.tab_size = NonZeroU32::new(tab_size); + // s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); + // s.defaults.preferred_line_length = Some(editor_width as u32); + // s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength); + // }); + + // let actual_invisibles = + // collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, editor_width); + + // // Whatever the editor size is, ensure it has the same invisible kinds in the same order + // // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets). + // let mut i = 0; + // for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() { + // i = actual_index; + // match expected_invisibles.get(i) { + // Some(expected_invisible) => match (expected_invisible, actual_invisible) { + // (Invisible::Whitespace { .. }, Invisible::Whitespace { .. }) + // | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {} + // _ => { + // panic!("At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}") + // } + // }, + // None => panic!("Unexpected extra invisible {actual_invisible:?} at index {i}"), + // } + // } + // let missing_expected_invisibles = &expected_invisibles[i + 1..]; + // assert!( + // missing_expected_invisibles.is_empty, + // "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}" + // ); + + // editor_width += resize_step; + // } + // } + + // fn collect_invisibles_from_new_editor( + // cx: &mut TestAppContext, + // editor_mode: EditorMode, + // input_text: &str, + // editor_width: f32, + // ) -> Vec { + // info!( + // "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'" + // ); + // let editor = cx + // .add_window(|cx| { + // let buffer = MultiBuffer::build_simple(&input_text, cx); + // Editor::new(editor_mode, buffer, None, None, cx) + // }) + // .root(cx); + + // let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); + // let (_, layout_state) = editor.update(cx, |editor, cx| { + // editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx); + // editor.set_wrap_width(Some(editor_width), cx); + + // element.layout( + // SizeConstraint::new(point(editor_width, 500.), point(editor_width, 500.)), + // editor, + // cx, + // ) + // }); + + // layout_state + // .position_map + // .line_layouts + // .iter() + // .map(|line_with_invisibles| &line_with_invisibles.invisibles) + // .flatten() + // .cloned() + // .collect() + // } +} pub fn register_action( view: &View,