diff options
Diffstat (limited to 'support/texlab/src/features/completion.rs')
-rw-r--r-- | support/texlab/src/features/completion.rs | 149 |
1 files changed, 74 insertions, 75 deletions
diff --git a/support/texlab/src/features/completion.rs b/support/texlab/src/features/completion.rs index 2a3f4ea9af..3b5187f8eb 100644 --- a/support/texlab/src/features/completion.rs +++ b/support/texlab/src/features/completion.rs @@ -21,18 +21,20 @@ mod util; use std::borrow::Cow; -use cancellation::CancellationToken; -use cstree::TextSize; use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher}; use itertools::Itertools; use lsp_types::{ CompletionItem, CompletionList, CompletionParams, CompletionTextEdit, Documentation, InsertTextFormat, MarkupContent, MarkupKind, TextEdit, }; +use rowan::{ast::AstNode, TextSize}; use rustc_hash::FxHashSet; use crate::{ - syntax::{bibtex, latex, CstNode}, + syntax::{ + bibtex::{self}, + latex, + }, LineIndexExt, }; @@ -69,39 +71,41 @@ use super::{ pub const COMPLETION_LIMIT: usize = 50; -pub fn complete( - request: FeatureRequest<CompletionParams>, - cancellation_token: &CancellationToken, -) -> Option<CompletionList> { +pub fn complete(request: FeatureRequest<CompletionParams>) -> Option<CompletionList> { let mut items = Vec::new(); let context = CursorContext::new(request); log::debug!("[Completion] Cursor: {:?}", context.cursor); - complete_entry_types(&context, &mut items, cancellation_token); - complete_fields(&context, &mut items, cancellation_token); - complete_arguments(&context, &mut items, cancellation_token); - complete_citations(&context, &mut items, cancellation_token); - complete_imports(&context, &mut items, cancellation_token); - complete_colors(&context, &mut items, cancellation_token); - complete_color_models(&context, &mut items, cancellation_token); - complete_acronyms(&context, &mut items, cancellation_token); - complete_glossary_entries(&context, &mut items, cancellation_token); - complete_includes(&context, &mut items, cancellation_token); - complete_labels(&context, &mut items, cancellation_token); - complete_tikz_libraries(&context, &mut items, cancellation_token); - complete_component_environments(&context, &mut items, cancellation_token); - complete_theorem_environments(&context, &mut items, cancellation_token); - complete_user_environments(&context, &mut items, cancellation_token); - complete_begin_command(&context, &mut items, cancellation_token); - complete_component_commands(&context, &mut items, cancellation_token); - complete_user_commands(&context, &mut items, cancellation_token); - - cancellation_token.result().ok()?; + complete_entry_types(&context, &mut items); + complete_fields(&context, &mut items); + complete_arguments(&context, &mut items); + complete_citations(&context, &mut items); + complete_imports(&context, &mut items); + complete_colors(&context, &mut items); + complete_color_models(&context, &mut items); + complete_acronyms(&context, &mut items); + complete_glossary_entries(&context, &mut items); + complete_includes(&context, &mut items); + complete_labels(&context, &mut items); + complete_tikz_libraries(&context, &mut items); + complete_component_environments(&context, &mut items); + complete_theorem_environments(&context, &mut items); + complete_user_environments(&context, &mut items); + complete_begin_command(&context, &mut items); + complete_component_commands(&context, &mut items); + complete_user_commands(&context, &mut items); let mut items = dedup(items); preselect(&context, &mut items); score(&context, &mut items); - items.sort_by_key(|item| (!item.preselect, -item.score.unwrap_or(std::i64::MIN + 1))); + items.sort_by(|a, b| { + a.preselect + .cmp(&b.preselect) + .reverse() + .then_with(|| a.score.cmp(&b.score).reverse()) + .then_with(|| a.data.label().cmp(b.data.label())) + }); + let items: Vec<_> = items .into_iter() .take(COMPLETION_LIMIT) @@ -113,14 +117,12 @@ pub fn complete( let is_incomplete = if context .request - .context + .workspace + .environment .client_info - .lock() - .unwrap() .as_ref() - .map(|info| info.name.as_str()) - .unwrap_or_default() - == "Visual Studio Code" + .as_ref() + .map_or(false, |info| info.name.as_str() == "Visual Studio Code") { true } else { @@ -159,10 +161,10 @@ fn score(context: &CursorContext<CompletionParams>, items: &mut Vec<InternalComp } } Cursor::Latex(token) if token.kind() == latex::WORD => { - if let Some(key) = latex::Key::cast(token.parent()) { + if let Some(key) = token.parent().and_then(latex::Key::cast) { key.words() .take_while(|word| word.text_range() != token.text_range()) - .chain(std::iter::once(token)) + .chain(std::iter::once(token.clone())) .filter(|word| word.text_range().start() < context.offset) .join(" ") .into() @@ -171,16 +173,17 @@ fn score(context: &CursorContext<CompletionParams>, items: &mut Vec<InternalComp } } Cursor::Latex(_) => "".into(), - Cursor::Bibtex(token) if token.kind().is_type() => token.text().into(), - Cursor::Bibtex(token) if token.kind() == bibtex::WORD => { - if let Some(key) = bibtex::Key::cast(token.parent()) { - key.to_string().into() - } else { - token.text().into() - } - } - Cursor::Bibtex(token) if token.kind() == bibtex::COMMAND_NAME => { - token.text().trim_end().into() + Cursor::Bibtex(token) + if matches!( + token.kind(), + bibtex::TYPE + | bibtex::NAME + | bibtex::WORD + | bibtex::COMMAND_NAME + | bibtex::ACCENT_NAME + ) => + { + token.text().into() } Cursor::Bibtex(_) => "".into(), Cursor::Nothing => "".into(), @@ -200,36 +203,32 @@ fn score(context: &CursorContext<CompletionParams>, items: &mut Vec<InternalComp matcher.fuzzy_match(name, &pattern) } InternalCompletionItemData::BeginCommand => matcher.fuzzy_match("begin", &pattern[1..]), - InternalCompletionItemData::Citation { key, .. } => matcher.fuzzy_match(&key, &pattern), + InternalCompletionItemData::Citation { key, .. } => matcher.fuzzy_match(key, &pattern), InternalCompletionItemData::ComponentCommand { name, .. } => { matcher.fuzzy_match(name, &pattern[1..]) } InternalCompletionItemData::ComponentEnvironment { name, .. } => { matcher.fuzzy_match(name, &pattern) } - InternalCompletionItemData::Class { name } => matcher.fuzzy_match(&name, &pattern), - InternalCompletionItemData::Package { name } => matcher.fuzzy_match(&name, &pattern), - InternalCompletionItemData::Color { name } => matcher.fuzzy_match(&name, &pattern), - InternalCompletionItemData::ColorModel { name } => matcher.fuzzy_match(&name, &pattern), - InternalCompletionItemData::Acronym { name } => matcher.fuzzy_match(&name, &pattern), + InternalCompletionItemData::Class { name } => matcher.fuzzy_match(name, &pattern), + InternalCompletionItemData::Package { name } => matcher.fuzzy_match(name, &pattern), + InternalCompletionItemData::Color { name } => matcher.fuzzy_match(name, &pattern), + InternalCompletionItemData::ColorModel { name } => matcher.fuzzy_match(name, &pattern), + InternalCompletionItemData::Acronym { name } => matcher.fuzzy_match(name, &pattern), InternalCompletionItemData::GlossaryEntry { name } => { - matcher.fuzzy_match(&name, &pattern) + matcher.fuzzy_match(name, &pattern) } - InternalCompletionItemData::File { name } => matcher.fuzzy_match(&name, file_pattern), + InternalCompletionItemData::File { name } => matcher.fuzzy_match(name, file_pattern), InternalCompletionItemData::Directory { name } => { - matcher.fuzzy_match(&name, file_pattern) - } - InternalCompletionItemData::Label { name, .. } => matcher.fuzzy_match(&name, &pattern), - InternalCompletionItemData::UserCommand { name } => { - matcher.fuzzy_match(&name, &pattern[1..]) + matcher.fuzzy_match(name, file_pattern) } + InternalCompletionItemData::Label { name, .. } => matcher.fuzzy_match(name, &pattern), + InternalCompletionItemData::UserCommand { name } => matcher.fuzzy_match(name, &pattern), InternalCompletionItemData::UserEnvironment { name } => { - matcher.fuzzy_match(&name, &pattern) - } - InternalCompletionItemData::PgfLibrary { name } => matcher.fuzzy_match(&name, &pattern), - InternalCompletionItemData::TikzLibrary { name } => { - matcher.fuzzy_match(&name, &pattern) + matcher.fuzzy_match(name, &pattern) } + InternalCompletionItemData::PgfLibrary { name } => matcher.fuzzy_match(name, &pattern), + InternalCompletionItemData::TikzLibrary { name } => matcher.fuzzy_match(name, &pattern), }; } } @@ -239,7 +238,7 @@ fn preselect( items: &mut [InternalCompletionItem], ) -> Option<()> { let name = context.cursor.as_latex()?; - let group = latex::CurlyGroupWord::cast(name.parent())?; + let group = latex::CurlyGroupWord::cast(name.parent()?)?; let end = latex::End::cast(group.syntax().parent()?)?; let environment = latex::Environment::cast(end.syntax().parent()?)?; let name = environment.begin()?.name()?.key()?.to_string(); @@ -308,17 +307,16 @@ fn convert_internal_items( data: Some(serde_json::to_value(CompletionItemData::Argument).unwrap()), text_edit: Some(CompletionTextEdit::Edit(text_edit)), documentation: image - .and_then(|image| image_documentation(&context.request, &name, image)), + .and_then(|image| image_documentation(&context.request, name, image)), ..CompletionItem::default() } } InternalCompletionItemData::BeginCommand => { if context .request - .context + .workspace + .environment .client_capabilities - .lock() - .unwrap() .text_document .as_ref() .and_then(|cap| cap.completion.as_ref()) @@ -379,7 +377,7 @@ fn convert_internal_items( |glyph| format!("{}, {}", glyph, component_detail(file_names)), ); let documentation = - image.and_then(|img| image_documentation(&context.request, &name, img)); + image.and_then(|img| image_documentation(&context.request, name, img)); let text_edit = TextEdit::new(range, name.to_string()); CompletionItem { kind: Some(adjust_kind( @@ -459,7 +457,7 @@ fn convert_internal_items( InternalCompletionItemData::Acronym { name } => { let text_edit = TextEdit::new(range, name.to_string()); CompletionItem { - label: name.into(), + label: name, kind: Some(adjust_kind( &context.request, Structure::GlossaryEntry.completion_kind(), @@ -472,7 +470,7 @@ fn convert_internal_items( InternalCompletionItemData::GlossaryEntry { name } => { let text_edit = TextEdit::new(range, name.to_string()); CompletionItem { - label: name.into(), + label: name, kind: Some(adjust_kind( &context.request, Structure::GlossaryEntry.completion_kind(), @@ -517,7 +515,7 @@ fn convert_internal_items( } => { let text_edit = TextEdit::new(range, name.to_string()); CompletionItem { - label: name.into(), + label: name, kind: Some(adjust_kind(&context.request, kind.completion_kind())), detail: header, documentation: footer.map(Documentation::String), @@ -530,6 +528,7 @@ fn convert_internal_items( } InternalCompletionItemData::UserCommand { name } => { let detail = "user-defined".into(); + let name = &name[1..]; let text_edit = TextEdit::new(range, name.to_string()); CompletionItem { kind: Some(adjust_kind( @@ -543,7 +542,7 @@ fn convert_internal_items( } InternalCompletionItemData::UserEnvironment { name } => { let detail = "user-defined".into(); - let text_edit = TextEdit::new(range, name.to_string()); + let text_edit = TextEdit::new(range, name.clone()); CompletionItem { kind: Some(adjust_kind( &context.request, @@ -551,7 +550,7 @@ fn convert_internal_items( )), data: Some(serde_json::to_value(CompletionItemData::Environment).unwrap()), text_edit: Some(CompletionTextEdit::Edit(text_edit)), - ..CompletionItem::new_simple(name.into(), detail) + ..CompletionItem::new_simple(name, detail) } } InternalCompletionItemData::PgfLibrary { name } => { |