diff options
Diffstat (limited to 'support/texlab/src/completion/latex/import.rs')
-rw-r--r-- | support/texlab/src/completion/latex/import.rs | 244 |
1 files changed, 161 insertions, 83 deletions
diff --git a/support/texlab/src/completion/latex/import.rs b/support/texlab/src/completion/latex/import.rs index 7f333f17ae..06c28e189b 100644 --- a/support/texlab/src/completion/latex/import.rs +++ b/support/texlab/src/completion/latex/import.rs @@ -1,44 +1,40 @@ use super::combinators::{self, Parameter}; -use crate::completion::factory; -use crate::completion::DATABASE; -use crate::syntax::*; -use crate::workspace::*; -use futures_boxed::boxed; -use lsp_types::{CompletionItem, CompletionParams, TextEdit}; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexClassImportProvider; - -impl FeatureProvider for LatexClassImportProvider { - type Params = CompletionParams; - type Output = Vec<CompletionItem>; - - #[boxed] - async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output { - import(request, LatexIncludeKind::Class, factory::class).await - } -} +use crate::{ + completion::types::{Item, ItemData}, + components::COMPONENT_DATABASE, + feature::FeatureRequest, + protocol::CompletionParams, + syntax::{LatexIncludeKind, LANGUAGE_DATA}, +}; +use std::{borrow::Cow, collections::HashSet}; -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexPackageImportProvider; - -impl FeatureProvider for LatexPackageImportProvider { - type Params = CompletionParams; - type Output = Vec<CompletionItem>; +pub async fn complete_latex_classes<'a>( + req: &'a FeatureRequest<CompletionParams>, + items: &mut Vec<Item<'a>>, +) { + complete_latex_imports(req, items, LatexIncludeKind::Class, |name| { + ItemData::Class { name } + }) + .await; +} - #[boxed] - async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output { - import(request, LatexIncludeKind::Package, factory::package).await - } +pub async fn complete_latex_packages<'a>( + req: &'a FeatureRequest<CompletionParams>, + items: &mut Vec<Item<'a>>, +) { + complete_latex_imports(req, items, LatexIncludeKind::Package, |name| { + ItemData::Package { name } + }) + .await; } -async fn import<F>( - request: &FeatureRequest<CompletionParams>, +async fn complete_latex_imports<'a, F>( + req: &'a FeatureRequest<CompletionParams>, + items: &mut Vec<Item<'a>>, kind: LatexIncludeKind, - factory: F, -) -> Vec<CompletionItem> -where - F: Fn(&FeatureRequest<CompletionParams>, &'static str, TextEdit) -> CompletionItem, + mut factory: F, +) where + F: FnMut(Cow<'a, str>) -> ItemData<'a>, { let extension = if kind == LatexIncludeKind::Package { "sty" @@ -50,61 +46,143 @@ where .include_commands .iter() .filter(|cmd| cmd.kind == kind) - .map(|cmd| Parameter::new(&cmd.name, cmd.index)); - - combinators::argument(request, parameters, |context| { - async move { - let mut items = Vec::new(); - for component in &DATABASE.components { - for file_name in &component.file_names { - if file_name.ends_with(extension) { - let stem = &file_name[0..file_name.len() - 4]; - let text_edit = TextEdit::new(context.range, stem.into()); - let item = factory(request, stem, text_edit); - items.push(item); - } - } - } - items - } + .map(|cmd| Parameter { + name: &cmd.name[1..], + index: cmd.index, + }); + + combinators::argument(req, parameters, |ctx| async move { + let resolver = req.distro.resolver().await; + let mut file_names = HashSet::new(); + COMPONENT_DATABASE + .components + .iter() + .flat_map(|comp| comp.file_names.iter()) + .filter(|file_name| file_name.ends_with(extension)) + .for_each(|file_name| { + file_names.insert(file_name); + let stem = &file_name[0..file_name.len() - 4]; + let data = factory(stem.into()); + let item = Item::new(ctx.range, data); + items.push(item); + }); + + resolver + .files_by_name + .keys() + .filter(|file_name| file_name.ends_with(extension) && !file_names.contains(file_name)) + .for_each(|file_name| { + let stem = &file_name[0..file_name.len() - 4]; + let data = factory(stem.to_owned().into()); + let item = Item::new(ctx.range, data); + items.push(item); + }); }) - .await + .await; } #[cfg(test)] mod tests { use super::*; - use lsp_types::Position; - - #[test] - fn test_class() { - let items = test_feature( - LatexClassImportProvider, - FeatureSpec { - files: vec![FeatureSpec::file("foo.tex", "\\documentclass{}")], - main_file: "foo.tex", - position: Position::new(0, 15), - ..FeatureSpec::default() - }, - ); - - assert!(items.iter().any(|item| item.label == "beamer")); - assert!(items.iter().all(|item| item.label != "amsmath")); + use crate::feature::FeatureTester; + + #[tokio::test] + async fn empty_latex_document_class() { + let req = FeatureTester::new() + .file("main.tex", "") + .main("main.tex") + .position(0, 0) + .test_completion_request() + .await; + let mut actual_items = Vec::new(); + + complete_latex_classes(&req, &mut actual_items).await; + + assert!(actual_items.is_empty()); + } + + #[tokio::test] + async fn empty_bibtex_document_class() { + let req = FeatureTester::new() + .file("main.bib", "") + .main("main.bib") + .position(0, 0) + .test_completion_request() + .await; + let mut actual_items = Vec::new(); + + complete_latex_classes(&req, &mut actual_items).await; + + assert!(actual_items.is_empty()); + } + + #[tokio::test] + async fn empty_latex_document_package() { + let req = FeatureTester::new() + .file("main.tex", "") + .main("main.tex") + .position(0, 0) + .test_completion_request() + .await; + let mut actual_items = Vec::new(); + + complete_latex_packages(&req, &mut actual_items).await; + + assert!(actual_items.is_empty()); } - #[test] - fn test_package() { - let items = test_feature( - LatexPackageImportProvider, - FeatureSpec { - files: vec![FeatureSpec::file("foo.tex", "\\usepackage{}")], - main_file: "foo.tex", - position: Position::new(0, 12), - ..FeatureSpec::default() - }, - ); - - assert!(items.iter().all(|item| item.label != "beamer")); - assert!(items.iter().any(|item| item.label == "amsmath")); + #[tokio::test] + async fn empty_bibtex_document_package() { + let req = FeatureTester::new() + .file("main.bib", "") + .main("main.bib") + .position(0, 0) + .test_completion_request() + .await; + let mut actual_items = Vec::new(); + + complete_latex_packages(&req, &mut actual_items).await; + + assert!(actual_items.is_empty()); + } + + #[tokio::test] + async fn class() { + let req = FeatureTester::new() + .file("main.tex", r#"\documentclass{}"#) + .main("main.tex") + .position(0, 15) + .test_completion_request() + .await; + let mut actual_items = Vec::new(); + + complete_latex_classes(&req, &mut actual_items).await; + + assert!(actual_items + .iter() + .any(|item| item.data.label() == "beamer")); + assert!(actual_items + .iter() + .all(|item| item.data.label() != "amsmath")); + } + + #[tokio::test] + async fn package() { + let req = FeatureTester::new() + .file("main.tex", r#"\usepackage{}"#) + .main("main.tex") + .position(0, 12) + .test_completion_request() + .await; + let mut actual_items = Vec::new(); + + complete_latex_packages(&req, &mut actual_items).await; + + assert!(actual_items + .iter() + .all(|item| item.data.label() != "beamer")); + assert!(actual_items + .iter() + .any(|item| item.data.label() == "amsmath")); } } |