summaryrefslogtreecommitdiff
path: root/support/texlab/src/completion/bibtex/field_name.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/completion/bibtex/field_name.rs')
-rw-r--r--support/texlab/src/completion/bibtex/field_name.rs308
1 files changed, 161 insertions, 147 deletions
diff --git a/support/texlab/src/completion/bibtex/field_name.rs b/support/texlab/src/completion/bibtex/field_name.rs
index abddad4f5a..039cc5d6b3 100644
--- a/support/texlab/src/completion/bibtex/field_name.rs
+++ b/support/texlab/src/completion/bibtex/field_name.rs
@@ -1,176 +1,190 @@
-use crate::completion::factory;
-use crate::range::RangeExt;
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::{CompletionItem, CompletionParams, Range, TextEdit};
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub struct BibtexFieldNameCompletionProvider;
-
-impl FeatureProvider for BibtexFieldNameCompletionProvider {
- type Params = CompletionParams;
- type Output = Vec<CompletionItem>;
-
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
- if let SyntaxTree::Bibtex(tree) = &request.document().tree {
- let position = request.params.text_document_position.position;
- match tree.find(position).last() {
- Some(BibtexNode::Field(field)) => {
- if field.name.range().contains(position) {
- return make_items(request, field.name.range());
- }
+use crate::{
+ completion::types::{Item, ItemData},
+ feature::FeatureRequest,
+ protocol::{CompletionParams, Range, RangeExt},
+ syntax::{bibtex, SyntaxNode, LANGUAGE_DATA},
+ workspace::DocumentContent,
+};
+
+pub async fn complete_bibtex_fields<'a>(
+ req: &'a FeatureRequest<CompletionParams>,
+ items: &mut Vec<Item<'a>>,
+) {
+ if let DocumentContent::Bibtex(tree) = &req.current().content {
+ let pos = req.params.text_document_position.position;
+ match tree
+ .find(pos)
+ .into_iter()
+ .last()
+ .map(|node| &tree.graph[node])
+ {
+ Some(bibtex::Node::Field(field)) => {
+ if field.name.range().contains(pos) {
+ make_items(items, field.name.range());
+ return;
}
- Some(BibtexNode::Entry(entry)) => {
- if !entry.is_comment() && !entry.ty.range().contains(position) {
- let edit_range = Range::new(position, position);
- if let Some(key) = &entry.key {
- if !key.range().contains(position) {
- return make_items(request, edit_range);
- }
- } else {
- return make_items(request, edit_range);
+ }
+ Some(bibtex::Node::Entry(entry)) => {
+ if !entry.is_comment() && !entry.ty.range().contains(pos) {
+ let range = Range::new(pos, pos);
+ if let Some(key) = &entry.key {
+ if !key.range().contains(pos) {
+ make_items(items, range);
+ return;
}
+ } else {
+ make_items(items, range);
+ return;
}
}
- _ => {}
}
+ _ => (),
}
- Vec::new()
}
}
-fn make_items(
- request: &FeatureRequest<CompletionParams>,
- edit_range: Range,
-) -> Vec<CompletionItem> {
- let mut items = Vec::new();
+fn make_items(items: &mut Vec<Item>, range: Range) {
for field in &LANGUAGE_DATA.fields {
- let text_edit = TextEdit::new(edit_range, (&field.name).into());
- let item = factory::field_name(request, field, text_edit);
+ let item = Item::new(range, ItemData::Field { field });
items.push(item);
}
- items
}
#[cfg(test)]
mod tests {
use super::*;
- use lsp_types::Position;
-
- #[test]
- fn test_inside_first_field() {
- let items = test_feature(
- BibtexFieldNameCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.bib", "@article{foo,\nbar}")],
- main_file: "foo.bib",
- position: Position::new(1, 1),
- ..FeatureSpec::default()
- },
- );
- assert!(!items.is_empty());
- assert_eq!(
- items[0].text_edit.as_ref().map(|edit| edit.range),
- Some(Range::new_simple(1, 0, 1, 3))
- );
+ use crate::{feature::FeatureTester, protocol::Range};
+ use indoc::indoc;
+
+ #[tokio::test]
+ async fn empty_latex_document() {
+ let req = FeatureTester::new()
+ .file("main.tex", "")
+ .main("main.tex")
+ .position(0, 0)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_bibtex_fields(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- #[test]
- fn test_inside_second_field() {
- let items = test_feature(
- BibtexFieldNameCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.bib",
- "@article{foo, bar = {baz}, qux}",
- )],
- main_file: "foo.bib",
- position: Position::new(0, 27),
- ..FeatureSpec::default()
- },
- );
- assert!(!items.is_empty());
- assert_eq!(
- items[0].text_edit.as_ref().map(|edit| edit.range),
- Some(Range::new_simple(0, 27, 0, 30))
- );
+ #[tokio::test]
+ async fn empty_bibtex_document() {
+ let req = FeatureTester::new()
+ .file("main.bib", "")
+ .main("main.bib")
+ .position(0, 0)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_bibtex_fields(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- #[test]
- fn test_inside_entry() {
- let items = test_feature(
- BibtexFieldNameCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.bib", "@article{foo, \n}")],
- main_file: "foo.bib",
- position: Position::new(1, 0),
- ..FeatureSpec::default()
- },
- );
- assert!(!items.is_empty());
- assert_eq!(
- items[0].text_edit.as_ref().map(|edit| edit.range),
- Some(Range::new_simple(1, 0, 1, 0))
- );
+ #[tokio::test]
+ async fn inside_first_field() {
+ let req = FeatureTester::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo,
+ bar}
+ "#
+ ),
+ )
+ .main("main.bib")
+ .position(1, 1)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_bibtex_fields(&req, &mut actual_items).await;
+
+ assert!(!actual_items.is_empty());
+ assert_eq!(actual_items[0].range, Range::new_simple(1, 0, 1, 3));
}
- #[test]
- fn test_inside_content() {
- let items = test_feature(
- BibtexFieldNameCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.bib", "@article{foo,\nbar = {baz}}")],
- main_file: "foo.bib",
- position: Position::new(1, 7),
- ..FeatureSpec::default()
- },
- );
- assert!(items.is_empty());
+ #[tokio::test]
+ async fn inside_second_field() {
+ let req = FeatureTester::new()
+ .file("main.bib", "@article{foo, bar = {baz}, qux}")
+ .main("main.bib")
+ .position(0, 27)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_bibtex_fields(&req, &mut actual_items).await;
+
+ assert!(!actual_items.is_empty());
+ assert_eq!(actual_items[0].range, Range::new_simple(0, 27, 0, 30));
+ }
+
+ #[tokio::test]
+ async fn inside_entry() {
+ let req = FeatureTester::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo,
+ }
+ "#
+ ),
+ )
+ .main("main.bib")
+ .position(1, 0)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_bibtex_fields(&req, &mut actual_items).await;
+
+ assert!(!actual_items.is_empty());
+ assert_eq!(actual_items[0].range, Range::new_simple(1, 0, 1, 0));
}
- #[test]
- fn test_inside_entry_type() {
- let items = test_feature(
- BibtexFieldNameCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.bib", "@article{foo,}")],
- main_file: "foo.bib",
- position: Position::new(0, 3),
- ..FeatureSpec::default()
- },
- );
- assert!(items.is_empty());
+ #[tokio::test]
+ async fn inside_content() {
+ let req = FeatureTester::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @article{foo,
+ bar = {baz}}
+ "#
+ ),
+ )
+ .main("main.bib")
+ .position(1, 7)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_bibtex_fields(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
- // TODO: Improve behavior of this provider
- //
- // #[test]
- // fn test_after_equals_sign() {
- // let items = test_feature(
- // BibtexFieldNameCompletionProvider,
- // FeatureSpec {
- // files: vec![FeatureSpec::file("foo.bib", "@article{foo, bar = \n}")],
- // main_file: "foo.bib",
- // position: Position::new(1, 0),
- // ..FeatureSpec::default()
- // },
- // );
- // assert!(items.is_empty());
- // }
-
- #[test]
- fn test_inside_latex() {
- let items = test_feature(
- BibtexFieldNameCompletionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "@article{foo,}")],
- main_file: "foo.tex",
- position: Position::new(0, 3),
- ..FeatureSpec::default()
- },
- );
- assert!(items.is_empty());
+ #[tokio::test]
+ async fn inside_entry_type() {
+ let req = FeatureTester::new()
+ .file("main.bib", "@article{foo,}")
+ .main("main.bib")
+ .position(0, 3)
+ .test_completion_request()
+ .await;
+ let mut actual_items = Vec::new();
+
+ complete_bibtex_fields(&req, &mut actual_items).await;
+
+ assert!(actual_items.is_empty());
}
}