@@ -4,7 +4,7 @@ use ego_tree::NodeId;
4
4
use linutil_core:: Tab ;
5
5
use ratatui:: {
6
6
layout:: { Position , Rect } ,
7
- style:: Style ,
7
+ style:: { Color , Style } ,
8
8
text:: Span ,
9
9
widgets:: { Block , Borders , Paragraph } ,
10
10
Frame ,
@@ -22,6 +22,7 @@ pub struct Filter {
22
22
in_search_mode : bool ,
23
23
input_position : usize ,
24
24
items : Vec < ListEntry > ,
25
+ completion_preview : Option < String > ,
25
26
}
26
27
27
28
impl Filter {
@@ -31,17 +32,23 @@ impl Filter {
31
32
in_search_mode : false ,
32
33
input_position : 0 ,
33
34
items : vec ! [ ] ,
35
+ completion_preview : None ,
34
36
}
35
37
}
38
+
36
39
pub fn item_list ( & self ) -> & [ ListEntry ] {
37
40
& self . items
38
41
}
42
+
39
43
pub fn activate_search ( & mut self ) {
40
44
self . in_search_mode = true ;
41
45
}
46
+
42
47
pub fn deactivate_search ( & mut self ) {
43
48
self . in_search_mode = false ;
49
+ self . completion_preview = None ;
44
50
}
51
+
45
52
pub fn update_items ( & mut self , tabs : & [ Tab ] , current_tab : usize , node : NodeId ) {
46
53
if self . search_input . is_empty ( ) {
47
54
let curr = tabs[ current_tab] . tree . get ( node) . unwrap ( ) ;
@@ -78,13 +85,34 @@ impl Filter {
78
85
}
79
86
self . items . sort_by ( |a, b| a. node . name . cmp ( & b. node . name ) ) ;
80
87
}
88
+
89
+ self . update_completion_preview ( ) ;
90
+ }
91
+
92
+ fn update_completion_preview ( & mut self ) {
93
+ if self . search_input . is_empty ( ) {
94
+ self . completion_preview = None ;
95
+ return ;
96
+ }
97
+
98
+ let input = self . search_input . iter ( ) . collect :: < String > ( ) . to_lowercase ( ) ;
99
+ self . completion_preview = self . items . iter ( ) . find_map ( |item| {
100
+ let item_name_lower = item. node . name . to_lowercase ( ) ;
101
+ if item_name_lower. starts_with ( & input) {
102
+ Some ( item_name_lower[ input. len ( ) ..] . to_string ( ) )
103
+ } else {
104
+ None
105
+ }
106
+ } ) ;
81
107
}
108
+
82
109
pub fn draw_searchbar ( & self , frame : & mut Frame , area : Rect , theme : & Theme ) {
83
110
//Set the search bar text (If empty use the placeholder)
84
111
let display_text = if !self . in_search_mode && self . search_input . is_empty ( ) {
85
112
Span :: raw ( "Press / to search" )
86
113
} else {
87
- Span :: raw ( self . search_input . iter ( ) . collect :: < String > ( ) )
114
+ let input_text = self . search_input . iter ( ) . collect :: < String > ( ) ;
115
+ Span :: styled ( input_text, Style :: default ( ) . fg ( theme. focused_color ( ) ) )
88
116
} ;
89
117
90
118
let search_color = if self . in_search_mode {
@@ -110,11 +138,22 @@ impl Filter {
110
138
let x = area. x + cursor_position as u16 + 1 ;
111
139
let y = area. y + 1 ;
112
140
frame. set_cursor_position ( Position :: new ( x, y) ) ;
141
+
142
+ if let Some ( preview) = & self . completion_preview {
143
+ let preview_span = Span :: styled ( preview, Style :: default ( ) . fg ( Color :: DarkGray ) ) ;
144
+ let preview_paragraph = Paragraph :: new ( preview_span) . style ( Style :: default ( ) ) ;
145
+ let preview_area = Rect :: new (
146
+ x,
147
+ y,
148
+ ( preview. len ( ) as u16 ) . min ( area. width - cursor_position as u16 - 1 ) ,
149
+ 1 ,
150
+ ) ;
151
+ frame. render_widget ( preview_paragraph, preview_area) ;
152
+ }
113
153
}
114
154
}
115
155
// Handles key events. Returns true if search must be exited
116
156
pub fn handle_key ( & mut self , event : & KeyEvent ) -> SearchAction {
117
- //Insert user input into the search bar
118
157
match event. code {
119
158
KeyCode :: Char ( 'c' ) if event. modifiers . contains ( KeyModifiers :: CONTROL ) => {
120
159
return self . exit_search ( )
@@ -124,10 +163,17 @@ impl Filter {
124
163
KeyCode :: Delete => self . remove_next ( ) ,
125
164
KeyCode :: Left => return self . cursor_left ( ) ,
126
165
KeyCode :: Right => return self . cursor_right ( ) ,
166
+ KeyCode :: Tab => return self . complete_search ( ) ,
167
+ KeyCode :: Esc => {
168
+ self . input_position = 0 ;
169
+ self . search_input . clear ( ) ;
170
+ self . completion_preview = None ;
171
+ return SearchAction :: Exit ;
172
+ }
127
173
KeyCode :: Enter => return SearchAction :: Exit ,
128
- KeyCode :: Esc => return self . exit_search ( ) ,
129
174
_ => return SearchAction :: None ,
130
175
} ;
176
+ self . update_completion_preview ( ) ;
131
177
SearchAction :: Update
132
178
}
133
179
@@ -141,29 +187,45 @@ impl Filter {
141
187
self . input_position = self . input_position . saturating_sub ( 1 ) ;
142
188
SearchAction :: None
143
189
}
190
+
144
191
fn cursor_right ( & mut self ) -> SearchAction {
145
192
if self . input_position < self . search_input . len ( ) {
146
193
self . input_position += 1 ;
147
194
}
148
195
SearchAction :: None
149
196
}
197
+
150
198
fn insert_char ( & mut self , input : char ) {
151
199
self . search_input . insert ( self . input_position , input) ;
152
200
self . cursor_right ( ) ;
153
201
}
202
+
154
203
fn remove_previous ( & mut self ) {
155
204
let current = self . input_position ;
156
205
if current > 0 {
157
206
self . search_input . remove ( current - 1 ) ;
158
207
self . cursor_left ( ) ;
159
208
}
160
209
}
210
+
161
211
fn remove_next ( & mut self ) {
162
212
let current = self . input_position ;
163
213
if current < self . search_input . len ( ) {
164
214
self . search_input . remove ( current) ;
165
215
}
166
216
}
217
+
218
+ fn complete_search ( & mut self ) -> SearchAction {
219
+ if let Some ( completion) = self . completion_preview . take ( ) {
220
+ self . search_input . extend ( completion. chars ( ) ) ;
221
+ self . input_position = self . search_input . len ( ) ;
222
+ self . update_completion_preview ( ) ;
223
+ SearchAction :: Update
224
+ } else {
225
+ SearchAction :: None
226
+ }
227
+ }
228
+
167
229
pub fn clear_search ( & mut self ) {
168
230
self . search_input . clear ( ) ;
169
231
self . input_position = 0 ;
0 commit comments