summaryrefslogtreecommitdiff
path: root/support/texlab/src/symbol
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/symbol')
-rw-r--r--support/texlab/src/symbol/bibtex_entry.rs216
-rw-r--r--support/texlab/src/symbol/bibtex_string.rs115
-rw-r--r--support/texlab/src/symbol/latex_section/enumeration.rs84
-rw-r--r--support/texlab/src/symbol/latex_section/equation.rs36
-rw-r--r--support/texlab/src/symbol/latex_section/float.rs46
-rw-r--r--support/texlab/src/symbol/latex_section/mod.rs781
-rw-r--r--support/texlab/src/symbol/latex_section/theorem.rs68
-rw-r--r--support/texlab/src/symbol/mod.rs231
-rw-r--r--support/texlab/src/symbol/project_order.rs179
-rw-r--r--support/texlab/src/symbol/types.rs104
10 files changed, 993 insertions, 867 deletions
diff --git a/support/texlab/src/symbol/bibtex_entry.rs b/support/texlab/src/symbol/bibtex_entry.rs
index f19c2fb734..f719937e80 100644
--- a/support/texlab/src/symbol/bibtex_entry.rs
+++ b/support/texlab/src/symbol/bibtex_entry.rs
@@ -1,42 +1,47 @@
-use super::{LatexSymbol, LatexSymbolKind};
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::*;
+use super::types::{LatexSymbol, LatexSymbolKind};
+use crate::{
+ feature::{FeatureProvider, FeatureRequest},
+ protocol::DocumentSymbolParams,
+ syntax::{bibtex, BibtexEntryTypeCategory, SyntaxNode, LANGUAGE_DATA},
+ workspace::DocumentContent,
+};
+use async_trait::async_trait;
+use petgraph::graph::NodeIndex;
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct BibtexEntrySymbolProvider;
+#[async_trait]
impl FeatureProvider for BibtexEntrySymbolProvider {
type Params = DocumentSymbolParams;
type Output = Vec<LatexSymbol>;
- #[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 symbols = Vec::new();
- if let SyntaxTree::Bibtex(tree) = &request.document().tree {
- for entry in tree
- .entries()
- .iter()
- .filter(|entry| !entry.is_comment())
- .filter(|entry| entry.key.is_some())
- {
- let category = LANGUAGE_DATA
- .find_entry_type(&entry.ty.text()[1..])
- .map(|ty| ty.category)
- .unwrap_or(BibtexEntryTypeCategory::Misc);
+ if let DocumentContent::Bibtex(tree) = &req.current().content {
+ for entry_node in tree.children(tree.root) {
+ if let Some(entry) = tree
+ .as_entry(entry_node)
+ .filter(|entry| !entry.is_comment())
+ .filter(|entry| entry.key.is_some())
+ {
+ let category = LANGUAGE_DATA
+ .find_entry_type(&entry.ty.text()[1..])
+ .map(|ty| ty.category)
+ .unwrap_or(BibtexEntryTypeCategory::Misc);
- let key = entry.key.as_ref().unwrap();
- let symbol = LatexSymbol {
- name: key.text().to_owned(),
- label: None,
- kind: LatexSymbolKind::Entry(category),
- deprecated: false,
- full_range: entry.range(),
- selection_range: key.range(),
- children: Self::field_symbols(&entry),
- };
- symbols.push(symbol);
+ let key = entry.key.as_ref().unwrap();
+ let symbol = LatexSymbol {
+ name: key.text().to_owned(),
+ label: None,
+ kind: LatexSymbolKind::Entry(category),
+ deprecated: false,
+ full_range: entry.range(),
+ selection_range: key.range(),
+ children: Self::field_symbols(tree, entry_node),
+ };
+ symbols.push(symbol);
+ }
}
}
symbols
@@ -44,9 +49,12 @@ impl FeatureProvider for BibtexEntrySymbolProvider {
}
impl BibtexEntrySymbolProvider {
- fn field_symbols(entry: &BibtexEntry) -> Vec<LatexSymbol> {
+ fn field_symbols(tree: &bibtex::Tree, entry_node: NodeIndex) -> Vec<LatexSymbol> {
let mut symbols = Vec::new();
- for field in &entry.fields {
+ for field in tree
+ .children(entry_node)
+ .filter_map(|node| tree.as_field(node))
+ {
let symbol = LatexSymbol {
name: field.name.text().to_owned(),
label: None,
@@ -65,83 +73,81 @@ impl BibtexEntrySymbolProvider {
#[cfg(test)]
mod tests {
use super::*;
- use crate::range::RangeExt;
+ use crate::{
+ feature::FeatureTester,
+ protocol::{Range, RangeExt},
+ };
- #[test]
- fn test_entry() {
- let symbols = test_feature(
- BibtexEntrySymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.bib",
- "@article{key, foo = bar, baz = qux}",
- )],
- main_file: "foo.bib",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
- name: "key".into(),
- label: None,
- kind: LatexSymbolKind::Entry(BibtexEntryTypeCategory::Article),
- deprecated: false,
- full_range: Range::new_simple(0, 0, 0, 35),
- selection_range: Range::new_simple(0, 9, 0, 12),
- children: vec![
- LatexSymbol {
- name: "foo".into(),
- label: None,
- kind: LatexSymbolKind::Field,
- deprecated: false,
- full_range: Range::new_simple(0, 14, 0, 24),
- selection_range: Range::new_simple(0, 14, 0, 17),
- children: Vec::new(),
- },
- LatexSymbol {
- name: "baz".into(),
- label: None,
- kind: LatexSymbolKind::Field,
- deprecated: false,
- full_range: Range::new_simple(0, 25, 0, 34),
- selection_range: Range::new_simple(0, 25, 0, 28),
- children: Vec::new(),
- },
- ],
- }]
- );
+ #[tokio::test]
+ async fn empty_latex_document() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.tex", "")
+ .main("main.tex")
+ .test_symbol(BibtexEntrySymbolProvider)
+ .await;
+
+ assert!(actual_symbols.is_empty());
}
- #[test]
- fn test_comment() {
- let symbols = test_feature(
- BibtexEntrySymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.bib",
- "@comment{key, foo = bar, baz = qux}",
- )],
- main_file: "foo.bib",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(symbols, Vec::new());
+ #[tokio::test]
+ async fn empty_bibtex_document() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.bib", "")
+ .main("main.bib")
+ .test_symbol(BibtexEntrySymbolProvider)
+ .await;
+
+ assert!(actual_symbols.is_empty());
+ }
+
+ #[tokio::test]
+ async fn entry() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.bib", r#"@article{key, foo = bar, baz = qux}"#)
+ .main("main.bib")
+ .test_symbol(BibtexEntrySymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "key".into(),
+ label: None,
+ kind: LatexSymbolKind::Entry(BibtexEntryTypeCategory::Article),
+ deprecated: false,
+ full_range: Range::new_simple(0, 0, 0, 35),
+ selection_range: Range::new_simple(0, 9, 0, 12),
+ children: vec![
+ LatexSymbol {
+ name: "foo".into(),
+ label: None,
+ kind: LatexSymbolKind::Field,
+ deprecated: false,
+ full_range: Range::new_simple(0, 14, 0, 24),
+ selection_range: Range::new_simple(0, 14, 0, 17),
+ children: Vec::new(),
+ },
+ LatexSymbol {
+ name: "baz".into(),
+ label: None,
+ kind: LatexSymbolKind::Field,
+ deprecated: false,
+ full_range: Range::new_simple(0, 25, 0, 34),
+ selection_range: Range::new_simple(0, 25, 0, 28),
+ children: Vec::new(),
+ },
+ ],
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
}
- #[test]
- fn test_latex() {
- let symbols = test_feature(
- BibtexEntrySymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.tex",
- "@article{key, foo = bar, baz = qux}",
- )],
- main_file: "foo.tex",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(symbols, Vec::new());
+ #[tokio::test]
+ async fn comment() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.bib", r#"@comment{key, foo = bar, baz = qux}"#)
+ .main("main.bib")
+ .test_symbol(BibtexEntrySymbolProvider)
+ .await;
+
+ assert!(actual_symbols.is_empty());
}
}
diff --git a/support/texlab/src/symbol/bibtex_string.rs b/support/texlab/src/symbol/bibtex_string.rs
index 8f1f436048..d2283f640b 100644
--- a/support/texlab/src/symbol/bibtex_string.rs
+++ b/support/texlab/src/symbol/bibtex_string.rs
@@ -1,25 +1,28 @@
-use super::{LatexSymbol, LatexSymbolKind};
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::*;
+use super::types::{LatexSymbol, LatexSymbolKind};
+use crate::{
+ feature::{FeatureProvider, FeatureRequest},
+ protocol::DocumentSymbolParams,
+ syntax::SyntaxNode,
+ workspace::DocumentContent,
+};
+use async_trait::async_trait;
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct BibtexStringSymbolProvider;
+#[async_trait]
impl FeatureProvider for BibtexStringSymbolProvider {
type Params = DocumentSymbolParams;
type Output = Vec<LatexSymbol>;
- #[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 symbols = Vec::new();
- if let SyntaxTree::Bibtex(tree) = &request.document().tree {
- for child in &tree.root.children {
- if let BibtexDeclaration::String(string) = &child {
+ if let DocumentContent::Bibtex(tree) = &req.current().content {
+ for string_node in tree.children(tree.root) {
+ if let Some(string) = &tree.as_string(string_node) {
if let Some(name) = &string.name {
symbols.push(LatexSymbol {
- name: name.text().to_owned(),
+ name: name.text().into(),
label: None,
kind: LatexSymbolKind::String,
deprecated: false,
@@ -38,42 +41,62 @@ impl FeatureProvider for BibtexStringSymbolProvider {
#[cfg(test)]
mod tests {
use super::*;
- use crate::range::RangeExt;
+ use crate::{
+ feature::FeatureTester,
+ protocol::{Range, RangeExt},
+ };
- #[test]
- fn test_valid() {
- let symbols = test_feature(
- BibtexStringSymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.bib", "@string{key = \"value\"}")],
- main_file: "foo.bib",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
- name: "key".into(),
- label: None,
- kind: LatexSymbolKind::String,
- deprecated: false,
- full_range: Range::new_simple(0, 0, 0, 22),
- selection_range: Range::new_simple(0, 8, 0, 11),
- children: Vec::new(),
- }]
- );
+ #[tokio::test]
+ async fn empty_latex_document() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.tex", "")
+ .main("main.tex")
+ .test_symbol(BibtexStringSymbolProvider)
+ .await;
+
+ assert!(actual_symbols.is_empty());
}
- #[test]
- fn test_invalid() {
- let symbols = test_feature(
- BibtexStringSymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file("foo.bib", "@string{}")],
- main_file: "foo.bib",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(symbols, Vec::new());
+ #[tokio::test]
+ async fn empty_bibtex_document() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.bib", "")
+ .main("main.bib")
+ .test_symbol(BibtexStringSymbolProvider)
+ .await;
+
+ assert!(actual_symbols.is_empty());
+ }
+
+ #[tokio::test]
+ async fn valid() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.bib", r#"@string{key = "value"}"#)
+ .main("main.bib")
+ .test_symbol(BibtexStringSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "key".into(),
+ label: None,
+ kind: LatexSymbolKind::String,
+ deprecated: false,
+ full_range: Range::new_simple(0, 0, 0, 22),
+ selection_range: Range::new_simple(0, 8, 0, 11),
+ children: Vec::new(),
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
+ }
+
+ #[tokio::test]
+ async fn invalid() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.bib", r#"@string{}"#)
+ .main("main.bib")
+ .test_symbol(BibtexStringSymbolProvider)
+ .await;
+
+ assert!(actual_symbols.is_empty());
}
}
diff --git a/support/texlab/src/symbol/latex_section/enumeration.rs b/support/texlab/src/symbol/latex_section/enumeration.rs
index 34f4dff72f..e96a856eac 100644
--- a/support/texlab/src/symbol/latex_section/enumeration.rs
+++ b/support/texlab/src/symbol/latex_section/enumeration.rs
@@ -1,59 +1,58 @@
use super::{label_name, selection_range};
-use crate::range::RangeExt;
-use crate::symbol::{LatexSymbol, LatexSymbolKind};
-use crate::syntax::*;
-use crate::workspace::*;
-use lsp_types::Range;
+use crate::{
+ feature::DocumentView,
+ outline::OutlineContext,
+ protocol::{Range, RangeExt},
+ symbol::types::{LatexSymbol, LatexSymbolKind},
+ syntax::{latex, SyntaxNode},
+};
+use titlecase::titlecase;
-pub fn symbols(view: &DocumentView, tree: &LatexSyntaxTree) -> Vec<LatexSymbol> {
- let mut symbols = Vec::new();
- for environment in &tree.env.environments {
- if environment.left.is_enum() {
- symbols.push(make_symbol(view, tree, environment));
- }
- }
- symbols
+pub fn symbols(view: &DocumentView, table: &latex::SymbolTable) -> Vec<LatexSymbol> {
+ table
+ .environments
+ .iter()
+ .filter(|env| env.left.is_enum(&table))
+ .map(|enumeration| make_symbol(view, table, *enumeration))
+ .collect()
}
fn make_symbol(
view: &DocumentView,
- tree: &LatexSyntaxTree,
- enumeration: &LatexEnvironment,
+ table: &latex::SymbolTable,
+ enumeration: latex::Environment,
) -> LatexSymbol {
- let name = titlelize(enumeration.left.name().unwrap().text());
+ let name = titlecase(enumeration.left.name(&table).unwrap().text());
- let items: Vec<_> = tree
- .structure
+ let items: Vec<_> = table
.items
.iter()
- .filter(|item| tree.is_enumeration_item(enumeration, item))
+ .filter(|item| table.is_enum_item(enumeration, **item))
.collect();
let mut children = Vec::new();
for i in 0..items.len() {
- let start = items[i].start();
+ let start = table[items[i].parent].start();
let end = items
.get(i + 1)
- .map(|item| item.start())
- .unwrap_or_else(|| enumeration.right.start());
+ .map(|item| table[item.parent].start())
+ .unwrap_or_else(|| table[enumeration.right.parent].start());
let range = Range::new(start, end);
- let label = find_item_label(tree, range);
+ let label = find_item_label(table, range);
- let number = items[i].name().or_else(|| {
- label
- .as_ref()
- .and_then(|label| OutlineContext::find_number(view, label))
- });
+ let number = items[i]
+ .name(&table)
+ .or_else(|| label.and_then(|label| OutlineContext::find_number(view, table, *label)));
let name = number.unwrap_or_else(|| "Item".into());
children.push(LatexSymbol {
name,
- label: label_name(label),
+ label: label_name(table, label),
kind: LatexSymbolKind::EnumerationItem,
deprecated: false,
full_range: range,
- selection_range: selection_range(items[i].range(), label),
+ selection_range: selection_range(table, table[items[i].parent].range(), label),
children: Vec::new(),
});
}
@@ -63,23 +62,18 @@ fn make_symbol(
label: None,
kind: LatexSymbolKind::Enumeration,
deprecated: false,
- full_range: enumeration.range(),
- selection_range: enumeration.range(),
+ full_range: enumeration.range(&table),
+ selection_range: enumeration.range(&table),
children,
}
}
-fn find_item_label(tree: &LatexSyntaxTree, item_range: Range) -> Option<&LatexLabel> {
- let label = tree.find_label_by_range(item_range)?;
- if tree
- .env
- .environments
- .iter()
- .filter(|env| item_range.contains(env.start()))
- .all(|env| !env.range().contains(label.start()))
- {
- Some(label)
- } else {
- None
- }
+fn find_item_label(table: &latex::SymbolTable, item_range: Range) -> Option<&latex::Label> {
+ table.find_label_by_range(item_range).filter(|label| {
+ table
+ .environments
+ .iter()
+ .filter(|env| item_range.contains(env.range(&table).start))
+ .all(|env| !env.range(&table).contains(table[label.parent].start()))
+ })
}
diff --git a/support/texlab/src/symbol/latex_section/equation.rs b/support/texlab/src/symbol/latex_section/equation.rs
index 4c2fcbe295..2889aefd4d 100644
--- a/support/texlab/src/symbol/latex_section/equation.rs
+++ b/support/texlab/src/symbol/latex_section/equation.rs
@@ -1,41 +1,41 @@
use super::{label_name, selection_range};
-use crate::symbol::{LatexSymbol, LatexSymbolKind};
-use crate::syntax::*;
-use crate::workspace::*;
-use lsp_types::Range;
+use crate::{
+ feature::DocumentView,
+ outline::OutlineContext,
+ protocol::Range,
+ symbol::types::{LatexSymbol, LatexSymbolKind},
+ syntax::latex,
+};
-pub fn symbols(view: &DocumentView, tree: &LatexSyntaxTree) -> Vec<LatexSymbol> {
+pub fn symbols(view: &DocumentView, table: &latex::SymbolTable) -> Vec<LatexSymbol> {
let mut symbols = Vec::new();
- for equation in &tree.math.equations {
- symbols.push(make_symbol(view, tree, equation.range()));
+ for equation in &table.equations {
+ symbols.push(make_symbol(view, table, equation.range(&table)));
}
- for equation in &tree.env.environments {
- if equation.left.is_math() {
- symbols.push(make_symbol(view, tree, equation.range()));
+ for equation in &table.environments {
+ if equation.left.is_math(&table) {
+ symbols.push(make_symbol(view, table, equation.range(&table)));
}
}
symbols
}
-fn make_symbol(view: &DocumentView, tree: &LatexSyntaxTree, full_range: Range) -> LatexSymbol {
- let label = tree.find_label_by_range(full_range);
+fn make_symbol(view: &DocumentView, table: &latex::SymbolTable, full_range: Range) -> LatexSymbol {
+ let label = table.find_label_by_range(full_range);
- let name = match label
- .as_ref()
- .and_then(|label| OutlineContext::find_number(view, label))
- {
+ let name = match label.and_then(|label| OutlineContext::find_number(view, table, *label)) {
Some(num) => format!("Equation ({})", num),
None => "Equation".to_owned(),
};
LatexSymbol {
name,
- label: label_name(label),
+ label: label_name(table, label),
kind: LatexSymbolKind::Equation,
deprecated: false,
full_range,
- selection_range: selection_range(full_range, label),
+ selection_range: selection_range(table, full_range, label),
children: Vec::new(),
}
}
diff --git a/support/texlab/src/symbol/latex_section/float.rs b/support/texlab/src/symbol/latex_section/float.rs
index 9356d0ed3e..7ade3602cf 100644
--- a/support/texlab/src/symbol/latex_section/float.rs
+++ b/support/texlab/src/symbol/latex_section/float.rs
@@ -1,38 +1,40 @@
use super::{label_name, selection_range};
-use crate::symbol::{LatexSymbol, LatexSymbolKind};
-use crate::syntax::*;
-use crate::workspace::*;
+use crate::{
+ feature::DocumentView,
+ outline::{OutlineCaptionKind, OutlineContext},
+ symbol::types::{LatexSymbol, LatexSymbolKind},
+ syntax::{latex, SyntaxNode},
+};
-pub fn symbols(view: &DocumentView, tree: &LatexSyntaxTree) -> Vec<LatexSymbol> {
- tree.structure
+pub fn symbols(view: &DocumentView, table: &latex::SymbolTable) -> Vec<LatexSymbol> {
+ table
.captions
.iter()
- .filter_map(|caption| make_symbol(view, tree, caption))
+ .filter_map(|caption| make_symbol(view, table, *caption))
.collect()
}
fn make_symbol(
view: &DocumentView,
- tree: &LatexSyntaxTree,
- caption: &LatexCaption,
+ table: &latex::SymbolTable,
+ caption: latex::Caption,
) -> Option<LatexSymbol> {
- let environment = tree
- .env
+ let env = table
.environments
.iter()
- .find(|env| tree.is_direct_child(env, caption.start()))?;
- let text = extract_group(&caption.command.args[caption.index]);
+ .find(|env| table.is_direct_child(**env, table[caption.parent].start()))?;
- let kind = environment
+ let text =
+ table.print_group_content(caption.parent, latex::GroupKind::Group, caption.arg_index)?;
+
+ let kind = env
.left
- .name()
- .map(LatexToken::text)
+ .name(&table)
+ .map(latex::Token::text)
.and_then(OutlineCaptionKind::parse)?;
- let label = tree.find_label_by_environment(environment);
- let number = label
- .as_ref()
- .and_then(|label| OutlineContext::find_number(view, label));
+ let label = table.find_label_by_environment(*env);
+ let number = label.and_then(|label| OutlineContext::find_number(view, table, *label));
let name = match &number {
Some(number) => format!("{} {}: {}", kind.as_str(), number, text),
@@ -41,7 +43,7 @@ fn make_symbol(
let symbol = LatexSymbol {
name,
- label: label_name(label),
+ label: label_name(table, label),
kind: match kind {
OutlineCaptionKind::Figure => LatexSymbolKind::Figure,
OutlineCaptionKind::Table => LatexSymbolKind::Table,
@@ -49,8 +51,8 @@ fn make_symbol(
OutlineCaptionKind::Algorithm => LatexSymbolKind::Algorithm,
},
deprecated: false,
- full_range: environment.range(),
- selection_range: selection_range(environment.range(), label),
+ full_range: env.range(&table),
+ selection_range: selection_range(table, env.range(&table), label),
children: Vec::new(),
};
Some(symbol)
diff --git a/support/texlab/src/symbol/latex_section/mod.rs b/support/texlab/src/symbol/latex_section/mod.rs
index f6fe2aede9..fc81695b50 100644
--- a/support/texlab/src/symbol/latex_section/mod.rs
+++ b/support/texlab/src/symbol/latex_section/mod.rs
@@ -3,38 +3,57 @@ mod equation;
mod float;
mod theorem;
-use super::{LatexSymbol, LatexSymbolKind};
-use crate::range::RangeExt;
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::*;
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+use super::types::{LatexSymbol, LatexSymbolKind};
+use crate::{
+ feature::{DocumentView, FeatureProvider, FeatureRequest},
+ outline::{Outline, OutlineContext, OutlineContextItem},
+ protocol::{DocumentSymbolParams, Options, Position, Range, RangeExt},
+ syntax::{latex, CharStream, LatexLabelKind, SyntaxNode},
+ workspace::DocumentContent,
+};
+use async_trait::async_trait;
+use std::path::Path;
+
+fn label_name(table: &latex::SymbolTable, label: Option<&latex::Label>) -> Option<String> {
+ label.map(|label| label.names(&table)[0].text().to_owned())
+}
+
+fn selection_range(
+ table: &latex::SymbolTable,
+ full_range: Range,
+ label: Option<&latex::Label>,
+) -> Range {
+ label
+ .map(|label| table[label.parent].range())
+ .unwrap_or(full_range)
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct LatexSectionSymbolProvider;
+#[async_trait]
impl FeatureProvider for LatexSectionSymbolProvider {
type Params = DocumentSymbolParams;
type Output = Vec<LatexSymbol>;
- #[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 symbols = Vec::new();
- if let SyntaxTree::Latex(tree) = &request.document().tree {
- let mut section_tree = build_section_tree(&request.view, tree);
- for symbol in enumeration::symbols(&request.view, tree) {
+ if let DocumentContent::Latex(table) = &req.current().content {
+ let mut section_tree =
+ build_section_tree(&req.view, table, &req.options, &req.current_dir);
+ for symbol in enumeration::symbols(&req.view, table) {
section_tree.insert_symbol(&symbol);
}
- for symbol in equation::symbols(&request.view, tree) {
+ for symbol in equation::symbols(&req.view, table) {
section_tree.insert_symbol(&symbol);
}
- for symbol in float::symbols(&request.view, tree) {
+ for symbol in float::symbols(&req.view, table) {
section_tree.insert_symbol(&symbol);
}
- for symbol in theorem::symbols(&request.view, tree) {
+ for symbol in theorem::symbols(&req.view, table) {
section_tree.insert_symbol(&symbol);
}
@@ -52,33 +71,36 @@ impl FeatureProvider for LatexSectionSymbolProvider {
pub fn build_section_tree<'a>(
view: &'a DocumentView,
- tree: &'a LatexSyntaxTree,
+ table: &'a latex::SymbolTable,
+ options: &'a Options,
+ current_dir: &'a Path,
) -> LatexSectionTree<'a> {
- let mut section_tree = LatexSectionTree::from(tree);
- section_tree.set_full_text(&view.document.text);
- let end_position = compute_end_position(tree, &view.document.text);
- LatexSectionNode::set_full_range(&mut section_tree.children, end_position);
- let outline = Outline::from(view);
+ let mut section_tree = LatexSectionTree::from(table);
+ section_tree.set_full_text(&view.current.text);
+ let end_position = compute_end_position(table, &view.current.text);
+ LatexSectionNode::set_full_range(&mut section_tree.children, table, end_position);
+ let outline = Outline::analyze(view, options, current_dir);
for child in &mut section_tree.children {
- child.set_label(tree, view, &outline);
+ child.set_label(view, &outline);
}
section_tree
}
-fn compute_end_position(tree: &LatexSyntaxTree, text: &str) -> Position {
+fn compute_end_position(table: &latex::SymbolTable, text: &str) -> Position {
let mut stream = CharStream::new(text);
while stream.next().is_some() {}
- tree.env
+ table
.environments
.iter()
- .find(|env| env.left.name().map(LatexToken::text) == Some("document"))
- .map(|env| env.right.start())
+ .find(|env| env.left.name(&table).map(latex::Token::text) == Some("document"))
+ .map(|env| table[env.right.parent].start())
.unwrap_or(stream.current_position)
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, Clone)]
pub struct LatexSectionNode<'a> {
- pub section: &'a LatexSection,
+ pub table: &'a latex::SymbolTable,
+ pub section: &'a latex::Section,
pub full_range: Range,
full_text: &'a str,
label: Option<String>,
@@ -88,8 +110,9 @@ pub struct LatexSectionNode<'a> {
}
impl<'a> LatexSectionNode<'a> {
- fn new(section: &'a LatexSection) -> Self {
+ fn new(table: &'a latex::SymbolTable, section: &'a latex::Section) -> Self {
Self {
+ table,
section,
full_range: Range::default(),
full_text: "",
@@ -108,37 +131,45 @@ impl<'a> LatexSectionNode<'a> {
}
fn name(&self) -> String {
- self.section
- .extract_text(self.full_text)
- .unwrap_or_else(|| "Unknown".to_owned())
+ self.table
+ .print_group_content(
+ self.section.parent,
+ latex::GroupKind::Group,
+ self.section.arg_index,
+ )
+ .unwrap_or_else(|| "Unknown".into())
}
- fn set_full_range(children: &mut Vec<Self>, end_position: Position) {
+ fn set_full_range(
+ children: &mut Vec<Self>,
+ table: &latex::SymbolTable,
+ end_position: Position,
+ ) {
for i in 0..children.len() {
let current_end = children
.get(i + 1)
- .map(|next| next.section.start())
+ .map(|next| table[next.section.parent].start())
.unwrap_or(end_position);
let mut current = &mut children[i];
- current.full_range = Range::new(current.section.start(), current_end);
- Self::set_full_range(&mut current.children, current_end);
+ current.full_range = Range::new(table[current.section.parent].start(), current_end);
+ Self::set_full_range(&mut current.children, table, current_end);
}
}
- fn set_label(&mut self, tree: &LatexSyntaxTree, view: &DocumentView, outline: &Outline) {
- if let Some(label) = tree
- .structure
+ fn set_label(&mut self, view: &DocumentView, outline: &Outline) {
+ if let Some(label) = self
+ .table
.labels
.iter()
.filter(|label| label.kind == LatexLabelKind::Definition)
- .find(|label| self.full_range.contains(label.start()))
+ .find(|label| self.full_range.contains(self.table[label.parent].start()))
{
- if let Some(ctx) = OutlineContext::parse(view, label, outline) {
+ if let Some(ctx) = OutlineContext::parse(view, outline, *label) {
let mut is_section = false;
if let OutlineContextItem::Section { text, .. } = &ctx.item {
if self.name() == *text {
- for name in label.names() {
+ for name in label.names(&self.table) {
self.label = Some(name.text().to_owned());
}
@@ -153,21 +184,25 @@ impl<'a> LatexSectionNode<'a> {
}
for child in &mut self.children {
- child.set_label(tree, view, outline);
+ child.set_label(view, outline);
}
}
- fn insert_section(nodes: &mut Vec<Self>, section: &'a LatexSection) {
+ fn insert_section(
+ nodes: &mut Vec<Self>,
+ table: &'a latex::SymbolTable,
+ section: &'a latex::Section,
+ ) {
match nodes.last_mut() {
Some(parent) => {
if parent.section.level < section.level {
- Self::insert_section(&mut parent.children, section);
+ Self::insert_section(&mut parent.children, table, section);
} else {
- nodes.push(LatexSectionNode::new(section));
+ nodes.push(LatexSectionNode::new(table, section));
}
}
None => {
- nodes.push(LatexSectionNode::new(section));
+ nodes.push(LatexSectionNode::new(table, section));
}
}
}
@@ -223,13 +258,13 @@ impl<'a> Into<LatexSymbol> for LatexSectionNode<'a> {
kind: LatexSymbolKind::Section,
deprecated: false,
full_range: self.full_range,
- selection_range: self.section.range(),
+ selection_range: self.table[self.section.parent].range(),
children,
}
}
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, Clone)]
pub struct LatexSectionTree<'a> {
symbols: Vec<LatexSymbol>,
children: Vec<LatexSectionNode<'a>>,
@@ -269,366 +304,368 @@ impl<'a> LatexSectionTree<'a> {
}
}
-impl<'a> From<&'a LatexSyntaxTree> for LatexSectionTree<'a> {
- fn from(tree: &'a LatexSyntaxTree) -> Self {
+impl<'a> From<&'a latex::SymbolTable> for LatexSectionTree<'a> {
+ fn from(table: &'a latex::SymbolTable) -> Self {
let mut root = Self::new();
- for section in &tree.structure.sections {
- LatexSectionNode::insert_section(&mut root.children, section);
+ for section in &table.sections {
+ LatexSectionNode::insert_section(&mut root.children, table, section);
}
root
}
}
-pub fn label_name(label: Option<&LatexLabel>) -> Option<String> {
- label.map(|label| label.names()[0].text().to_owned())
-}
-
-pub fn selection_range(full_range: Range, label: Option<&LatexLabel>) -> Range {
- label.map(|label| label.range()).unwrap_or(full_range)
-}
-
#[cfg(test)]
mod tests {
use super::*;
- use crate::range::RangeExt;
-
- #[test]
- fn test_subsection() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file(
- "foo.tex",
- "\\section{Foo}\n\\subsection{Bar}\\label{sec:bar}\n\\subsection{Baz}\n\\section{Qux}",
- ),
- FeatureSpec::file(
- "foo.aux",
- "\\newlabel{sec:bar}{{\\relax 2.1}{4}{Bar\\relax }{figure.caption.4}{}}"
- ),
- ],
- main_file: "foo.tex",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![
- LatexSymbol {
- name: "Foo".into(),
- label: None,
- kind: LatexSymbolKind::Section,
- deprecated: false,
- full_range: Range::new_simple(0, 0, 3, 0),
- selection_range: Range::new_simple(0, 0, 0, 13),
- children: vec![
- LatexSymbol {
- name: "2.1 Bar".into(),
- label: Some("sec:bar".into()),
- kind: LatexSymbolKind::Section,
- deprecated: false,
- full_range: Range::new_simple(1, 0, 2, 0),
- selection_range: Range::new_simple(1, 0, 1, 16),
- children: Vec::new(),
- },
- LatexSymbol {
- name: "Baz".into(),
- label: None,
- kind: LatexSymbolKind::Section,
- deprecated: false,
- full_range: Range::new_simple(2, 0, 3, 0),
- selection_range: Range::new_simple(2, 0, 2, 16),
- children: Vec::new(),
- },
- ],
- },
- LatexSymbol {
- name: "Qux".into(),
- label: None,
- kind: LatexSymbolKind::Section,
- deprecated: false,
- full_range: Range::new_simple(3, 0, 3, 13),
- selection_range: Range::new_simple(3, 0, 3, 13),
- children: Vec::new(),
- }
- ]
- );
+ use crate::feature::FeatureTester;
+ use indoc::indoc;
+
+ #[tokio::test]
+ async fn empty_latex_document() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.tex", "")
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ assert!(actual_symbols.is_empty());
}
- #[test]
- fn test_section_inside_document_environment() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.tex",
- "\\begin{document}\\section{Foo}\\relax\n\\end{document}",
- )],
- main_file: "foo.tex",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
+ #[tokio::test]
+ async fn empty_bibtex_document() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.bib", "")
+ .main("main.bib")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ assert!(actual_symbols.is_empty());
+ }
+
+ #[tokio::test]
+ async fn subsection() {
+ let actual_symbols = FeatureTester::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \section{Foo}
+ \subsection{Bar}\label{sec:bar}
+ \subsection{Baz}
+ \section{Qux}
+ "#
+ ),
+ )
+ .file(
+ "main.aux",
+ r#"\newlabel{sec:bar}{{\relax 2.1}{4}{Bar\relax }{figure.caption.4}{}}"#,
+ )
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![
+ LatexSymbol {
name: "Foo".into(),
label: None,
kind: LatexSymbolKind::Section,
deprecated: false,
- full_range: Range::new_simple(0, 16, 1, 0),
- selection_range: Range::new_simple(0, 16, 0, 29),
- children: Vec::new()
- }]
- );
- }
-
- #[test]
- fn test_enumeration() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.tex",
- "\\section{Foo}\n\\begin{enumerate}\n\\end{enumerate}",
- )],
- main_file: "foo.tex",
- ..FeatureSpec::default()
+ full_range: Range::new_simple(0, 0, 3, 0),
+ selection_range: Range::new_simple(0, 0, 0, 13),
+ children: vec![
+ LatexSymbol {
+ name: "2.1 Bar".into(),
+ label: Some("sec:bar".into()),
+ kind: LatexSymbolKind::Section,
+ deprecated: false,
+ full_range: Range::new_simple(1, 0, 2, 0),
+ selection_range: Range::new_simple(1, 0, 1, 16),
+ children: Vec::new(),
+ },
+ LatexSymbol {
+ name: "Baz".into(),
+ label: None,
+ kind: LatexSymbolKind::Section,
+ deprecated: false,
+ full_range: Range::new_simple(2, 0, 3, 0),
+ selection_range: Range::new_simple(2, 0, 2, 16),
+ children: Vec::new(),
+ },
+ ],
},
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
- name: "Foo".into(),
+ LatexSymbol {
+ name: "Qux".into(),
label: None,
kind: LatexSymbolKind::Section,
deprecated: false,
- full_range: Range::new_simple(0, 0, 2, 15),
- selection_range: Range::new_simple(0, 0, 0, 13),
- children: vec![LatexSymbol {
- name: "Enumerate".into(),
- label: None,
- kind: LatexSymbolKind::Enumeration,
- deprecated: false,
- full_range: Range::new_simple(1, 0, 2, 15),
- selection_range: Range::new_simple(1, 0, 2, 15),
- children: Vec::new(),
- },],
- },]
- );
+ full_range: Range::new_simple(3, 0, 3, 13),
+ selection_range: Range::new_simple(3, 0, 3, 13),
+ children: Vec::new(),
+ },
+ ];
+
+ assert_eq!(actual_symbols, expected_symbols);
}
- #[test]
- fn test_equation() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.tex",
- "\\[Foo\\]\n\\begin{equation}\\label{eq:foo}\\end{equation}",
- )],
- main_file: "foo.tex",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![
- LatexSymbol {
- name: "Equation".into(),
- label: None,
- kind: LatexSymbolKind::Equation,
- deprecated: false,
- full_range: Range::new_simple(0, 0, 0, 7),
- selection_range: Range::new_simple(0, 0, 0, 7),
- children: Vec::new(),
- },
- LatexSymbol {
- name: "Equation".into(),
- label: Some("eq:foo".into()),
- kind: LatexSymbolKind::Equation,
- deprecated: false,
- full_range: Range::new_simple(1, 0, 1, 44),
- selection_range: Range::new_simple(1, 16, 1, 30),
- children: Vec::new(),
- },
- ]
- );
+ #[tokio::test]
+ async fn section_inside_document_environment() {
+ let actual_symbols = FeatureTester::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \begin{document}\section{Foo}\relax
+ \end{document}
+ "#
+ ),
+ )
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "Foo".into(),
+ label: None,
+ kind: LatexSymbolKind::Section,
+ deprecated: false,
+ full_range: Range::new_simple(0, 16, 1, 0),
+ selection_range: Range::new_simple(0, 16, 0, 29),
+ children: Vec::new(),
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
}
- #[test]
- fn test_equation_number() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file("foo.tex", "\\[\\label{eq:foo}\\]"),
- FeatureSpec::file(
- "foo.aux",
- "\\newlabel{eq:foo}{{\\relax 2.1}{4}{Bar\\relax }{figure.caption.4}{}}",
- ),
- ],
- main_file: "foo.tex",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
- name: "Equation (2.1)".into(),
- label: Some("eq:foo".into()),
- kind: LatexSymbolKind::Equation,
+ #[tokio::test]
+ async fn enumeration() {
+ let actual_symbols = FeatureTester::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \section{Foo}
+ \begin{enumerate}
+ \end{enumerate}
+ "#
+ ),
+ )
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "Foo".into(),
+ label: None,
+ kind: LatexSymbolKind::Section,
+ deprecated: false,
+ full_range: Range::new_simple(0, 0, 2, 15),
+ selection_range: Range::new_simple(0, 0, 0, 13),
+ children: vec![LatexSymbol {
+ name: "Enumerate".into(),
+ label: None,
+ kind: LatexSymbolKind::Enumeration,
deprecated: false,
- full_range: Range::new_simple(0, 0, 0, 18),
- selection_range: Range::new_simple(0, 2, 0, 16),
+ full_range: Range::new_simple(1, 0, 2, 15),
+ selection_range: Range::new_simple(1, 0, 2, 15),
children: Vec::new(),
- },]
- );
+ }],
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
}
- #[test]
- fn test_table() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.tex",
- "\\begin{table}\\caption{Foo}\\end{table}",
- )],
- main_file: "foo.tex",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
- name: "Table: Foo".into(),
+ #[tokio::test]
+ async fn equation() {
+ let actual_symbols = FeatureTester::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \[Foo\]
+ \begin{equation}\label{eq:foo}\end{equation}
+ "#
+ ),
+ )
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![
+ LatexSymbol {
+ name: "Equation".into(),
label: None,
- kind: LatexSymbolKind::Table,
+ kind: LatexSymbolKind::Equation,
deprecated: false,
- full_range: Range::new_simple(0, 0, 0, 37),
- selection_range: Range::new_simple(0, 0, 0, 37),
+ full_range: Range::new_simple(0, 0, 0, 7),
+ selection_range: Range::new_simple(0, 0, 0, 7),
children: Vec::new(),
- },]
- );
- }
-
- #[test]
- fn test_figure_number() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file(
- "foo.tex",
- "\\begin{figure}\\caption{Foo}\\label{fig:foo}\\end{figure}",
- ),
- FeatureSpec::file(
- "foo.aux",
- "\\newlabel{fig:foo}{{\\relax 2.1}{4}{Bar\\relax }{figure.caption.4}{}}",
- ),
- ],
- main_file: "foo.tex",
- ..FeatureSpec::default()
},
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
- name: "Figure 2.1: Foo".into(),
- label: Some("fig:foo".into()),
- kind: LatexSymbolKind::Figure,
+ LatexSymbol {
+ name: "Equation".into(),
+ label: Some("eq:foo".into()),
+ kind: LatexSymbolKind::Equation,
deprecated: false,
- full_range: Range::new_simple(0, 0, 0, 54),
- selection_range: Range::new_simple(0, 27, 0, 42),
+ full_range: Range::new_simple(1, 0, 1, 44),
+ selection_range: Range::new_simple(1, 16, 1, 30),
children: Vec::new(),
- },]
- );
+ },
+ ];
+
+ assert_eq!(actual_symbols, expected_symbols);
}
- #[test]
- fn test_lemma() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.tex",
- "\\newtheorem{lemma}{Lemma}\\begin{lemma}\\end{lemma}",
- )],
- main_file: "foo.tex",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
- name: "Lemma".into(),
- label: None,
- kind: LatexSymbolKind::Theorem,
- deprecated: false,
- full_range: Range::new_simple(0, 25, 0, 49),
- selection_range: Range::new_simple(0, 25, 0, 49),
- children: Vec::new(),
- },]
- );
+ #[tokio::test]
+ async fn equation_number() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.tex", r#"\[\label{eq:foo}\]"#)
+ .file(
+ "main.aux",
+ r#"\newlabel{eq:foo}{{\relax 2.1}{4}{Bar\relax }{figure.caption.4}{}}"#,
+ )
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "Equation (2.1)".into(),
+ label: Some("eq:foo".into()),
+ kind: LatexSymbolKind::Equation,
+ deprecated: false,
+ full_range: Range::new_simple(0, 0, 0, 18),
+ selection_range: Range::new_simple(0, 2, 0, 16),
+ children: Vec::new(),
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
}
- #[test]
- fn test_lemma_number() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![
- FeatureSpec::file(
- "foo.tex",
- "\\newtheorem{lemma}{Lemma}\n\\begin{lemma}\\label{thm:foo}\\end{lemma}",
- ),
- FeatureSpec::file(
- "foo.aux",
- "\\newlabel{thm:foo}{{\\relax 2.1}{4}{Bar\\relax }{figure.caption.4}{}}",
- ),
- ],
- main_file: "foo.tex",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
- name: "Lemma 2.1".into(),
- label: Some("thm:foo".into()),
- kind: LatexSymbolKind::Theorem,
- deprecated: false,
- full_range: Range::new_simple(1, 0, 1, 39),
- selection_range: Range::new_simple(1, 13, 1, 28),
- children: Vec::new(),
- },]
- );
+ #[tokio::test]
+ async fn table() {
+ let actual_symbols = FeatureTester::new()
+ .file("main.tex", r#"\begin{table}\caption{Foo}\end{table}"#)
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "Table: Foo".into(),
+ label: None,
+ kind: LatexSymbolKind::Table,
+ deprecated: false,
+ full_range: Range::new_simple(0, 0, 0, 37),
+ selection_range: Range::new_simple(0, 0, 0, 37),
+ children: Vec::new(),
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
}
- #[test]
- fn test_lemma_description() {
- let symbols = test_feature(
- LatexSectionSymbolProvider,
- FeatureSpec {
- files: vec![FeatureSpec::file(
- "foo.tex",
- "\\newtheorem{lemma}{Lemma}\\begin{lemma}[Foo]\\end{lemma}",
- )],
- main_file: "foo.tex",
- ..FeatureSpec::default()
- },
- );
- assert_eq!(
- symbols,
- vec![LatexSymbol {
- name: "Lemma (Foo)".into(),
- label: None,
- kind: LatexSymbolKind::Theorem,
- deprecated: false,
- full_range: Range::new_simple(0, 25, 0, 54),
- selection_range: Range::new_simple(0, 25, 0, 54),
- children: Vec::new(),
- },]
- );
+ #[tokio::test]
+ async fn figure_number() {
+ let actual_symbols = FeatureTester::new()
+ .file(
+ "main.tex",
+ r#"\begin{figure}\caption{Foo}\label{fig:foo}\end{figure}"#,
+ )
+ .file(
+ "main.aux",
+ r#"\newlabel{fig:foo}{{\relax 2.1}{4}{Bar\relax }{figure.caption.4}{}}"#,
+ )
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "Figure 2.1: Foo".into(),
+ label: Some("fig:foo".into()),
+ kind: LatexSymbolKind::Figure,
+ deprecated: false,
+ full_range: Range::new_simple(0, 0, 0, 54),
+ selection_range: Range::new_simple(0, 27, 0, 42),
+ children: Vec::new(),
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
+ }
+
+ #[tokio::test]
+ async fn lemma() {
+ let actual_symbols = FeatureTester::new()
+ .file(
+ "main.tex",
+ r#"\newtheorem{lemma}{Lemma}\begin{lemma}\end{lemma}"#,
+ )
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "Lemma".into(),
+ label: None,
+ kind: LatexSymbolKind::Theorem,
+ deprecated: false,
+ full_range: Range::new_simple(0, 25, 0, 49),
+ selection_range: Range::new_simple(0, 25, 0, 49),
+ children: Vec::new(),
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
+ }
+
+ #[tokio::test]
+ async fn lemma_number() {
+ let actual_symbols = FeatureTester::new()
+ .file(
+ "main.tex",
+ indoc!(
+ r#"
+ \newtheorem{lemma}{Lemma}
+ \begin{lemma}\label{thm:foo}\end{lemma}
+ "#
+ ),
+ )
+ .file(
+ "main.aux",
+ r#"\newlabel{thm:foo}{{\relax 2.1}{4}{Bar\relax }{figure.caption.4}{}}"#,
+ )
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "Lemma 2.1".into(),
+ label: Some("thm:foo".into()),
+ kind: LatexSymbolKind::Theorem,
+ deprecated: false,
+ full_range: Range::new_simple(1, 0, 1, 39),
+ selection_range: Range::new_simple(1, 13, 1, 28),
+ children: Vec::new(),
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
+ }
+
+ #[tokio::test]
+ async fn lemma_description() {
+ let actual_symbols = FeatureTester::new()
+ .file(
+ "main.tex",
+ r#"\newtheorem{lemma}{Lemma}\begin{lemma}[Foo]\end{lemma}"#,
+ )
+ .main("main.tex")
+ .test_symbol(LatexSectionSymbolProvider)
+ .await;
+
+ let expected_symbols = vec![LatexSymbol {
+ name: "Lemma (Foo)".into(),
+ label: None,
+ kind: LatexSymbolKind::Theorem,
+ deprecated: false,
+ full_range: Range::new_simple(0, 25, 0, 54),
+ selection_range: Range::new_simple(0, 25, 0, 54),
+ children: Vec::new(),
+ }];
+
+ assert_eq!(actual_symbols, expected_symbols);
}
}
diff --git a/support/texlab/src/symbol/latex_section/theorem.rs b/support/texlab/src/symbol/latex_section/theorem.rs
index 1f2190ea47..dac231fbfa 100644
--- a/support/texlab/src/symbol/latex_section/theorem.rs
+++ b/support/texlab/src/symbol/latex_section/theorem.rs
@@ -1,47 +1,51 @@
use super::{label_name, selection_range};
-use crate::symbol::{LatexSymbol, LatexSymbolKind};
-use crate::syntax::*;
-use crate::workspace::*;
+use crate::{
+ feature::DocumentView,
+ outline::OutlineContext,
+ symbol::types::{LatexSymbol, LatexSymbolKind},
+ syntax::latex,
+ workspace::DocumentContent,
+};
+use titlecase::titlecase;
-pub fn symbols(view: &DocumentView, tree: &LatexSyntaxTree) -> Vec<LatexSymbol> {
- tree.env
+pub fn symbols(view: &DocumentView, table: &latex::SymbolTable) -> Vec<LatexSymbol> {
+ table
.environments
.iter()
- .filter_map(|env| make_symbol(view, tree, env))
+ .filter_map(|env| make_symbol(view, table, *env))
.collect()
}
fn make_symbol(
view: &DocumentView,
- main_tree: &LatexSyntaxTree,
- environment: &LatexEnvironment,
+ main_table: &latex::SymbolTable,
+ env: latex::Environment,
) -> Option<LatexSymbol> {
- let environment_name = environment.left.name().map(LatexToken::text)?;
+ let env_name = env.left.name(&main_table).map(latex::Token::text)?;
- for document in &view.related_documents {
- if let SyntaxTree::Latex(tree) = &document.tree {
- for definition in &tree.math.theorem_definitions {
- if environment_name == definition.name().text() {
- let kind = definition
- .command
- .args
- .get(definition.index + 1)
- .map(|content| extract_group(content))
- .unwrap_or_else(|| titlelize(environment_name));
+ for document in &view.related {
+ if let DocumentContent::Latex(table) = &document.content {
+ for definition in &table.theorem_definitions {
+ if definition.name(&table).text() == env_name {
+ let kind = table
+ .print_group_content(
+ definition.parent,
+ latex::GroupKind::Group,
+ definition.arg_index + 1,
+ )
+ .unwrap_or_else(|| titlecase(env_name));
- let description = environment
- .left
- .command
- .options
- .get(0)
- .map(|content| extract_group(content));
+ let desc = main_table.print_group_content(
+ env.left.parent,
+ latex::GroupKind::Options,
+ 0,
+ );
- let label = main_tree.find_label_by_environment(environment);
+ let label = main_table.find_label_by_environment(env);
let number = label
- .as_ref()
- .and_then(|label| OutlineContext::find_number(view, label));
+ .and_then(|label| OutlineContext::find_number(view, &main_table, *label));
- let name = match (description, number) {
+ let name = match (desc, number) {
(Some(desc), Some(num)) => format!("{} {} ({})", kind, num, desc),
(Some(desc), None) => format!("{} ({})", kind, desc),
(None, Some(num)) => format!("{} {}", kind, num),
@@ -50,11 +54,11 @@ fn make_symbol(
let symbol = LatexSymbol {
name,
- label: label_name(label),
+ label: label_name(main_table, label),
kind: LatexSymbolKind::Theorem,
deprecated: false,
- full_range: environment.range(),
- selection_range: selection_range(environment.range(), label),
+ full_range: env.range(&main_table),
+ selection_range: selection_range(main_table, env.range(&main_table), label),
children: Vec::new(),
};
return Some(symbol);
diff --git a/support/texlab/src/symbol/mod.rs b/support/texlab/src/symbol/mod.rs
index ec68d70c3e..b292f15bb2 100644
--- a/support/texlab/src/symbol/mod.rs
+++ b/support/texlab/src/symbol/mod.rs
@@ -2,122 +2,30 @@ mod bibtex_entry;
mod bibtex_string;
mod latex_section;
mod project_order;
-
-use self::bibtex_entry::BibtexEntrySymbolProvider;
-use self::bibtex_string::BibtexStringSymbolProvider;
-use self::latex_section::LatexSectionSymbolProvider;
-use self::project_order::ProjectOrdering;
-use crate::capabilities::ClientCapabilitiesExt;
-use crate::lsp_kind::Structure;
-use crate::syntax::*;
-use crate::workspace::*;
-use futures_boxed::boxed;
-use lsp_types::*;
-use serde::{Deserialize, Serialize};
-use std::cmp::Reverse;
-use std::sync::Arc;
+mod types;
pub use self::latex_section::{build_section_tree, LatexSectionNode, LatexSectionTree};
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum LatexSymbolKind {
- Section,
- Figure,
- Algorithm,
- Table,
- Listing,
- Enumeration,
- EnumerationItem,
- Theorem,
- Equation,
- Entry(BibtexEntryTypeCategory),
- Field,
- String,
-}
-
-impl Into<SymbolKind> for LatexSymbolKind {
- fn into(self) -> SymbolKind {
- match self {
- Self::Section => Structure::Section.symbol_kind(),
- Self::Figure | Self::Algorithm | Self::Table | Self::Listing => {
- Structure::Float.symbol_kind()
- }
- Self::Enumeration => Structure::Environment.symbol_kind(),
- Self::EnumerationItem => Structure::Item.symbol_kind(),
- Self::Theorem => Structure::Theorem.symbol_kind(),
- Self::Equation => Structure::Equation.symbol_kind(),
- Self::Entry(category) => Structure::Entry(category).symbol_kind(),
- Self::Field => Structure::Field.symbol_kind(),
- Self::String => Structure::Entry(BibtexEntryTypeCategory::String).symbol_kind(),
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct LatexSymbol {
- pub name: String,
- pub label: Option<String>,
- pub kind: LatexSymbolKind,
- pub deprecated: bool,
- pub full_range: Range,
- pub selection_range: Range,
- pub children: Vec<LatexSymbol>,
-}
-
-impl LatexSymbol {
- pub fn search_text(&self) -> String {
- let kind = match self.kind {
- LatexSymbolKind::Section => "latex section",
- LatexSymbolKind::Figure => "latex float figure",
- LatexSymbolKind::Algorithm => "latex float algorithm",
- LatexSymbolKind::Table => "latex float table",
- LatexSymbolKind::Listing => "latex float listing",
- LatexSymbolKind::Enumeration => "latex enumeration",
- LatexSymbolKind::EnumerationItem => "latex enumeration item",
- LatexSymbolKind::Theorem => "latex math",
- LatexSymbolKind::Equation => "latex math equation",
- LatexSymbolKind::Entry(_) => "bibtex entry",
- LatexSymbolKind::Field => "bibtex field",
- LatexSymbolKind::String => "bibtex string",
- };
- format!("{} {}", kind, self.name).to_lowercase()
- }
-
- pub fn flatten(mut self, buffer: &mut Vec<Self>) {
- if self.kind == LatexSymbolKind::Field {
- return;
- }
- for symbol in self.children.drain(..) {
- symbol.flatten(buffer);
- }
- buffer.push(self);
- }
-
- pub fn into_symbol_info(self, uri: Uri) -> SymbolInformation {
- SymbolInformation {
- name: self.name,
- deprecated: Some(self.deprecated),
- kind: self.kind.into(),
- container_name: None,
- location: Location::new(uri.clone().into(), self.full_range),
- }
- }
-}
-
-impl Into<DocumentSymbol> for LatexSymbol {
- fn into(self) -> DocumentSymbol {
- let children = self.children.into_iter().map(Into::into).collect();
- DocumentSymbol {
- name: self.name,
- deprecated: Some(self.deprecated),
- detail: self.label,
- kind: self.kind.into(),
- selection_range: self.selection_range,
- range: self.full_range,
- children: Some(children),
- }
- }
-}
+use self::{
+ bibtex_entry::BibtexEntrySymbolProvider, bibtex_string::BibtexStringSymbolProvider,
+ latex_section::LatexSectionSymbolProvider, project_order::ProjectOrdering, types::LatexSymbol,
+};
+use crate::{
+ feature::{ConcatProvider, DocumentView, FeatureProvider, FeatureRequest},
+ protocol::{
+ ClientCapabilities, ClientCapabilitiesExt, DocumentSymbolParams, DocumentSymbolResponse,
+ Options, PartialResultParams, SymbolInformation, TextDocumentIdentifier, Uri,
+ WorkDoneProgressParams, WorkspaceSymbolParams,
+ },
+ tex::Distribution,
+ workspace::Snapshot,
+};
+use async_trait::async_trait;
+use std::{
+ cmp::Reverse,
+ path::{Path, PathBuf},
+ sync::Arc,
+};
pub struct SymbolProvider {
provider: ConcatProvider<DocumentSymbolParams, LatexSymbol>,
@@ -141,44 +49,37 @@ impl Default for SymbolProvider {
}
}
+#[async_trait]
impl FeatureProvider for SymbolProvider {
type Params = DocumentSymbolParams;
type Output = Vec<LatexSymbol>;
- #[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, Serialize, Deserialize)]
-pub enum SymbolResponse {
- Flat(Vec<SymbolInformation>),
- Hierarchical(Vec<DocumentSymbol>),
-}
-
-impl SymbolResponse {
- pub fn new(
- client_capabilities: &ClientCapabilities,
- workspace: &Workspace,
- uri: &Uri,
- symbols: Vec<LatexSymbol>,
- ) -> Self {
- if client_capabilities.has_hierarchical_document_symbol_support() {
- Self::Hierarchical(symbols.into_iter().map(Into::into).collect())
- } else {
- let mut buffer = Vec::new();
- for symbol in symbols {
- symbol.flatten(&mut buffer);
- }
- let mut buffer = buffer
- .into_iter()
- .map(|symbol| symbol.into_symbol_info(uri.clone()))
- .collect();
- sort_symbols(workspace, &mut buffer);
- Self::Flat(buffer)
+pub fn document_symbols(
+ client_capabilities: &ClientCapabilities,
+ snapshot: &Snapshot,
+ uri: &Uri,
+ options: &Options,
+ current_dir: &Path,
+ symbols: Vec<LatexSymbol>,
+) -> DocumentSymbolResponse {
+ if client_capabilities.has_hierarchical_document_symbol_support() {
+ DocumentSymbolResponse::Nested(symbols.into_iter().map(Into::into).collect())
+ } else {
+ let mut buffer = Vec::new();
+ for symbol in symbols {
+ symbol.flatten(&mut buffer);
}
+ let mut buffer = buffer
+ .into_iter()
+ .map(|symbol| symbol.into_symbol_info(uri.clone()))
+ .collect();
+ sort_symbols(snapshot, options, &current_dir, &mut buffer);
+ DocumentSymbolResponse::Flat(buffer)
}
}
@@ -187,28 +88,39 @@ struct WorkspaceSymbol {
search_text: String,
}
-pub async fn workspace_symbols(
- distribution: Arc<Box<dyn tex::Distribution>>,
+pub async fn workspace_symbols<'a>(
+ distro: Arc<dyn Distribution>,
client_capabilities: Arc<ClientCapabilities>,
- workspace: Arc<Workspace>,
- params: &WorkspaceSymbolParams,
+ snapshot: Arc<Snapshot>,
+ options: &'a Options,
+ current_dir: Arc<PathBuf>,
+ params: &'a WorkspaceSymbolParams,
) -> Vec<SymbolInformation> {
let provider = SymbolProvider::new();
let mut symbols = Vec::new();
- for document in &workspace.documents {
- let uri: Uri = document.uri.clone();
- let request = FeatureRequest {
- client_capabilities: Arc::clone(&client_capabilities),
- view: DocumentView::new(Arc::clone(&workspace), Arc::clone(&document)),
+ for doc in &snapshot.0 {
+ let uri: Uri = doc.uri.clone();
+ let req = FeatureRequest {
params: DocumentSymbolParams {
text_document: TextDocumentIdentifier::new(uri.clone().into()),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ partial_result_params: PartialResultParams::default(),
},
- distribution: Arc::clone(&distribution),
+ view: DocumentView::analyze(
+ Arc::clone(&snapshot),
+ Arc::clone(&doc),
+ &options,
+ &current_dir,
+ ),
+ distro: distro.clone(),
+ client_capabilities: Arc::clone(&client_capabilities),
+ options: options.clone(),
+ current_dir: Arc::clone(&current_dir),
};
let mut buffer = Vec::new();
- for symbol in provider.execute(&request).await {
+ for symbol in provider.execute(&req).await {
symbol.flatten(&mut buffer);
}
@@ -239,12 +151,17 @@ pub async fn workspace_symbols(
filtered.push(symbol.info);
}
}
- sort_symbols(&workspace, &mut filtered);
+ sort_symbols(&snapshot, options, &current_dir, &mut filtered);
filtered
}
-fn sort_symbols(workspace: &Workspace, symbols: &mut Vec<SymbolInformation>) {
- let ordering = ProjectOrdering::new(workspace);
+fn sort_symbols(
+ snapshot: &Snapshot,
+ options: &Options,
+ current_dir: &Path,
+ symbols: &mut Vec<SymbolInformation>,
+) {
+ let ordering = ProjectOrdering::analyze(snapshot, options, current_dir);
symbols.sort_by(|left, right| {
let left_key = (
ordering.get(&Uri::from(left.location.uri.clone())),
diff --git a/support/texlab/src/symbol/project_order.rs b/support/texlab/src/symbol/project_order.rs
index 31b71f36bb..8a2ec5f6d0 100644
--- a/support/texlab/src/symbol/project_order.rs
+++ b/support/texlab/src/symbol/project_order.rs
@@ -1,9 +1,9 @@
-use crate::syntax::*;
-use crate::workspace::*;
-use petgraph::algo::tarjan_scc;
-use petgraph::{Directed, Graph};
-use std::collections::HashSet;
-use std::sync::Arc;
+use crate::{
+ protocol::{Options, Uri},
+ workspace::{Document, DocumentContent, Snapshot},
+};
+use petgraph::{algo::tarjan_scc, Directed, Graph};
+use std::{collections::HashSet, path::Path, sync::Arc, usize};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ProjectOrdering {
@@ -11,27 +11,34 @@ pub struct ProjectOrdering {
}
impl ProjectOrdering {
- pub fn new(workspace: &Workspace) -> Self {
+ pub fn get(&self, uri: &Uri) -> usize {
+ self.ordering
+ .iter()
+ .position(|doc| doc.uri == *uri)
+ .unwrap_or(usize::MAX)
+ }
+
+ pub fn analyze(snapshot: &Snapshot, options: &Options, current_dir: &Path) -> Self {
let mut ordering = Vec::new();
- let connected_components = Self::connected_components(workspace);
- for connected_component in connected_components {
- let graph = Self::build_dependency_graph(&connected_component);
+ let comps = Self::connected_components(snapshot, options, current_dir);
+ for comp in comps {
+ let graph = Self::build_dependency_graph(&comp);
let mut visited = HashSet::new();
let root_index = *graph.node_weight(tarjan_scc(&graph)[0][0]).unwrap();
- let mut stack = vec![Arc::clone(&connected_component[root_index])];
+ let mut stack = vec![Arc::clone(&comp[root_index])];
- while let Some(document) = stack.pop() {
- if !visited.insert(document.uri.as_str().to_owned()) {
+ while let Some(doc) = stack.pop() {
+ if !visited.insert(doc.uri.as_str().to_owned()) {
continue;
}
- ordering.push(Arc::clone(&document));
- if let SyntaxTree::Latex(tree) = &document.tree {
+ ordering.push(Arc::clone(&doc));
+ if let DocumentContent::Latex(tree) = &doc.content {
for include in tree.includes.iter().rev() {
for targets in &include.all_targets {
for target in targets {
- if let Some(child) = workspace.find(target) {
+ if let Some(child) = snapshot.find(target) {
stack.push(child);
}
}
@@ -44,36 +51,40 @@ impl ProjectOrdering {
Self { ordering }
}
- fn connected_components(workspace: &Workspace) -> Vec<Vec<Arc<Document>>> {
- let mut components = Vec::new();
+ fn connected_components(
+ snapshot: &Snapshot,
+ options: &Options,
+ current_dir: &Path,
+ ) -> Vec<Vec<Arc<Document>>> {
+ let mut comps = Vec::new();
let mut visited = HashSet::new();
- for root in &workspace.documents {
+ for root in &snapshot.0 {
if !visited.insert(root.uri.clone()) {
continue;
}
- let component = workspace.related_documents(&root.uri);
- for document in &component {
+ let comp = snapshot.relations(&root.uri, options, current_dir);
+ for document in &comp {
visited.insert(document.uri.clone());
}
- components.push(component);
+ comps.push(comp);
}
- components
+ comps
}
- fn build_dependency_graph(documents: &[Arc<Document>]) -> Graph<usize, (), Directed> {
+ fn build_dependency_graph(docs: &[Arc<Document>]) -> Graph<usize, (), Directed> {
let mut graph = Graph::new();
- let nodes: Vec<_> = (0..documents.len()).map(|i| graph.add_node(i)).collect();
+ let nodes: Vec<_> = (0..docs.len()).map(|i| graph.add_node(i)).collect();
- for (i, document) in documents.iter().enumerate() {
- if let SyntaxTree::Latex(tree) = &document.tree {
+ for (i, doc) in docs.iter().enumerate() {
+ if let DocumentContent::Latex(tree) = &doc.content {
for targets in tree
.includes
.iter()
.flat_map(|include| &include.all_targets)
{
for target in targets {
- if let Some(j) = documents.iter().position(|doc| doc.uri == *target) {
+ if let Some(j) = docs.iter().position(|doc| doc.uri == *target) {
graph.add_edge(nodes[j], nodes[i], ());
break;
}
@@ -83,60 +94,88 @@ impl ProjectOrdering {
}
graph
}
-
- pub fn get(&self, uri: &Uri) -> usize {
- self.ordering
- .iter()
- .position(|doc| doc.uri == *uri)
- .unwrap_or(std::usize::MAX)
- }
}
#[cfg(test)]
mod tests {
use super::*;
+ use crate::{
+ tex::{Language, Resolver},
+ workspace::DocumentParams,
+ };
+ use std::env;
+
+ fn create_simple_document(uri: &Uri, language: Language, text: &str) -> Arc<Document> {
+ Arc::new(Document::open(DocumentParams {
+ uri: uri.clone(),
+ text: text.into(),
+ language,
+ resolver: &Resolver::default(),
+ options: &Options::default(),
+ current_dir: &env::current_dir().unwrap(),
+ }))
+ }
#[test]
- fn test_no_cycles() {
- let mut builder = WorkspaceBuilder::new();
- let a = builder.document("a.tex", "");
- let b = builder.document("b.tex", "");
- let c = builder.document("c.tex", "\\include{b}\\include{a}");
-
- let project_ordering = ProjectOrdering::new(&builder.workspace);
-
- assert_eq!(project_ordering.get(&a), 2);
- assert_eq!(project_ordering.get(&b), 1);
- assert_eq!(project_ordering.get(&c), 0);
+ fn no_cycles() {
+ let a = Uri::parse("http://example.com/a.tex").unwrap();
+ let b = Uri::parse("http://example.com/b.tex").unwrap();
+ let c = Uri::parse("http://example.com/c.tex").unwrap();
+ let mut snapshot = Snapshot::new();
+ snapshot.0 = vec![
+ create_simple_document(&a, Language::Latex, ""),
+ create_simple_document(&b, Language::Latex, ""),
+ create_simple_document(&c, Language::Latex, r#"\include{b}\include{a}"#),
+ ];
+
+ let current_dir = env::current_dir().unwrap();
+ let ordering = ProjectOrdering::analyze(&snapshot, &Options::default(), &current_dir);
+
+ assert_eq!(ordering.get(&a), 2);
+ assert_eq!(ordering.get(&b), 1);
+ assert_eq!(ordering.get(&c), 0);
}
#[test]
- fn test_cycles() {
- let mut builder = WorkspaceBuilder::new();
- let a = builder.document("a.tex", "\\include{b}");
- let b = builder.document("b.tex", "\\include{a}");
- let c = builder.document("c.tex", "\\include{a}");
-
- let project_ordering = ProjectOrdering::new(&builder.workspace);
-
- assert_eq!(project_ordering.get(&a), 1);
- assert_eq!(project_ordering.get(&b), 2);
- assert_eq!(project_ordering.get(&c), 0);
+ fn cycles() {
+ let a = Uri::parse("http://example.com/a.tex").unwrap();
+ let b = Uri::parse("http://example.com/b.tex").unwrap();
+ let c = Uri::parse("http://example.com/c.tex").unwrap();
+ let mut snapshot = Snapshot::new();
+ snapshot.0 = vec![
+ create_simple_document(&a, Language::Latex, r#"\include{b}"#),
+ create_simple_document(&b, Language::Latex, r#"\include{a}"#),
+ create_simple_document(&c, Language::Latex, r#"\include{a}"#),
+ ];
+
+ let current_dir = env::current_dir().unwrap();
+ let ordering = ProjectOrdering::analyze(&snapshot, &Options::default(), &current_dir);
+
+ assert_eq!(ordering.get(&a), 1);
+ assert_eq!(ordering.get(&b), 2);
+ assert_eq!(ordering.get(&c), 0);
}
#[test]
- fn test_multiple_roots() {
- let mut builder = WorkspaceBuilder::new();
- let a = builder.document("a.tex", "\\include{b}");
- let b = builder.document("b.tex", "");
- let c = builder.document("c.tex", "");
- let d = builder.document("d.tex", "\\include{c}");
-
- let project_ordering = ProjectOrdering::new(&builder.workspace);
-
- assert_eq!(project_ordering.get(&a), 0);
- assert_eq!(project_ordering.get(&b), 1);
- assert_eq!(project_ordering.get(&d), 2);
- assert_eq!(project_ordering.get(&c), 3);
+ fn multiple_roots() {
+ let a = Uri::parse("http://example.com/a.tex").unwrap();
+ let b = Uri::parse("http://example.com/b.tex").unwrap();
+ let c = Uri::parse("http://example.com/c.tex").unwrap();
+ let d = Uri::parse("http://example.com/d.tex").unwrap();
+ let mut snapshot = Snapshot::new();
+ snapshot.0 = vec![
+ create_simple_document(&a, Language::Latex, r#"\include{b}"#),
+ create_simple_document(&b, Language::Latex, ""),
+ create_simple_document(&c, Language::Latex, ""),
+ create_simple_document(&d, Language::Latex, r#"\include{c}"#),
+ ];
+
+ let current_dir = env::current_dir().unwrap();
+ let ordering = ProjectOrdering::analyze(&snapshot, &Options::default(), &current_dir);
+
+ assert_eq!(ordering.get(&a), 0);
+ assert_eq!(ordering.get(&b), 1);
+ assert_eq!(ordering.get(&d), 2);
+ assert_eq!(ordering.get(&c), 3);
}
}
diff --git a/support/texlab/src/symbol/types.rs b/support/texlab/src/symbol/types.rs
new file mode 100644
index 0000000000..f10f43c637
--- /dev/null
+++ b/support/texlab/src/symbol/types.rs
@@ -0,0 +1,104 @@
+use crate::{
+ protocol::{DocumentSymbol, Location, Range, SymbolInformation, SymbolKind, Uri},
+ syntax::{BibtexEntryTypeCategory, Structure},
+};
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum LatexSymbolKind {
+ Section,
+ Figure,
+ Algorithm,
+ Table,
+ Listing,
+ Enumeration,
+ EnumerationItem,
+ Theorem,
+ Equation,
+ Entry(BibtexEntryTypeCategory),
+ Field,
+ String,
+}
+
+impl Into<SymbolKind> for LatexSymbolKind {
+ fn into(self) -> SymbolKind {
+ match self {
+ Self::Section => Structure::Section.symbol_kind(),
+ Self::Figure | Self::Algorithm | Self::Table | Self::Listing => {
+ Structure::Float.symbol_kind()
+ }
+ Self::Enumeration => Structure::Environment.symbol_kind(),
+ Self::EnumerationItem => Structure::Item.symbol_kind(),
+ Self::Theorem => Structure::Theorem.symbol_kind(),
+ Self::Equation => Structure::Equation.symbol_kind(),
+ Self::Entry(category) => Structure::Entry(category).symbol_kind(),
+ Self::Field => Structure::Field.symbol_kind(),
+ Self::String => Structure::Entry(BibtexEntryTypeCategory::String).symbol_kind(),
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct LatexSymbol {
+ pub name: String,
+ pub label: Option<String>,
+ pub kind: LatexSymbolKind,
+ pub deprecated: bool,
+ pub full_range: Range,
+ pub selection_range: Range,
+ pub children: Vec<LatexSymbol>,
+}
+
+impl LatexSymbol {
+ pub fn search_text(&self) -> String {
+ let kind = match self.kind {
+ LatexSymbolKind::Section => "latex section",
+ LatexSymbolKind::Figure => "latex float figure",
+ LatexSymbolKind::Algorithm => "latex float algorithm",
+ LatexSymbolKind::Table => "latex float table",
+ LatexSymbolKind::Listing => "latex float listing",
+ LatexSymbolKind::Enumeration => "latex enumeration",
+ LatexSymbolKind::EnumerationItem => "latex enumeration item",
+ LatexSymbolKind::Theorem => "latex math",
+ LatexSymbolKind::Equation => "latex math equation",
+ LatexSymbolKind::Entry(_) => "bibtex entry",
+ LatexSymbolKind::Field => "bibtex field",
+ LatexSymbolKind::String => "bibtex string",
+ };
+ format!("{} {}", kind, self.name).to_lowercase()
+ }
+
+ pub fn flatten(mut self, buffer: &mut Vec<Self>) {
+ if self.kind == LatexSymbolKind::Field {
+ return;
+ }
+ for symbol in self.children.drain(..) {
+ symbol.flatten(buffer);
+ }
+ buffer.push(self);
+ }
+
+ pub fn into_symbol_info(self, uri: Uri) -> SymbolInformation {
+ SymbolInformation {
+ name: self.name,
+ deprecated: Some(self.deprecated),
+ kind: self.kind.into(),
+ container_name: None,
+ location: Location::new(uri.into(), self.full_range),
+ }
+ }
+}
+
+impl Into<DocumentSymbol> for LatexSymbol {
+ fn into(self) -> DocumentSymbol {
+ let children = self.children.into_iter().map(Into::into).collect();
+ DocumentSymbol {
+ name: self.name,
+ deprecated: Some(self.deprecated),
+ detail: self.label,
+ kind: self.kind.into(),
+ selection_range: self.selection_range,
+ range: self.full_range,
+ children: Some(children),
+ }
+ }
+}