summaryrefslogtreecommitdiff
path: root/support/texlab/src/completion/latex/component.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/completion/latex/component.rs')
-rw-r--r--support/texlab/src/completion/latex/component.rs557
1 files changed, 315 insertions, 242 deletions
diff --git a/support/texlab/src/completion/latex/component.rs b/support/texlab/src/completion/latex/component.rs
index 3699a4959e..8cf9f494a4 100644
--- a/support/texlab/src/completion/latex/component.rs
+++ b/support/texlab/src/completion/latex/component.rs
@@ -1,278 +1,351 @@
use super::combinators;
-use crate::completion::factory::{self, LatexComponentId};
-use crate::completion::DATABASE;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::*;
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub struct LatexComponentCommandCompletionProvider;
-
-impl FeatureProvider for LatexComponentCommandCompletionProvider {
- type Params = CompletionParams;
- type Output = Vec<CompletionItem>;
-
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
- combinators::command(request, |command| {
- async move {
- let range = command.short_name_range();
- let mut items = Vec::new();
- for component in DATABASE.related_components(request.related_documents()) {
- let file_names = component.file_names.iter().map(AsRef::as_ref).collect();
- let id = LatexComponentId::Component(file_names);
- for command in &component.commands {
- let text_edit = TextEdit::new(range, (&command.name).into());
- let item = factory::command(
- request,
- (&command.name).into(),
- command.image.as_ref().map(AsRef::as_ref),
- command.glyph.as_ref().map(AsRef::as_ref),
- text_edit,
- &id,
- );
- items.push(item);
- }
- }
- items
+use crate::{
+ completion::types::{Item, ItemData},
+ feature::FeatureRequest,
+ protocol::CompletionParams,
+};
+
+pub async fn complete_latex_component_commands<'a>(
+ req: &'a FeatureRequest<CompletionParams>,
+ items: &mut Vec<Item<'a>>,
+) {
+ combinators::command(req, |cmd_node| async move {
+ let table = req.current().content.as_latex().unwrap();
+ let cmd = table.as_command(cmd_node).unwrap();
+ let range = cmd.short_name_range();
+
+ for comp in req.view.components() {
+ for cmd in &comp.commands {
+ items.push(Item::new(
+ range,
+ ItemData::ComponentCommand {
+ name: &cmd.name,
+ image: cmd.image.as_deref(),
+ glyph: cmd.glyph.as_deref(),
+ file_names: &comp.file_names,
+ },
+ ));
}
- })
- .await
- }
+ }
+ })
+ .await;
}
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub struct LatexComponentEnvironmentCompletionProvider;
-
-impl FeatureProvider for LatexComponentEnvironmentCompletionProvider {
- type Params = CompletionParams;
- type Output = Vec<CompletionItem>;
-
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
- combinators::environment(request, |context| {
- async move {
- let mut items = Vec::new();
- for component in DATABASE.related_components(request.related_documents()) {
- let file_names = component.file_names.iter().map(AsRef::as_ref).collect();
- let id = LatexComponentId::Component(file_names);
- for environment in &component.environments {
- let text_edit = TextEdit::new(context.range, environment.into());
- let item =
- factory::environment(request, environment.into(), text_edit, &id);
- items.push(item);
- }
- }
- items
+pub async fn complete_latex_component_environments<'a>(
+ req: &'a FeatureRequest<CompletionParams>,
+ items: &mut Vec<Item<'a>>,
+) {
+ combinators::environment(req, |ctx| async move {
+ for comp in req.view.components() {
+ for env in &comp.environments {
+ items.push(Item::new(
+ ctx.range,
+ ItemData::ComponentEnvironment {
+ name: env,
+ file_names: &comp.file_names,
+ },
+ ));
}
- })
- .await
- }
+ }
+ })
+ .await;
}
#[cfg(test)]
mod tests {
use super::*;
- use crate::range::RangeExt;
- use lsp_types::{Position, Range};
-
- #[test]
- fn test_command_start() {
- let items = test_feature(
- LatexComponentCommandCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\use")],
- main_file: "foo.tex",
- position: Position::new(0, 0),
- ..FeatureSpec::default()
- },
- );
- assert!(items.is_empty());
+ use crate::{
+ feature::FeatureTester,
+ protocol::{Range, RangeExt},
+ };
+ use indoc::indoc;
+
+ #[tokio::test]
+ async fn empty_latex_document_command() {
+ 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_component_commands(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- #[test]
- fn test_command_end() {
- let items = test_feature(
- LatexComponentCommandCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\use")],
- main_file: "foo.tex",
- position: Position::new(0, 4),
- ..FeatureSpec::default()
- },
- );
- assert!(!items.is_empty());
- assert_eq!(
- items[0].text_edit.as_ref().map(|edit| edit.range),
- Some(Range::new_simple(0, 1, 0, 4))
- );
+ #[tokio::test]
+ async fn empty_bibtex_document_command() {
+ 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_component_commands(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- #[test]
- fn test_command_word() {
- let items = test_feature(
- LatexComponentCommandCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "use")],
- main_file: "foo.tex",
- position: Position::new(0, 2),
- ..FeatureSpec::default()
- },
- );
- assert!(items.is_empty());
+ #[tokio::test]
+ async fn empty_latex_document_environment() {
+ 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_component_environments(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- #[test]
- fn test_command_package() {
- let items = test_feature(
- LatexComponentCommandCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\usepackage{lipsum}\n\\lips")],
- main_file: "foo.tex",
- position: Position::new(1, 2),
- ..FeatureSpec::default()
- },
- );
- assert!(items.iter().any(|item| item.label == "lipsum"));
+ #[tokio::test]
+ async fn empty_bibtex_document_environment() {
+ 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_component_environments(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- #[test]
- fn test_command_package_comma_separated() {
- let items = test_feature(
- LatexComponentCommandCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.tex",
- "\\usepackage{geometry, lipsum}\n\\lips",
- )],
- main_file: "foo.tex",
- position: Position::new(1, 2),
- ..FeatureSpec::default()
- },
- );
- assert!(items.iter().any(|item| item.label == "lipsum"));
+ #[tokio::test]
+ async fn command_start() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"\use"#)
+ .main("main.tex")
+ .position(0, 0)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_commands(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- #[test]
- fn test_command_class() {
- let items = test_feature(
- LatexComponentCommandCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.tex",
- "\\documentclass{book}\n\\chap",
- )],
- main_file: "foo.tex",
- position: Position::new(1, 2),
- ..FeatureSpec::default()
- },
- );
- assert!(items.iter().any(|item| item.label == "chapter"));
+ #[tokio::test]
+ async fn command_end() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"\use"#)
+ .main("main.tex")
+ .position(0, 4)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_commands(&req, &mut actual_items).await;
+
+ assert!(!actual_items.is_empty());
+ assert_eq!(actual_items[0].range, Range::new_simple(0, 1, 0, 4));
}
- #[test]
- fn test_environment_inside_of_empty_begin() {
- let items = test_feature(
- LatexComponentEnvironmentCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\begin{}")],
- main_file: "foo.tex",
- position: Position::new(0, 7),
- ..FeatureSpec::default()
- },
- );
- assert!(!items.is_empty());
- assert_eq!(
- items[0].text_edit.as_ref().map(|edit| edit.range),
- Some(Range::new_simple(0, 7, 0, 7))
- );
+ #[tokio::test]
+ async fn command_word() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"use"#)
+ .main("main.tex")
+ .position(0, 2)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_commands(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- #[test]
- fn test_environment_inside_of_non_empty_end() {
- let items = test_feature(
- LatexComponentEnvironmentCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\end{foo}")],
- main_file: "foo.tex",
- position: Position::new(0, 6),
- ..FeatureSpec::default()
- },
- );
- assert!(!items.is_empty());
- assert_eq!(
- items[0].text_edit.as_ref().map(|edit| edit.range),
- Some(Range::new_simple(0, 5, 0, 8))
- );
+ #[tokio::test]
+ async fn command_package() {
+ let req = FeatureTester::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \usepackage{lipsum}
+ \lips
+ "#
+ ),
+ )
+ .main("main.tex")
+ .position(1, 2)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_commands(&req, &mut actual_items).await;
+
+ assert!(actual_items
+ .iter()
+ .any(|item| item.data.label() == "lipsum"));
}
- #[test]
- fn test_environment_outside_of_empty_begin() {
- let items = test_feature(
- LatexComponentEnvironmentCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\begin{}")],
- main_file: "foo.tex",
- position: Position::new(0, 6),
- ..FeatureSpec::default()
- },
- );
- assert!(items.is_empty());
+ #[tokio::test]
+ async fn command_package_comma_separated() {
+ let req = FeatureTester::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \usepackage{geometry, lipsum}
+ \lips
+ "#
+ ),
+ )
+ .main("main.tex")
+ .position(1, 2)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_commands(&req, &mut actual_items).await;
+
+ assert!(actual_items
+ .iter()
+ .any(|item| item.data.label() == "lipsum"));
}
- #[test]
- fn test_environment_outside_of_empty_end() {
- let items = test_feature(
- LatexComponentEnvironmentCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\end{}")],
- main_file: "foo.tex",
- position: Position::new(0, 6),
- ..FeatureSpec::default()
- },
- );
- assert!(items.is_empty());
+ #[tokio::test]
+ async fn command_class() {
+ let req = FeatureTester::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \documentclass{book}
+ \chap
+ "#
+ ),
+ )
+ .main("main.tex")
+ .position(1, 2)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_commands(&req, &mut actual_items).await;
+
+ assert!(actual_items
+ .iter()
+ .any(|item| item.data.label() == "chapter"));
+ }
+
+ #[tokio::test]
+ async fn environment_inside_of_empty_begin() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"\begin{}"#)
+ .main("main.tex")
+ .position(0, 7)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_environments(&req, &mut actual_items).await;
+
+ assert!(!actual_items.is_empty());
+ assert_eq!(actual_items[0].range, Range::new_simple(0, 7, 0, 7));
}
- #[test]
- fn test_environment_inside_of_other_command() {
- let items = test_feature(
- LatexComponentEnvironmentCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\foo{bar}")],
- main_file: "foo.tex",
- position: Position::new(0, 6),
- ..FeatureSpec::default()
- },
- );
- assert!(items.is_empty());
+ #[tokio::test]
+ async fn environment_inside_of_non_empty_end() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"\end{foo}"#)
+ .main("main.tex")
+ .position(0, 6)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_environments(&req, &mut actual_items).await;
+
+ assert!(!actual_items.is_empty());
+ assert_eq!(actual_items[0].range, Range::new_simple(0, 5, 0, 8));
}
- #[test]
- fn test_environment_inside_second_argument() {
- let items = test_feature(
- LatexComponentEnvironmentCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\begin{foo}{bar}")],
- main_file: "foo.tex",
- position: Position::new(0, 14),
- ..FeatureSpec::default()
- },
- );
- assert!(items.is_empty());
+ #[tokio::test]
+ async fn environment_outside_of_empty_begin() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"\begin{}"#)
+ .main("main.tex")
+ .position(0, 6)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_environments(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- #[test]
- fn test_environment_unterminated() {
- let items = test_feature(
- LatexComponentEnvironmentCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "\\begin{ foo")],
- main_file: "foo.tex",
- position: Position::new(0, 7),
- ..FeatureSpec::default()
- },
- );
- assert!(!items.is_empty());
+ #[tokio::test]
+ async fn environment_outside_of_empty_end() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"\end{}"#)
+ .main("main.tex")
+ .position(0, 6)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_environments(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
+ }
+
+ #[tokio::test]
+ async fn environment_inside_of_other_command() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"\foo{bar}"#)
+ .main("main.tex")
+ .position(0, 6)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_environments(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
+ }
+
+ #[tokio::test]
+ async fn environment_inside_second_argument() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"\begin{foo}{bar}"#)
+ .main("main.tex")
+ .position(0, 14)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_environments(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
+ }
+
+ #[tokio::test]
+ async fn environment_unterminated() {
+ let req = FeatureTester::new()
+ .file("main.tex", r#"\begin{foo"#)
+ .main("main.tex")
+ .position(0, 7)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_latex_component_environments(&req, &mut actual_items).await;
+
+ assert!(!actual_items.is_empty());
+ assert_eq!(actual_items[0].range, Range::new_simple(0, 7, 0, 10));
}
}