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
//! Search query state and manipulation
/// Search query state
#[derive(Debug, Clone, Default)]
pub struct SearchState {
/// Current search query
query: String,
/// Cursor position within the search query (character index, not byte index)
cursor_position: usize,
}
impl SearchState {
/// Create a new SearchState
pub fn new() -> Self {
Self::default()
}
/// Get the search query
pub fn query(&self) -> &str {
&self.query
}
/// Get the cursor position
pub fn cursor_position(&self) -> usize {
self.cursor_position
}
/// Check if search query is empty
pub fn is_empty(&self) -> bool {
self.query.is_empty()
}
/// Set the search query directly (with lowercase conversion for case-insensitive search)
pub fn set_query(
&mut self,
query: String,
) {
self.query = query.to_ascii_lowercase();
self.cursor_position = self.query.chars().count();
}
/// Clear the search query and reset cursor
pub fn clear(&mut self) {
self.query.clear();
self.cursor_position = 0;
}
/// Insert a character at the cursor position
/// Uppercase letters are automatically converted to lowercase for case-insensitive search
pub fn insert_char(
&mut self,
c: char,
) {
// Convert uppercase letters to lowercase, leave special chars/symbols unchanged
let char_to_insert = if c.is_ascii_uppercase() { c.to_ascii_lowercase() } else { c };
// Map character index to byte index for safe UTF-8 insertion
let byte_index =
self.query.char_indices().nth(self.cursor_position).map(|(idx, _)| idx).unwrap_or(self.query.len()); // If at end, use total byte length
self.query.insert(byte_index, char_to_insert);
self.cursor_position += 1;
}
/// Delete the character before the cursor in the search query
pub fn delete_char(&mut self) {
if self.cursor_position > 0 {
// Map character index to byte index for the character to delete
// We need to find the byte range of the character at position (cursor_position - 1)
if let Some((byte_idx, ch)) = self.query.char_indices().nth(self.cursor_position - 1) {
// Calculate the byte length of the character to remove
let char_len = ch.len_utf8();
// Remove the byte range for this character
self.query.drain(byte_idx..byte_idx + char_len);
self.cursor_position -= 1;
}
}
}
/// Move search cursor left
pub fn move_cursor_left(&mut self) {
if self.cursor_position > 0 {
self.cursor_position -= 1;
}
}
/// Move search cursor right
pub fn move_cursor_right(&mut self) {
// Use character count, not byte length
let char_count = self.query.chars().count();
if self.cursor_position < char_count {
self.cursor_position += 1;
}
}
}
#[cfg(test)]
#[path = "search_tests.rs"]
mod search_tests;