diff options
Diffstat (limited to 'support/texlab/src/symbol/latex_section')
-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 |
5 files changed, 526 insertions, 489 deletions
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); |