crates/symbols/src/workspace/sort.rs
use base_db::{Document, Workspace};
use itertools::Itertools;
use url::Url;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ProjectOrdering<'a> {
inner: Vec<&'a Document>,
}
impl<'a> ProjectOrdering<'a> {
pub fn get(&self, uri: &Url) -> usize {
self.inner
.iter()
.position(|doc| doc.uri == *uri)
.unwrap_or(std::usize::MAX)
}
}
impl<'a> From<&'a Workspace> for ProjectOrdering<'a> {
fn from(workspace: &'a Workspace) -> Self {
let inner = workspace
.iter()
.filter(|document| {
let data = document.data.as_tex();
data.map_or(false, |data| data.semantics.can_be_root)
})
.chain(workspace.iter())
.flat_map(|document| {
let graph = &workspace.graphs()[&document.uri];
graph.preorder(workspace).rev().collect_vec()
})
.unique()
.collect_vec();
Self { inner }
}
}
#[cfg(test)]
mod tests {
use base_db::Owner;
use distro::Language;
use line_index::LineCol;
use super::{ProjectOrdering, Url, Workspace};
#[test]
fn test_no_cycles() {
let mut workspace = Workspace::default();
let a = Url::parse("http://example.com/a.tex").unwrap();
let b = Url::parse("http://example.com/b.tex").unwrap();
let c = Url::parse("http://example.com/c.tex").unwrap();
workspace.open(
a.clone(),
String::new(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
workspace.open(
b.clone(),
String::new(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
workspace.open(
c.clone(),
r#"\documentclass{article}\include{b}\include{a}"#.to_string(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
let ordering = ProjectOrdering::from(&workspace);
assert_eq!(ordering.get(&a), 0);
assert_eq!(ordering.get(&b), 1);
assert_eq!(ordering.get(&c), 2);
}
#[test]
fn test_two_layers() {
let mut workspace = Workspace::default();
let a = Url::parse("http://example.com/a.tex").unwrap();
let b = Url::parse("http://example.com/b.tex").unwrap();
let c = Url::parse("http://example.com/c.tex").unwrap();
workspace.open(
a.clone(),
String::new(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
workspace.open(
b.clone(),
r#"\include{a}"#.to_string(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
workspace.open(
c.clone(),
r#"\begin{documnent}\include{b}\end{document}"#.to_string(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
let ordering = ProjectOrdering::from(&workspace);
assert_eq!(ordering.get(&a), 0);
assert_eq!(ordering.get(&b), 1);
assert_eq!(ordering.get(&c), 2);
}
#[test]
fn test_cycles() {
let mut workspace = Workspace::default();
let a = Url::parse("http://example.com/a.tex").unwrap();
let b = Url::parse("http://example.com/b.tex").unwrap();
let c = Url::parse("http://example.com/c.tex").unwrap();
workspace.open(
a.clone(),
r#"\begin{document}\include{b}\end{document}"#.to_string(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
workspace.open(
b.clone(),
r#"\include{a}"#.to_string(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
workspace.open(
c.clone(),
r#"\include{a}"#.to_string(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
let ordering = ProjectOrdering::from(&workspace);
assert_ne!(ordering.get(&a), 0);
}
#[test]
fn test_multiple_roots() {
let mut workspace = Workspace::default();
let a = Url::parse("http://example.com/a.tex").unwrap();
let b = Url::parse("http://example.com/b.tex").unwrap();
let c = Url::parse("http://example.com/c.tex").unwrap();
let d = Url::parse("http://example.com/d.tex").unwrap();
workspace.open(
a.clone(),
r#"\begin{document}\include{b}\end{document}"#.to_string(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
workspace.open(
b.clone(),
String::new(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
workspace.open(
c.clone(),
String::new(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
workspace.open(
d.clone(),
r#"\begin{document}\include{c}\end{document}"#.to_string(),
Language::Tex,
Owner::Client,
LineCol { line: 0, col: 0 },
);
let ordering = ProjectOrdering::from(&workspace);
assert!(ordering.get(&b) < ordering.get(&a));
assert!(ordering.get(&c) < ordering.get(&d));
}
}