diff options
Diffstat (limited to 'support/texlab/src/symbol')
-rw-r--r-- | support/texlab/src/symbol/bibtex_entry.rs | 216 | ||||
-rw-r--r-- | support/texlab/src/symbol/bibtex_string.rs | 115 | ||||
-rw-r--r-- | support/texlab/src/symbol/latex_section/enumeration.rs | 84 | ||||
-rw-r--r-- | support/texlab/src/symbol/latex_section/equation.rs | 36 | ||||
-rw-r--r-- | support/texlab/src/symbol/latex_section/float.rs | 46 | ||||
-rw-r--r-- | support/texlab/src/symbol/latex_section/mod.rs | 781 | ||||
-rw-r--r-- | support/texlab/src/symbol/latex_section/theorem.rs | 68 | ||||
-rw-r--r-- | support/texlab/src/symbol/mod.rs | 231 | ||||
-rw-r--r-- | support/texlab/src/symbol/project_order.rs | 179 | ||||
-rw-r--r-- | support/texlab/src/symbol/types.rs | 104 |
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, ¤t_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, + ¤t_dir, + ), + distro: distro.clone(), + client_capabilities: Arc::clone(&client_capabilities), + options: options.clone(), + current_dir: Arc::clone(¤t_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, ¤t_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(), ¤t_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(), ¤t_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(), ¤t_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), + } + } +} |