Skip to content

Commit dc230eb

Browse files
Fix panic in fuzzy-select when using non-ASCII characters
1 parent 9baa851 commit dc230eb

File tree

3 files changed

+32
-39
lines changed

3 files changed

+32
-39
lines changed

src/prompts/fuzzy_select.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ impl FuzzySelect<'_> {
195195

196196
fn _interact_on(self, term: &Term, allow_quit: bool) -> Result<Option<usize>> {
197197
// Place cursor at the end of the search term
198-
let mut position = self.initial_text.len();
198+
let mut cursor = self.initial_text.chars().count();
199199
let mut search_term = self.initial_text.to_owned();
200200

201201
let mut render = TermThemeRenderer::new(term, self.theme);
@@ -224,8 +224,15 @@ impl FuzzySelect<'_> {
224224
let mut vim_mode = false;
225225

226226
loop {
227+
let mut byte_indices = search_term
228+
.char_indices()
229+
.map(|(index, _)| index)
230+
.collect::<Vec<_>>();
231+
232+
byte_indices.push(search_term.len());
233+
227234
render.clear()?;
228-
render.fuzzy_select_prompt(self.prompt.as_str(), &search_term, position)?;
235+
render.fuzzy_select_prompt(self.prompt.as_str(), &search_term, byte_indices[cursor])?;
229236

230237
// Maps all items to a tuple of item and its match score.
231238
let mut filtered_list = self
@@ -304,14 +311,14 @@ impl FuzzySelect<'_> {
304311
}
305312
term.flush()?;
306313
}
307-
(Key::ArrowLeft, _, _) | (Key::Char('h'), _, true) if position > 0 => {
308-
position -= 1;
314+
(Key::ArrowLeft, _, _) | (Key::Char('h'), _, true) if cursor > 0 => {
315+
cursor -= 1;
309316
term.flush()?;
310317
}
311318
(Key::ArrowRight, _, _) | (Key::Char('l'), _, true)
312-
if position < search_term.len() =>
319+
if cursor < byte_indices.len() - 1 =>
313320
{
314-
position += 1;
321+
cursor += 1;
315322
term.flush()?;
316323
}
317324
(Key::Enter, Some(sel), _) if !filtered_list.is_empty() => {
@@ -331,14 +338,14 @@ impl FuzzySelect<'_> {
331338
term.show_cursor()?;
332339
return Ok(sel_string_pos_in_items);
333340
}
334-
(Key::Backspace, _, _) if position > 0 => {
335-
position -= 1;
336-
search_term.remove(position);
341+
(Key::Backspace, _, _) if cursor > 0 => {
342+
cursor -= 1;
343+
search_term.remove(byte_indices[cursor]);
337344
term.flush()?;
338345
}
339346
(Key::Char(chr), _, _) if !chr.is_ascii_control() => {
340-
search_term.insert(position, chr);
341-
position += 1;
347+
search_term.insert(byte_indices[cursor], chr);
348+
cursor += 1;
342349
term.flush()?;
343350
sel = Some(0);
344351
starting_row = 0;

src/theme/colorful.rs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -408,31 +408,24 @@ impl Theme for ColorfulTheme {
408408
f: &mut dyn fmt::Write,
409409
prompt: &str,
410410
search_term: &str,
411-
cursor_pos: usize,
411+
bytes_pos: usize,
412412
) -> fmt::Result {
413413
if !prompt.is_empty() {
414414
write!(
415415
f,
416416
"{} {} ",
417-
&self.prompt_prefix,
417+
self.prompt_prefix,
418418
self.prompt_style.apply_to(prompt)
419419
)?;
420420
}
421421

422-
if cursor_pos < search_term.len() {
423-
let st_head = search_term[0..cursor_pos].to_string();
424-
let st_tail = search_term[cursor_pos + 1..search_term.len()].to_string();
425-
let st_cursor = self
426-
.fuzzy_cursor_style
427-
.apply_to(search_term.to_string().chars().nth(cursor_pos).unwrap());
428-
write!(
429-
f,
430-
"{} {}{}{}",
431-
&self.prompt_suffix, st_head, st_cursor, st_tail
432-
)
433-
} else {
434-
let cursor = self.fuzzy_cursor_style.apply_to(" ");
435-
write!(f, "{} {}{}", &self.prompt_suffix, search_term, cursor)
436-
}
422+
let (st_head, remaining) = search_term.split_at(bytes_pos);
423+
let mut chars = remaining.chars();
424+
let chr = chars.next().unwrap_or(' ');
425+
let st_cursor = self.fuzzy_cursor_style.apply_to(chr);
426+
let st_tail = chars.as_str();
427+
428+
let prompt_suffix = &self.prompt_suffix;
429+
write!(f, "{prompt_suffix} {st_head}{st_cursor}{st_tail}",)
437430
}
438431
}

src/theme/mod.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -253,20 +253,13 @@ pub trait Theme {
253253
f: &mut dyn fmt::Write,
254254
prompt: &str,
255255
search_term: &str,
256-
cursor_pos: usize,
256+
bytes_pos: usize,
257257
) -> fmt::Result {
258258
if !prompt.is_empty() {
259-
write!(f, "{} ", prompt)?;
259+
write!(f, "{prompt} ")?;
260260
}
261261

262-
if cursor_pos < search_term.len() {
263-
let st_head = search_term[0..cursor_pos].to_string();
264-
let st_tail = search_term[cursor_pos..search_term.len()].to_string();
265-
let st_cursor = "|".to_string();
266-
write!(f, "{}{}{}", st_head, st_cursor, st_tail)
267-
} else {
268-
let cursor = "|".to_string();
269-
write!(f, "{}{}", search_term, cursor)
270-
}
262+
let (st_head, st_tail) = search_term.split_at(bytes_pos);
263+
write!(f, "{st_head}|{st_tail}")
271264
}
272265
}

0 commit comments

Comments
 (0)