diff options
Diffstat (limited to 'support/texlab/src/folding')
-rw-r--r-- | support/texlab/src/folding/bibtex_declaration.rs | 160 | ||||
-rw-r--r-- | support/texlab/src/folding/latex_environment.rs | 74 | ||||
-rw-r--r-- | support/texlab/src/folding/latex_section.rs | 100 | ||||
-rw-r--r-- | support/texlab/src/folding/mod.rs | 45 |
4 files changed, 379 insertions, 0 deletions
diff --git a/support/texlab/src/folding/bibtex_declaration.rs b/support/texlab/src/folding/bibtex_declaration.rs new file mode 100644 index 0000000000..cea17daa7d --- /dev/null +++ b/support/texlab/src/folding/bibtex_declaration.rs @@ -0,0 +1,160 @@ +use crate::syntax::*; +use crate::workspace::*; +use futures_boxed::boxed; +use lsp_types::{FoldingRange, FoldingRangeKind, FoldingRangeParams}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct BibtexDeclarationFoldingProvider; + +impl FeatureProvider for BibtexDeclarationFoldingProvider { + type Params = FoldingRangeParams; + type Output = Vec<FoldingRange>; + + #[boxed] + async fn execute<'a>( + &'a self, + request: &'a FeatureRequest<FoldingRangeParams>, + ) -> Vec<FoldingRange> { + if let SyntaxTree::Bibtex(tree) = &request.document().tree { + tree.root.children.iter().flat_map(Self::fold).collect() + } else { + Vec::new() + } + } +} + +impl BibtexDeclarationFoldingProvider { + fn fold(declaration: &BibtexDeclaration) -> Option<FoldingRange> { + let ty = match declaration { + BibtexDeclaration::Comment(_) => None, + BibtexDeclaration::Preamble(preamble) => Some(&preamble.ty), + BibtexDeclaration::String(string) => Some(&string.ty), + BibtexDeclaration::Entry(entry) => Some(&entry.ty), + }?; + + let right = match declaration { + BibtexDeclaration::Comment(_) => None, + BibtexDeclaration::Preamble(preamble) => preamble.right.as_ref(), + BibtexDeclaration::String(string) => string.right.as_ref(), + BibtexDeclaration::Entry(entry) => entry.right.as_ref(), + }?; + + Some(FoldingRange { + start_line: ty.range().start.line, + start_character: Some(ty.range().start.character), + end_line: right.range().start.line, + end_character: Some(right.range().start.character), + kind: Some(FoldingRangeKind::Region), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_preamble() { + let foldings = test_feature( + BibtexDeclarationFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.bib", "\n@preamble{\"foo\"}")], + main_file: "foo.bib", + ..FeatureSpec::default() + }, + ); + assert_eq!( + foldings, + vec![FoldingRange { + start_line: 1, + start_character: Some(0), + end_line: 1, + end_character: Some(15), + kind: Some(FoldingRangeKind::Region), + }] + ); + } + + #[test] + fn test_string() { + let foldings = test_feature( + BibtexDeclarationFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.bib", "@string{foo = \"bar\"}")], + main_file: "foo.bib", + ..FeatureSpec::default() + }, + ); + assert_eq!( + foldings, + vec![FoldingRange { + start_line: 0, + start_character: Some(0), + end_line: 0, + end_character: Some(19), + kind: Some(FoldingRangeKind::Region), + }] + ); + } + + #[test] + fn test_entry() { + let foldings = test_feature( + BibtexDeclarationFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.bib", "@article{foo, bar = baz\n}")], + main_file: "foo.bib", + ..FeatureSpec::default() + }, + ); + assert_eq!( + foldings, + vec![FoldingRange { + start_line: 0, + start_character: Some(0), + end_line: 1, + end_character: Some(0), + kind: Some(FoldingRangeKind::Region), + }] + ); + } + + #[test] + fn test_comment() { + let foldings = test_feature( + BibtexDeclarationFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.bib", "foo")], + main_file: "foo.bib", + ..FeatureSpec::default() + }, + ); + assert!(foldings.is_empty()); + } + + #[test] + fn test_entry_invalid() { + let foldings = test_feature( + BibtexDeclarationFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.bib", "@article{foo,")], + main_file: "foo.bib", + ..FeatureSpec::default() + }, + ); + assert!(foldings.is_empty()); + } + + #[test] + fn test_latex() { + let foldings = test_feature( + BibtexDeclarationFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.tex", "")], + main_file: "foo.tex", + ..FeatureSpec::default() + }, + ); + assert!(foldings.is_empty()); + } +} diff --git a/support/texlab/src/folding/latex_environment.rs b/support/texlab/src/folding/latex_environment.rs new file mode 100644 index 0000000000..853ece7a09 --- /dev/null +++ b/support/texlab/src/folding/latex_environment.rs @@ -0,0 +1,74 @@ +use crate::syntax::*; +use crate::workspace::*; +use futures_boxed::boxed; +use lsp_types::{FoldingRange, FoldingRangeKind, FoldingRangeParams}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct LatexEnvironmentFoldingProvider; + +impl FeatureProvider for LatexEnvironmentFoldingProvider { + type Params = FoldingRangeParams; + type Output = Vec<FoldingRange>; + + #[boxed] + async fn execute<'a>( + &'a self, + request: &'a FeatureRequest<FoldingRangeParams>, + ) -> Vec<FoldingRange> { + let mut foldings = Vec::new(); + if let SyntaxTree::Latex(tree) = &request.document().tree { + for environment in &tree.env.environments { + let start = environment.left.command.end(); + let end = environment.right.command.start(); + foldings.push(FoldingRange { + start_line: start.line, + start_character: Some(start.character), + end_line: end.line, + end_character: Some(end.character), + kind: Some(FoldingRangeKind::Region), + }) + } + } + foldings + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_multiline() { + let foldings = test_feature( + LatexEnvironmentFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.tex", "\\begin{foo}\n\\end{foo}")], + main_file: "foo.tex", + ..FeatureSpec::default() + }, + ); + assert_eq!( + foldings, + vec![FoldingRange { + start_line: 0, + start_character: Some(11), + end_line: 1, + end_character: Some(0), + kind: Some(FoldingRangeKind::Region), + }] + ); + } + + #[test] + fn test_bibtex() { + let foldings = test_feature( + LatexEnvironmentFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.bib", "@article{foo, bar = baz}")], + main_file: "foo.bib", + ..FeatureSpec::default() + }, + ); + assert!(foldings.is_empty()); + } +} diff --git a/support/texlab/src/folding/latex_section.rs b/support/texlab/src/folding/latex_section.rs new file mode 100644 index 0000000000..ca7b8387be --- /dev/null +++ b/support/texlab/src/folding/latex_section.rs @@ -0,0 +1,100 @@ +use crate::syntax::*; +use crate::workspace::*; +use futures_boxed::boxed; +use lsp_types::{FoldingRange, FoldingRangeKind, FoldingRangeParams}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct LatexSectionFoldingProvider; + +impl FeatureProvider for LatexSectionFoldingProvider { + type Params = FoldingRangeParams; + type Output = Vec<FoldingRange>; + + #[boxed] + async fn execute<'a>( + &'a self, + request: &'a FeatureRequest<FoldingRangeParams>, + ) -> Vec<FoldingRange> { + let mut foldings = Vec::new(); + if let SyntaxTree::Latex(tree) = &request.document().tree { + let sections = &tree.structure.sections; + for i in 0..sections.len() { + let current = §ions[i]; + let next = sections + .iter() + .skip(i + 1) + .find(|sec| current.level >= sec.level); + + if let Some(next) = next { + 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); + } + } + } + } + foldings + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_nesting() { + let foldings = test_feature( + LatexSectionFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.tex", "\\section{Foo}\nfoo\n\\subsection{Bar}\nbar\n\\section{Baz}\nbaz\n\\section{Qux}")], + main_file: "foo.tex", + ..FeatureSpec::default() + } + ); + assert_eq!( + foldings, + vec![ + FoldingRange { + start_line: 0, + start_character: Some(13), + end_line: 3, + end_character: Some(0), + kind: Some(FoldingRangeKind::Region), + }, + FoldingRange { + start_line: 2, + start_character: Some(16), + end_line: 3, + end_character: Some(0), + kind: Some(FoldingRangeKind::Region), + }, + FoldingRange { + start_line: 4, + start_character: Some(13), + end_line: 5, + end_character: Some(0), + kind: Some(FoldingRangeKind::Region), + } + ] + ); + } + + #[test] + fn test_bibtex() { + let foldings = test_feature( + LatexSectionFoldingProvider, + FeatureSpec { + files: vec![FeatureSpec::file("foo.bib", "@article{foo, bar = baz}")], + main_file: "foo.bib", + ..FeatureSpec::default() + }, + ); + assert!(foldings.is_empty()); + } +} diff --git a/support/texlab/src/folding/mod.rs b/support/texlab/src/folding/mod.rs new file mode 100644 index 0000000000..52c8149a31 --- /dev/null +++ b/support/texlab/src/folding/mod.rs @@ -0,0 +1,45 @@ +mod bibtex_declaration; +mod latex_environment; +mod latex_section; + +use self::bibtex_declaration::BibtexDeclarationFoldingProvider; +use self::latex_environment::LatexEnvironmentFoldingProvider; +use self::latex_section::LatexSectionFoldingProvider; +use crate::workspace::*; +use futures_boxed::boxed; +use lsp_types::{FoldingRange, FoldingRangeParams}; + +pub struct FoldingProvider { + provider: ConcatProvider<FoldingRangeParams, FoldingRange>, +} + +impl FoldingProvider { + pub fn new() -> Self { + Self { + provider: ConcatProvider::new(vec![ + Box::new(BibtexDeclarationFoldingProvider), + Box::new(LatexEnvironmentFoldingProvider), + Box::new(LatexSectionFoldingProvider), + ]), + } + } +} + +impl Default for FoldingProvider { + fn default() -> Self { + Self::new() + } +} + +impl FeatureProvider for FoldingProvider { + type Params = FoldingRangeParams; + type Output = Vec<FoldingRange>; + + #[boxed] + async fn execute<'a>( + &'a self, + request: &'a FeatureRequest<FoldingRangeParams>, + ) -> Vec<FoldingRange> { + self.provider.execute(request).await + } +} |