Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:57:51.428Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

fuzzy_nucleo.rs

143 lines · 4.3 KB · rust
1mod matcher;
2mod paths;
3mod strings;
4
5use fuzzy::CharBag;
6use nucleo::pattern::{AtomKind, CaseMatching, Normalization, Pattern};
7
8pub use paths::{
9    PathMatch, PathMatchCandidate, PathMatchCandidateSet, match_fixed_path_set, match_path_sets,
10};
11pub use strings::{StringMatch, StringMatchCandidate, match_strings, match_strings_async};
12
13pub(crate) struct Cancelled;
14
15#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16pub enum Case {
17    Smart,
18    Ignore,
19}
20
21impl Case {
22    pub fn smart_if_uppercase_in(query: &str) -> Self {
23        if query.chars().any(|c| c.is_uppercase()) {
24            Self::Smart
25        } else {
26            Self::Ignore
27        }
28    }
29
30    pub fn is_smart(self) -> bool {
31        matches!(self, Self::Smart)
32    }
33}
34
35#[derive(Copy, Clone, Debug, PartialEq, Eq)]
36pub enum LengthPenalty {
37    On,
38    Off,
39}
40
41impl LengthPenalty {
42    pub fn from_bool(on: bool) -> Self {
43        if on { Self::On } else { Self::Off }
44    }
45
46    pub fn is_on(self) -> bool {
47        matches!(self, Self::On)
48    }
49}
50
51// Matching is always case-insensitive at the nucleo level — using
52// `CaseMatching::Smart` there would *reject* candidates whose capitalization
53// doesn't match the query, breaking pickers like the command palette
54// (`"Editor: Backspace"` against the action named `"editor: backspace"`).
55// `Case::Smart` is honored as a *scoring hint* instead: when the query
56// contains uppercase, candidates whose matched characters disagree in case
57// are downranked by a per-mismatch penalty rather than dropped.
58pub(crate) struct Query {
59    pub(crate) pattern: Pattern,
60    /// Non-whitespace query chars in input order, populated only when a smart-case
61    /// penalty will actually be charged. Aligns 1:1 with the indices appended by
62    /// `Pattern::indices` (atom-order, needle-order within each atom).
63    pub(crate) query_chars: Option<Vec<char>>,
64    pub(crate) char_bag: CharBag,
65}
66
67impl Query {
68    pub(crate) fn build(query: &str, case: Case) -> Option<Self> {
69        if query.chars().all(char::is_whitespace) {
70            return None;
71        }
72        let normalized = query.split_whitespace().collect::<Vec<_>>().join(" ");
73        let pattern = Pattern::new(
74            &normalized,
75            CaseMatching::Ignore,
76            Normalization::Smart,
77            AtomKind::Fuzzy,
78        );
79        let wants_case_penalty = case.is_smart() && query.chars().any(|c| c.is_uppercase());
80        let query_chars =
81            wants_case_penalty.then(|| query.chars().filter(|c| !c.is_whitespace()).collect());
82        Some(Query {
83            pattern,
84            query_chars,
85            char_bag: CharBag::from(query),
86        })
87    }
88}
89
90#[inline]
91pub(crate) fn count_case_mismatches(
92    query_chars: Option<&[char]>,
93    matched_chars: &[u32],
94    candidate: &str,
95    candidate_chars: &mut Vec<char>,
96) -> u32 {
97    let Some(query_chars) = query_chars else {
98        return 0;
99    };
100    if query_chars.len() != matched_chars.len() {
101        return 0;
102    }
103    candidate_chars.clear();
104    candidate_chars.extend(candidate.chars());
105    let mut mismatches: u32 = 0;
106    for (&query_char, &pos) in query_chars.iter().zip(matched_chars) {
107        if let Some(&candidate_char) = candidate_chars.get(pos as usize)
108            && candidate_char != query_char
109            && candidate_char.eq_ignore_ascii_case(&query_char)
110        {
111            mismatches += 1;
112        }
113    }
114    mismatches
115}
116
117const SMART_CASE_PENALTY_PER_MISMATCH: f64 = 0.9;
118
119#[inline]
120pub(crate) fn case_penalty(mismatches: u32) -> f64 {
121    if mismatches == 0 {
122        1.0
123    } else {
124        SMART_CASE_PENALTY_PER_MISMATCH.powi(mismatches as i32)
125    }
126}
127
128/// Reconstruct byte-offset match positions from a list of matched char offsets
129/// that is already sorted ascending and deduplicated.
130pub(crate) fn positions_from_sorted(s: &str, sorted_char_indices: &[u32]) -> Vec<usize> {
131    let mut iter = sorted_char_indices.iter().copied().peekable();
132    let mut out = Vec::with_capacity(sorted_char_indices.len());
133    for (char_offset, (byte_offset, _)) in s.char_indices().enumerate() {
134        if iter.peek().is_none() {
135            break;
136        }
137        if iter.next_if(|&m| m == char_offset as u32).is_some() {
138            out.push(byte_offset);
139        }
140    }
141    out
142}
143
Served at tenant.openagents/omega Member data and write actions are omitted.