Implement completion-resolution in editor2

Enable the completion tests in editor2.
This commit is contained in:
Max Brunsfeld 2023-11-29 14:20:44 -08:00
parent 2bd428102b
commit 9451bd605d
2 changed files with 486 additions and 492 deletions

View file

@ -73,7 +73,7 @@ use ordered_float::OrderedFloat;
use parking_lot::{Mutex, RwLock};
use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
use rand::prelude::*;
use rpc::proto::*;
use rpc::proto::{self, *};
use scroll::{
autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
};
@ -970,95 +970,94 @@ impl CompletionsMenu {
fn pre_resolve_completion_documentation(
&self,
_editor: &Editor,
_cx: &mut ViewContext<Editor>,
editor: &Editor,
cx: &mut ViewContext<Editor>,
) -> Option<Task<()>> {
// todo!("implementation below ");
None
let settings = EditorSettings::get_global(cx);
if !settings.show_completion_documentation {
return None;
}
let Some(project) = editor.project.clone() else {
return None;
};
let client = project.read(cx).client();
let language_registry = project.read(cx).languages().clone();
let is_remote = project.read(cx).is_remote();
let project_id = project.read(cx).remote_id();
let completions = self.completions.clone();
let completion_indices: Vec<_> = self.matches.iter().map(|m| m.candidate_id).collect();
Some(cx.spawn(move |this, mut cx| async move {
if is_remote {
let Some(project_id) = project_id else {
log::error!("Remote project without remote_id");
return;
};
for completion_index in completion_indices {
let completions_guard = completions.read();
let completion = &completions_guard[completion_index];
if completion.documentation.is_some() {
continue;
}
let server_id = completion.server_id;
let completion = completion.lsp_completion.clone();
drop(completions_guard);
Self::resolve_completion_documentation_remote(
project_id,
server_id,
completions.clone(),
completion_index,
completion,
client.clone(),
language_registry.clone(),
)
.await;
_ = this.update(&mut cx, |_, cx| cx.notify());
}
} else {
for completion_index in completion_indices {
let completions_guard = completions.read();
let completion = &completions_guard[completion_index];
if completion.documentation.is_some() {
continue;
}
let server_id = completion.server_id;
let completion = completion.lsp_completion.clone();
drop(completions_guard);
let server = project
.read_with(&mut cx, |project, _| {
project.language_server_for_id(server_id)
})
.ok()
.flatten();
let Some(server) = server else {
return;
};
Self::resolve_completion_documentation_local(
server,
completions.clone(),
completion_index,
completion,
language_registry.clone(),
)
.await;
_ = this.update(&mut cx, |_, cx| cx.notify());
}
}
}))
}
// {
// let settings = EditorSettings::get_global(cx);
// if !settings.show_completion_documentation {
// return None;
// }
// let Some(project) = editor.project.clone() else {
// return None;
// };
// let client = project.read(cx).client();
// let language_registry = project.read(cx).languages().clone();
// let is_remote = project.read(cx).is_remote();
// let project_id = project.read(cx).remote_id();
// let completions = self.completions.clone();
// let completion_indices: Vec<_> = self.matches.iter().map(|m| m.candidate_id).collect();
// Some(cx.spawn(move |this, mut cx| async move {
// if is_remote {
// let Some(project_id) = project_id else {
// log::error!("Remote project without remote_id");
// return;
// };
// for completion_index in completion_indices {
// let completions_guard = completions.read();
// let completion = &completions_guard[completion_index];
// if completion.documentation.is_some() {
// continue;
// }
// let server_id = completion.server_id;
// let completion = completion.lsp_completion.clone();
// drop(completions_guard);
// Self::resolve_completion_documentation_remote(
// project_id,
// server_id,
// completions.clone(),
// completion_index,
// completion,
// client.clone(),
// language_registry.clone(),
// )
// .await;
// _ = this.update(&mut cx, |_, cx| cx.notify());
// }
// } else {
// for completion_index in completion_indices {
// let completions_guard = completions.read();
// let completion = &completions_guard[completion_index];
// if completion.documentation.is_some() {
// continue;
// }
// let server_id = completion.server_id;
// let completion = completion.lsp_completion.clone();
// drop(completions_guard);
// let server = project.read_with(&mut cx, |project, _| {
// project.language_server_for_id(server_id)
// });
// let Some(server) = server else {
// return;
// };
// Self::resolve_completion_documentation_local(
// server,
// completions.clone(),
// completion_index,
// completion,
// language_registry.clone(),
// )
// .await;
// _ = this.update(&mut cx, |_, cx| cx.notify());
// }
// }
// }))
// }
fn attempt_resolve_selected_completion_documentation(
&mut self,
@ -1079,10 +1078,9 @@ impl CompletionsMenu {
let completions = self.completions.clone();
let completions_guard = completions.read();
let completion = &completions_guard[completion_index];
// todo!()
// if completion.documentation.is_some() {
// return;
// }
if completion.documentation.is_some() {
return;
}
let server_id = completion.server_id;
let completion = completion.lsp_completion.clone();
@ -1141,41 +1139,40 @@ impl CompletionsMenu {
client: Arc<Client>,
language_registry: Arc<LanguageRegistry>,
) {
// todo!()
// let request = proto::ResolveCompletionDocumentation {
// project_id,
// language_server_id: server_id.0 as u64,
// lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
// };
let request = proto::ResolveCompletionDocumentation {
project_id,
language_server_id: server_id.0 as u64,
lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(),
};
// let Some(response) = client
// .request(request)
// .await
// .context("completion documentation resolve proto request")
// .log_err()
// else {
// return;
// };
let Some(response) = client
.request(request)
.await
.context("completion documentation resolve proto request")
.log_err()
else {
return;
};
// if response.text.is_empty() {
// let mut completions = completions.write();
// let completion = &mut completions[completion_index];
// completion.documentation = Some(Documentation::Undocumented);
// }
if response.text.is_empty() {
let mut completions = completions.write();
let completion = &mut completions[completion_index];
completion.documentation = Some(Documentation::Undocumented);
}
// let documentation = if response.is_markdown {
// Documentation::MultiLineMarkdown(
// markdown::parse_markdown(&response.text, &language_registry, None).await,
// )
// } else if response.text.lines().count() <= 1 {
// Documentation::SingleLine(response.text)
// } else {
// Documentation::MultiLinePlainText(response.text)
// };
let documentation = if response.is_markdown {
Documentation::MultiLineMarkdown(
markdown::parse_markdown(&response.text, &language_registry, None).await,
)
} else if response.text.lines().count() <= 1 {
Documentation::SingleLine(response.text)
} else {
Documentation::MultiLinePlainText(response.text)
};
// let mut completions = completions.write();
// let completion = &mut completions[completion_index];
// completion.documentation = Some(documentation);
let mut completions = completions.write();
let completion = &mut completions[completion_index];
completion.documentation = Some(documentation);
}
async fn resolve_completion_documentation_local(
@ -1185,38 +1182,37 @@ impl CompletionsMenu {
completion: lsp::CompletionItem,
language_registry: Arc<LanguageRegistry>,
) {
// todo!()
// let can_resolve = server
// .capabilities()
// .completion_provider
// .as_ref()
// .and_then(|options| options.resolve_provider)
// .unwrap_or(false);
// if !can_resolve {
// return;
// }
let can_resolve = server
.capabilities()
.completion_provider
.as_ref()
.and_then(|options| options.resolve_provider)
.unwrap_or(false);
if !can_resolve {
return;
}
// let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
// let Some(completion_item) = request.await.log_err() else {
// return;
// };
let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
let Some(completion_item) = request.await.log_err() else {
return;
};
// if let Some(lsp_documentation) = completion_item.documentation {
// let documentation = language::prepare_completion_documentation(
// &lsp_documentation,
// &language_registry,
// None, // TODO: Try to reasonably work out which language the completion is for
// )
// .await;
if let Some(lsp_documentation) = completion_item.documentation {
let documentation = language::prepare_completion_documentation(
&lsp_documentation,
&language_registry,
None, // TODO: Try to reasonably work out which language the completion is for
)
.await;
// let mut completions = completions.write();
// let completion = &mut completions[completion_index];
// completion.documentation = Some(documentation);
// } else {
// let mut completions = completions.write();
// let completion = &mut completions[completion_index];
// completion.documentation = Some(Documentation::Undocumented);
// }
let mut completions = completions.write();
let completion = &mut completions[completion_index];
completion.documentation = Some(documentation);
} else {
let mut completions = completions.write();
let completion = &mut completions[completion_index];
completion.documentation = Some(Documentation::Undocumented);
}
}
fn visible(&self) -> bool {

View file

@ -5427,178 +5427,177 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext)
);
}
//todo!(completion)
// #[gpui::test]
// async fn test_completion(cx: &mut gpui::TestAppContext) {
// init_test(cx, |_| {});
#[gpui::test]
async fn test_completion(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
// let mut cx = EditorLspTestContext::new_rust(
// lsp::ServerCapabilities {
// completion_provider: Some(lsp::CompletionOptions {
// trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
// resolve_provider: Some(true),
// ..Default::default()
// }),
// ..Default::default()
// },
// cx,
// )
// .await;
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
resolve_provider: Some(true),
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
// cx.set_state(indoc! {"
// oneˇ
// two
// three
// "});
// cx.simulate_keystroke(".");
// handle_completion_request(
// &mut cx,
// indoc! {"
// one.|<>
// two
// three
// "},
// vec!["first_completion", "second_completion"],
// )
// .await;
// cx.condition(|editor, _| editor.context_menu_visible())
// .await;
// let apply_additional_edits = cx.update_editor(|editor, cx| {
// editor.context_menu_next(&Default::default(), cx);
// editor
// .confirm_completion(&ConfirmCompletion::default(), cx)
// .unwrap()
// });
// cx.assert_editor_state(indoc! {"
// one.second_completionˇ
// two
// three
// "});
cx.set_state(indoc! {"
oneˇ
two
three
"});
cx.simulate_keystroke(".");
handle_completion_request(
&mut cx,
indoc! {"
one.|<>
two
three
"},
vec!["first_completion", "second_completion"],
)
.await;
cx.condition(|editor, _| editor.context_menu_visible())
.await;
let apply_additional_edits = cx.update_editor(|editor, cx| {
editor.context_menu_next(&Default::default(), cx);
editor
.confirm_completion(&ConfirmCompletion::default(), cx)
.unwrap()
});
cx.assert_editor_state(indoc! {"
one.second_completionˇ
two
three
"});
// handle_resolve_completion_request(
// &mut cx,
// Some(vec![
// (
// //This overlaps with the primary completion edit which is
// //misbehavior from the LSP spec, test that we filter it out
// indoc! {"
// one.second_ˇcompletion
// two
// threeˇ
// "},
// "overlapping additional edit",
// ),
// (
// indoc! {"
// one.second_completion
// two
// threeˇ
// "},
// "\nadditional edit",
// ),
// ]),
// )
// .await;
// apply_additional_edits.await.unwrap();
// cx.assert_editor_state(indoc! {"
// one.second_completionˇ
// two
// three
// additional edit
// "});
handle_resolve_completion_request(
&mut cx,
Some(vec![
(
//This overlaps with the primary completion edit which is
//misbehavior from the LSP spec, test that we filter it out
indoc! {"
one.second_ˇcompletion
two
threeˇ
"},
"overlapping additional edit",
),
(
indoc! {"
one.second_completion
two
threeˇ
"},
"\nadditional edit",
),
]),
)
.await;
apply_additional_edits.await.unwrap();
cx.assert_editor_state(indoc! {"
one.second_completionˇ
two
three
additional edit
"});
// cx.set_state(indoc! {"
// one.second_completion
// twoˇ
// threeˇ
// additional edit
// "});
// cx.simulate_keystroke(" ");
// assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
// cx.simulate_keystroke("s");
// assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
cx.set_state(indoc! {"
one.second_completion
twoˇ
threeˇ
additional edit
"});
cx.simulate_keystroke(" ");
assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
cx.simulate_keystroke("s");
assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
// cx.assert_editor_state(indoc! {"
// one.second_completion
// two sˇ
// three sˇ
// additional edit
// "});
// handle_completion_request(
// &mut cx,
// indoc! {"
// one.second_completion
// two s
// three <s|>
// additional edit
// "},
// vec!["fourth_completion", "fifth_completion", "sixth_completion"],
// )
// .await;
// cx.condition(|editor, _| editor.context_menu_visible())
// .await;
cx.assert_editor_state(indoc! {"
one.second_completion
two
three
additional edit
"});
handle_completion_request(
&mut cx,
indoc! {"
one.second_completion
two s
three <s|>
additional edit
"},
vec!["fourth_completion", "fifth_completion", "sixth_completion"],
)
.await;
cx.condition(|editor, _| editor.context_menu_visible())
.await;
// cx.simulate_keystroke("i");
cx.simulate_keystroke("i");
// handle_completion_request(
// &mut cx,
// indoc! {"
// one.second_completion
// two si
// three <si|>
// additional edit
// "},
// vec!["fourth_completion", "fifth_completion", "sixth_completion"],
// )
// .await;
// cx.condition(|editor, _| editor.context_menu_visible())
// .await;
handle_completion_request(
&mut cx,
indoc! {"
one.second_completion
two si
three <si|>
additional edit
"},
vec!["fourth_completion", "fifth_completion", "sixth_completion"],
)
.await;
cx.condition(|editor, _| editor.context_menu_visible())
.await;
// let apply_additional_edits = cx.update_editor(|editor, cx| {
// editor
// .confirm_completion(&ConfirmCompletion::default(), cx)
// .unwrap()
// });
// cx.assert_editor_state(indoc! {"
// one.second_completion
// two sixth_completionˇ
// three sixth_completionˇ
// additional edit
// "});
let apply_additional_edits = cx.update_editor(|editor, cx| {
editor
.confirm_completion(&ConfirmCompletion::default(), cx)
.unwrap()
});
cx.assert_editor_state(indoc! {"
one.second_completion
two sixth_completionˇ
three sixth_completionˇ
additional edit
"});
// handle_resolve_completion_request(&mut cx, None).await;
// apply_additional_edits.await.unwrap();
handle_resolve_completion_request(&mut cx, None).await;
apply_additional_edits.await.unwrap();
// cx.update(|cx| {
// cx.update_global::<SettingsStore, _, _>(|settings, cx| {
// settings.update_user_settings::<EditorSettings>(cx, |settings| {
// settings.show_completions_on_input = Some(false);
// });
// })
// });
// cx.set_state("editorˇ");
// cx.simulate_keystroke(".");
// assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
// cx.simulate_keystroke("c");
// cx.simulate_keystroke("l");
// cx.simulate_keystroke("o");
// cx.assert_editor_state("editor.cloˇ");
// assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
// cx.update_editor(|editor, cx| {
// editor.show_completions(&ShowCompletions, cx);
// });
// handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
// cx.condition(|editor, _| editor.context_menu_visible())
// .await;
// let apply_additional_edits = cx.update_editor(|editor, cx| {
// editor
// .confirm_completion(&ConfirmCompletion::default(), cx)
// .unwrap()
// });
// cx.assert_editor_state("editor.closeˇ");
// handle_resolve_completion_request(&mut cx, None).await;
// apply_additional_edits.await.unwrap();
// }
cx.update(|cx| {
cx.update_global::<SettingsStore, _>(|settings, cx| {
settings.update_user_settings::<EditorSettings>(cx, |settings| {
settings.show_completions_on_input = Some(false);
});
})
});
cx.set_state("editorˇ");
cx.simulate_keystroke(".");
assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
cx.simulate_keystroke("c");
cx.simulate_keystroke("l");
cx.simulate_keystroke("o");
cx.assert_editor_state("editor.cloˇ");
assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
cx.update_editor(|editor, cx| {
editor.show_completions(&ShowCompletions, cx);
});
handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
cx.condition(|editor, _| editor.context_menu_visible())
.await;
let apply_additional_edits = cx.update_editor(|editor, cx| {
editor
.confirm_completion(&ConfirmCompletion::default(), cx)
.unwrap()
});
cx.assert_editor_state("editor.closeˇ");
handle_resolve_completion_request(&mut cx, None).await;
apply_additional_edits.await.unwrap();
}
#[gpui::test]
async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
@ -7803,197 +7802,196 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test
);
}
//todo!(completions)
// #[gpui::test]
// async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
// init_test(cx, |_| {});
#[gpui::test]
async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
// let mut cx = EditorLspTestContext::new_rust(
// lsp::ServerCapabilities {
// completion_provider: Some(lsp::CompletionOptions {
// trigger_characters: Some(vec![".".to_string()]),
// resolve_provider: Some(true),
// ..Default::default()
// }),
// ..Default::default()
// },
// cx,
// )
// .await;
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
trigger_characters: Some(vec![".".to_string()]),
resolve_provider: Some(true),
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
// cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
// cx.simulate_keystroke(".");
// let completion_item = lsp::CompletionItem {
// label: "some".into(),
// kind: Some(lsp::CompletionItemKind::SNIPPET),
// detail: Some("Wrap the expression in an `Option::Some`".to_string()),
// documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
// kind: lsp::MarkupKind::Markdown,
// value: "```rust\nSome(2)\n```".to_string(),
// })),
// deprecated: Some(false),
// sort_text: Some("fffffff2".to_string()),
// filter_text: Some("some".to_string()),
// insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
// range: lsp::Range {
// start: lsp::Position {
// line: 0,
// character: 22,
// },
// end: lsp::Position {
// line: 0,
// character: 22,
// },
// },
// new_text: "Some(2)".to_string(),
// })),
// additional_text_edits: Some(vec![lsp::TextEdit {
// range: lsp::Range {
// start: lsp::Position {
// line: 0,
// character: 20,
// },
// end: lsp::Position {
// line: 0,
// character: 22,
// },
// },
// new_text: "".to_string(),
// }]),
// ..Default::default()
// };
cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
cx.simulate_keystroke(".");
let completion_item = lsp::CompletionItem {
label: "some".into(),
kind: Some(lsp::CompletionItemKind::SNIPPET),
detail: Some("Wrap the expression in an `Option::Some`".to_string()),
documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown,
value: "```rust\nSome(2)\n```".to_string(),
})),
deprecated: Some(false),
sort_text: Some("fffffff2".to_string()),
filter_text: Some("some".to_string()),
insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: lsp::Range {
start: lsp::Position {
line: 0,
character: 22,
},
end: lsp::Position {
line: 0,
character: 22,
},
},
new_text: "Some(2)".to_string(),
})),
additional_text_edits: Some(vec![lsp::TextEdit {
range: lsp::Range {
start: lsp::Position {
line: 0,
character: 20,
},
end: lsp::Position {
line: 0,
character: 22,
},
},
new_text: "".to_string(),
}]),
..Default::default()
};
// let closure_completion_item = completion_item.clone();
// let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
// let task_completion_item = closure_completion_item.clone();
// async move {
// Ok(Some(lsp::CompletionResponse::Array(vec![
// task_completion_item,
// ])))
// }
// });
let closure_completion_item = completion_item.clone();
let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
let task_completion_item = closure_completion_item.clone();
async move {
Ok(Some(lsp::CompletionResponse::Array(vec![
task_completion_item,
])))
}
});
// request.next().await;
request.next().await;
// cx.condition(|editor, _| editor.context_menu_visible())
// .await;
// let apply_additional_edits = cx.update_editor(|editor, cx| {
// editor
// .confirm_completion(&ConfirmCompletion::default(), cx)
// .unwrap()
// });
// cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
cx.condition(|editor, _| editor.context_menu_visible())
.await;
let apply_additional_edits = cx.update_editor(|editor, cx| {
editor
.confirm_completion(&ConfirmCompletion::default(), cx)
.unwrap()
});
cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
// cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
// let task_completion_item = completion_item.clone();
// async move { Ok(task_completion_item) }
// })
// .next()
// .await
// .unwrap();
// apply_additional_edits.await.unwrap();
// cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
// }
cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
let task_completion_item = completion_item.clone();
async move { Ok(task_completion_item) }
})
.next()
.await
.unwrap();
apply_additional_edits.await.unwrap();
cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
}
// #[gpui::test]
// async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
// init_test(cx, |_| {});
#[gpui::test]
async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
// let mut cx = EditorLspTestContext::new(
// Language::new(
// LanguageConfig {
// path_suffixes: vec!["jsx".into()],
// overrides: [(
// "element".into(),
// LanguageConfigOverride {
// word_characters: Override::Set(['-'].into_iter().collect()),
// ..Default::default()
// },
// )]
// .into_iter()
// .collect(),
// ..Default::default()
// },
// Some(tree_sitter_typescript::language_tsx()),
// )
// .with_override_query("(jsx_self_closing_element) @element")
// .unwrap(),
// lsp::ServerCapabilities {
// completion_provider: Some(lsp::CompletionOptions {
// trigger_characters: Some(vec![":".to_string()]),
// ..Default::default()
// }),
// ..Default::default()
// },
// cx,
// )
// .await;
let mut cx = EditorLspTestContext::new(
Language::new(
LanguageConfig {
path_suffixes: vec!["jsx".into()],
overrides: [(
"element".into(),
LanguageConfigOverride {
word_characters: Override::Set(['-'].into_iter().collect()),
..Default::default()
},
)]
.into_iter()
.collect(),
..Default::default()
},
Some(tree_sitter_typescript::language_tsx()),
)
.with_override_query("(jsx_self_closing_element) @element")
.unwrap(),
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
trigger_characters: Some(vec![":".to_string()]),
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
// cx.lsp
// .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
// Ok(Some(lsp::CompletionResponse::Array(vec![
// lsp::CompletionItem {
// label: "bg-blue".into(),
// ..Default::default()
// },
// lsp::CompletionItem {
// label: "bg-red".into(),
// ..Default::default()
// },
// lsp::CompletionItem {
// label: "bg-yellow".into(),
// ..Default::default()
// },
// ])))
// });
cx.lsp
.handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
Ok(Some(lsp::CompletionResponse::Array(vec![
lsp::CompletionItem {
label: "bg-blue".into(),
..Default::default()
},
lsp::CompletionItem {
label: "bg-red".into(),
..Default::default()
},
lsp::CompletionItem {
label: "bg-yellow".into(),
..Default::default()
},
])))
});
// cx.set_state(r#"<p class="bgˇ" />"#);
cx.set_state(r#"<p class="bgˇ" />"#);
// // Trigger completion when typing a dash, because the dash is an extra
// // word character in the 'element' scope, which contains the cursor.
// cx.simulate_keystroke("-");
// cx.executor().run_until_parked();
// cx.update_editor(|editor, _| {
// if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
// assert_eq!(
// menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
// &["bg-red", "bg-blue", "bg-yellow"]
// );
// } else {
// panic!("expected completion menu to be open");
// }
// });
// Trigger completion when typing a dash, because the dash is an extra
// word character in the 'element' scope, which contains the cursor.
cx.simulate_keystroke("-");
cx.executor().run_until_parked();
cx.update_editor(|editor, _| {
if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
assert_eq!(
menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
&["bg-red", "bg-blue", "bg-yellow"]
);
} else {
panic!("expected completion menu to be open");
}
});
// cx.simulate_keystroke("l");
// cx.executor().run_until_parked();
// cx.update_editor(|editor, _| {
// if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
// assert_eq!(
// menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
// &["bg-blue", "bg-yellow"]
// );
// } else {
// panic!("expected completion menu to be open");
// }
// });
cx.simulate_keystroke("l");
cx.executor().run_until_parked();
cx.update_editor(|editor, _| {
if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
assert_eq!(
menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
&["bg-blue", "bg-yellow"]
);
} else {
panic!("expected completion menu to be open");
}
});
// // When filtering completions, consider the character after the '-' to
// // be the start of a subword.
// cx.set_state(r#"<p class="yelˇ" />"#);
// cx.simulate_keystroke("l");
// cx.executor().run_until_parked();
// cx.update_editor(|editor, _| {
// if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
// assert_eq!(
// menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
// &["bg-yellow"]
// );
// } else {
// panic!("expected completion menu to be open");
// }
// });
// }
// When filtering completions, consider the character after the '-' to
// be the start of a subword.
cx.set_state(r#"<p class="yelˇ" />"#);
cx.simulate_keystroke("l");
cx.executor().run_until_parked();
cx.update_editor(|editor, _| {
if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
assert_eq!(
menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
&["bg-yellow"]
);
} else {
panic!("expected completion menu to be open");
}
});
}
#[gpui::test]
async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {