UI: AutoComplete folds case beyond ASCII and cuts the proposed rest on characters (#842)
This commit is contained in:
parent
7a8235a586
commit
9c3db37ca2
|
|
@ -251,9 +251,10 @@ pub fn column_completion(sheet: &gridcraft_engine::model::Sheet, at: CellRef, ty
|
|||
match sheet.value(CellRef::new(row, at.col)) {
|
||||
gridcraft_engine::core::Value::Empty => false,
|
||||
gridcraft_engine::core::Value::Text(t) => {
|
||||
if t.to_lowercase().starts_with(&lower) && t.len() > typed.len() {
|
||||
let folded = t.to_lowercase();
|
||||
if folded.starts_with(&lower) && folded.len() > lower.len() {
|
||||
match &found {
|
||||
Some(f) if !f.eq_ignore_ascii_case(&t) => ambiguous = true,
|
||||
Some(f) if f.to_lowercase() != folded => ambiguous = true,
|
||||
None => found = Some(t.to_string()),
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -281,6 +282,13 @@ pub fn column_completion(sheet: &gridcraft_engine::model::Sheet, at: CellRef, ty
|
|||
if ambiguous { None } else { found }
|
||||
}
|
||||
|
||||
/// The part of `full` after the `typed` prefix, counted in characters: case folding can change
|
||||
/// a character's byte length ("ß" is two bytes, "ẞ" three).
|
||||
pub fn completion_rest<'a>(full: &'a str, typed: &str) -> Option<&'a str> {
|
||||
let start = full.char_indices().nth(typed.chars().count())?.0;
|
||||
full.get(start..)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -336,8 +336,7 @@ pub fn editor_widget(app: &mut SheetApp, ui: &mut egui::Ui, id: egui::Id, font:
|
|||
}
|
||||
// Column AutoComplete: the rest of the suggested entry, shown selected after the caret.
|
||||
if let Some(full) = &ed.completion
|
||||
&& full.len() > ed.text.len()
|
||||
&& let Some(rest) = full.get(ed.text.len()..)
|
||||
&& let Some(rest) = crate::editor::completion_rest(full, &ed.text)
|
||||
{
|
||||
let pos = resp.rect.left_top() + egui::vec2(output.galley.rect.width(), 0.0);
|
||||
let g = ui.painter().layout_no_wrap(
|
||||
|
|
|
|||
|
|
@ -537,6 +537,25 @@ fn column_autocomplete_skips_ambiguous_prefixes_and_numbers() {
|
|||
assert_eq!(completion(&h), ("12".into(), None), "a number is not completed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn column_autocomplete_folds_case_beyond_ascii() {
|
||||
let mut s = blank();
|
||||
s.execute("range.setValues", json!({"range": "A1", "values": [["Ébène"], ["Ébano"], ["ébano"], ["STRAẞENBAHN"]]})).unwrap();
|
||||
s.execute("selection.set", json!({"cell": "A5"})).unwrap();
|
||||
let mut h = harness(s);
|
||||
text(&mut h, "éba");
|
||||
assert_eq!(completion(&h), ("éba".into(), Some("ébano".into())), "Ébano and ébano are one entry, Ébène doesn't match");
|
||||
h.state_mut().cancel_edit();
|
||||
text(&mut h, "éb");
|
||||
assert_eq!(completion(&h).1, None, "Ébène and Ébano differ past the prefix");
|
||||
h.state_mut().cancel_edit();
|
||||
// "ß" is two bytes, "ẞ" three: the proposed rest starts after the fifth character, not the sixth byte.
|
||||
text(&mut h, "straß");
|
||||
assert_eq!(completion(&h), ("straß".into(), Some("STRAẞENBAHN".into())));
|
||||
let painted = h.output().shapes.iter().any(|c| matches!(&c.shape, egui::Shape::Text(t) if t.galley.job.text == "ENBAHN"));
|
||||
assert!(painted, "the rest \"ENBAHN\" is drawn after the caret");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn column_autocomplete_can_be_turned_off() {
|
||||
assert!(gridcraft_ui_egui::UiState::default().autocomplete);
|
||||
|
|
|
|||
Loading…
Reference in New Issue