Restore completion provider tests

Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
Agus Zubiaga 2025-08-13 11:16:03 -03:00
parent 6ed2155b9a
commit d26f80ebad
2 changed files with 370 additions and 380 deletions

View file

@ -180,7 +180,7 @@ pub struct MentionLink<'a>(&'a MentionUri);
impl fmt::Display for MentionLink<'_> { impl fmt::Display for MentionLink<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}]({})", self.0.name(), self.0.to_uri()) write!(f, "[@{}]({})", self.0.name(), self.0.to_uri())
} }
} }

View file

@ -1034,438 +1034,428 @@ impl MentionCompletion {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use super::*; use super::*;
// use editor::AnchorRangeExt; use editor::AnchorRangeExt;
// use gpui::{EventEmitter, FocusHandle, Focusable, TestAppContext, VisualTestContext}; use gpui::{EventEmitter, FocusHandle, Focusable, TestAppContext, VisualTestContext};
// use project::{Project, ProjectPath}; use project::{Project, ProjectPath};
// use serde_json::json; use serde_json::json;
// use settings::SettingsStore; use settings::SettingsStore;
// use std::{ops::Deref, rc::Rc}; use std::{ops::Deref, rc::Rc};
// use util::path; use util::path;
// use workspace::{AppState, Item}; use workspace::{AppState, Item};
// #[test] #[test]
// fn test_mention_completion_parse() { fn test_mention_completion_parse() {
// assert_eq!(MentionCompletion::try_parse("Lorem Ipsum", 0), None); assert_eq!(MentionCompletion::try_parse("Lorem Ipsum", 0), None);
// assert_eq!( assert_eq!(
// MentionCompletion::try_parse("Lorem @", 0), MentionCompletion::try_parse("Lorem @", 0),
// Some(MentionCompletion { Some(MentionCompletion {
// source_range: 6..7, source_range: 6..7,
// mode: None, mode: None,
// argument: None, argument: None,
// }) })
// ); );
// assert_eq!( assert_eq!(
// MentionCompletion::try_parse("Lorem @file", 0), MentionCompletion::try_parse("Lorem @file", 0),
// Some(MentionCompletion { Some(MentionCompletion {
// source_range: 6..11, source_range: 6..11,
// mode: Some(ContextPickerMode::File), mode: Some(ContextPickerMode::File),
// argument: None, argument: None,
// }) })
// ); );
// assert_eq!( assert_eq!(
// MentionCompletion::try_parse("Lorem @file ", 0), MentionCompletion::try_parse("Lorem @file ", 0),
// Some(MentionCompletion { Some(MentionCompletion {
// source_range: 6..12, source_range: 6..12,
// mode: Some(ContextPickerMode::File), mode: Some(ContextPickerMode::File),
// argument: None, argument: None,
// }) })
// ); );
// assert_eq!( assert_eq!(
// MentionCompletion::try_parse("Lorem @file main.rs", 0), MentionCompletion::try_parse("Lorem @file main.rs", 0),
// Some(MentionCompletion { Some(MentionCompletion {
// source_range: 6..19, source_range: 6..19,
// mode: Some(ContextPickerMode::File), mode: Some(ContextPickerMode::File),
// argument: Some("main.rs".to_string()), argument: Some("main.rs".to_string()),
// }) })
// ); );
// assert_eq!( assert_eq!(
// MentionCompletion::try_parse("Lorem @file main.rs ", 0), MentionCompletion::try_parse("Lorem @file main.rs ", 0),
// Some(MentionCompletion { Some(MentionCompletion {
// source_range: 6..19, source_range: 6..19,
// mode: Some(ContextPickerMode::File), mode: Some(ContextPickerMode::File),
// argument: Some("main.rs".to_string()), argument: Some("main.rs".to_string()),
// }) })
// ); );
// assert_eq!( assert_eq!(
// MentionCompletion::try_parse("Lorem @file main.rs Ipsum", 0), MentionCompletion::try_parse("Lorem @file main.rs Ipsum", 0),
// Some(MentionCompletion { Some(MentionCompletion {
// source_range: 6..19, source_range: 6..19,
// mode: Some(ContextPickerMode::File), mode: Some(ContextPickerMode::File),
// argument: Some("main.rs".to_string()), argument: Some("main.rs".to_string()),
// }) })
// ); );
// assert_eq!( assert_eq!(
// MentionCompletion::try_parse("Lorem @main", 0), MentionCompletion::try_parse("Lorem @main", 0),
// Some(MentionCompletion { Some(MentionCompletion {
// source_range: 6..11, source_range: 6..11,
// mode: None, mode: None,
// argument: Some("main".to_string()), argument: Some("main".to_string()),
// }) })
// ); );
// assert_eq!(MentionCompletion::try_parse("test@", 0), None); assert_eq!(MentionCompletion::try_parse("test@", 0), None);
// } }
// struct AtMentionEditor(Entity<Editor>); struct AtMentionEditor(Entity<Editor>);
// impl Item for AtMentionEditor { impl Item for AtMentionEditor {
// type Event = (); type Event = ();
// fn include_in_nav_history() -> bool { fn include_in_nav_history() -> bool {
// false false
// } }
// fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString { fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString {
// "Test".into() "Test".into()
// } }
// } }
// impl EventEmitter<()> for AtMentionEditor {} impl EventEmitter<()> for AtMentionEditor {}
// impl Focusable for AtMentionEditor { impl Focusable for AtMentionEditor {
// fn focus_handle(&self, cx: &App) -> FocusHandle { fn focus_handle(&self, cx: &App) -> FocusHandle {
// self.0.read(cx).focus_handle(cx).clone() self.0.read(cx).focus_handle(cx).clone()
// } }
// } }
// impl Render for AtMentionEditor { impl Render for AtMentionEditor {
// fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
// self.0.clone().into_any_element() self.0.clone().into_any_element()
// } }
// } }
// #[gpui::test] #[gpui::test]
// async fn test_context_completion_provider(cx: &mut TestAppContext) { async fn test_context_completion_provider(cx: &mut TestAppContext) {
// init_test(cx); init_test(cx);
// let app_state = cx.update(AppState::test); let app_state = cx.update(AppState::test);
// cx.update(|cx| { cx.update(|cx| {
// language::init(cx); language::init(cx);
// editor::init(cx); editor::init(cx);
// workspace::init(app_state.clone(), cx); workspace::init(app_state.clone(), cx);
// Project::init_settings(cx); Project::init_settings(cx);
// }); });
// app_state app_state
// .fs .fs
// .as_fake() .as_fake()
// .insert_tree( .insert_tree(
// path!("/dir"), path!("/dir"),
// json!({ json!({
// "editor": "", "editor": "",
// "a": { "a": {
// "one.txt": "", "one.txt": "",
// "two.txt": "", "two.txt": "",
// "three.txt": "", "three.txt": "",
// "four.txt": "" "four.txt": ""
// }, },
// "b": { "b": {
// "five.txt": "", "five.txt": "",
// "six.txt": "", "six.txt": "",
// "seven.txt": "", "seven.txt": "",
// "eight.txt": "", "eight.txt": "",
// } }
// }), }),
// ) )
// .await; .await;
// let project = Project::test(app_state.fs.clone(), [path!("/dir").as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), [path!("/dir").as_ref()], cx).await;
// let window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); let window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
// let workspace = window.root(cx).unwrap(); let workspace = window.root(cx).unwrap();
// let worktree = project.update(cx, |project, cx| { let worktree = project.update(cx, |project, cx| {
// let mut worktrees = project.worktrees(cx).collect::<Vec<_>>(); let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
// assert_eq!(worktrees.len(), 1); assert_eq!(worktrees.len(), 1);
// worktrees.pop().unwrap() worktrees.pop().unwrap()
// }); });
// let worktree_id = worktree.read_with(cx, |worktree, _| worktree.id()); let worktree_id = worktree.read_with(cx, |worktree, _| worktree.id());
// let mut cx = VisualTestContext::from_window(*window.deref(), cx); let mut cx = VisualTestContext::from_window(*window.deref(), cx);
// let paths = vec![ let paths = vec![
// path!("a/one.txt"), path!("a/one.txt"),
// path!("a/two.txt"), path!("a/two.txt"),
// path!("a/three.txt"), path!("a/three.txt"),
// path!("a/four.txt"), path!("a/four.txt"),
// path!("b/five.txt"), path!("b/five.txt"),
// path!("b/six.txt"), path!("b/six.txt"),
// path!("b/seven.txt"), path!("b/seven.txt"),
// path!("b/eight.txt"), path!("b/eight.txt"),
// ]; ];
// let mut opened_editors = Vec::new(); let mut opened_editors = Vec::new();
// for path in paths { for path in paths {
// let buffer = workspace let buffer = workspace
// .update_in(&mut cx, |workspace, window, cx| { .update_in(&mut cx, |workspace, window, cx| {
// workspace.open_path( workspace.open_path(
// ProjectPath { ProjectPath {
// worktree_id, worktree_id,
// path: Path::new(path).into(), path: Path::new(path).into(),
// }, },
// None, None,
// false, false,
// window, window,
// cx, cx,
// ) )
// }) })
// .await .await
// .unwrap(); .unwrap();
// opened_editors.push(buffer); opened_editors.push(buffer);
// } }
// let editor = workspace.update_in(&mut cx, |workspace, window, cx| { let editor = workspace.update_in(&mut cx, |workspace, window, cx| {
// let editor = cx.new(|cx| { let editor = cx.new(|cx| {
// Editor::new( Editor::new(
// editor::EditorMode::full(), editor::EditorMode::full(),
// multi_buffer::MultiBuffer::build_simple("", cx), multi_buffer::MultiBuffer::build_simple("", cx),
// None, None,
// window, window,
// cx, cx,
// ) )
// }); });
// workspace.active_pane().update(cx, |pane, cx| { workspace.active_pane().update(cx, |pane, cx| {
// pane.add_item( pane.add_item(
// Box::new(cx.new(|_| AtMentionEditor(editor.clone()))), Box::new(cx.new(|_| AtMentionEditor(editor.clone()))),
// true, true,
// true, true,
// None, None,
// window, window,
// cx, cx,
// ); );
// }); });
// editor editor
// }); });
// let context_store = cx.new(|_| ContextStore::new(project.downgrade(), None)); let mention_set = Arc::new(Mutex::new(MentionSet::default()));
// let editor_entity = editor.downgrade(); let editor_entity = editor.downgrade();
// editor.update_in(&mut cx, |editor, window, cx| { editor.update_in(&mut cx, |editor, window, cx| {
// let last_opened_buffer = opened_editors.last().and_then(|editor| { window.focus(&editor.focus_handle(cx));
// editor editor.set_completion_provider(Some(Rc::new(ContextPickerCompletionProvider::new(
// .downcast::<Editor>()? mention_set.clone(),
// .read(cx) workspace.downgrade(),
// .buffer() WeakEntity::new_invalid(),
// .read(cx) WeakEntity::new_invalid(),
// .as_singleton() editor_entity,
// .as_ref() ))));
// .map(Entity::downgrade) });
// });
// window.focus(&editor.focus_handle(cx));
// editor.set_completion_provider(Some(Rc::new(ContextPickerCompletionProvider::new(
// workspace.downgrade(),
// context_store.downgrade(),
// None,
// None,
// editor_entity,
// last_opened_buffer,
// ))));
// });
// cx.simulate_input("Lorem "); cx.simulate_input("Lorem ");
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!(editor.text(cx), "Lorem "); assert_eq!(editor.text(cx), "Lorem ");
// assert!(!editor.has_visible_completions_menu()); assert!(!editor.has_visible_completions_menu());
// }); });
// cx.simulate_input("@"); cx.simulate_input("@");
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!(editor.text(cx), "Lorem @"); assert_eq!(editor.text(cx), "Lorem @");
// assert!(editor.has_visible_completions_menu()); assert!(editor.has_visible_completions_menu());
// assert_eq!( assert_eq!(
// current_completion_labels(editor), current_completion_labels(editor),
// &[ &[
// "seven.txt dir/b/", "eight.txt dir/b/",
// "six.txt dir/b/", "seven.txt dir/b/",
// "five.txt dir/b/", "six.txt dir/b/",
// "four.txt dir/a/", "five.txt dir/b/",
// "Files & Directories", "Files & Directories",
// "Symbols", "Symbols",
// "Fetch" "Threads",
// ] "Fetch"
// ); ]
// }); );
});
// // Select and confirm "File" // Select and confirm "File"
// editor.update_in(&mut cx, |editor, window, cx| { editor.update_in(&mut cx, |editor, window, cx| {
// assert!(editor.has_visible_completions_menu()); assert!(editor.has_visible_completions_menu());
// editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx); editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx);
// editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx); editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx);
// editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx); editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx);
// editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx); editor.context_menu_next(&editor::actions::ContextMenuNext, window, cx);
// editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx); editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx);
// }); });
// cx.run_until_parked(); cx.run_until_parked();
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!(editor.text(cx), "Lorem @file "); assert_eq!(editor.text(cx), "Lorem @file ");
// assert!(editor.has_visible_completions_menu()); assert!(editor.has_visible_completions_menu());
// }); });
// cx.simulate_input("one"); cx.simulate_input("one");
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!(editor.text(cx), "Lorem @file one"); assert_eq!(editor.text(cx), "Lorem @file one");
// assert!(editor.has_visible_completions_menu()); assert!(editor.has_visible_completions_menu());
// assert_eq!(current_completion_labels(editor), vec!["one.txt dir/a/"]); assert_eq!(current_completion_labels(editor), vec!["one.txt dir/a/"]);
// }); });
// editor.update_in(&mut cx, |editor, window, cx| { editor.update_in(&mut cx, |editor, window, cx| {
// assert!(editor.has_visible_completions_menu()); assert!(editor.has_visible_completions_menu());
// editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx); editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx);
// }); });
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt) "); assert_eq!(editor.text(cx), "Lorem [@one.txt](file:///dir/a/one.txt) ");
// assert!(!editor.has_visible_completions_menu()); assert!(!editor.has_visible_completions_menu());
// assert_eq!( assert_eq!(
// fold_ranges(editor, cx), fold_ranges(editor, cx),
// vec![Point::new(0, 6)..Point::new(0, 37)] vec![Point::new(0, 6)..Point::new(0, 39)]
// ); );
// }); });
// cx.simulate_input(" "); cx.simulate_input(" ");
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!(editor.text(cx), "Lorem [@one.txt](@file:dir/a/one.txt) "); assert_eq!(editor.text(cx), "Lorem [@one.txt](file:///dir/a/one.txt) ");
// assert!(!editor.has_visible_completions_menu()); assert!(!editor.has_visible_completions_menu());
// assert_eq!( assert_eq!(
// fold_ranges(editor, cx), fold_ranges(editor, cx),
// vec![Point::new(0, 6)..Point::new(0, 37)] vec![Point::new(0, 6)..Point::new(0, 39)]
// ); );
// }); });
// cx.simulate_input("Ipsum "); cx.simulate_input("Ipsum ");
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!( assert_eq!(
// editor.text(cx), editor.text(cx),
// "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum ", "Lorem [@one.txt](file:///dir/a/one.txt) Ipsum ",
// ); );
// assert!(!editor.has_visible_completions_menu()); assert!(!editor.has_visible_completions_menu());
// assert_eq!( assert_eq!(
// fold_ranges(editor, cx), fold_ranges(editor, cx),
// vec![Point::new(0, 6)..Point::new(0, 37)] vec![Point::new(0, 6)..Point::new(0, 39)]
// ); );
// }); });
// cx.simulate_input("@file "); cx.simulate_input("@file ");
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!( assert_eq!(
// editor.text(cx), editor.text(cx),
// "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum @file ", "Lorem [@one.txt](file:///dir/a/one.txt) Ipsum @file ",
// ); );
// assert!(editor.has_visible_completions_menu()); assert!(editor.has_visible_completions_menu());
// assert_eq!( assert_eq!(
// fold_ranges(editor, cx), fold_ranges(editor, cx),
// vec![Point::new(0, 6)..Point::new(0, 37)] vec![Point::new(0, 6)..Point::new(0, 39)]
// ); );
// }); });
// editor.update_in(&mut cx, |editor, window, cx| { editor.update_in(&mut cx, |editor, window, cx| {
// editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx); editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx);
// }); });
// cx.run_until_parked(); cx.run_until_parked();
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!( assert_eq!(
// editor.text(cx), editor.text(cx),
// "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt) " "Lorem [@one.txt](file:///dir/a/one.txt) Ipsum [@eight.txt](file:///dir/b/eight.txt) "
// ); );
// assert!(!editor.has_visible_completions_menu()); assert!(!editor.has_visible_completions_menu());
// assert_eq!( assert_eq!(
// fold_ranges(editor, cx), fold_ranges(editor, cx),
// vec![ vec![
// Point::new(0, 6)..Point::new(0, 37), Point::new(0, 6)..Point::new(0, 39),
// Point::new(0, 45)..Point::new(0, 80) Point::new(0, 47)..Point::new(0, 84)
// ] ]
// ); );
// }); });
// cx.simulate_input("\n@"); cx.simulate_input("\n@");
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!( assert_eq!(
// editor.text(cx), editor.text(cx),
// "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt) \n@" "Lorem [@one.txt](file:///dir/a/one.txt) Ipsum [@eight.txt](file:///dir/b/eight.txt) \n@"
// ); );
// assert!(editor.has_visible_completions_menu()); assert!(editor.has_visible_completions_menu());
// assert_eq!( assert_eq!(
// fold_ranges(editor, cx), fold_ranges(editor, cx),
// vec![ vec![
// Point::new(0, 6)..Point::new(0, 37), Point::new(0, 6)..Point::new(0, 39),
// Point::new(0, 45)..Point::new(0, 80) Point::new(0, 47)..Point::new(0, 84)
// ] ]
// ); );
// }); });
// editor.update_in(&mut cx, |editor, window, cx| { editor.update_in(&mut cx, |editor, window, cx| {
// editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx); editor.confirm_completion(&editor::actions::ConfirmCompletion::default(), window, cx);
// }); });
// cx.run_until_parked(); cx.run_until_parked();
// editor.update(&mut cx, |editor, cx| { editor.update(&mut cx, |editor, cx| {
// assert_eq!( assert_eq!(
// editor.text(cx), editor.text(cx),
// "Lorem [@one.txt](@file:dir/a/one.txt) Ipsum [@seven.txt](@file:dir/b/seven.txt) \n[@six.txt](@file:dir/b/six.txt) " "Lorem [@one.txt](file:///dir/a/one.txt) Ipsum [@eight.txt](file:///dir/b/eight.txt) \n[@seven.txt](file:///dir/b/seven.txt) "
// ); );
// assert!(!editor.has_visible_completions_menu()); assert!(!editor.has_visible_completions_menu());
// assert_eq!( assert_eq!(
// fold_ranges(editor, cx), fold_ranges(editor, cx),
// vec![ vec![
// Point::new(0, 6)..Point::new(0, 37), Point::new(0, 6)..Point::new(0, 39),
// Point::new(0, 45)..Point::new(0, 80), Point::new(0, 47)..Point::new(0, 84),
// Point::new(1, 0)..Point::new(1, 31) Point::new(1, 0)..Point::new(1, 37)
// ] ]
// ); );
// }); });
// } }
// fn fold_ranges(editor: &Editor, cx: &mut App) -> Vec<Range<Point>> { fn fold_ranges(editor: &Editor, cx: &mut App) -> Vec<Range<Point>> {
// let snapshot = editor.buffer().read(cx).snapshot(cx); let snapshot = editor.buffer().read(cx).snapshot(cx);
// editor.display_map.update(cx, |display_map, cx| { editor.display_map.update(cx, |display_map, cx| {
// display_map display_map
// .snapshot(cx) .snapshot(cx)
// .folds_in_range(0..snapshot.len()) .folds_in_range(0..snapshot.len())
// .map(|fold| fold.range.to_point(&snapshot)) .map(|fold| fold.range.to_point(&snapshot))
// .collect() .collect()
// }) })
// } }
// fn current_completion_labels(editor: &Editor) -> Vec<String> { fn current_completion_labels(editor: &Editor) -> Vec<String> {
// let completions = editor.current_completions().expect("Missing completions"); let completions = editor.current_completions().expect("Missing completions");
// completions completions
// .into_iter() .into_iter()
// .map(|completion| completion.label.text.to_string()) .map(|completion| completion.label.text.to_string())
// .collect::<Vec<_>>() .collect::<Vec<_>>()
// } }
// pub(crate) fn init_test(cx: &mut TestAppContext) { pub(crate) fn init_test(cx: &mut TestAppContext) {
// cx.update(|cx| { cx.update(|cx| {
// let store = SettingsStore::test(cx); let store = SettingsStore::test(cx);
// cx.set_global(store); cx.set_global(store);
// theme::init(theme::LoadThemes::JustBase, cx); theme::init(theme::LoadThemes::JustBase, cx);
// client::init_settings(cx); client::init_settings(cx);
// language::init(cx); language::init(cx);
// Project::init_settings(cx); Project::init_settings(cx);
// workspace::init_settings(cx); workspace::init_settings(cx);
// editor::init_settings(cx); editor::init_settings(cx);
// }); });
// } }
// } }