use crate::{ display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer, }; use futures::Future; use gpui::{ AnyWindowHandle, AppContext, ForegroundExecutor, Keystroke, ModelContext, View, ViewContext, }; use indoc::indoc; use language::{Buffer, BufferSnapshot}; use project::{FakeFs, Project}; use std::{ any::TypeId, ops::{Deref, DerefMut, Range}, }; use util::{ assert_set_eq, test::{generate_marked_text, marked_text_ranges}, }; // use super::build_editor_with_project; // pub struct EditorTestContext<'a> { // pub cx: &'a mut gpui::TestAppContext, // pub window: AnyWindowHandle, // pub editor: View, // } // impl<'a> EditorTestContext<'a> { // pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> { // let fs = FakeFs::new(cx.background()); // // fs.insert_file("/file", "".to_owned()).await; // fs.insert_tree( // "/root", // gpui::serde_json::json!({ // "file": "", // }), // ) // .await; // let project = Project::test(fs, ["/root".as_ref()], cx).await; // let buffer = project // .update(cx, |project, cx| { // project.open_local_buffer("/root/file", cx) // }) // .await // .unwrap(); // let window = cx.add_window(|cx| { // cx.focus_self(); // build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx) // }); // let editor = window.root(cx); // Self { // cx, // window: window.into(), // editor, // } // } // pub fn condition( // &self, // predicate: impl FnMut(&Editor, &AppContext) -> bool, // ) -> impl Future { // self.editor.condition(self.cx, predicate) // } // pub fn editor(&self, read: F) -> T // where // F: FnOnce(&Editor, &ViewContext) -> T, // { // self.editor.update(self.cx, read) // } // pub fn update_editor(&mut self, update: F) -> T // where // F: FnOnce(&mut Editor, &mut ViewContext) -> T, // { // self.editor.update(self.cx, update) // } // pub fn multibuffer(&self, read: F) -> T // where // F: FnOnce(&MultiBuffer, &AppContext) -> T, // { // self.editor(|editor, cx| read(editor.buffer().read(cx), cx)) // } // pub fn update_multibuffer(&mut self, update: F) -> T // where // F: FnOnce(&mut MultiBuffer, &mut ModelContext) -> T, // { // self.update_editor(|editor, cx| editor.buffer().update(cx, update)) // } // pub fn buffer_text(&self) -> String { // self.multibuffer(|buffer, cx| buffer.snapshot(cx).text()) // } // pub fn buffer(&self, read: F) -> T // where // F: FnOnce(&Buffer, &AppContext) -> T, // { // self.multibuffer(|multibuffer, cx| { // let buffer = multibuffer.as_singleton().unwrap().read(cx); // read(buffer, cx) // }) // } // pub fn update_buffer(&mut self, update: F) -> T // where // F: FnOnce(&mut Buffer, &mut ModelContext) -> T, // { // self.update_multibuffer(|multibuffer, cx| { // let buffer = multibuffer.as_singleton().unwrap(); // buffer.update(cx, update) // }) // } // pub fn buffer_snapshot(&self) -> BufferSnapshot { // self.buffer(|buffer, _| buffer.snapshot()) // } // pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle { // let keystroke_under_test_handle = // self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text)); // let keystroke = Keystroke::parse(keystroke_text).unwrap(); // self.cx.dispatch_keystroke(self.window, keystroke, false); // keystroke_under_test_handle // } // pub fn simulate_keystrokes( // &mut self, // keystroke_texts: [&str; COUNT], // ) -> ContextHandle { // let keystrokes_under_test_handle = // self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts)); // for keystroke_text in keystroke_texts.into_iter() { // self.simulate_keystroke(keystroke_text); // } // // it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete // // before returning. // // NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too // // quickly races with async actions. // if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() { // executor.run_until_parked(); // } else { // unreachable!(); // } // keystrokes_under_test_handle // } // pub fn ranges(&self, marked_text: &str) -> Vec> { // let (unmarked_text, ranges) = marked_text_ranges(marked_text, false); // assert_eq!(self.buffer_text(), unmarked_text); // ranges // } // pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint { // let ranges = self.ranges(marked_text); // let snapshot = self // .editor // .update(self.cx, |editor, cx| editor.snapshot(cx)); // ranges[0].start.to_display_point(&snapshot) // } // // Returns anchors for the current buffer using `«` and `»` // pub fn text_anchor_range(&self, marked_text: &str) -> Range { // let ranges = self.ranges(marked_text); // let snapshot = self.buffer_snapshot(); // snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end) // } // pub fn set_diff_base(&mut self, diff_base: Option<&str>) { // let diff_base = diff_base.map(String::from); // self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx)); // } // /// Change the editor's text and selections using a string containing // /// embedded range markers that represent the ranges and directions of // /// each selection. // /// // /// Returns a context handle so that assertion failures can print what // /// editor state was needed to cause the failure. // /// // /// See the `util::test::marked_text_ranges` function for more information. // pub fn set_state(&mut self, marked_text: &str) -> ContextHandle { // let state_context = self.add_assertion_context(format!( // "Initial Editor State: \"{}\"", // marked_text.escape_debug().to_string() // )); // let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); // self.editor.update(self.cx, |editor, cx| { // editor.set_text(unmarked_text, cx); // editor.change_selections(Some(Autoscroll::fit()), cx, |s| { // s.select_ranges(selection_ranges) // }) // }); // state_context // } // /// Only change the editor's selections // pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle { // let state_context = self.add_assertion_context(format!( // "Initial Editor State: \"{}\"", // marked_text.escape_debug().to_string() // )); // let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); // self.editor.update(self.cx, |editor, cx| { // assert_eq!(editor.text(cx), unmarked_text); // editor.change_selections(Some(Autoscroll::fit()), cx, |s| { // s.select_ranges(selection_ranges) // }) // }); // state_context // } // /// Make an assertion about the editor's text and the ranges and directions // /// of its selections using a string containing embedded range markers. // /// // /// See the `util::test::marked_text_ranges` function for more information. // #[track_caller] // pub fn assert_editor_state(&mut self, marked_text: &str) { // let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true); // let buffer_text = self.buffer_text(); // if buffer_text != unmarked_text { // panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}"); // } // self.assert_selections(expected_selections, marked_text.to_string()) // } // pub fn editor_state(&mut self) -> String { // generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true) // } // #[track_caller] // pub fn assert_editor_background_highlights(&mut self, marked_text: &str) { // let expected_ranges = self.ranges(marked_text); // let actual_ranges: Vec> = self.update_editor(|editor, cx| { // let snapshot = editor.snapshot(cx); // editor // .background_highlights // .get(&TypeId::of::()) // .map(|h| h.1.clone()) // .unwrap_or_default() // .into_iter() // .map(|range| range.to_offset(&snapshot.buffer_snapshot)) // .collect() // }); // assert_set_eq!(actual_ranges, expected_ranges); // } // #[track_caller] // pub fn assert_editor_text_highlights(&mut self, marked_text: &str) { // let expected_ranges = self.ranges(marked_text); // let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); // let actual_ranges: Vec> = snapshot // .text_highlight_ranges::() // .map(|ranges| ranges.as_ref().clone().1) // .unwrap_or_default() // .into_iter() // .map(|range| range.to_offset(&snapshot.buffer_snapshot)) // .collect(); // assert_set_eq!(actual_ranges, expected_ranges); // } // #[track_caller] // pub fn assert_editor_selections(&mut self, expected_selections: Vec>) { // let expected_marked_text = // generate_marked_text(&self.buffer_text(), &expected_selections, true); // self.assert_selections(expected_selections, expected_marked_text) // } // fn editor_selections(&self) -> Vec> { // self.editor // .read_with(self.cx, |editor, cx| editor.selections.all::(cx)) // .into_iter() // .map(|s| { // if s.reversed { // s.end..s.start // } else { // s.start..s.end // } // }) // .collect::>() // } // #[track_caller] // fn assert_selections( // &mut self, // expected_selections: Vec>, // expected_marked_text: String, // ) { // let actual_selections = self.editor_selections(); // let actual_marked_text = // generate_marked_text(&self.buffer_text(), &actual_selections, true); // if expected_selections != actual_selections { // panic!( // indoc! {" // {}Editor has unexpected selections. // Expected selections: // {} // Actual selections: // {} // "}, // self.assertion_context(), // expected_marked_text, // actual_marked_text, // ); // } // } // } impl<'a> Deref for EditorTestContext<'a> { type Target = gpui::TestAppContext; fn deref(&self) -> &Self::Target { self.cx } } impl<'a> DerefMut for EditorTestContext<'a> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.cx } }