feat: support macOS modified backspace deletion

This commit is contained in:
2026-10-12 17:36:43 +02:00
parent 5e6b602127
commit 2260817fc0
6 changed files with 522 additions and 60 deletions
+52 -14
View File
@@ -3,7 +3,6 @@ use std::collections::{HashMap, HashSet};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use unicode_segmentation::UnicodeSegmentation;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FilePresentation {
@@ -385,10 +384,20 @@ impl FilePicker {
}
pub(crate) fn handle_filter_input(&mut self, input: &crate::editor::Input) -> bool {
use crate::editor::Input;
use crate::editor::{BackspaceKind, Input};
match input {
Input::Ctrl('u') => self.clear_filter(),
Input::Backspace if self.view == FileView::List || self.searching => self.backspace(),
input
if BackspaceKind::from_input(input).is_some()
&& (self.view == FileView::List || self.searching) =>
{
if BackspaceKind::from_input(input)
.expect("guard checked backspace input")
.truncate(&mut self.query)
{
self.refilter();
}
}
Input::Char('i') if self.view == FileView::Tree && !self.searching => {
self.start_search()
}
@@ -604,13 +613,6 @@ impl FilePicker {
self.clear_error();
}
pub(crate) fn backspace(&mut self) {
if let Some((byte, _)) = self.query.grapheme_indices(true).next_back() {
self.query.truncate(byte);
self.refilter();
}
}
pub(crate) fn navigate(&mut self, forward: bool) {
let count = self.rows.len();
self.selected = if count == 0 {
@@ -932,11 +934,11 @@ mod tests {
picker.refilter();
assert_eq!(paths(&picker), [PathBuf::from("a space 界[e\u{301}].txt")]);
picker.query = "e\u{301}👩‍💻".into();
picker.backspace();
assert!(picker.handle_filter_input(&crate::editor::Input::Backspace));
assert_eq!(picker.query, "e\u{301}");
picker.backspace();
assert!(picker.handle_filter_input(&crate::editor::Input::Backspace));
assert_eq!(picker.query, "");
picker.backspace();
assert!(picker.handle_filter_input(&crate::editor::Input::Backspace));
assert_eq!(picker.entries().len(), 3);
picker.query = "no match".into();
picker.refilter();
@@ -947,12 +949,48 @@ mod tests {
}
}
#[test]
fn word_and_line_deletion_refilter_only_editable_queries() {
use crate::editor::Input;
for view in [FileView::List, FileView::Tree] {
let mut picker = FilePicker::from_paths(
PathBuf::new(),
["alpha one.txt", "alpha two.txt", "beta.txt"]
.map(PathBuf::from)
.into_iter()
.enumerate(),
);
picker.set_view(view);
picker.query = "alpha missing".into();
picker.refilter();
assert_eq!(picker.entries().len(), 0);
if view == FileView::Tree {
for input in [Input::Backspace, Input::AltBackspace, Input::SuperBackspace] {
assert!(!picker.handle_filter_input(&input));
assert_eq!(picker.query, "alpha missing");
}
picker.start_search();
}
assert!(picker.handle_filter_input(&Input::AltBackspace));
assert_eq!(picker.query, "alpha ");
assert_eq!(picker.entries().len(), 2);
assert!(picker.handle_filter_input(&Input::SuperBackspace));
assert_eq!(picker.query, "");
assert_eq!(picker.entries().len(), 3);
for input in [Input::Backspace, Input::AltBackspace, Input::SuperBackspace] {
assert!(picker.handle_filter_input(&input));
assert_eq!(picker.entries().len(), 3);
}
}
}
#[test]
fn empty_scan_refresh_and_failure_preserve_reachable_state() {
let directory = Directory::new();
let mut picker = directory.scan();
picker.navigate(false);
picker.backspace();
assert!(picker.handle_filter_input(&crate::editor::Input::Backspace));
assert_eq!(picker.entries().len(), 0);
directory.put("new.txt", "text");
picker.query = "new".into();