summaryrefslogtreecommitdiff
path: root/support/texlab/crates/folding/src
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/crates/folding/src')
-rw-r--r--support/texlab/crates/folding/src/lib.rs107
-rw-r--r--support/texlab/crates/folding/src/tests.rs88
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(&section);
+ } 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,
+ },
+ ]
+ "#]],
+ );
+}