summaryrefslogtreecommitdiff
path: root/support/texlab/src/definition
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/definition')
-rw-r--r--support/texlab/src/definition/bibtex_string.rs225
-rw-r--r--support/texlab/src/definition/latex_citation.rs161
-rw-r--r--support/texlab/src/definition/latex_cmd.rs154
-rw-r--r--support/texlab/src/definition/latex_command.rs106
-rw-r--r--support/texlab/src/definition/latex_label.rs192
-rw-r--r--support/texlab/src/definition/mod.rs32
6 files changed, 482 insertions, 388 deletions
diff --git a/support/texlab/src/definition/bibtex_string.rs b/support/texlab/src/definition/bibtex_string.rs
index 0e4f643153..a1fe5132c0 100644
--- a/support/texlab/src/definition/bibtex_string.rs
+++ b/support/texlab/src/definition/bibtex_string.rs
@@ -1,20 +1,23 @@
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::{LocationLink, Position, TextDocumentPositionParams};
+use crate::{
+ feature::{FeatureProvider, FeatureRequest},
+ protocol::{LocationLink, Position, TextDocumentPositionParams, Uri},
+ syntax::{bibtex, SyntaxNode},
+ workspace::DocumentContent,
+};
+use async_trait::async_trait;
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct BibtexStringDefinitionProvider;
+#[async_trait]
impl FeatureProvider for BibtexStringDefinitionProvider {
type Params = TextDocumentPositionParams;
type Output = Vec<LocationLink>;
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
- if let SyntaxTree::Bibtex(tree) = &request.document().tree {
- if let Some(reference) = Self::find_reference(tree, request.params.position) {
- return Self::find_definitions(&request.view.document.uri, tree, reference);
+ async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output {
+ if let DocumentContent::Bibtex(tree) = &req.current().content {
+ if let Some(reference) = Self::find_reference(tree, req.params.position) {
+ return Self::find_definitions(&req.current().uri, tree, reference);
}
}
Vec::new()
@@ -22,31 +25,36 @@ impl FeatureProvider for BibtexStringDefinitionProvider {
}
impl BibtexStringDefinitionProvider {
- fn find_reference(tree: &BibtexSyntaxTree, position: Position) -> Option<&BibtexToken> {
- let mut nodes = tree.find(position);
+ fn find_reference(tree: &bibtex::Tree, pos: Position) -> Option<&bibtex::Token> {
+ let mut nodes = tree.find(pos);
nodes.reverse();
- match (&nodes[0], &nodes.get(1)) {
- (BibtexNode::Word(word), Some(BibtexNode::Field(_)))
- | (BibtexNode::Word(word), Some(BibtexNode::Concat(_))) => Some(&word.token),
+ match (
+ &tree.graph[nodes[0]],
+ nodes.get(1).map(|node| &tree.graph[*node]),
+ ) {
+ (bibtex::Node::Word(word), Some(bibtex::Node::Field(_)))
+ | (bibtex::Node::Word(word), Some(bibtex::Node::Concat(_))) => Some(&word.token),
_ => None,
}
}
fn find_definitions(
uri: &Uri,
- tree: &BibtexSyntaxTree,
- reference: &BibtexToken,
+ tree: &bibtex::Tree,
+ reference: &bibtex::Token,
) -> Vec<LocationLink> {
let mut links = Vec::new();
- for string in tree.strings() {
- if let Some(name) = &string.name {
- if name.text() == reference.text() {
- links.push(LocationLink {
- origin_selection_range: Some(reference.range()),
- target_uri: uri.clone().into(),
- target_range: string.range(),
- target_selection_range: name.range(),
- });
+ for node in tree.children(tree.root) {
+ if let Some(string) = tree.as_string(node) {
+ if let Some(name) = &string.name {
+ if name.text() == reference.text() {
+ links.push(LocationLink {
+ origin_selection_range: Some(reference.range()),
+ target_uri: uri.clone().into(),
+ target_range: string.range(),
+ target_selection_range: name.range(),
+ });
+ }
}
}
}
@@ -57,76 +65,107 @@ impl BibtexStringDefinitionProvider {
#[cfg(test)]
mod tests {
use super::*;
- use crate::range::RangeExt;
- use lsp_types::{Position, Range};
-
- #[test]
- fn test_simple() {
- let links = test_feature(
- BibtexStringDefinitionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.bib",
- "@string{foo = {bar}}\n@article{bar, author = foo}",
- )],
- main_file: "foo.bib",
- position: Position::new(1, 24),
- ..FeatureSpec::default()
- },
- );
-
- assert_eq!(
- links,
- vec![LocationLink {
- origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)),
- target_uri: FeatureSpec::uri("foo.bib"),
- target_range: Range::new_simple(0, 0, 0, 20),
- target_selection_range: Range::new_simple(0, 8, 0, 11)
- }]
- );
+ use crate::{
+ feature::FeatureTester,
+ protocol::{Range, RangeExt},
+ };
+ use indoc::indoc;
+
+ #[tokio::test]
+ async fn empty_latex_document() {
+ let actual_links = FeatureTester::new()
+ .file("main.tex", "")
+ .main("main.tex")
+ .position(0, 0)
+ .test_position(BibtexStringDefinitionProvider)
+ .await;
+
+ assert!(actual_links.is_empty());
+ }
+
+ #[tokio::test]
+ async fn empty_bibtex_document() {
+ let actual_links = FeatureTester::new()
+ .file("main.bib", "")
+ .main("main.bib")
+ .position(0, 0)
+ .test_position(BibtexStringDefinitionProvider)
+ .await;
+
+ assert!(actual_links.is_empty());
+ }
+
+ #[tokio::test]
+ async fn simple() {
+ let actual_links = FeatureTester::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @string{foo = {bar}}
+ @article{bar, author = foo}
+ "#
+ ),
+ )
+ .main("main.bib")
+ .position(1, 24)
+ .test_position(BibtexStringDefinitionProvider)
+ .await;
+
+ let expected_links = vec![LocationLink {
+ origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)),
+ target_uri: FeatureTester::uri("main.bib").into(),
+ target_range: Range::new_simple(0, 0, 0, 20),
+ target_selection_range: Range::new_simple(0, 8, 0, 11),
+ }];
+
+ assert_eq!(actual_links, expected_links);
}
- #[test]
- fn test_concat() {
- let links = test_feature(
- BibtexStringDefinitionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.bib",
- "@string{foo = {bar}}\n@article{bar, author = foo # \"bar\"}",
- )],
- main_file: "foo.bib",
- position: Position::new(1, 24),
- ..FeatureSpec::default()
- },
- );
-
- assert_eq!(
- links,
- vec![LocationLink {
- origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)),
- target_uri: FeatureSpec::uri("foo.bib"),
- target_range: Range::new_simple(0, 0, 0, 20),
- target_selection_range: Range::new_simple(0, 8, 0, 11)
- }]
- );
+ #[tokio::test]
+ async fn concat() {
+ let actual_links = FeatureTester::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @string{foo = {bar}}
+ @article{bar, author = foo # "bar"}
+ "#
+ ),
+ )
+ .main("main.bib")
+ .position(1, 24)
+ .test_position(BibtexStringDefinitionProvider)
+ .await;
+
+ let expected_links = vec![LocationLink {
+ origin_selection_range: Some(Range::new_simple(1, 23, 1, 26)),
+ target_uri: FeatureTester::uri("main.bib").into(),
+ target_range: Range::new_simple(0, 0, 0, 20),
+ target_selection_range: Range::new_simple(0, 8, 0, 11),
+ }];
+
+ assert_eq!(actual_links, expected_links);
}
- #[test]
- fn test_field() {
- let links = test_feature(
- BibtexStringDefinitionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.bib",
- "@string{foo = {bar}}\n@article{bar, author = foo}",
- )],
- main_file: "foo.bib",
- position: Position::new(1, 18),
- ..FeatureSpec::default()
- },
- );
-
- assert!(links.is_empty());
+ #[tokio::test]
+ async fn field() {
+ let actual_links = FeatureTester::new()
+ .file(
+ "main.bib",
+ indoc!(
+ r#"
+ @string{foo = {bar}}
+ @article{bar, author = foo}
+ "#
+ ),
+ )
+ .main("main.bib")
+ .position(1, 18)
+ .test_position(BibtexStringDefinitionProvider)
+ .await;
+
+ assert!(actual_links.is_empty());
}
}
diff --git a/support/texlab/src/definition/latex_citation.rs b/support/texlab/src/definition/latex_citation.rs
index daefa8b2ef..aa93e25fc0 100644
--- a/support/texlab/src/definition/latex_citation.rs
+++ b/support/texlab/src/definition/latex_citation.rs
@@ -1,22 +1,24 @@
-use crate::range::RangeExt;
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::{LocationLink, TextDocumentPositionParams};
+use crate::{
+ feature::{FeatureProvider, FeatureRequest},
+ protocol::{LocationLink, RangeExt, TextDocumentPositionParams},
+ syntax::{latex, SyntaxNode},
+ workspace::{Document, DocumentContent},
+};
+use async_trait::async_trait;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct LatexCitationDefinitionProvider;
+#[async_trait]
impl FeatureProvider for LatexCitationDefinitionProvider {
type Params = TextDocumentPositionParams;
type Output = Vec<LocationLink>;
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
+ async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output {
let mut links = Vec::new();
- if let Some(reference) = Self::find_reference(&request) {
- for document in request.related_documents() {
- Self::find_definitions(&document, &reference, &mut links);
+ if let Some(reference) = Self::find_reference(req) {
+ for doc in req.related() {
+ Self::find_definitions(&doc, &reference, &mut links);
}
}
links
@@ -24,96 +26,93 @@ impl FeatureProvider for LatexCitationDefinitionProvider {
}
impl LatexCitationDefinitionProvider {
- fn find_definitions(
- document: &Document,
- reference: &LatexToken,
- links: &mut Vec<LocationLink>,
- ) {
- if let SyntaxTree::Bibtex(tree) = &document.tree {
- for entry in tree.entries() {
+ fn find_reference(req: &FeatureRequest<TextDocumentPositionParams>) -> Option<&latex::Token> {
+ req.current().content.as_latex().and_then(|table| {
+ table
+ .citations
+ .iter()
+ .flat_map(|citation| citation.keys(&table))
+ .find(|key| key.range().contains(req.params.position))
+ })
+ }
+
+ fn find_definitions(doc: &Document, reference: &latex::Token, links: &mut Vec<LocationLink>) {
+ if let DocumentContent::Bibtex(tree) = &doc.content {
+ for entry in tree
+ .children(tree.root)
+ .filter_map(|node| tree.as_entry(node))
+ {
if let Some(key) = &entry.key {
if key.text() == reference.text() {
- let link = LocationLink {
+ links.push(LocationLink {
origin_selection_range: Some(reference.range()),
- target_uri: document.uri.clone().into(),
+ target_uri: doc.uri.clone().into(),
target_range: entry.range(),
target_selection_range: key.range(),
- };
- links.push(link);
+ });
}
}
}
}
}
-
- fn find_reference(request: &FeatureRequest<TextDocumentPositionParams>) -> Option<&LatexToken> {
- if let SyntaxTree::Latex(tree) = &request.document().tree {
- tree.citations
- .iter()
- .flat_map(LatexCitation::keys)
- .find(|key| key.range().contains(request.params.position))
- } else {
- None
- }
- }
}
#[cfg(test)]
mod tests {
use super::*;
- use lsp_types::{Position, Range};
+ use crate::{feature::FeatureTester, protocol::Range};
+ use indoc::indoc;
+
+ #[tokio::test]
+ async fn empty_latex_document() {
+ let actual_links = FeatureTester::new()
+ .file("main.tex", "")
+ .main("main.tex")
+ .position(0, 0)
+ .test_position(LatexCitationDefinitionProvider)
+ .await;
- #[test]
- fn test_has_definition() {
- let links = test_feature(
- LatexCitationDefinitionProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file("foo.tex", "\\addbibresource{baz.bib}\n\\cite{foo}"),
- FeatureSpec::file("bar.bib", "@article{foo, bar = {baz}}"),
- FeatureSpec::file("baz.bib", "@article{foo, bar = {baz}}"),
- ],
- main_file: "foo.tex",
- position: Position::new(1, 6),
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- links,
- vec![LocationLink {
- origin_selection_range: Some(Range::new_simple(1, 6, 1, 9)),
- target_uri: FeatureSpec::uri("baz.bib"),
- target_range: Range::new_simple(0, 0, 0, 26),
- target_selection_range: Range::new_simple(0, 9, 0, 12)
- }]
- );
+ assert!(actual_links.is_empty());
}
- #[test]
- fn test_no_definition_latex() {
- let links = test_feature(
- LatexCitationDefinitionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "")],
- main_file: "foo.tex",
- position: Position::new(0, 0),
- ..FeatureSpec::default()
- },
- );
- assert!(links.is_empty());
+ #[tokio::test]
+ async fn empty_bibtex_document() {
+ let actual_links = FeatureTester::new()
+ .file("main.bib", "")
+ .main("main.bib")
+ .position(0, 0)
+ .test_position(LatexCitationDefinitionProvider)
+ .await;
+
+ assert!(actual_links.is_empty());
}
- #[test]
- fn test_no_definition_bibtex() {
- let links = test_feature(
- LatexCitationDefinitionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.bib", "")],
- main_file: "foo.bib",
- position: Position::new(0, 0),
- ..FeatureSpec::default()
- },
- );
- assert!(links.is_empty());
+ #[tokio::test]
+ async fn has_definition() {
+ let actual_links = FeatureTester::new()
+ .file(
+ "foo.tex",
+ indoc!(
+ r#"
+ \addbibresource{baz.bib}
+ \cite{foo}
+ "#
+ ),
+ )
+ .file("bar.bib", r#"@article{foo, bar = {baz}}"#)
+ .file("baz.bib", r#"@article{foo, bar = {baz}}"#)
+ .main("foo.tex")
+ .position(1, 6)
+ .test_position(LatexCitationDefinitionProvider)
+ .await;
+
+ let exepcted_links = vec![LocationLink {
+ origin_selection_range: Some(Range::new_simple(1, 6, 1, 9)),
+ target_uri: FeatureTester::uri("baz.bib").into(),
+ target_range: Range::new_simple(0, 0, 0, 26),
+ target_selection_range: Range::new_simple(0, 9, 0, 12),
+ }];
+
+ assert_eq!(actual_links, exepcted_links);
}
}
diff --git a/support/texlab/src/definition/latex_cmd.rs b/support/texlab/src/definition/latex_cmd.rs
new file mode 100644
index 0000000000..35fc42be4f
--- /dev/null
+++ b/support/texlab/src/definition/latex_cmd.rs
@@ -0,0 +1,154 @@
+use crate::{
+ feature::{FeatureProvider, FeatureRequest},
+ protocol::{LocationLink, TextDocumentPositionParams},
+ syntax::SyntaxNode,
+ workspace::DocumentContent,
+};
+use async_trait::async_trait;
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
+pub struct LatexCommandDefinitionProvider;
+
+#[async_trait]
+impl FeatureProvider for LatexCommandDefinitionProvider {
+ type Params = TextDocumentPositionParams;
+ type Output = Vec<LocationLink>;
+
+ async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output {
+ let mut links = Vec::new();
+ if let DocumentContent::Latex(table) = &req.current().content {
+ if let Some(cmd) = table
+ .find(req.params.position)
+ .last()
+ .and_then(|node| table.as_command(*node))
+ {
+ for doc in req.related() {
+ if let DocumentContent::Latex(table) = &doc.content {
+ table
+ .command_definitions
+ .iter()
+ .filter(|def| def.definition_name(&table) == cmd.name.text())
+ .map(|def| {
+ let def_range = table[def.parent].range();
+ LocationLink {
+ origin_selection_range: Some(cmd.range()),
+ target_uri: doc.uri.clone().into(),
+ target_range: def_range,
+ target_selection_range: def_range,
+ }
+ })
+ .for_each(|link| links.push(link));
+
+ table
+ .math_operators
+ .iter()
+ .filter(|op| op.definition_name(&table) == cmd.name.text())
+ .map(|op| {
+ let def_range = table[op.parent].range();
+ LocationLink {
+ origin_selection_range: Some(cmd.range()),
+ target_uri: doc.uri.clone().into(),
+ target_range: def_range,
+ target_selection_range: def_range,
+ }
+ })
+ .for_each(|link| links.push(link));
+ }
+ }
+ }
+ }
+ links
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::{
+ feature::FeatureTester,
+ protocol::{Range, RangeExt},
+ };
+ use indoc::indoc;
+
+ #[tokio::test]
+ async fn empty_latex_document() {
+ let actual_links = FeatureTester::new()
+ .file("main.tex", "")
+ .main("main.tex")
+ .position(0, 0)
+ .test_position(LatexCommandDefinitionProvider)
+ .await;
+
+ assert!(actual_links.is_empty());
+ }
+
+ #[tokio::test]
+ async fn empty_bibtex_document() {
+ let actual_links = FeatureTester::new()
+ .file("main.bib", "")
+ .main("main.bib")
+ .position(0, 0)
+ .test_position(LatexCommandDefinitionProvider)
+ .await;
+
+ assert!(actual_links.is_empty());
+ }
+
+ #[tokio::test]
+ async fn command_definition() {
+ let actual_links = FeatureTester::new()
+ .file(
+ "foo.tex",
+ indoc!(
+ r#"
+ \include{bar}
+ \foo
+ "#
+ ),
+ )
+ .file("bar.tex", r#"\newcommand{\foo}{bar}"#)
+ .file("baz.tex", r#"\newcommand{\foo}{baz}"#)
+ .main("foo.tex")
+ .position(1, 3)
+ .test_position(LatexCommandDefinitionProvider)
+ .await;
+
+ let expected_links = vec![LocationLink {
+ origin_selection_range: Some(Range::new_simple(1, 0, 1, 4)),
+ target_uri: FeatureTester::uri("bar.tex").into(),
+ target_range: Range::new_simple(0, 0, 0, 22),
+ target_selection_range: Range::new_simple(0, 0, 0, 22),
+ }];
+
+ assert_eq!(actual_links, expected_links);
+ }
+
+ #[tokio::test]
+ async fn math_operator() {
+ let actual_links = FeatureTester::new()
+ .file(
+ "foo.tex",
+ indoc!(
+ r#"
+ \include{bar}
+ \foo
+ "#
+ ),
+ )
+ .file("bar.tex", r#"\DeclareMathOperator{\foo}{bar}"#)
+ .file("baz.tex", r#"\DeclareMathOperator{\foo}{baz}"#)
+ .main("foo.tex")
+ .position(1, 3)
+ .test_position(LatexCommandDefinitionProvider)
+ .await;
+
+ let expected_links = vec![LocationLink {
+ origin_selection_range: Some(Range::new_simple(1, 0, 1, 4)),
+ target_uri: FeatureTester::uri("bar.tex").into(),
+ target_range: Range::new_simple(0, 0, 0, 31),
+ target_selection_range: Range::new_simple(0, 0, 0, 31),
+ }];
+
+ assert_eq!(actual_links, expected_links);
+ }
+}
diff --git a/support/texlab/src/definition/latex_command.rs b/support/texlab/src/definition/latex_command.rs
deleted file mode 100644
index 7f91412ef7..0000000000
--- a/support/texlab/src/definition/latex_command.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::{LocationLink, TextDocumentPositionParams};
-
-pub struct LatexCommandDefinitionProvider;
-
-impl FeatureProvider for LatexCommandDefinitionProvider {
- type Params = TextDocumentPositionParams;
- type Output = Vec<LocationLink>;
-
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
- let mut definitions = Vec::new();
- if let SyntaxTree::Latex(tree) = &request.document().tree {
- if let Some(LatexNode::Command(command)) = tree.find(request.params.position).last() {
- for document in request.related_documents() {
- if let SyntaxTree::Latex(tree) = &document.tree {
- tree.command_definitions
- .iter()
- .filter(|def| def.definition.name.text() == command.name.text())
- .map(|def| LocationLink {
- origin_selection_range: Some(command.range()),
- target_uri: document.uri.clone().into(),
- target_range: def.range(),
- target_selection_range: def.range(),
- })
- .for_each(|def| definitions.push(def));
-
- tree.math
- .operators
- .iter()
- .filter(|op| op.definition.name.text() == command.name.text())
- .map(|op| LocationLink {
- origin_selection_range: Some(command.range()),
- target_uri: document.uri.clone().into(),
- target_range: op.range(),
- target_selection_range: op.range(),
- })
- .for_each(|def| definitions.push(def));
- }
- }
- }
- }
- definitions
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::range::RangeExt;
- use lsp_types::{Position, Range};
-
- #[test]
- fn test_command_definition() {
- let links = test_feature(
- LatexCommandDefinitionProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file("foo.tex", "\\include{bar}\n\\foo"),
- FeatureSpec::file("bar.tex", "\\newcommand{\\foo}{bar}"),
- FeatureSpec::file("baz.tex", "\\newcommand{\\foo}{baz}"),
- ],
- main_file: "foo.tex",
- position: Position::new(1, 3),
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- links,
- vec![LocationLink {
- origin_selection_range: Some(Range::new_simple(1, 0, 1, 4)),
- target_uri: FeatureSpec::uri("bar.tex"),
- target_range: Range::new_simple(0, 0, 0, 22),
- target_selection_range: Range::new_simple(0, 0, 0, 22),
- }]
- );
- }
-
- #[test]
- fn test_math_operator() {
- let links = test_feature(
- LatexCommandDefinitionProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file("foo.tex", "\\include{bar}\n\\foo"),
- FeatureSpec::file("bar.tex", "\\DeclareMathOperator{\\foo}{bar}"),
- FeatureSpec::file("baz.tex", "\\DeclareMathOperator{\\foo}{baz}"),
- ],
- main_file: "foo.tex",
- position: Position::new(1, 3),
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- links,
- vec![LocationLink {
- origin_selection_range: Some(Range::new_simple(1, 0, 1, 4)),
- target_uri: FeatureSpec::uri("bar.tex"),
- target_range: Range::new_simple(0, 0, 0, 31),
- target_selection_range: Range::new_simple(0, 0, 0, 31)
- }]
- );
- }
-}
diff --git a/support/texlab/src/definition/latex_label.rs b/support/texlab/src/definition/latex_label.rs
index 8d1a415749..5318982ccd 100644
--- a/support/texlab/src/definition/latex_label.rs
+++ b/support/texlab/src/definition/latex_label.rs
@@ -1,26 +1,40 @@
-use crate::range::RangeExt;
-use crate::symbol::build_section_tree;
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::{LocationLink, TextDocumentPositionParams};
-use std::sync::Arc;
+use crate::{
+ feature::{DocumentView, FeatureProvider, FeatureRequest},
+ outline::{Outline, OutlineContext, OutlineContextItem},
+ protocol::{LocationLink, Options, RangeExt, TextDocumentPositionParams},
+ symbol::build_section_tree,
+ syntax::{latex, LatexLabelKind, SyntaxNode},
+ workspace::DocumentContent,
+};
+use async_trait::async_trait;
+use std::{path::Path, sync::Arc};
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct LatexLabelDefinitionProvider;
+#[async_trait]
impl FeatureProvider for LatexLabelDefinitionProvider {
type Params = TextDocumentPositionParams;
type Output = Vec<LocationLink>;
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
+ async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output {
let mut links = Vec::new();
- if let Some(reference) = Self::find_reference(&request) {
- for document in request.related_documents() {
- let workspace = Arc::clone(&request.view.workspace);
- let view = DocumentView::new(workspace, Arc::clone(&document));
- Self::find_definitions(&view, &reference, &mut links);
+ if let Some(reference) = Self::find_reference(req) {
+ for doc in req.related() {
+ let snapshot = Arc::clone(&req.view.snapshot);
+ let view = DocumentView::analyze(
+ snapshot,
+ Arc::clone(&doc),
+ &req.options,
+ &req.current_dir,
+ );
+ Self::find_definitions(
+ &view,
+ &req.options,
+ &req.current_dir,
+ &reference,
+ &mut links,
+ );
}
}
links
@@ -28,18 +42,32 @@ impl FeatureProvider for LatexLabelDefinitionProvider {
}
impl LatexLabelDefinitionProvider {
+ fn find_reference(req: &FeatureRequest<TextDocumentPositionParams>) -> Option<&latex::Token> {
+ if let DocumentContent::Latex(table) = &req.current().content {
+ table
+ .labels
+ .iter()
+ .flat_map(|label| label.names(&table))
+ .find(|label| label.range().contains(req.params.position))
+ } else {
+ None
+ }
+ }
+
fn find_definitions(
view: &DocumentView,
- reference: &LatexToken,
+ options: &Options,
+ current_dir: &Path,
+ reference: &latex::Token,
links: &mut Vec<LocationLink>,
) {
- if let SyntaxTree::Latex(tree) = &view.document.tree {
- let outline = Outline::from(view);
- let section_tree = build_section_tree(view, tree);
- for label in &tree.structure.labels {
+ if let DocumentContent::Latex(table) = &view.current.content {
+ let outline = Outline::analyze(view, options, current_dir);
+ let section_tree = build_section_tree(view, table, options, current_dir);
+ for label in &table.labels {
if label.kind == LatexLabelKind::Definition {
- let context = OutlineContext::parse(view, label, &outline);
- for name in label.names() {
+ let context = OutlineContext::parse(view, &outline, *label);
+ for name in label.names(&table) {
if name.text() == reference.text() {
let target_range = if let Some(OutlineContextItem::Section { .. }) =
context.as_ref().map(|ctx| &ctx.item)
@@ -53,9 +81,10 @@ impl LatexLabelDefinitionProvider {
links.push(LocationLink {
origin_selection_range: Some(reference.range()),
- target_uri: view.document.uri.clone().into(),
- target_range: target_range.unwrap_or_else(|| label.range()),
- target_selection_range: label.range(),
+ target_uri: view.current.uri.clone().into(),
+ target_range: target_range
+ .unwrap_or_else(|| table[label.parent].range()),
+ target_selection_range: table[label.parent].range(),
});
}
}
@@ -63,79 +92,64 @@ impl LatexLabelDefinitionProvider {
}
}
}
-
- fn find_reference(request: &FeatureRequest<TextDocumentPositionParams>) -> Option<&LatexToken> {
- if let SyntaxTree::Latex(tree) = &request.document().tree {
- tree.structure
- .labels
- .iter()
- .flat_map(LatexLabel::names)
- .find(|label| label.range().contains(request.params.position))
- } else {
- None
- }
- }
}
#[cfg(test)]
mod tests {
use super::*;
- use lsp_types::{Position, Range};
+ use crate::{feature::FeatureTester, protocol::Range};
+ use indoc::indoc;
- #[test]
- fn test_has_definition() {
- let links = test_feature(
- LatexLabelDefinitionProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file("foo.tex", "\\label{foo}"),
- FeatureSpec::file(
- "bar.tex",
- "\\begin{a}\\begin{b}\\label{foo}\\end{b}\\end{a}\n\\input{baz.tex}",
- ),
- FeatureSpec::file("baz.tex", "\\ref{foo}"),
- ],
- main_file: "baz.tex",
- position: Position::new(0, 5),
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- links,
- vec![LocationLink {
- origin_selection_range: Some(Range::new_simple(0, 5, 0, 8)),
- target_uri: FeatureSpec::uri("bar.tex"),
- target_range: Range::new_simple(0, 18, 0, 29),
- target_selection_range: Range::new_simple(0, 18, 0, 29)
- }]
- );
+ #[tokio::test]
+ async fn empty_latex_document() {
+ let actual_links = FeatureTester::new()
+ .file("main.tex", "")
+ .main("main.tex")
+ .position(0, 0)
+ .test_position(LatexLabelDefinitionProvider)
+ .await;
+
+ assert!(actual_links.is_empty());
}
- #[test]
- fn test_no_definition_latex() {
- let links = test_feature(
- LatexLabelDefinitionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.tex", "")],
- main_file: "foo.tex",
- position: Position::new(0, 0),
- ..FeatureSpec::default()
- },
- );
- assert!(links.is_empty());
+ #[tokio::test]
+ async fn empty_bibtex_document() {
+ let actual_links = FeatureTester::new()
+ .file("main.bib", "")
+ .main("main.bib")
+ .position(0, 0)
+ .test_position(LatexLabelDefinitionProvider)
+ .await;
+
+ assert!(actual_links.is_empty());
}
- #[test]
- fn test_no_definition_bibtex() {
- let links = test_feature(
- LatexLabelDefinitionProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.bib", "")],
- main_file: "foo.bib",
- position: Position::new(0, 0),
- ..FeatureSpec::default()
- },
- );
- assert!(links.is_empty());
+ #[tokio::test]
+ async fn unknown_context() {
+ let actual_links = FeatureTester::new()
+ .file("foo.tex", r#"\label{foo}"#)
+ .file(
+ "bar.tex",
+ indoc!(
+ r#"
+ \begin{a}\begin{b}\label{foo}\end{b}\end{a}
+ \input{baz.tex}
+ "#
+ ),
+ )
+ .file("baz.tex", r#"\ref{foo}"#)
+ .main("baz.tex")
+ .position(0, 5)
+ .test_position(LatexLabelDefinitionProvider)
+ .await;
+
+ let expected_links = vec![LocationLink {
+ origin_selection_range: Some(Range::new_simple(0, 5, 0, 8)),
+ target_uri: FeatureTester::uri("bar.tex").into(),
+ target_range: Range::new_simple(0, 18, 0, 29),
+ target_selection_range: Range::new_simple(0, 18, 0, 29),
+ }];
+
+ assert_eq!(actual_links, expected_links);
}
}
diff --git a/support/texlab/src/definition/mod.rs b/support/texlab/src/definition/mod.rs
index 9c7ac0145a..08901454fe 100644
--- a/support/texlab/src/definition/mod.rs
+++ b/support/texlab/src/definition/mod.rs
@@ -1,16 +1,17 @@
mod bibtex_string;
mod latex_citation;
-mod latex_command;
+mod latex_cmd;
mod latex_label;
-use self::bibtex_string::BibtexStringDefinitionProvider;
-use self::latex_citation::LatexCitationDefinitionProvider;
-use self::latex_command::LatexCommandDefinitionProvider;
-use self::latex_label::LatexLabelDefinitionProvider;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::{Location, LocationLink, TextDocumentPositionParams};
-use serde::{Deserialize, Serialize};
+use self::{
+ bibtex_string::BibtexStringDefinitionProvider, latex_citation::LatexCitationDefinitionProvider,
+ latex_cmd::LatexCommandDefinitionProvider, latex_label::LatexLabelDefinitionProvider,
+};
+use crate::{
+ feature::{ConcatProvider, FeatureProvider, FeatureRequest},
+ protocol::{LocationLink, TextDocumentPositionParams},
+};
+use async_trait::async_trait;
pub struct DefinitionProvider {
provider: ConcatProvider<TextDocumentPositionParams, LocationLink>,
@@ -35,19 +36,12 @@ impl Default for DefinitionProvider {
}
}
+#[async_trait]
impl FeatureProvider for DefinitionProvider {
type Params = TextDocumentPositionParams;
type Output = Vec<LocationLink>;
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
- self.provider.execute(request).await
+ async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output {
+ self.provider.execute(req).await
}
}
-
-#[serde(untagged)]
-#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
-pub enum DefinitionResponse {
- Locations(Vec<Location>),
- LocationLinks(Vec<LocationLink>),
-}