use std::cell::RefCell; use std::rc::Rc; use gpui::{ AppContext as _, Context, Entity, IntoElement, ParentElement as _, Render, SharedString, Styled as _, TestAppContext, Window, div, }; use gpui_native_core::command_palette::{Command, CommandPalette, CommandPaletteState}; use gpui_native_core::init; fn command(label: &'static str) -> Command { Command::new(SharedString::from(label), SharedString::from(label)) } struct Host { palette: Entity, renders: Rc>, } impl Render for Host { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { *self.renders.borrow_mut() += 1; div().size_full().child(self.palette.clone()) } } #[test] fn typing_with_the_palette_open_is_not_pathologically_slow() { let cx = TestAppContext::single(); cx.update(init); let (state, renders, window) = cx.update(|cx| { let renders = Rc::new(RefCell::new(0)); let state_cell: Rc>>> = Rc::new(RefCell::new(None)); let renders_for_host = renders.clone(); let cell = state_cell.clone(); let window = cx .open_window(gpui::WindowOptions::default(), |window, cx| { let matcher = |query: &str, commands: &[Command]| { let query = query.to_lowercase(); commands .iter() .filter(|command| command.label.to_lowercase().contains(&query)) .cloned() .collect() }; let state = cx.new(|cx| { CommandPaletteState::new( CommandPalette::new( "bench-palette", vec![ command("Open File"), command("Close Tab"), command("Save All"), command("Toggle Theme"), ], matcher, ), window, cx, ) }); let state_for_host = state.clone(); cell.borrow_mut().replace(state.clone()); cx.new(|_| Host { palette: state_for_host, renders: renders_for_host, }) }) .unwrap(); let state = state_cell.borrow().clone().expect("state captured"); (state, renders, window) }); let mut visual = gpui::VisualTestContext::from_window(window.into(), &cx); visual.update(|window, cx| { state.update(cx, |state, cx| state.open(window, cx)); window.draw(cx).clear(cx); }); // The open palette must sit idle on executor timers, never frame requests. let frames = visual.update(|window, cx| window.simulate_next_frame(cx)); assert_eq!( frames, 0, "an idle open palette must not request animation frames" ); for letter in "abcdefghijklmnopqrstuvwxyz".chars() { visual.simulate_keystrokes(&letter.to_string()); visual.update(|window, cx| { window.draw(cx).clear(cx); }); } let total_renders = *renders.borrow(); println!( "26 real keystrokes with the palette open: {total_renders} host renders ({:.2} per keystroke)", total_renders as f32 / 26. ); assert!( total_renders >= 26, "every keystroke must redraw the page; got {total_renders} renders" ); assert!( total_renders <= 26 * 3, "typing caused {total_renders} host renders; healthy filtering stays under 3 per keystroke" ); }