diff --git a/TASKS.md b/TASKS.md index b30db6b..6eef187 100644 --- a/TASKS.md +++ b/TASKS.md @@ -61,9 +61,9 @@ Per-component lines live in `tasks/`. Tags there: - [x] T16d Finance · Trading - [x] T16e Finance · Markets & assets - [ ] T17a Editor · Core -- [ ] T17b Editor · Intelligence -- [ ] T17c Editor · Search -- [ ] T17d Editor · Panels +- [x] T17b Editor · Intelligence +- [x] T17c Editor · Search +- [x] T17d Editor · Panels - [x] T17e Editor · Status items - [ ] T18 Terminal - [ ] T19 Git @@ -230,11 +230,12 @@ Per-component lines live in `tasks/`. Tags there: | T16e | 1 | FAIL | a smaller loss than expected read as a miss | | T16e | 2 | PASS | | | T17a | 1 | FAIL | hidden numbers crashed the gutter; an edit with nothing to change tripped an assert; a read-only editor moved its caret and undid; a hint past a multi-line ghost fell off its row; the newest cursor lost the lead on merge; a slow composition left its pinyin in undo; gpui tests broke a featureless test build | -| T17a | 2 | — | pending review | +| T17a | 2 | FAIL | a selection touching the newest caret took the lead; a composition cleared to nothing stayed open and kept later edits out of undo | +| T17a | 3 | — | pending review | | T17b | 1 | FAIL | two calls of one name shared a tree key; references counted lines, not matches | -| T17b | 2 | — | pending review | +| T17b | 2 | PASS | | | T17c | 1 | FAIL | a match on trailing spaces ran past its trimmed line; `**/` needed a folder | -| T17c | 2 | — | pending review | +| T17c | 2 | PASS | | | T17d | 1 | FAIL | a path match marked a project name mid-character; pin and remove also opened the row; number settings showed no places | -| T17d | 2 | — | pending review | +| T17d | 2 | PASS | | | T17e | 1 | PASS | | diff --git a/src/editor/cursor.rs b/src/editor/cursor.rs index 14b7e03..2e137bd 100644 --- a/src/editor/cursor.rs +++ b/src/editor/cursor.rs @@ -38,14 +38,14 @@ impl Selection { } /// Sorted by where they start, overlapping selections joined into one; the last given, the primary, stays last. -pub(crate) fn merged(mut all: Vec) -> Vec { - let primary = all - .last() - .expect("an editor keeps at least one cursor") - .range(); - all.sort_by_key(|selection| selection.range().start); +pub(crate) fn merged(all: Vec) -> Vec { + assert!(!all.is_empty(), "an editor keeps at least one cursor"); + let primary = all.len() - 1; + let mut all: Vec<(usize, Selection)> = all.into_iter().enumerate().collect(); + all.sort_by_key(|(_, selection)| selection.range().start); let mut out: Vec = Vec::with_capacity(all.len()); - for next in all { + let mut lead = 0; + for (given, next) in all { match out.last_mut() { Some(last) if next.range().start < last.range().end @@ -61,14 +61,10 @@ pub(crate) fn merged(mut all: Vec) -> Vec { } _ => out.push(next), } + if given == primary { + lead = out.len() - 1; + } } - let lead = out - .iter() - .position(|selection| { - let range = selection.range(); - range.start <= primary.start && primary.end <= range.end - }) - .expect("a merge keeps every cursor inside one"); let lead = out.remove(lead); out.push(lead); out @@ -174,6 +170,19 @@ pub(crate) fn replace_each( mod tests { use super::*; + #[test] + fn the_last_cursor_given_leads_even_beside_a_touching_selection() { + let all = merged(vec![ + Selection { + anchor: 0, + head: 1, + goal: None, + }, + Selection::caret(1), + ]); + assert_eq!(all.last(), Some(&Selection::caret(1))); + } + #[test] fn overlapping_selections_join_and_carets_on_one_spot_become_one() { let joined = merged(vec![ diff --git a/src/editor/ime.rs b/src/editor/ime.rs index c7b7fd5..4ea404f 100644 --- a/src/editor/ime.rs +++ b/src/editor/ime.rs @@ -182,6 +182,9 @@ impl EntityInputHandler for CodeEditor { } self.apply(vec![(range.clone(), text.to_string())], true, cx); self.marked = (!text.is_empty()).then(|| range.start..range.start + text.len()); + if self.marked.is_none() { + self.end_composition(); + } let caret = match selected_utf16 { Some(inner) => range.start + from_utf16(text, inner.end), None => range.start + text.len(), diff --git a/src/editor/tests.rs b/src/editor/tests.rs index 6728b0e..81d28aa 100644 --- a/src/editor/tests.rs +++ b/src/editor/tests.rs @@ -194,6 +194,18 @@ fn a_slow_composition_undoes_in_one_step(cx: &mut TestAppContext) { }); } +#[gpui::test] +fn a_composition_cleared_to_nothing_ends(cx: &mut TestAppContext) { + let (editor, cx) = editor("", cx); + editor.update_in(cx, |editor, window, cx| { + editor.replace_and_mark_text_in_range(None, "n", None, window, cx); + editor.replace_and_mark_text_in_range(None, "", None, window, cx); + editor.type_text("x", cx); + editor.undo(cx); + assert_eq!(editor.text(), "", "typing after the cleared mark undoes"); + }); +} + impl CodeEditor { /// Whether line `line` has a row now. fn frame_shows(&self, line: usize) -> bool {