diff options
Diffstat (limited to 'support/texlab/crates/folding/src')
-rw-r--r-- | support/texlab/crates/folding/src/lib.rs | 107 | ||||
-rw-r--r-- | support/texlab/crates/folding/src/tests.rs | 88 |
2 files changed, 195 insertions, 0 deletions
diff --git a/support/texlab/crates/folding/src/lib.rs b/support/texlab/crates/folding/src/lib.rs new file mode 100644 index 0000000000..c87021015d --- /dev/null +++ b/support/texlab/crates/folding/src/lib.rs @@ -0,0 +1,107 @@ +use base_db::{Document, DocumentData}; +use rowan::{ast::AstNode, TextRange}; +use syntax::{ + bibtex::{self, HasDelims, HasName}, + latex, +}; + +#[derive(Debug)] +pub struct FoldingRange { + pub range: TextRange, + pub kind: FoldingRangeKind, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub enum FoldingRangeKind { + Section, + Environment, + Entry, +} + +pub fn find_all(document: &Document) -> Vec<FoldingRange> { + let mut builder = FoldingBuilder::default(); + + if let DocumentData::Tex(data) = &document.data { + for node in data.root_node().descendants() { + if let Some(section) = latex::Section::cast(node.clone()) { + builder.fold_section(§ion); + } else if let Some(item) = latex::EnumItem::cast(node.clone()) { + builder.fold_enum_item(&item); + } else if let Some(env) = latex::Environment::cast(node) { + builder.fold_environment(env); + } + } + } else if let DocumentData::Bib(data) = &document.data { + for node in data.root_node().descendants() { + if let Some(entry) = bibtex::Entry::cast(node.clone()) { + builder.fold_entry(&entry); + } else if let Some(string) = bibtex::StringDef::cast(node) { + builder.fold_entry(&string); + } + } + } + + builder.ranges +} + +#[derive(Debug, Default)] +struct FoldingBuilder { + ranges: Vec<FoldingRange>, +} + +impl FoldingBuilder { + fn fold_section(&mut self, section: &latex::Section) -> Option<()> { + let start = section + .name() + .map(|name| latex::small_range(&name).end()) + .or_else(|| section.command().map(|cmd| cmd.text_range().end()))?; + let end = section.syntax().text_range().end(); + + self.ranges.push(FoldingRange { + range: TextRange::new(start, end), + kind: FoldingRangeKind::Section, + }); + + Some(()) + } + + fn fold_enum_item(&mut self, item: &latex::EnumItem) -> Option<()> { + let start = item + .label() + .map(|label| latex::small_range(&label).end()) + .or_else(|| item.command().map(|cmd| cmd.text_range().end()))?; + + let end = item.syntax().text_range().end(); + self.ranges.push(FoldingRange { + range: TextRange::new(start, end), + kind: FoldingRangeKind::Section, + }); + + Some(()) + } + + fn fold_environment(&mut self, env: latex::Environment) -> Option<()> { + let start = latex::small_range(&env.begin()?).end(); + let end = latex::small_range(&env.end()?).start(); + self.ranges.push(FoldingRange { + range: TextRange::new(start, end), + kind: FoldingRangeKind::Environment, + }); + + Some(()) + } + + fn fold_entry(&mut self, entry: &(impl HasName + HasDelims)) -> Option<()> { + let start = entry.name_token()?.text_range().end(); + let end = entry.right_delim_token()?.text_range().start(); + self.ranges.push(FoldingRange { + range: TextRange::new(start, end), + kind: FoldingRangeKind::Entry, + }); + + Some(()) + } +} + +#[cfg(test)] +mod tests; diff --git a/support/texlab/crates/folding/src/tests.rs b/support/texlab/crates/folding/src/tests.rs new file mode 100644 index 0000000000..405b56bda1 --- /dev/null +++ b/support/texlab/crates/folding/src/tests.rs @@ -0,0 +1,88 @@ +use expect_test::{expect, Expect}; + +fn check(input: &str, expect: Expect) { + let fixture = test_utils::fixture::Fixture::parse(input); + let workspace = &fixture.workspace; + let document = workspace.lookup(&fixture.documents[0].uri).unwrap(); + let data = crate::find_all(document); + expect.assert_debug_eq(&data); +} + +#[test] +fn test_latex() { + check( + r#" +%! main.tex +\begin{document} + \section{Foo} + foo + \subsection{Bar} + bar + \section{Baz} + baz + \section{Qux} +\end{document} +|"#, + expect![[r#" + [ + FoldingRange { + range: 16..116, + kind: Environment, + }, + FoldingRange { + range: 34..76, + kind: Section, + }, + FoldingRange { + range: 63..76, + kind: Section, + }, + FoldingRange { + range: 89..102, + kind: Section, + }, + FoldingRange { + range: 115..116, + kind: Section, + }, + ] + "#]], + ); +} + +#[test] +fn test_bibtex() { + check( + r#" +%! main.bib +some junk +here + +@article{foo, + author = {bar}, + title = {baz} +} + +@string{foo = "bar"} + +@comment{foo, + author = {bar}, + title = {baz} +} + +@preamble{"foo"} +|"#, + expect![[r#" + [ + FoldingRange { + range: 28..68, + kind: Entry, + }, + FoldingRange { + range: 82..90, + kind: Entry, + }, + ] + "#]], + ); +} |