Skip to main content

_diffctx/
types.rs

1use std::cmp::Ordering;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4use std::path::PathBuf;
5use std::sync::Arc;
6
7use once_cell::sync::Lazy;
8use regex::Regex;
9use rustc_hash::{FxHashMap, FxHashSet};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum FragmentKind {
13    Function,
14    Class,
15    Struct,
16    Impl,
17    Interface,
18    Enum,
19    Module,
20    Type,
21    Variable,
22    Record,
23    Property,
24    Declaration,
25    Definition,
26    Section,
27    Chunk,
28    FunctionSignature,
29    ClassSignature,
30    MethodSignature,
31    StructSignature,
32    InterfaceSignature,
33    EnumSignature,
34}
35
36impl FragmentKind {
37    pub fn from_str(s: &str) -> Self {
38        match s {
39            "function" => Self::Function,
40            "class" => Self::Class,
41            "struct" => Self::Struct,
42            "impl" => Self::Impl,
43            "interface" => Self::Interface,
44            "enum" => Self::Enum,
45            "module" => Self::Module,
46            "type" => Self::Type,
47            "variable" => Self::Variable,
48            "record" => Self::Record,
49            "property" => Self::Property,
50            "declaration" => Self::Declaration,
51            "definition" => Self::Definition,
52            "section" => Self::Section,
53            "chunk" => Self::Chunk,
54            "function_signature" => Self::FunctionSignature,
55            "class_signature" => Self::ClassSignature,
56            "method_signature" => Self::MethodSignature,
57            "struct_signature" => Self::StructSignature,
58            "interface_signature" => Self::InterfaceSignature,
59            "enum_signature" => Self::EnumSignature,
60            _ => Self::Chunk,
61        }
62    }
63
64    pub fn as_str(&self) -> &'static str {
65        match self {
66            Self::Function => "function",
67            Self::Class => "class",
68            Self::Struct => "struct",
69            Self::Impl => "impl",
70            Self::Interface => "interface",
71            Self::Enum => "enum",
72            Self::Module => "module",
73            Self::Type => "type",
74            Self::Variable => "variable",
75            Self::Record => "record",
76            Self::Property => "property",
77            Self::Declaration => "declaration",
78            Self::Definition => "definition",
79            Self::Section => "section",
80            Self::Chunk => "chunk",
81            Self::FunctionSignature => "function_signature",
82            Self::ClassSignature => "class_signature",
83            Self::MethodSignature => "method_signature",
84            Self::StructSignature => "struct_signature",
85            Self::InterfaceSignature => "interface_signature",
86            Self::EnumSignature => "enum_signature",
87        }
88    }
89
90    pub fn is_semantic(&self) -> bool {
91        matches!(
92            self,
93            Self::Function
94                | Self::Class
95                | Self::Struct
96                | Self::Impl
97                | Self::Interface
98                | Self::Enum
99                | Self::Module
100                | Self::Type
101                | Self::Variable
102                | Self::Record
103                | Self::Property
104                | Self::Declaration
105                | Self::Definition
106                | Self::Section
107        )
108    }
109
110    pub fn is_container(&self) -> bool {
111        matches!(self, Self::Class | Self::Interface | Self::Struct)
112    }
113
114    pub fn is_signature(&self) -> bool {
115        matches!(
116            self,
117            Self::FunctionSignature
118                | Self::ClassSignature
119                | Self::MethodSignature
120                | Self::StructSignature
121                | Self::InterfaceSignature
122                | Self::EnumSignature
123        )
124    }
125}
126
127impl fmt::Display for FragmentKind {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        f.write_str(self.as_str())
130    }
131}
132
133#[derive(Clone)]
134pub struct FragmentId {
135    pub path: Arc<str>,
136    pub start_line: u32,
137    pub end_line: u32,
138    cached_hash: u64,
139}
140
141impl Hash for FragmentId {
142    fn hash<H: Hasher>(&self, state: &mut H) {
143        state.write_u64(self.cached_hash);
144    }
145}
146
147impl PartialEq for FragmentId {
148    fn eq(&self, other: &Self) -> bool {
149        self.cached_hash == other.cached_hash
150            && self.start_line == other.start_line
151            && self.end_line == other.end_line
152            && self.path == other.path
153    }
154}
155
156impl Eq for FragmentId {}
157
158impl PartialOrd for FragmentId {
159    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
160        Some(self.cmp(other))
161    }
162}
163
164impl Ord for FragmentId {
165    fn cmp(&self, other: &Self) -> Ordering {
166        self.path
167            .as_ref()
168            .cmp(other.path.as_ref())
169            .then(self.start_line.cmp(&other.start_line))
170            .then(self.end_line.cmp(&other.end_line))
171    }
172}
173
174impl fmt::Display for FragmentId {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        write!(f, "{}:{}-{}", self.path, self.start_line, self.end_line)
177    }
178}
179
180impl fmt::Debug for FragmentId {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        write!(f, "FragmentId({})", self)
183    }
184}
185
186impl FragmentId {
187    pub fn new(path: Arc<str>, start_line: u32, end_line: u32) -> Self {
188        use std::hash::DefaultHasher;
189        let mut hasher = DefaultHasher::new();
190        path.as_ref().hash(&mut hasher);
191        start_line.hash(&mut hasher);
192        end_line.hash(&mut hasher);
193        let cached_hash = hasher.finish();
194        Self {
195            path,
196            start_line,
197            end_line,
198            cached_hash,
199        }
200    }
201
202    pub fn path_buf(&self) -> PathBuf {
203        PathBuf::from(self.path.as_ref())
204    }
205}
206
207#[derive(Clone)]
208pub struct Fragment {
209    pub id: FragmentId,
210    pub kind: FragmentKind,
211    pub content: Arc<str>,
212    pub identifiers: FxHashSet<String>,
213    pub token_count: u32,
214    pub symbol_name: Option<String>,
215}
216
217impl Fragment {
218    pub fn path(&self) -> &str {
219        &self.id.path
220    }
221
222    pub fn start_line(&self) -> u32 {
223        self.id.start_line
224    }
225
226    pub fn end_line(&self) -> u32 {
227        self.id.end_line
228    }
229
230    pub fn line_count(&self) -> u32 {
231        self.id.end_line - self.id.start_line + 1
232    }
233}
234
235#[derive(Debug, Clone)]
236pub struct DiffHunk {
237    pub path: Arc<str>,
238    pub new_start: u32,
239    pub new_len: u32,
240    pub old_start: u32,
241    pub old_len: u32,
242}
243
244impl DiffHunk {
245    pub fn end_line(&self) -> u32 {
246        if self.new_len == 0 {
247            self.new_start
248        } else {
249            self.new_start + self.new_len - 1
250        }
251    }
252
253    pub fn is_deletion(&self) -> bool {
254        self.new_len == 0 && self.old_len > 0
255    }
256
257    pub fn core_selection_range(&self) -> (u32, u32) {
258        if self.is_deletion() {
259            let anchor = self.new_start.max(1);
260            (anchor, anchor)
261        } else {
262            (self.new_start, self.end_line())
263        }
264    }
265}
266
267static IDENT_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"[A-Za-z_]\w*").unwrap());
268
269pub fn extract_identifiers(text: &str, min_length: usize) -> FxHashSet<String> {
270    IDENT_RE
271        .find_iter(text)
272        .filter(|m| m.as_str().len() >= min_length)
273        .map(|m| m.as_str().to_lowercase())
274        .collect()
275}
276
277pub fn extract_identifier_list(text: &str, min_length: usize) -> Vec<String> {
278    IDENT_RE
279        .find_iter(text)
280        .filter(|m| m.as_str().len() >= min_length)
281        .map(|m| m.as_str().to_lowercase())
282        .collect()
283}
284
285pub fn extract_identifier_counts(text: &str, min_length: usize) -> (FxHashMap<String, u32>, u32) {
286    let mut counts: FxHashMap<String, u32> = FxHashMap::default();
287    let mut total = 0u32;
288    for m in IDENT_RE.find_iter(text) {
289        if m.as_str().len() < min_length {
290            continue;
291        }
292        *counts.entry(m.as_str().to_lowercase()).or_insert(0) += 1;
293        total += 1;
294    }
295    (counts, total)
296}