1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Fold range types for code folding.
//!
//! This module defines types for representing foldable regions in source code,
//! such as function bodies, class definitions, and import blocks.
/// Kind of fold (what construct it represents).
///
/// Classifies foldable regions by the type of syntax construct they contain.
///
/// # Example
///
/// ```
/// use reovim_driver_syntax::FoldKind;
///
/// let kind = FoldKind::Function;
/// assert_eq!(kind, FoldKind::Function);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FoldKind {
/// Function or method body.
Function,
/// Class, struct, or impl body.
Class,
/// Import/use group.
Import,
/// Comment block (multi-line comments or doc comments).
Comment,
/// Generic block (if, for, while, match arms, etc.).
Block,
}
impl FoldKind {
/// Check if this fold represents a definition (function or class).
#[must_use]
pub const fn is_definition(self) -> bool {
matches!(self, Self::Function | Self::Class)
}
}
/// A foldable range in the buffer.
///
/// Fold ranges are identified by the syntax driver and can be
/// collapsed by the editor UI. Lines are 0-indexed.
///
/// # Example
///
/// ```
/// use reovim_driver_syntax::{FoldRange, FoldKind};
///
/// let fold = FoldRange::new(5, 10, FoldKind::Function, "fn foo() {");
/// assert!(fold.is_foldable());
/// assert_eq!(fold.line_count(), 6);
/// ```
#[derive(Debug, Clone)]
pub struct FoldRange {
/// Starting line (0-indexed).
pub start_line: u32,
/// Ending line (0-indexed, inclusive).
pub end_line: u32,
/// Kind of fold.
pub kind: FoldKind,
/// Preview text (first line content, for display when folded).
pub preview: String,
}
impl FoldRange {
/// Create a new fold range.
#[must_use]
pub fn new(start_line: u32, end_line: u32, kind: FoldKind, preview: impl Into<String>) -> Self {
Self {
start_line,
end_line,
kind,
preview: preview.into(),
}
}
/// Create a fold range without a preview.
#[must_use]
pub const fn without_preview(start_line: u32, end_line: u32, kind: FoldKind) -> Self {
Self {
start_line,
end_line,
kind,
preview: String::new(),
}
}
/// Check if this range spans multiple lines (is foldable).
#[must_use]
pub const fn is_foldable(&self) -> bool {
self.end_line > self.start_line
}
/// Get the number of lines in this fold.
#[must_use]
pub const fn line_count(&self) -> u32 {
self.end_line - self.start_line + 1
}
/// Get the number of hidden lines when folded.
///
/// This is `line_count() - 1` since the first line is shown.
#[must_use]
pub const fn hidden_lines(&self) -> u32 {
self.end_line.saturating_sub(self.start_line)
}
/// Check if this fold contains a line.
#[must_use]
pub const fn contains_line(&self, line: u32) -> bool {
line >= self.start_line && line <= self.end_line
}
/// Check if this fold overlaps with another.
#[must_use]
pub const fn overlaps(&self, other: &Self) -> bool {
self.start_line <= other.end_line && self.end_line >= other.start_line
}
/// Check if this fold contains another fold entirely.
#[must_use]
pub const fn contains(&self, other: &Self) -> bool {
self.start_line <= other.start_line && self.end_line >= other.end_line
}
}
#[cfg(test)]
#[path = "fold_tests.rs"]
mod tests;