Files
nuwiki-rs/crates/nuwiki-lsp/tests/folding.rs
T
gffranco 29a4efb6bc
CI / cargo fmt --check (push) Successful in 59s
CI / cargo clippy (push) Successful in 1m20s
CI / cargo test (push) Successful in 1m29s
CI / editor keymaps (push) Failing after 33s
feat: initial nuwiki Rust workspace (core, lsp, ls)
2026-06-24 00:01:59 +00:00

112 lines
3.8 KiB
Rust

//! LSP folding-range provider.
//!
//! Drives `folding::folding_ranges` directly. The handler wiring is
//! shallow — it just calls into this function — so we verify the
//! shapes here and trust the integration via the LSP test harness in
//! `lsp_helpers.rs`.
use nuwiki_core::syntax::vimwiki::VimwikiSyntax;
use nuwiki_core::syntax::SyntaxPlugin;
use nuwiki_lsp::folding;
fn parse(src: &str) -> nuwiki_core::ast::DocumentNode {
VimwikiSyntax::new().parse(src)
}
#[test]
fn no_headings_no_lists_emits_no_folds() {
let src = "just text\non two lines\n";
let ast = parse(src);
let folds = folding::folding_ranges(&ast, folding::line_count(src));
assert!(folds.is_empty());
}
#[test]
fn single_heading_folds_to_end_of_document() {
let src = "= Title =\nbody line 1\nbody line 2\n";
let ast = parse(src);
let folds = folding::folding_ranges(&ast, folding::line_count(src));
assert_eq!(folds.len(), 1);
assert_eq!(folds[0].start_line, 0);
// body line 2 is line 2; trailing newline pushes line_count to 4 so
// last_line == 3 (the empty line after the final newline).
assert!(folds[0].end_line >= 2);
}
#[test]
fn sibling_headings_each_get_their_own_fold() {
let src = "= One =\nbody\n= Two =\nbody2\n= Three =\n";
let ast = parse(src);
let folds = folding::folding_ranges(&ast, folding::line_count(src));
let heading_folds: Vec<_> = folds
.iter()
.filter(|f| f.start_line == 0 || f.start_line == 2 || f.start_line == 4)
.collect();
assert_eq!(heading_folds.len(), 3, "got: {folds:?}");
// One closes before Two
assert_eq!(heading_folds[0].end_line, 1);
// Two closes before Three
assert_eq!(heading_folds[1].end_line, 3);
}
#[test]
fn nested_heading_folds_inside_parent() {
let src = "= Outer =\nintro\n== Inner ==\nbody\n= Other =\n";
let ast = parse(src);
let folds = folding::folding_ranges(&ast, folding::line_count(src));
// Outer fold spans up to the line before `= Other =` (line 4).
let outer = folds
.iter()
.find(|f| f.start_line == 0)
.expect("outer fold");
assert_eq!(outer.end_line, 3);
// Inner fold is bounded by the next h1 too (since the only following
// heading is h1, which has level <= 2).
let inner = folds
.iter()
.find(|f| f.start_line == 2)
.expect("inner fold");
assert_eq!(inner.end_line, 3);
}
#[test]
fn top_level_list_gets_its_own_fold() {
let src = "before\n- item one\n- item two\n- item three\nafter\n";
let ast = parse(src);
let folds = folding::folding_ranges(&ast, folding::line_count(src));
let list_fold = folds.iter().find(|f| f.start_line == 1);
assert!(list_fold.is_some(), "expected a list fold: {folds:?}");
}
#[test]
fn single_line_blocks_are_not_folded() {
let src = "= Solo =\n";
let ast = parse(src);
let folds = folding::folding_ranges(&ast, folding::line_count(src));
// A heading on the only line of the document covers nothing
// foldable, so it's omitted.
let none_for_solo = folds
.iter()
.all(|f| !(f.start_line == 0 && f.end_line == 0));
assert!(none_for_solo, "got: {folds:?}");
}
#[test]
fn fold_kind_is_region() {
use tower_lsp::lsp_types::FoldingRangeKind;
let src = "= H =\nbody\n";
let ast = parse(src);
let folds = folding::folding_ranges(&ast, folding::line_count(src));
assert!(!folds.is_empty());
assert_eq!(folds[0].kind, Some(FoldingRangeKind::Region));
}
#[test]
fn line_count_matches_intuitive_definition() {
assert_eq!(folding::line_count(""), 0);
assert_eq!(folding::line_count("one"), 1);
assert_eq!(folding::line_count("one\n"), 2); // trailing newline → virtual empty line
assert_eq!(folding::line_count("one\ntwo"), 2);
assert_eq!(folding::line_count("one\ntwo\n"), 3);
}