diff options
Diffstat (limited to 'support/texlab/src/symbol/project_order.rs')
-rw-r--r-- | support/texlab/src/symbol/project_order.rs | 179 |
1 files changed, 109 insertions, 70 deletions
diff --git a/support/texlab/src/symbol/project_order.rs b/support/texlab/src/symbol/project_order.rs index 31b71f36bb..8a2ec5f6d0 100644 --- a/support/texlab/src/symbol/project_order.rs +++ b/support/texlab/src/symbol/project_order.rs @@ -1,9 +1,9 @@ -use crate::syntax::*; -use crate::workspace::*; -use petgraph::algo::tarjan_scc; -use petgraph::{Directed, Graph}; -use std::collections::HashSet; -use std::sync::Arc; +use crate::{ + protocol::{Options, Uri}, + workspace::{Document, DocumentContent, Snapshot}, +}; +use petgraph::{algo::tarjan_scc, Directed, Graph}; +use std::{collections::HashSet, path::Path, sync::Arc, usize}; #[derive(Debug, PartialEq, Eq, Clone)] pub struct ProjectOrdering { @@ -11,27 +11,34 @@ pub struct ProjectOrdering { } impl ProjectOrdering { - pub fn new(workspace: &Workspace) -> Self { + pub fn get(&self, uri: &Uri) -> usize { + self.ordering + .iter() + .position(|doc| doc.uri == *uri) + .unwrap_or(usize::MAX) + } + + pub fn analyze(snapshot: &Snapshot, options: &Options, current_dir: &Path) -> Self { let mut ordering = Vec::new(); - let connected_components = Self::connected_components(workspace); - for connected_component in connected_components { - let graph = Self::build_dependency_graph(&connected_component); + let comps = Self::connected_components(snapshot, options, current_dir); + for comp in comps { + let graph = Self::build_dependency_graph(&comp); let mut visited = HashSet::new(); let root_index = *graph.node_weight(tarjan_scc(&graph)[0][0]).unwrap(); - let mut stack = vec![Arc::clone(&connected_component[root_index])]; + let mut stack = vec![Arc::clone(&comp[root_index])]; - while let Some(document) = stack.pop() { - if !visited.insert(document.uri.as_str().to_owned()) { + while let Some(doc) = stack.pop() { + if !visited.insert(doc.uri.as_str().to_owned()) { continue; } - ordering.push(Arc::clone(&document)); - if let SyntaxTree::Latex(tree) = &document.tree { + ordering.push(Arc::clone(&doc)); + if let DocumentContent::Latex(tree) = &doc.content { for include in tree.includes.iter().rev() { for targets in &include.all_targets { for target in targets { - if let Some(child) = workspace.find(target) { + if let Some(child) = snapshot.find(target) { stack.push(child); } } @@ -44,36 +51,40 @@ impl ProjectOrdering { Self { ordering } } - fn connected_components(workspace: &Workspace) -> Vec<Vec<Arc<Document>>> { - let mut components = Vec::new(); + fn connected_components( + snapshot: &Snapshot, + options: &Options, + current_dir: &Path, + ) -> Vec<Vec<Arc<Document>>> { + let mut comps = Vec::new(); let mut visited = HashSet::new(); - for root in &workspace.documents { + for root in &snapshot.0 { if !visited.insert(root.uri.clone()) { continue; } - let component = workspace.related_documents(&root.uri); - for document in &component { + let comp = snapshot.relations(&root.uri, options, current_dir); + for document in &comp { visited.insert(document.uri.clone()); } - components.push(component); + comps.push(comp); } - components + comps } - fn build_dependency_graph(documents: &[Arc<Document>]) -> Graph<usize, (), Directed> { + fn build_dependency_graph(docs: &[Arc<Document>]) -> Graph<usize, (), Directed> { let mut graph = Graph::new(); - let nodes: Vec<_> = (0..documents.len()).map(|i| graph.add_node(i)).collect(); + let nodes: Vec<_> = (0..docs.len()).map(|i| graph.add_node(i)).collect(); - for (i, document) in documents.iter().enumerate() { - if let SyntaxTree::Latex(tree) = &document.tree { + for (i, doc) in docs.iter().enumerate() { + if let DocumentContent::Latex(tree) = &doc.content { for targets in tree .includes .iter() .flat_map(|include| &include.all_targets) { for target in targets { - if let Some(j) = documents.iter().position(|doc| doc.uri == *target) { + if let Some(j) = docs.iter().position(|doc| doc.uri == *target) { graph.add_edge(nodes[j], nodes[i], ()); break; } @@ -83,60 +94,88 @@ impl ProjectOrdering { } graph } - - pub fn get(&self, uri: &Uri) -> usize { - self.ordering - .iter() - .position(|doc| doc.uri == *uri) - .unwrap_or(std::usize::MAX) - } } #[cfg(test)] mod tests { use super::*; + use crate::{ + tex::{Language, Resolver}, + workspace::DocumentParams, + }; + use std::env; + + fn create_simple_document(uri: &Uri, language: Language, text: &str) -> Arc<Document> { + Arc::new(Document::open(DocumentParams { + uri: uri.clone(), + text: text.into(), + language, + resolver: &Resolver::default(), + options: &Options::default(), + current_dir: &env::current_dir().unwrap(), + })) + } #[test] - fn test_no_cycles() { - let mut builder = WorkspaceBuilder::new(); - let a = builder.document("a.tex", ""); - let b = builder.document("b.tex", ""); - let c = builder.document("c.tex", "\\include{b}\\include{a}"); - - let project_ordering = ProjectOrdering::new(&builder.workspace); - - assert_eq!(project_ordering.get(&a), 2); - assert_eq!(project_ordering.get(&b), 1); - assert_eq!(project_ordering.get(&c), 0); + fn no_cycles() { + let a = Uri::parse("http://example.com/a.tex").unwrap(); + let b = Uri::parse("http://example.com/b.tex").unwrap(); + let c = Uri::parse("http://example.com/c.tex").unwrap(); + let mut snapshot = Snapshot::new(); + snapshot.0 = vec![ + create_simple_document(&a, Language::Latex, ""), + create_simple_document(&b, Language::Latex, ""), + create_simple_document(&c, Language::Latex, r#"\include{b}\include{a}"#), + ]; + + let current_dir = env::current_dir().unwrap(); + let ordering = ProjectOrdering::analyze(&snapshot, &Options::default(), ¤t_dir); + + assert_eq!(ordering.get(&a), 2); + assert_eq!(ordering.get(&b), 1); + assert_eq!(ordering.get(&c), 0); } #[test] - fn test_cycles() { - let mut builder = WorkspaceBuilder::new(); - let a = builder.document("a.tex", "\\include{b}"); - let b = builder.document("b.tex", "\\include{a}"); - let c = builder.document("c.tex", "\\include{a}"); - - let project_ordering = ProjectOrdering::new(&builder.workspace); - - assert_eq!(project_ordering.get(&a), 1); - assert_eq!(project_ordering.get(&b), 2); - assert_eq!(project_ordering.get(&c), 0); + fn cycles() { + let a = Uri::parse("http://example.com/a.tex").unwrap(); + let b = Uri::parse("http://example.com/b.tex").unwrap(); + let c = Uri::parse("http://example.com/c.tex").unwrap(); + let mut snapshot = Snapshot::new(); + snapshot.0 = vec![ + create_simple_document(&a, Language::Latex, r#"\include{b}"#), + create_simple_document(&b, Language::Latex, r#"\include{a}"#), + create_simple_document(&c, Language::Latex, r#"\include{a}"#), + ]; + + let current_dir = env::current_dir().unwrap(); + let ordering = ProjectOrdering::analyze(&snapshot, &Options::default(), ¤t_dir); + + assert_eq!(ordering.get(&a), 1); + assert_eq!(ordering.get(&b), 2); + assert_eq!(ordering.get(&c), 0); } #[test] - fn test_multiple_roots() { - let mut builder = WorkspaceBuilder::new(); - let a = builder.document("a.tex", "\\include{b}"); - let b = builder.document("b.tex", ""); - let c = builder.document("c.tex", ""); - let d = builder.document("d.tex", "\\include{c}"); - - let project_ordering = ProjectOrdering::new(&builder.workspace); - - assert_eq!(project_ordering.get(&a), 0); - assert_eq!(project_ordering.get(&b), 1); - assert_eq!(project_ordering.get(&d), 2); - assert_eq!(project_ordering.get(&c), 3); + fn multiple_roots() { + let a = Uri::parse("http://example.com/a.tex").unwrap(); + let b = Uri::parse("http://example.com/b.tex").unwrap(); + let c = Uri::parse("http://example.com/c.tex").unwrap(); + let d = Uri::parse("http://example.com/d.tex").unwrap(); + let mut snapshot = Snapshot::new(); + snapshot.0 = vec![ + create_simple_document(&a, Language::Latex, r#"\include{b}"#), + create_simple_document(&b, Language::Latex, ""), + create_simple_document(&c, Language::Latex, ""), + create_simple_document(&d, Language::Latex, r#"\include{c}"#), + ]; + + let current_dir = env::current_dir().unwrap(); + let ordering = ProjectOrdering::analyze(&snapshot, &Options::default(), ¤t_dir); + + assert_eq!(ordering.get(&a), 0); + assert_eq!(ordering.get(&b), 1); + assert_eq!(ordering.get(&d), 2); + assert_eq!(ordering.get(&c), 3); } } |