From ed2a3bfa750d17af5ccad81565c2e6815346fc10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20F=C3=B6rster?= Date: Sat, 18 May 2019 10:34:03 +0200 Subject: [PATCH] Improve performance --- src/completion/latex/combinators.rs | 33 +++--- src/completion/latex/data/types.rs | 6 +- src/completion/latex/include.rs | 2 +- src/completion/latex/label.rs | 8 +- src/completion/latex/user_command.rs | 5 +- src/completion/quality.rs | 52 +++++----- src/definition/latex_citation.rs | 11 +- src/definition/latex_label.rs | 20 ++-- src/folding/latex_environment.rs | 5 +- src/folding/latex_section.rs | 23 ++--- src/highlight/latex_label.rs | 17 ++-- src/hover/latex_citation.rs | 15 +-- src/hover/latex_component.rs | 10 +- src/link/latex_include.rs | 10 +- src/reference/bibtex_entry.rs | 8 +- src/reference/latex_label.rs | 20 ++-- src/rename/bibtex_entry.rs | 20 ++-- src/rename/latex_command.rs | 9 +- src/rename/latex_environment.rs | 9 +- src/rename/latex_label.rs | 18 ++-- src/syntax/latex/analysis/citation.rs | 54 +++------- src/syntax/latex/analysis/command.rs | 24 ++--- src/syntax/latex/analysis/environment.rs | 123 +++++++++-------------- src/syntax/latex/analysis/equation.rs | 66 ++++-------- src/syntax/latex/analysis/finder.rs | 79 ++++----------- src/syntax/latex/analysis/include.rs | 104 +++++++------------ src/syntax/latex/analysis/label.rs | 68 +++++-------- src/syntax/latex/analysis/section.rs | 63 ++++-------- src/syntax/latex/ast.rs | 49 +++++---- src/syntax/latex/mod.rs | 66 +++++++++++- src/syntax/latex/parser.rs | 28 +++--- src/workspace.rs | 19 +--- 32 files changed, 422 insertions(+), 622 deletions(-) diff --git a/src/completion/latex/combinators.rs b/src/completion/latex/combinators.rs index 16c4fbdc0..afb53b640 100644 --- a/src/completion/latex/combinators.rs +++ b/src/completion/latex/combinators.rs @@ -2,22 +2,21 @@ use crate::feature::FeatureRequest; use crate::syntax::latex::*; use crate::syntax::SyntaxTree; use lsp_types::{CompletionItem, CompletionParams}; +use std::sync::Arc; pub struct LatexCombinators; impl LatexCombinators { - pub async fn command<'a, E, F>( - request: &'a FeatureRequest, + pub async fn command( + request: &FeatureRequest, execute: E, ) -> Vec where - E: Fn(&'a LatexCommand) -> F, + E: Fn(Arc) -> F, F: std::future::Future>, { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut finder = LatexCommandFinder::new(request.params.position); - finder.visit_root(&tree.root); - if let Some(command) = finder.result { + if let Some(command) = tree.find_command(request.params.position) { return await!(execute(command)); } } @@ -31,15 +30,15 @@ impl LatexCombinators { execute: E, ) -> Vec where - E: Fn(&'a LatexCommand) -> F, + E: Fn(Arc) -> F, F: std::future::Future>, { - let find_command = |nodes: &[LatexNode<'a>], node_index: usize| { - if let LatexNode::Group(group) = nodes[node_index] { - if let LatexNode::Command(command) = nodes[node_index + 1] { + let find_command = |nodes: &[LatexNode], node_index: usize| { + if let LatexNode::Group(group) = &nodes[node_index] { + if let LatexNode::Command(command) = nodes[node_index + 1].clone() { if command_names.contains(&command.name.text()) && command.args.len() > argument_index - && command.args[argument_index] == *group + && &command.args[argument_index] == group { return Some(command); } @@ -48,7 +47,7 @@ impl LatexCombinators { None }; - let find_non_empty_command = |nodes: &[LatexNode<'a>]| { + let find_non_empty_command = |nodes: &[LatexNode]| { if nodes.len() >= 3 { if let LatexNode::Text(_) = nodes[0] { return find_command(nodes, 1); @@ -57,7 +56,7 @@ impl LatexCombinators { None }; - let find_empty_command = |nodes: &[LatexNode<'a>]| { + let find_empty_command = |nodes: &[LatexNode]| { if nodes.len() >= 2 { find_command(nodes, 0) } else { @@ -66,9 +65,7 @@ impl LatexCombinators { }; if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut finder = LatexFinder::new(request.params.position); - finder.visit_root(&tree.root); - let mut nodes = finder.results; + let mut nodes = tree.find(request.params.position); nodes.reverse(); let command = find_non_empty_command(&nodes).or_else(|| find_empty_command(&nodes)); @@ -78,7 +75,7 @@ impl LatexCombinators { .range .contains_exclusive(request.params.position) { - return await!(execute(command)); + return await!(execute(Arc::clone(&command))); } } } @@ -90,7 +87,7 @@ impl LatexCombinators { execute: E, ) -> Vec where - E: Fn(&LatexCommand) -> F, + E: Fn(Arc) -> F, F: std::future::Future>, { await!(Self::argument(&request, &ENVIRONMENT_COMMANDS, 0, execute)) diff --git a/src/completion/latex/data/types.rs b/src/completion/latex/data/types.rs index bea430ac0..7ba48cdea 100644 --- a/src/completion/latex/data/types.rs +++ b/src/completion/latex/data/types.rs @@ -1,4 +1,3 @@ -use crate::syntax::latex::{LatexIncludeAnalyzer, LatexVisitor}; use crate::syntax::SyntaxTree; use crate::workspace::Document; use itertools::Itertools; @@ -29,10 +28,7 @@ impl LatexComponentDatabase { let mut start_components = Vec::new(); for document in documents { if let SyntaxTree::Latex(tree) = &document.tree { - let mut analyzer = LatexIncludeAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .included_components + tree.components .iter() .flat_map(|file| self.find(&file)) .for_each(|component| start_components.push(component)) diff --git a/src/completion/latex/include.rs b/src/completion/latex/include.rs index f6e88fc76..0c511a757 100644 --- a/src/completion/latex/include.rs +++ b/src/completion/latex/include.rs @@ -40,7 +40,7 @@ impl LatexIncludeCompletionProvider { .into_iter() { if let Ok(entry) = entry { - if entry.file_type().is_file() && is_included(command, &entry.path()) { + if entry.file_type().is_file() && is_included(&command, &entry.path()) { let mut path = entry.into_path(); if NO_EXTENSION_COMMANDS.contains(&command.name.text()) { remove_extension(&mut path); diff --git a/src/completion/latex/label.rs b/src/completion/latex/label.rs index 5be935354..2b2b34e1a 100644 --- a/src/completion/latex/label.rs +++ b/src/completion/latex/label.rs @@ -18,13 +18,11 @@ impl LatexLabelCompletionProvider { let mut items = Vec::new(); for document in &request.related_documents { if let SyntaxTree::Latex(tree) = &document.tree { - let mut analyzer = LatexLabelAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer + tree .labels .iter() - .filter(|label| label.kind == LatexLabelKind::Definition) - .map(|label| Cow::from(label.name.text().to_owned())) + .filter(|label| label.kind() == LatexLabelKind::Definition) + .map(|label| Cow::from(label.name().text().to_owned())) .map(factory::create_label) .for_each(|item| items.push(item)) } diff --git a/src/completion/latex/user_command.rs b/src/completion/latex/user_command.rs index d788f25f5..7fbd12b59 100644 --- a/src/completion/latex/user_command.rs +++ b/src/completion/latex/user_command.rs @@ -2,7 +2,6 @@ use crate::completion::factory; use crate::completion::factory::LatexComponentId; use crate::completion::latex::combinators::LatexCombinators; use crate::feature::FeatureRequest; -use crate::syntax::latex::{LatexCommandAnalyzer, LatexVisitor}; use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use itertools::Itertools; @@ -19,9 +18,7 @@ impl LatexUserCommandCompletionProvider { let mut items = Vec::new(); for document in &request.related_documents { if let SyntaxTree::Latex(tree) = &document.tree { - let mut analyzer = LatexCommandAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer + tree .commands .iter() .filter(|command| command.range() != current_command.range()) diff --git a/src/completion/quality.rs b/src/completion/quality.rs index 983269dc4..09d1b9c7c 100644 --- a/src/completion/quality.rs +++ b/src/completion/quality.rs @@ -5,6 +5,7 @@ use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use crate::workspace::Document; use lsp_types::{CompletionItem, CompletionParams, Position}; +use std::borrow::Cow; pub struct OrderByQualityCompletionProvider; @@ -19,61 +20,62 @@ impl OrderByQualityCompletionProvider { { let query = Self::get_query(&request.document, request.params.position); let mut items = await!(execute(&request)); - items.sort_by_key(|item| -Self::get_quality(query, &item.label)); + items.sort_by_key(|item| -Self::get_quality(&query, &item.label)); items } - fn get_query(document: &Document, position: Position) -> Option<&str> { + fn get_query(document: &Document, position: Position) -> Option> { match &document.tree { SyntaxTree::Latex(tree) => { - let mut command_finder = LatexCommandFinder::new(position); - command_finder.visit_root(&tree.root); - let mut node = command_finder.result.map(LatexNode::Command).or_else(|| { - let mut finder = LatexFinder::new(position); - finder.visit_root(&tree.root); - finder.results.into_iter().last() - })?; + let node = tree + .find_command(position) + .map(LatexNode::Command) + .or_else(|| tree.find(position).into_iter().last())?; match node { - LatexNode::Root(_) | LatexNode::Group(_) => Some(""), - LatexNode::Command(command) => Some(&command.name.text()[1..]), - LatexNode::Text(text) => text.words.last().map(LatexToken::text), + LatexNode::Root(_) | LatexNode::Group(_) => Some(Cow::from("")), + LatexNode::Command(command) => { + Some(Cow::from(command.name.text()[1..].to_owned())) + } + LatexNode::Text(text) => { + text.words.last().map(|w| Cow::from(w.text().to_owned())) + } } } SyntaxTree::Bibtex(tree) => { - fn get_type_query(ty: &BibtexToken, position: Position) -> Option<&str> { + fn get_type_query(ty: &BibtexToken, position: Position) -> Option> { if ty.range().contains(position) { - Some(&ty.text()[1..]) + Some(Cow::from(&ty.text()[1..])) } else { - Some("") + Some(Cow::from("")) } } let mut finder = BibtexFinder::new(position); finder.visit_root(&tree.root); match finder.results.pop()? { - BibtexNode::Root(_) => Some(""), + BibtexNode::Root(_) => Some(Cow::from("")), BibtexNode::Preamble(preamble) => get_type_query(&preamble.ty, position), BibtexNode::String(string) => get_type_query(&string.ty, position), BibtexNode::Entry(entry) => get_type_query(&entry.ty, position), - BibtexNode::Comment(comment) => Some(comment.token.text()), + BibtexNode::Comment(comment) => Some(Cow::from(comment.token.text())), BibtexNode::Field(field) => { if field.name.range().contains(position) { - Some(field.name.text()) + Some(Cow::from(field.name.text())) } else { - Some("") + Some(Cow::from("")) } } - BibtexNode::Word(word) => Some(word.token.text()), - BibtexNode::Command(command) => Some(&command.token.text()[1..]), + BibtexNode::Word(word) => Some(Cow::from(word.token.text())), + BibtexNode::Command(command) => Some(Cow::from(&command.token.text()[1..])), BibtexNode::QuotedContent(_) | BibtexNode::BracedContent(_) - | BibtexNode::Concat(_) => Some(""), + | BibtexNode::Concat(_) => Some(Cow::from("")), } } } } - fn get_quality(query: Option<&str>, label: &str) -> i32 { + fn get_quality(query: &Option>, label: &str) -> i32 { if let Some(query) = query { if label == query { return 7; @@ -83,7 +85,7 @@ impl OrderByQualityCompletionProvider { return 6; } - if label.starts_with(query) { + if label.starts_with(query.as_ref()) { return 5; } @@ -91,7 +93,7 @@ impl OrderByQualityCompletionProvider { return 4; } - if label.contains(query) { + if label.contains(query.as_ref()) { return 3; } diff --git a/src/definition/latex_citation.rs b/src/definition/latex_citation.rs index e4bfe0ba9..5cdd63c1e 100644 --- a/src/definition/latex_citation.rs +++ b/src/definition/latex_citation.rs @@ -1,6 +1,5 @@ use crate::feature::FeatureRequest; use crate::syntax::bibtex::BibtexDeclaration; -use crate::syntax::latex::{LatexCitationAnalyzer, LatexVisitor}; use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use crate::workspace::Document; @@ -37,13 +36,11 @@ impl LatexCitationDefinitionProvider { fn find_reference(request: &FeatureRequest) -> Option<&str> { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexCitationAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .citations + tree.citations .iter() - .find(|citation| citation.key.range().contains(request.params.position)) - .map(|citation| citation.key.text()) + .map(|citation| citation.key()) + .find(|key| key.range().contains(request.params.position)) + .map(|key| key.text()) } else { None } diff --git a/src/definition/latex_label.rs b/src/definition/latex_label.rs index 41ea43be6..0be5a2f3d 100644 --- a/src/definition/latex_label.rs +++ b/src/definition/latex_label.rs @@ -21,14 +21,11 @@ impl LatexLabelDefinitionProvider { fn find_definition(document: &Document, reference: &str) -> Option { if let SyntaxTree::Latex(tree) = &document.tree { - let mut analyzer = LatexLabelAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .labels + tree.labels .iter() - .filter(|label| label.kind == LatexLabelKind::Definition) - .find(|label| label.name.text() == reference) - .map(|label| Location::new(document.uri.clone(), label.name.range())) + .filter(|label| label.kind() == LatexLabelKind::Definition) + .find(|label| label.name().text() == reference) + .map(|label| Location::new(document.uri.clone(), label.name().range())) } else { None } @@ -36,13 +33,10 @@ impl LatexLabelDefinitionProvider { fn find_reference(request: &FeatureRequest) -> Option<&str> { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexLabelAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .labels + tree.labels .iter() - .find(|label| label.name.range().contains(request.params.position)) - .map(|label| label.name.text()) + .find(|label| label.name().range().contains(request.params.position)) + .map(|label| label.name().text()) } else { None } diff --git a/src/folding/latex_environment.rs b/src/folding/latex_environment.rs index 6baf56093..9197f7c3a 100644 --- a/src/folding/latex_environment.rs +++ b/src/folding/latex_environment.rs @@ -1,5 +1,4 @@ use crate::feature::FeatureRequest; -use crate::syntax::latex::{LatexEnvironmentAnalyzer, LatexVisitor}; use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use lsp_types::{FoldingRange, FoldingRangeKind, FoldingRangeParams}; @@ -10,9 +9,7 @@ impl LatexEnvironmentFoldingProvider { pub async fn execute(request: &FeatureRequest) -> Vec { let mut foldings = Vec::new(); if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexEnvironmentAnalyzer::new(); - analyzer.visit_root(&tree.root); - for environment in &analyzer.environments { + for environment in &tree.environments { let start = environment.left.command.end(); let end = environment.right.command.start(); foldings.push(FoldingRange { diff --git a/src/folding/latex_section.rs b/src/folding/latex_section.rs index 8a74ce8e4..106c98f36 100644 --- a/src/folding/latex_section.rs +++ b/src/folding/latex_section.rs @@ -1,5 +1,4 @@ use crate::feature::FeatureRequest; -use crate::syntax::latex::{LatexSectionAnalyzer, LatexVisitor}; use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use lsp_types::{FoldingRange, FoldingRangeKind, FoldingRangeParams}; @@ -10,9 +9,7 @@ impl LatexSectionFoldingProvider { pub async fn execute(request: &FeatureRequest) -> Vec { let mut foldings = Vec::new(); if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexSectionAnalyzer::new(); - analyzer.visit_root(&tree.root); - let sections = analyzer.sections; + let sections = &tree.sections; for i in 0..sections.len() { let current = §ions[i]; let mut next = None; @@ -24,14 +21,16 @@ impl LatexSectionFoldingProvider { } if let Some(next) = next { - let folding = FoldingRange { - start_line: current.command.end().line, - start_character: Some(current.command.end().character), - end_line: next.command.start().line - 1, - end_character: Some(0), - kind: Some(FoldingRangeKind::Region), - }; - foldings.push(folding); + if next.command.start().line > 0 { + let folding = FoldingRange { + start_line: current.command.end().line, + start_character: Some(current.command.end().character), + end_line: next.command.start().line - 1, + end_character: Some(0), + kind: Some(FoldingRangeKind::Region), + }; + foldings.push(folding); + } } } } diff --git a/src/highlight/latex_label.rs b/src/highlight/latex_label.rs index 8b20b7831..d0dc8ebc1 100644 --- a/src/highlight/latex_label.rs +++ b/src/highlight/latex_label.rs @@ -11,22 +11,19 @@ impl LatexLabelHighlightProvider { request: &FeatureRequest, ) -> Vec { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexLabelAnalyzer::new(); - analyzer.visit_root(&tree.root); - - if let Some(name) = analyzer + if let Some(name) = tree .labels .iter() - .find(|label| label.name.range().contains(request.params.position)) - .map(|label| label.name.text()) + .find(|label| label.name().range().contains(request.params.position)) + .map(|label| label.name().text()) { - return analyzer + return tree .labels .iter() - .filter(|label| label.name.text() == name) + .filter(|label| label.name().text() == name) .map(|label| DocumentHighlight { - range: label.name.range(), - kind: Some(match label.kind { + range: label.name().range(), + kind: Some(match label.kind() { LatexLabelKind::Definition => DocumentHighlightKind::Write, LatexLabelKind::Reference => DocumentHighlightKind::Read, }), diff --git a/src/hover/latex_citation.rs b/src/hover/latex_citation.rs index fe93c7d69..be9fced65 100644 --- a/src/hover/latex_citation.rs +++ b/src/hover/latex_citation.rs @@ -1,7 +1,6 @@ use crate::feature::FeatureRequest; use crate::formatting::{BibtexFormatter, BibtexFormattingOptions}; use crate::syntax::bibtex::{BibtexDeclaration, BibtexEntry}; -use crate::syntax::latex::{LatexCitationAnalyzer, LatexToken, LatexVisitor}; use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use lsp_types::{Hover, HoverContents, MarkupContent, MarkupKind, TextDocumentPositionParams}; @@ -47,15 +46,11 @@ impl LatexCitationHoverProvider { fn get_key(request: &FeatureRequest) -> Option<&str> { match &request.document.tree { - SyntaxTree::Latex(tree) => { - let mut analyzer = LatexCitationAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .citations - .iter() - .find(|citation| citation.command.range.contains(request.params.position)) - .map(|citation| citation.key.text()) - } + SyntaxTree::Latex(tree) => tree + .citations + .iter() + .find(|citation| citation.command.range.contains(request.params.position)) + .map(|citation| citation.key().text()), SyntaxTree::Bibtex(tree) => { for declaration in &tree.root.children { if let BibtexDeclaration::Entry(entry) = &declaration { diff --git a/src/hover/latex_component.rs b/src/hover/latex_component.rs index 82ed8e6c9..9d6a9036c 100644 --- a/src/hover/latex_component.rs +++ b/src/hover/latex_component.rs @@ -1,6 +1,6 @@ use crate::data::component_doc::ComponentDocumentation; use crate::feature::FeatureRequest; -use crate::syntax::latex::{LatexIncludeAnalyzer, LatexIncludeKind, LatexVisitor}; +use crate::syntax::latex::{LatexIncludeKind, LatexVisitor}; use crate::syntax::SyntaxTree; use lsp_types::{Hover, HoverContents, TextDocumentPositionParams}; @@ -9,17 +9,15 @@ pub struct LatexComponentHoverProvider; impl LatexComponentHoverProvider { pub async fn execute(request: &FeatureRequest) -> Option { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexIncludeAnalyzer::new(); - analyzer.visit_root(&tree.root); - let documentation = await!(analyzer - .included_files + let documentation = await!(tree + .includes .iter() .filter(|include| { include.kind == LatexIncludeKind::Package || include.kind == LatexIncludeKind::Class }) .find(|include| include.command.range.contains(request.params.position)) - .map(|include| include.path.text()) + .map(|include| include.path().text()) .map(|name| ComponentDocumentation::lookup(name))?)?; Some(Hover { diff --git a/src/link/latex_include.rs b/src/link/latex_include.rs index 6e9ae0cec..d39250864 100644 --- a/src/link/latex_include.rs +++ b/src/link/latex_include.rs @@ -9,10 +9,8 @@ pub struct LatexIncludeLinkProvider; impl LatexIncludeLinkProvider { pub async fn execute(request: &FeatureRequest) -> Vec { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexIncludeAnalyzer::new(); - analyzer.visit_root(&tree.root); - return analyzer - .included_files + return tree + .includes .iter() .flat_map(|include| Self::resolve(&request, &include)) .collect(); @@ -26,9 +24,9 @@ impl LatexIncludeLinkProvider { ) -> Option { request .workspace - .resolve_document(&request.document.uri, include.path.text()) + .resolve_document(&request.document.uri, include.path().text()) .map(|target| DocumentLink { - range: include.path.range(), + range: include.path().range(), target: target.uri.clone(), }) } diff --git a/src/reference/bibtex_entry.rs b/src/reference/bibtex_entry.rs index 5f405465a..d52dedb7e 100644 --- a/src/reference/bibtex_entry.rs +++ b/src/reference/bibtex_entry.rs @@ -1,6 +1,5 @@ use crate::feature::FeatureRequest; use crate::syntax::bibtex::BibtexDeclaration; -use crate::syntax::latex::{LatexCitationAnalyzer, LatexVisitor}; use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use lsp_types::{Location, ReferenceParams}; @@ -13,12 +12,9 @@ impl BibtexEntryReferenceProvider { if let Some(key) = Self::find_definition(request) { for document in &request.related_documents { if let SyntaxTree::Latex(tree) = &document.tree { - let mut analyzer = LatexCitationAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .citations + tree.citations .iter() - .filter(|citation| citation.key.text() == key) + .filter(|citation| citation.key().text() == key) .map(|citation| Location::new(document.uri.clone(), citation.command.range)) .for_each(|location| references.push(location)) } diff --git a/src/reference/latex_label.rs b/src/reference/latex_label.rs index 53ddfc6d6..ad48762de 100644 --- a/src/reference/latex_label.rs +++ b/src/reference/latex_label.rs @@ -1,5 +1,5 @@ use crate::feature::FeatureRequest; -use crate::syntax::latex::{LatexLabelAnalyzer, LatexLabelKind, LatexVisitor}; +use crate::syntax::latex::LatexLabelKind; use crate::syntax::SyntaxTree; use lsp_types::{Location, ReferenceParams}; @@ -11,13 +11,10 @@ impl LatexLabelReferenceProvider { if let Some(definition) = Self::find_definition(request) { for document in &request.related_documents { if let SyntaxTree::Latex(tree) = &document.tree { - let mut analyzer = LatexLabelAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .labels + tree.labels .iter() - .filter(|label| label.kind == LatexLabelKind::Reference) - .filter(|label| label.name.text() == definition) + .filter(|label| label.kind() == LatexLabelKind::Reference) + .filter(|label| label.name().text() == definition) .map(|label| Location::new(document.uri.clone(), label.command.range)) .for_each(|location| references.push(location)) } @@ -28,16 +25,13 @@ impl LatexLabelReferenceProvider { fn find_definition(request: &FeatureRequest) -> Option<&str> { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexLabelAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .labels + tree.labels .iter() .find(|label| { - label.kind == LatexLabelKind::Definition + label.kind() == LatexLabelKind::Definition && label.command.range.contains(request.params.position) }) - .map(|label| label.name.text()) + .map(|label| label.name().text()) } else { None } diff --git a/src/rename/bibtex_entry.rs b/src/rename/bibtex_entry.rs index c762ef295..676344a34 100644 --- a/src/rename/bibtex_entry.rs +++ b/src/rename/bibtex_entry.rs @@ -1,6 +1,6 @@ use crate::feature::FeatureRequest; use crate::syntax::bibtex::{BibtexDeclaration, BibtexSyntaxTree}; -use crate::syntax::latex::{LatexCitationAnalyzer, LatexSyntaxTree, LatexVisitor}; +use crate::syntax::latex::{LatexSyntaxTree, LatexVisitor}; use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use lsp_types::*; @@ -21,15 +21,12 @@ impl BibtexEntryRenameProvider { let mut edits = Vec::new(); match &document.tree { SyntaxTree::Latex(tree) => { - let mut analyzer = LatexCitationAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .citations + tree.citations .iter() - .filter(|citation| citation.key.text() == key_name) + .filter(|citation| citation.key().text() == key_name) .map(|citation| { TextEdit::new( - citation.key.range(), + citation.key().range(), Cow::from(request.params.new_name.clone()), ) }) @@ -56,11 +53,10 @@ impl BibtexEntryRenameProvider { } fn find_citation(tree: &LatexSyntaxTree, position: Position) -> Option<&str> { - let mut analyzer = LatexCitationAnalyzer::new(); - analyzer.visit_root(&tree.root); - for citation in &analyzer.citations { - if citation.key.range().contains(position) { - return Some(&citation.key.text()); + for citation in &tree.citations { + let key = citation.key(); + if key.range().contains(position) { + return Some(key.text()); } } None diff --git a/src/rename/latex_command.rs b/src/rename/latex_command.rs index 76093827c..20c8d08e4 100644 --- a/src/rename/latex_command.rs +++ b/src/rename/latex_command.rs @@ -14,9 +14,7 @@ impl LatexCommandRenameProvider { let mut changes = HashMap::new(); for document in &request.related_documents { if let SyntaxTree::Latex(tree) = &document.tree { - let mut analyzer = LatexCommandAnalyzer::new(); - analyzer.visit_root(&tree.root); - let edits: Vec = analyzer + let edits: Vec = tree .commands .iter() .filter(|command| command.name.text() == name) @@ -35,10 +33,7 @@ impl LatexCommandRenameProvider { fn find_command(request: &FeatureRequest) -> Option<&str> { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexCommandAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .commands + tree.commands .iter() .find(|command| command.name.range().contains(request.params.position)) .map(|command| command.name.text()) diff --git a/src/rename/latex_environment.rs b/src/rename/latex_environment.rs index f7d44dfc0..197ab6867 100644 --- a/src/rename/latex_environment.rs +++ b/src/rename/latex_environment.rs @@ -1,5 +1,4 @@ use crate::feature::FeatureRequest; -use crate::syntax::latex::{LatexEnvironmentAnalyzer, LatexVisitor}; use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use lsp_types::{RenameParams, TextEdit, WorkspaceEdit}; @@ -11,11 +10,9 @@ pub struct LatexEnvironmentRenameProvider; impl LatexEnvironmentRenameProvider { pub async fn execute(request: &FeatureRequest) -> Option { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexEnvironmentAnalyzer::new(); - analyzer.visit_root(&tree.root); - for environment in &analyzer.environments { - if let Some(left_name) = environment.left.name { - if let Some(right_name) = environment.right.name { + for environment in &tree.environments { + if let Some(left_name) = environment.left.name() { + if let Some(right_name) = environment.right.name() { if left_name.range().contains(request.params.position) || right_name.range().contains(request.params.position) { diff --git a/src/rename/latex_label.rs b/src/rename/latex_label.rs index 6517fe34b..9e18c6a15 100644 --- a/src/rename/latex_label.rs +++ b/src/rename/latex_label.rs @@ -1,5 +1,4 @@ use crate::feature::FeatureRequest; -use crate::syntax::latex::{LatexLabelAnalyzer, LatexVisitor}; use crate::syntax::text::SyntaxNode; use crate::syntax::SyntaxTree; use lsp_types::{RenameParams, TextEdit, WorkspaceEdit}; @@ -14,15 +13,13 @@ impl LatexLabelRenameProvider { let mut changes = HashMap::new(); for document in &request.related_documents { if let SyntaxTree::Latex(tree) = &document.tree { - let mut analyzer = LatexLabelAnalyzer::new(); - analyzer.visit_root(&tree.root); - let edits = analyzer + let edits = tree .labels .iter() - .filter(|label| label.name.text() == name) + .filter(|label| label.name().text() == name) .map(|label| { TextEdit::new( - label.name.range(), + label.name().range(), Cow::from(request.params.new_name.clone()), ) }) @@ -35,13 +32,10 @@ impl LatexLabelRenameProvider { fn find_label(request: &FeatureRequest) -> Option<&str> { if let SyntaxTree::Latex(tree) = &request.document.tree { - let mut analyzer = LatexLabelAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer - .labels + tree.labels .iter() - .find(|label| label.name.range().contains(request.params.position)) - .map(|label| label.name.text()) + .find(|label| label.name().range().contains(request.params.position)) + .map(|label| label.name().text()) } else { None } diff --git a/src/syntax/latex/analysis/citation.rs b/src/syntax/latex/analysis/citation.rs index 8c032d0d5..cafcc65e0 100644 --- a/src/syntax/latex/analysis/citation.rs +++ b/src/syntax/latex/analysis/citation.rs @@ -1,48 +1,29 @@ use crate::syntax::latex::ast::*; +use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexCitation<'a> { - pub command: &'a LatexCommand, - pub key: &'a LatexToken, +pub struct LatexCitation { + pub command: Arc, } -impl<'a> LatexCitation<'a> { - pub fn new(command: &'a LatexCommand, key: &'a LatexToken) -> Self { - LatexCitation { command, key } +impl LatexCitation { + pub fn new(command: Arc) -> LatexCitation { + LatexCitation { command } } -} - -pub struct LatexCitationAnalyzer<'a> { - pub citations: Vec>, -} - -impl<'a> LatexCitationAnalyzer<'a> { - pub fn new() -> Self { - LatexCitationAnalyzer { - citations: Vec::new(), - } - } -} -impl<'a> LatexVisitor<'a> for LatexCitationAnalyzer<'a> { - fn visit_root(&mut self, root: &'a LatexRoot) { - LatexWalker::walk_root(self, root); + pub fn key(&self) -> &LatexToken { + self.command.extract_word(0).unwrap() } - fn visit_group(&mut self, group: &'a LatexGroup) { - LatexWalker::walk_group(self, group); - } - - fn visit_command(&mut self, command: &'a LatexCommand) { - if CITATION_COMMANDS.contains(&command.name.text()) { - if let Some(key) = command.extract_word(0) { - self.citations.push(LatexCitation::new(command, key)); + pub fn parse(commands: &[Arc]) -> Vec { + let mut citations = Vec::new(); + for command in commands { + if CITATION_COMMANDS.contains(&command.name.text()) && command.has_word(0) { + citations.push(LatexCitation::new(Arc::clone(&command))); } } - LatexWalker::walk_command(self, command); + citations } - - fn visit_text(&mut self, text: &'a LatexText) {} } pub const CITATION_COMMANDS: &'static [&'static str] = &[ @@ -106,17 +87,14 @@ pub const CITATION_COMMANDS: &'static [&'static str] = &[ #[cfg(test)] mod tests { - use super::*; use crate::syntax::latex::LatexSyntaxTree; fn verify(text: &str, expected: Vec<&str>) { let tree = LatexSyntaxTree::from(text); - let mut analyzer = LatexCitationAnalyzer::new(); - analyzer.visit_root(&tree.root); - let actual: Vec<&str> = analyzer + let actual: Vec<&str> = tree .citations .iter() - .map(|citation| citation.key.text()) + .map(|citation| citation.key().text()) .collect(); assert_eq!(expected, actual); } diff --git a/src/syntax/latex/analysis/command.rs b/src/syntax/latex/analysis/command.rs index 0b3974aae..5ce714044 100644 --- a/src/syntax/latex/analysis/command.rs +++ b/src/syntax/latex/analysis/command.rs @@ -1,10 +1,11 @@ use crate::syntax::latex::ast::*; +use std::sync::Arc; -pub struct LatexCommandAnalyzer<'a> { - pub commands: Vec<&'a LatexCommand>, +pub struct LatexCommandAnalyzer { + pub commands: Vec>, } -impl<'a> LatexCommandAnalyzer<'a> { +impl LatexCommandAnalyzer { pub fn new() -> Self { LatexCommandAnalyzer { commands: Vec::new(), @@ -12,34 +13,31 @@ impl<'a> LatexCommandAnalyzer<'a> { } } -impl<'a> LatexVisitor<'a> for LatexCommandAnalyzer<'a> { - fn visit_root(&mut self, root: &'a LatexRoot) { +impl LatexVisitor for LatexCommandAnalyzer { + fn visit_root(&mut self, root: Arc) { LatexWalker::walk_root(self, root); } - fn visit_group(&mut self, group: &'a LatexGroup) { + fn visit_group(&mut self, group: Arc) { LatexWalker::walk_group(self, group); } - fn visit_command(&mut self, command: &'a LatexCommand) { - self.commands.push(command); + fn visit_command(&mut self, command: Arc) { + self.commands.push(Arc::clone(&command)); LatexWalker::walk_command(self, command); } - fn visit_text(&mut self, text: &'a LatexText) {} + fn visit_text(&mut self, _text: Arc) {} } #[cfg(test)] mod tests { - use super::*; use crate::syntax::latex::LatexSyntaxTree; #[test] fn test() { let tree = LatexSyntaxTree::from("\\a[\\b]{\\c}{d}"); - let mut analyzer = LatexCommandAnalyzer::new(); - analyzer.visit_root(&tree.root); - let commands: Vec<&str> = analyzer + let commands: Vec<&str> = tree .commands .iter() .map(|command| command.name.text()) diff --git a/src/syntax/latex/analysis/environment.rs b/src/syntax/latex/analysis/environment.rs index 38d8a6950..19b096c53 100644 --- a/src/syntax/latex/analysis/environment.rs +++ b/src/syntax/latex/analysis/environment.rs @@ -1,83 +1,63 @@ use crate::syntax::latex::ast::*; +use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexEnvironmentDelimiter<'a> { - pub command: &'a LatexCommand, - pub name: Option<&'a LatexToken>, +pub struct LatexEnvironmentDelimiter { + pub command: Arc, } -impl<'a> LatexEnvironmentDelimiter<'a> { - pub fn new(command: &'a LatexCommand, name: Option<&'a LatexToken>) -> Self { - LatexEnvironmentDelimiter { command, name } +impl LatexEnvironmentDelimiter { + pub fn name(&self) -> Option<&LatexToken> { + self.command.extract_word(0) + } + + pub fn new(command: Arc) -> Self { + LatexEnvironmentDelimiter { command } } } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexEnvironment<'a> { - pub left: LatexEnvironmentDelimiter<'a>, - pub right: LatexEnvironmentDelimiter<'a>, +pub struct LatexEnvironment { + pub left: LatexEnvironmentDelimiter, + pub right: LatexEnvironmentDelimiter, } -impl<'a> LatexEnvironment<'a> { - pub fn new(left: LatexEnvironmentDelimiter<'a>, right: LatexEnvironmentDelimiter<'a>) -> Self { +impl LatexEnvironment { + pub fn new(left: LatexEnvironmentDelimiter, right: LatexEnvironmentDelimiter) -> Self { LatexEnvironment { left, right } } -} -pub struct LatexEnvironmentAnalyzer<'a> { - pub environments: Vec>, - stack: Vec>, -} - -impl<'a> LatexEnvironmentAnalyzer<'a> { - pub fn new() -> Self { - LatexEnvironmentAnalyzer { - environments: Vec::new(), - stack: Vec::new(), + fn parse_delimiter(command: Arc) -> Option { + if !ENVIRONMENT_COMMANDS.contains(&command.name.text()) { + return None; } - } -} -impl<'a> LatexVisitor<'a> for LatexEnvironmentAnalyzer<'a> { - fn visit_root(&mut self, root: &'a LatexRoot) { - LatexWalker::walk_root(self, root); - } + if command.has_word(0) { + let delimiter = LatexEnvironmentDelimiter::new(Arc::clone(&command)); + return Some(delimiter); + } - fn visit_group(&mut self, group: &'a LatexGroup) { - LatexWalker::walk_group(self, group); + if !command.args.is_empty() && command.args[0].children.is_empty() { + let delimiter = LatexEnvironmentDelimiter::new(Arc::clone(&command)); + Some(delimiter) + } else { + None + } } - fn visit_command(&mut self, command: &'a LatexCommand) { - if let Some(delimiter) = parse_delimiter(command) { - if delimiter.command.name.text() == ENVIRONMENT_COMMANDS[0] { - self.stack.push(delimiter); - } else if let Some(begin) = self.stack.pop() { - self.environments - .push(LatexEnvironment::new(begin, delimiter)); + pub fn parse(commands: &[Arc]) -> Vec { + let mut stack = Vec::new(); + let mut environments = Vec::new(); + for command in commands { + if let Some(delimiter) = Self::parse_delimiter(Arc::clone(&command)) { + if delimiter.command.name.text() == ENVIRONMENT_COMMANDS[0] { + stack.push(delimiter); + } else if let Some(begin) = stack.pop() { + environments.push(LatexEnvironment::new(begin, delimiter)); + } } } - - LatexWalker::walk_command(self, command); - } - - fn visit_text(&mut self, text: &'a LatexText) {} -} - -fn parse_delimiter(command: &LatexCommand) -> Option { - if !ENVIRONMENT_COMMANDS.contains(&command.name.text()) { - return None; - } - - if let Some(name) = command.extract_word(0) { - let delimiter = LatexEnvironmentDelimiter::new(command, Some(name)); - return Some(delimiter); - } - - if !command.args.is_empty() && command.args[0].children.is_empty() { - let delimiter = LatexEnvironmentDelimiter::new(command, None); - Some(delimiter) - } else { - None + environments } } @@ -85,38 +65,31 @@ pub const ENVIRONMENT_COMMANDS: &'static [&'static str] = &["\\begin", "\\end"]; #[cfg(test)] mod tests { - use super::*; use crate::syntax::latex::LatexSyntaxTree; - fn analyze(tree: &LatexSyntaxTree) -> Vec { - let mut analyzer = LatexEnvironmentAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer.environments - } - #[test] fn test_nested() { let tree = LatexSyntaxTree::from("\\begin{foo}\\begin{bar}\\end{baz}\\end{qux}"); - let environments = analyze(&tree); + let environments = tree.environments; assert_eq!(2, environments.len()); - assert_eq!("bar", environments[0].left.name.unwrap().text()); - assert_eq!("baz", environments[0].right.name.unwrap().text()); - assert_eq!("foo", environments[1].left.name.unwrap().text()); - assert_eq!("qux", environments[1].right.name.unwrap().text()); + assert_eq!("bar", environments[0].left.name().unwrap().text()); + assert_eq!("baz", environments[0].right.name().unwrap().text()); + assert_eq!("foo", environments[1].left.name().unwrap().text()); + assert_eq!("qux", environments[1].right.name().unwrap().text()); } #[test] fn test_empty_name() { let tree = LatexSyntaxTree::from("\\begin{}\\end{}"); - let environments = analyze(&tree); + let environments = tree.environments; assert_eq!(1, environments.len()); - assert_eq!(None, environments[0].left.name); - assert_eq!(None, environments[0].right.name); + assert_eq!(None, environments[0].left.name()); + assert_eq!(None, environments[0].right.name()); } #[test] fn test_ummatched() { let tree = LatexSyntaxTree::from("\\end{foo} \\begin{bar}"); - assert_eq!(analyze(&tree), Vec::new()); + assert_eq!(tree.environments, Vec::new()); } } diff --git a/src/syntax/latex/analysis/equation.rs b/src/syntax/latex/analysis/equation.rs index 26b11aa9f..f22d275da 100644 --- a/src/syntax/latex/analysis/equation.rs +++ b/src/syntax/latex/analysis/equation.rs @@ -1,72 +1,44 @@ use crate::syntax::latex::ast::*; +use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexEquation<'a> { - pub left: &'a LatexCommand, - pub right: &'a LatexCommand, +pub struct LatexEquation { + pub left: Arc, + pub right: Arc, } -impl<'a> LatexEquation<'a> { - pub fn new(left: &'a LatexCommand, right: &'a LatexCommand) -> Self { +impl LatexEquation { + pub fn new(left: Arc, right: Arc) -> Self { LatexEquation { left, right } } -} - -pub struct LatexEquationAnalyzer<'a> { - pub equations: Vec>, - left: Option<&'a LatexCommand>, -} - -impl<'a> LatexEquationAnalyzer<'a> { - pub fn new() -> Self { - LatexEquationAnalyzer { - equations: Vec::new(), - left: None, - } - } -} -impl<'a> LatexVisitor<'a> for LatexEquationAnalyzer<'a> { - fn visit_root(&mut self, root: &'a LatexRoot) { - LatexWalker::walk_root(self, root); - } - - fn visit_group(&mut self, group: &'a LatexGroup) { - LatexWalker::walk_group(self, group); - } - - fn visit_command(&mut self, command: &'a LatexCommand) { - if command.name.text() == EQUATION_COMMANDS[0] { - self.left = Some(command); - } else if let Some(left) = self.left { - self.equations.push(LatexEquation::new(left, command)); - self.left = None; + pub fn parse(commands: &[Arc]) -> Vec { + let mut equations = Vec::new(); + let mut left = None; + for command in commands { + if command.name.text() == EQUATION_COMMANDS[0] { + left = Some(command); + } else if let Some(begin) = left { + equations.push(LatexEquation::new(Arc::clone(&begin), Arc::clone(&command))); + left = None; + } } - LatexWalker::walk_command(self, command); + equations } - - fn visit_text(&mut self, text: &'a LatexText) {} } pub const EQUATION_COMMANDS: &'static [&'static str] = &["\\[", "\\]"]; #[cfg(test)] mod tests { - use super::*; use crate::syntax::latex::LatexSyntaxTree; use crate::syntax::text::SyntaxNode; use lsp_types::Range; - fn analyze(tree: &LatexSyntaxTree) -> Vec { - let mut analyzer = LatexEquationAnalyzer::new(); - analyzer.visit_root(&tree.root); - analyzer.equations - } - #[test] fn test_matched() { let tree = LatexSyntaxTree::from("\\[ foo \\]"); - let equations = analyze(&tree); + let equations = tree.equations; assert_eq!(1, equations.len()); assert_eq!(Range::new_simple(0, 0, 0, 2), equations[0].left.range()); assert_eq!(Range::new_simple(0, 7, 0, 9), equations[0].right.range()); @@ -75,6 +47,6 @@ mod tests { #[test] fn test_unmatched() { let tree = LatexSyntaxTree::from("\\] \\["); - assert_eq!(analyze(&tree), Vec::new()); + assert_eq!(tree.equations, Vec::new()); } } diff --git a/src/syntax/latex/analysis/finder.rs b/src/syntax/latex/analysis/finder.rs index 3438a10ac..5ea4c2000 100644 --- a/src/syntax/latex/analysis/finder.rs +++ b/src/syntax/latex/analysis/finder.rs @@ -1,20 +1,22 @@ use crate::syntax::latex::ast::*; use crate::syntax::text::SyntaxNode; use lsp_types::Position; - -pub enum LatexNode<'a> { - Root(&'a LatexRoot), - Group(&'a LatexGroup), - Command(&'a LatexCommand), - Text(&'a LatexText), +use std::sync::Arc; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum LatexNode { + Root(Arc), + Group(Arc), + Command(Arc), + Text(Arc), } -pub struct LatexFinder<'a> { +pub struct LatexFinder { pub position: Position, - pub results: Vec>, + pub results: Vec, } -impl<'a> LatexFinder<'a> { +impl LatexFinder { pub fn new(position: Position) -> Self { LatexFinder { position, @@ -23,74 +25,31 @@ impl<'a> LatexFinder<'a> { } } -impl<'a> LatexVisitor<'a> for LatexFinder<'a> { - fn visit_root(&mut self, root: &'a LatexRoot) { +impl LatexVisitor for LatexFinder { + fn visit_root(&mut self, root: Arc) { if root.range().contains(self.position) { - self.results.push(LatexNode::Root(root)); + self.results.push(LatexNode::Root(Arc::clone(&root))); LatexWalker::walk_root(self, root); } } - fn visit_group(&mut self, group: &'a LatexGroup) { + fn visit_group(&mut self, group: Arc) { if group.range.contains(self.position) { - self.results.push(LatexNode::Group(group)); + self.results.push(LatexNode::Group(Arc::clone(&group))); LatexWalker::walk_group(self, group); } } - fn visit_command(&mut self, command: &'a LatexCommand) { + fn visit_command(&mut self, command: Arc) { if command.range.contains(self.position) { - self.results.push(LatexNode::Command(command)); + self.results.push(LatexNode::Command(Arc::clone(&command))); LatexWalker::walk_command(self, command); } } - fn visit_text(&mut self, text: &'a LatexText) { + fn visit_text(&mut self, text: Arc) { if text.range.contains(self.position) { self.results.push(LatexNode::Text(text)); } } } - -pub struct LatexCommandFinder<'a> { - position: Position, - pub result: Option<&'a LatexCommand>, -} - -impl<'a> LatexCommandFinder<'a> { - pub fn new(position: Position) -> Self { - LatexCommandFinder { - position, - result: None, - } - } -} - -impl<'a> LatexVisitor<'a> for LatexCommandFinder<'a> { - fn visit_root(&mut self, root: &'a LatexRoot) { - if root.range().contains(self.position) { - LatexWalker::walk_root(self, root); - } - } - - fn visit_group(&mut self, group: &'a LatexGroup) { - if group.range.contains(self.position) { - LatexWalker::walk_group(self, group); - } - } - - fn visit_command(&mut self, command: &'a LatexCommand) { - if command.name.range().contains(self.position) - && command.name.start().character != self.position.character - { - self.result = Some(command); - return; - } - - if command.range.contains(self.position) { - LatexWalker::walk_command(self, command); - } - } - - fn visit_text(&mut self, text: &'a LatexText) {} -} diff --git a/src/syntax/latex/analysis/include.rs b/src/syntax/latex/analysis/include.rs index 7553c6359..6f7666c16 100644 --- a/src/syntax/latex/analysis/include.rs +++ b/src/syntax/latex/analysis/include.rs @@ -1,4 +1,5 @@ use crate::syntax::latex::ast::*; +use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum LatexIncludeKind { @@ -9,92 +10,65 @@ pub enum LatexIncludeKind { } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexInclude<'a> { - pub command: &'a LatexCommand, - pub path: &'a LatexToken, +pub struct LatexInclude { + pub command: Arc, pub kind: LatexIncludeKind, } -impl<'a> LatexInclude<'a> { - pub fn new(command: &'a LatexCommand, path: &'a LatexToken, kind: LatexIncludeKind) -> Self { - LatexInclude { - command, - path, - kind, - } +impl LatexInclude { + pub fn path(&self) -> &LatexToken { + self.command.extract_word(0).unwrap() } -} - -pub struct LatexIncludeAnalyzer<'a> { - pub included_files: Vec>, - pub included_components: Vec, -} -impl<'a> LatexIncludeAnalyzer<'a> { - pub fn new() -> Self { - LatexIncludeAnalyzer { - included_files: Vec::new(), - included_components: Vec::new(), - } + pub fn new(command: Arc, kind: LatexIncludeKind) -> Self { + LatexInclude { command, kind } } - fn register_component(&mut self, include: &LatexInclude) { - let path = include.path.text(); - match include.kind { - LatexIncludeKind::Package => self.included_components.push(format!("{}.sty", path)), - LatexIncludeKind::Class => self.included_components.push(format!("{}.cls", path)), - LatexIncludeKind::TexFile | LatexIncludeKind::BibFile => (), - } - } -} - -impl<'a> LatexVisitor<'a> for LatexIncludeAnalyzer<'a> { - fn visit_root(&mut self, root: &'a LatexRoot) { - LatexWalker::walk_root(self, root); - } - - fn visit_group(&mut self, group: &'a LatexGroup) { - LatexWalker::walk_group(self, group); - } - - fn visit_command(&mut self, command: &'a LatexCommand) { - let kind = match command.name.text() { - "\\include" | "\\input" => Some(LatexIncludeKind::TexFile), - "\\bibliography" | "\\addbibresource" => Some(LatexIncludeKind::BibFile), - "\\usepackage" => Some(LatexIncludeKind::Package), - "\\documentclass" => Some(LatexIncludeKind::Class), - _ => None, - }; - - if let Some(kind) = kind { - if let Some(path) = command.extract_word(0) { - let include = LatexInclude::new(command, path, kind); - self.register_component(&include); - self.included_files.push(include); + pub fn parse(commands: &[Arc]) -> (Vec, Vec) { + let mut includes = Vec::new(); + let mut components = Vec::new(); + for command in commands { + let kind = match command.name.text() { + "\\include" | "\\input" => Some(LatexIncludeKind::TexFile), + "\\bibliography" | "\\addbibresource" => Some(LatexIncludeKind::BibFile), + "\\usepackage" => Some(LatexIncludeKind::Package), + "\\documentclass" => Some(LatexIncludeKind::Class), + _ => None, + }; + + if let Some(kind) = kind { + if command.has_word(0) { + let include = LatexInclude::new(Arc::clone(&command), kind); + match include.kind { + LatexIncludeKind::Package => { + components.push(format!("{}.sty", include.path().text())); + } + LatexIncludeKind::Class => { + components.push(format!("{}.cls", include.path().text())); + } + LatexIncludeKind::TexFile | LatexIncludeKind::BibFile => {} + } + includes.push(include); + } } } - LatexWalker::walk_command(self, command); + (includes, components) } - - fn visit_text(&mut self, text: &'a LatexText) {} } #[cfg(test)] mod tests { - use super::*; use crate::syntax::latex::LatexSyntaxTree; fn verify(text: &str, includes: Vec<&str>, components: Vec<&str>) { let tree = LatexSyntaxTree::from(text); - let mut analyzer = LatexIncludeAnalyzer::new(); - analyzer.visit_root(&tree.root); - let actual_includes: Vec<&str> = analyzer - .included_files + let actual_includes: Vec<&str> = tree + .includes .iter() - .map(|include| include.path.text()) + .map(|include| include.path().text()) .collect(); assert_eq!(includes, actual_includes); - assert_eq!(components, analyzer.included_components); + assert_eq!(components, tree.components); } #[test] diff --git a/src/syntax/latex/analysis/label.rs b/src/syntax/latex/analysis/label.rs index 1ebd42578..95489e372 100644 --- a/src/syntax/latex/analysis/label.rs +++ b/src/syntax/latex/analysis/label.rs @@ -1,4 +1,5 @@ use crate::syntax::latex::ast::*; +use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum LatexLabelKind { @@ -7,55 +8,39 @@ pub enum LatexLabelKind { } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexLabel<'a> { - pub command: &'a LatexCommand, - pub name: &'a LatexToken, - pub kind: LatexLabelKind, +pub struct LatexLabel { + pub command: Arc, } -impl<'a> LatexLabel<'a> { - pub fn new(command: &'a LatexCommand, name: &'a LatexToken, kind: LatexLabelKind) -> Self { - LatexLabel { - command, - name, - kind, - } +impl LatexLabel { + pub fn new(command: Arc) -> Self { + LatexLabel { command } } -} -pub struct LatexLabelAnalyzer<'a> { - pub labels: Vec>, -} - -impl<'a> LatexLabelAnalyzer<'a> { - pub fn new() -> Self { - LatexLabelAnalyzer { labels: Vec::new() } + pub fn name(&self) -> &LatexToken { + self.command.extract_word(0).unwrap() } -} -impl<'a> LatexVisitor<'a> for LatexLabelAnalyzer<'a> { - fn visit_root(&mut self, root: &'a LatexRoot) { - LatexWalker::walk_root(self, root); - } - - fn visit_group(&mut self, group: &'a LatexGroup) { - LatexWalker::walk_group(self, group); + pub fn kind(&self) -> LatexLabelKind { + if LABEL_DEFINITION_COMMANDS.contains(&self.command.name.text()) { + LatexLabelKind::Definition + } else { + LatexLabelKind::Reference + } } - fn visit_command(&mut self, command: &'a LatexCommand) { - if let Some(name) = command.extract_word(0) { - if LABEL_DEFINITION_COMMANDS.contains(&command.name.text()) { - self.labels - .push(LatexLabel::new(command, name, LatexLabelKind::Definition)); - } else if LABEL_REFERENCE_COMMANDS.contains(&command.name.text()) { - self.labels - .push(LatexLabel::new(command, name, LatexLabelKind::Reference)); + pub fn parse(commands: &[Arc]) -> Vec { + let mut labels = Vec::new(); + for command in commands { + if command.has_word(0) + && (LABEL_DEFINITION_COMMANDS.contains(&command.name.text()) + || LABEL_REFERENCE_COMMANDS.contains(&command.name.text())) + { + labels.push(LatexLabel::new(Arc::clone(command))); } } - LatexWalker::walk_command(self, command); + labels } - - fn visit_text(&mut self, text: &'a LatexText) {} } pub const LABEL_DEFINITION_COMMANDS: &'static [&'static str] = &["\\label"]; @@ -64,17 +49,14 @@ pub const LABEL_REFERENCE_COMMANDS: &'static [&'static str] = &["\\ref", "\\auto #[cfg(test)] mod tests { - use super::*; use crate::syntax::latex::LatexSyntaxTree; fn verify(text: &str, expected: Vec<&str>) { let tree = LatexSyntaxTree::from(text); - let mut analyzer = LatexLabelAnalyzer::new(); - analyzer.visit_root(&tree.root); - let actual: Vec<&str> = analyzer + let actual: Vec<&str> = tree .labels .iter() - .map(|label| label.name.text()) + .map(|label| label.name().text()) .collect(); assert_eq!(expected, actual); } diff --git a/src/syntax/latex/analysis/section.rs b/src/syntax/latex/analysis/section.rs index 7563fe004..deeec4734 100644 --- a/src/syntax/latex/analysis/section.rs +++ b/src/syntax/latex/analysis/section.rs @@ -1,61 +1,41 @@ use crate::syntax::latex::ast::*; +use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone)] -pub struct LatexSection<'a> { - pub command: &'a LatexCommand, +pub struct LatexSection { + pub command: Arc, pub text: String, pub level: usize, } -impl<'a> LatexSection<'a> { - pub fn new(command: &'a LatexCommand, text: String, level: usize) -> Self { +impl LatexSection { + pub fn new(command: Arc, text: String, level: usize) -> Self { LatexSection { command, text, level, } } -} - -pub struct LatexSectionAnalyzer<'a> { - pub sections: Vec>, -} - -impl<'a> LatexSectionAnalyzer<'a> { - pub fn new() -> Self { - LatexSectionAnalyzer { - sections: Vec::new(), - } - } -} - -impl<'a> LatexVisitor<'a> for LatexSectionAnalyzer<'a> { - fn visit_root(&mut self, root: &'a LatexRoot) { - LatexWalker::walk_root(self, root); - } - fn visit_group(&mut self, group: &'a LatexGroup) { - LatexWalker::walk_group(self, group); - } - - fn visit_command(&mut self, command: &'a LatexCommand) { - if SECTION_COMMANDS.contains(&command.name.text()) { - if let Some(text) = command.extract_content(0) { - let mut level = 0; - for name in SECTION_COMMANDS { - if &command.name.text() == name { - break; + pub fn parse(commands: &[Arc]) -> Vec { + let mut sections = Vec::new(); + for command in commands { + if SECTION_COMMANDS.contains(&command.name.text()) { + if let Some(text) = command.extract_content(0) { + let mut level = 0; + for name in SECTION_COMMANDS { + if &command.name.text() == name { + break; + } + level += 1; } - level += 1; + level /= 2; + sections.push(LatexSection::new(Arc::clone(&command), text, level)); } - level /= 2; - self.sections.push(LatexSection::new(command, text, level)); } } - LatexWalker::walk_command(self, command); + sections } - - fn visit_text(&mut self, text: &'a LatexText) {} } pub const SECTION_COMMANDS: &'static [&'static str] = &[ @@ -75,14 +55,11 @@ pub const SECTION_COMMANDS: &'static [&'static str] = &[ #[cfg(test)] mod tests { - use super::*; use crate::syntax::latex::LatexSyntaxTree; fn verify(text: &str, expected: Vec<&str>) { let tree = LatexSyntaxTree::from(text); - let mut analyzer = LatexSectionAnalyzer::new(); - analyzer.visit_root(&tree.root); - let actual: Vec<&str> = analyzer + let actual: Vec<&str> = tree .sections .iter() .map(|section| section.text.as_ref()) diff --git a/src/syntax/latex/ast.rs b/src/syntax/latex/ast.rs index 8e9dc4b5b..f1c3bbde0 100644 --- a/src/syntax/latex/ast.rs +++ b/src/syntax/latex/ast.rs @@ -1,5 +1,6 @@ use crate::syntax::text::{Span, SyntaxNode}; use lsp_types::Range; +use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum LatexTokenKind { @@ -60,17 +61,17 @@ impl SyntaxNode for LatexRoot { #[derive(Debug, PartialEq, Eq, Clone)] pub enum LatexContent { - Group(Box), - Command(Box), - Text(Box), + Group(Arc), + Command(Arc), + Text(Arc), } impl LatexContent { - pub fn accept<'a>(&'a self, visitor: &mut LatexVisitor<'a>) { + pub fn accept(&self, visitor: &mut LatexVisitor) { match self { - LatexContent::Group(group) => visitor.visit_group(group), - LatexContent::Command(command) => visitor.visit_command(command), - LatexContent::Text(text) => visitor.visit_text(text), + LatexContent::Group(group) => visitor.visit_group(Arc::clone(&group)), + LatexContent::Command(command) => visitor.visit_command(Arc::clone(&command)), + LatexContent::Text(text) => visitor.visit_text(Arc::clone(&text)), } } } @@ -136,12 +137,16 @@ impl SyntaxNode for LatexGroup { pub struct LatexCommand { pub range: Range, pub name: LatexToken, - pub options: Option, - pub args: Vec, + pub options: Option>, + pub args: Vec>, } impl LatexCommand { - pub fn new(name: LatexToken, options: Option, args: Vec) -> Self { + pub fn new( + name: LatexToken, + options: Option>, + args: Vec>, + ) -> Self { let end = if !args.is_empty() { args[args.len() - 1].end() } else if let Some(ref options) = options { @@ -187,6 +192,10 @@ impl LatexCommand { } Some(words.join(" ")) } + + pub fn has_word(&self, index: usize) -> bool { + self.extract_word(index).is_some() + } } impl SyntaxNode for LatexCommand { @@ -216,38 +225,38 @@ impl SyntaxNode for LatexText { } } -pub trait LatexVisitor<'a> { - fn visit_root(&mut self, root: &'a LatexRoot); +pub trait LatexVisitor { + fn visit_root(&mut self, root: Arc); - fn visit_group(&mut self, group: &'a LatexGroup); + fn visit_group(&mut self, group: Arc); - fn visit_command(&mut self, command: &'a LatexCommand); + fn visit_command(&mut self, command: Arc); - fn visit_text(&mut self, text: &'a LatexText); + fn visit_text(&mut self, text: Arc); } pub struct LatexWalker; impl LatexWalker { - pub fn walk_root<'a>(visitor: &mut LatexVisitor<'a>, root: &'a LatexRoot) { + pub fn walk_root(visitor: &mut LatexVisitor, root: Arc) { for child in &root.children { child.accept(visitor); } } - pub fn walk_group<'a>(visitor: &mut LatexVisitor<'a>, group: &'a LatexGroup) { + pub fn walk_group(visitor: &mut LatexVisitor, group: Arc) { for child in &group.children { child.accept(visitor); } } - pub fn walk_command<'a>(visitor: &mut LatexVisitor<'a>, command: &'a LatexCommand) { + pub fn walk_command(visitor: &mut LatexVisitor, command: Arc) { if let Some(ref options) = command.options { - visitor.visit_group(options); + visitor.visit_group(Arc::clone(&options)); } for arg in &command.args { - visitor.visit_group(arg); + visitor.visit_group(Arc::clone(&arg)); } } } diff --git a/src/syntax/latex/mod.rs b/src/syntax/latex/mod.rs index 49ea31738..7b7a5b049 100644 --- a/src/syntax/latex/mod.rs +++ b/src/syntax/latex/mod.rs @@ -3,9 +3,6 @@ mod ast; mod lexer; mod parser; -use crate::syntax::latex::lexer::LatexLexer; -use crate::syntax::latex::parser::LatexParser; - pub use crate::syntax::latex::analysis::citation::*; pub use crate::syntax::latex::analysis::command::*; pub use crate::syntax::latex::analysis::environment::*; @@ -15,15 +12,74 @@ pub use crate::syntax::latex::analysis::include::*; pub use crate::syntax::latex::analysis::label::*; pub use crate::syntax::latex::analysis::section::*; pub use crate::syntax::latex::ast::*; +use crate::syntax::latex::lexer::LatexLexer; +use crate::syntax::latex::parser::LatexParser; +use crate::syntax::text::SyntaxNode; +use lsp_types::Position; +use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone)] pub struct LatexSyntaxTree { - pub root: LatexRoot, + pub root: Arc, + pub commands: Vec>, + pub includes: Vec, + pub components: Vec, + pub environments: Vec, + pub labels: Vec, + pub sections: Vec, + pub citations: Vec, + pub equations: Vec, + pub is_standalone: bool, +} + +impl LatexSyntaxTree { + pub fn find(&self, position: Position) -> Vec { + let mut finder = LatexFinder::new(position); + finder.visit_root(Arc::clone(&self.root)); + finder.results + } + + pub fn find_command(&self, position: Position) -> Option> { + for result in self.find(position) { + if let LatexNode::Command(command) = result { + if command.name.range().contains(position) + && command.name.start().character != position.character + { + return Some(command); + } + } + } + None + } } impl From for LatexSyntaxTree { fn from(root: LatexRoot) -> Self { - LatexSyntaxTree { root } + let root = Arc::new(root); + let mut analyzer = LatexCommandAnalyzer::new(); + analyzer.visit_root(Arc::clone(&root)); + let commands = analyzer.commands; + let (includes, components) = LatexInclude::parse(&commands); + let environments = LatexEnvironment::parse(&commands); + let labels = LatexLabel::parse(&commands); + let sections = LatexSection::parse(&commands); + let citations = LatexCitation::parse(&commands); + let equations = LatexEquation::parse(&commands); + let is_standalone = environments + .iter() + .any(|env| env.left.name().map(LatexToken::text) == Some("document")); + LatexSyntaxTree { + root, + commands, + includes, + components, + environments, + labels, + sections, + citations, + equations, + is_standalone, + } } } diff --git a/src/syntax/latex/parser.rs b/src/syntax/latex/parser.rs index f69dcbb33..fb8f22d94 100644 --- a/src/syntax/latex/parser.rs +++ b/src/syntax/latex/parser.rs @@ -1,5 +1,6 @@ use crate::syntax::latex::ast::*; use std::iter::Peekable; +use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum LatexScope { @@ -30,24 +31,20 @@ impl> LatexParser { while let Some(ref token) = self.tokens.peek() { match token.kind { LatexTokenKind::Word | LatexTokenKind::BeginOptions => { - let text = Box::new(self.text(scope)); - children.push(LatexContent::Text(text)); + children.push(LatexContent::Text(self.text(scope))); } LatexTokenKind::Command => { - let command = Box::new(self.command()); - children.push(LatexContent::Command(command)); + children.push(LatexContent::Command(self.command())); } LatexTokenKind::Math => { if scope == LatexScope::Math { return children; } else { - let group = Box::new(self.group(LatexGroupKind::Math)); - children.push(LatexContent::Group(group)); + children.push(LatexContent::Group(self.group(LatexGroupKind::Math))); } } LatexTokenKind::BeginGroup => { - let group = Box::new(self.group(LatexGroupKind::Group)); - children.push(LatexContent::Group(group)); + children.push(LatexContent::Group(self.group(LatexGroupKind::Group))); } LatexTokenKind::EndGroup => { if scope == LatexScope::Root { @@ -60,8 +57,7 @@ impl> LatexParser { if scope == LatexScope::Options { return children; } else { - let text = Box::new(self.text(scope)); - children.push(LatexContent::Text(text)); + children.push(LatexContent::Text(self.text(scope))); } } } @@ -69,7 +65,7 @@ impl> LatexParser { children } - fn command(&mut self) -> LatexCommand { + fn command(&mut self) -> Arc { let name = self.tokens.next().unwrap(); let options = if self.next_of_kind(LatexTokenKind::BeginOptions) { Some(self.group(LatexGroupKind::Options)) @@ -82,10 +78,10 @@ impl> LatexParser { args.push(self.group(LatexGroupKind::Group)); } - LatexCommand::new(name, options, args) + Arc::new(LatexCommand::new(name, options, args)) } - fn group(&mut self, kind: LatexGroupKind) -> LatexGroup { + fn group(&mut self, kind: LatexGroupKind) -> Arc { let left = self.tokens.next().unwrap(); let scope = match kind { LatexGroupKind::Group => LatexScope::Group, @@ -105,10 +101,10 @@ impl> LatexParser { None }; - LatexGroup::new(left, children, right, kind) + Arc::new(LatexGroup::new(left, children, right, kind)) } - fn text(&mut self, scope: LatexScope) -> LatexText { + fn text(&mut self, scope: LatexScope) -> Arc { let mut words = Vec::new(); while let Some(ref token) = self.tokens.peek() { let kind = token.kind; @@ -119,7 +115,7 @@ impl> LatexParser { break; } } - LatexText::new(words) + Arc::new(LatexText::new(words)) } fn next_of_kind(&mut self, kind: LatexTokenKind) -> bool { diff --git a/src/workspace.rs b/src/workspace.rs index 4040350f9..b63a1b068 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -69,10 +69,9 @@ impl Workspace { let mut edges: Vec<(Arc, Arc)> = Vec::new(); for parent in self.documents.iter().filter(|document| document.is_file()) { if let SyntaxTree::Latex(tree) = &parent.tree { - let mut analyzer = LatexIncludeAnalyzer::new(); - analyzer.visit_root(&tree.root); - for include in analyzer.included_files { - if let Some(ref child) = self.resolve_document(&parent.uri, include.path.text()) + for include in &tree.includes { + if let Some(ref child) = + self.resolve_document(&parent.uri, include.path().text()) { edges.push((Arc::clone(&parent), Arc::clone(&child))); edges.push((Arc::clone(&child), Arc::clone(&parent))); @@ -106,17 +105,7 @@ impl Workspace { pub fn find_parent(&self, uri: &Uri) -> Option> { for document in self.related_documents(uri) { if let SyntaxTree::Latex(tree) = &document.tree { - let mut analyzer = LatexEnvironmentAnalyzer::new(); - analyzer.visit_root(&tree.root); - let is_standalone = analyzer.environments.iter().any(|environment| { - environment - .left - .name - .map(LatexToken::text) - .unwrap_or_default() - == "document" - }); - if is_standalone { + if tree.is_standalone { return Some(document); } }