T17 round 2 fixes: merge keeps the newest caret in the lead by where it was given, and a composition cleared to nothing ends
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a42f8e44a8
commit
30ffe7a0b2
4 files changed
+46
-21
No files matched your search
@@ -61,9 +61,9 @@ Per-component lines live in `tasks/`. Tags there:
|
|||||||
- [x] T16d Finance · Trading
|
- [x] T16d Finance · Trading
|
||||||
- [x] T16e Finance · Markets & assets
|
- [x] T16e Finance · Markets & assets
|
||||||
- [ ] T17a Editor · Core
|
- [ ] T17a Editor · Core
|
||||||
- [ ] T17b Editor · Intelligence
|
- [x] T17b Editor · Intelligence
|
||||||
- [ ] T17c Editor · Search
|
- [x] T17c Editor · Search
|
||||||
- [ ] T17d Editor · Panels
|
- [x] T17d Editor · Panels
|
||||||
- [x] T17e Editor · Status items
|
- [x] T17e Editor · Status items
|
||||||
- [ ] T18 Terminal
|
- [ ] T18 Terminal
|
||||||
- [ ] T19 Git
|
- [ ] 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 | 1 | FAIL | a smaller loss than expected read as a miss |
|
||||||
| T16e | 2 | PASS | |
|
| 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 | 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 | 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 | 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 | 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 | |
|
| T17e | 1 | PASS | |
|
||||||
+23
-14
@@ -38,14 +38,14 @@ impl Selection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sorted by where they start, overlapping selections joined into one; the last given, the primary, stays last.
|
/// Sorted by where they start, overlapping selections joined into one; the last given, the primary, stays last.
|
||||||
pub(crate) fn merged(mut all: Vec<Selection>) -> Vec<Selection> {
|
pub(crate) fn merged(all: Vec<Selection>) -> Vec<Selection> {
|
||||||
let primary = all
|
assert!(!all.is_empty(), "an editor keeps at least one cursor");
|
||||||
.last()
|
let primary = all.len() - 1;
|
||||||
.expect("an editor keeps at least one cursor")
|
let mut all: Vec<(usize, Selection)> = all.into_iter().enumerate().collect();
|
||||||
.range();
|
all.sort_by_key(|(_, selection)| selection.range().start);
|
||||||
all.sort_by_key(|selection| selection.range().start);
|
|
||||||
let mut out: Vec<Selection> = Vec::with_capacity(all.len());
|
let mut out: Vec<Selection> = Vec::with_capacity(all.len());
|
||||||
for next in all {
|
let mut lead = 0;
|
||||||
|
for (given, next) in all {
|
||||||
match out.last_mut() {
|
match out.last_mut() {
|
||||||
Some(last)
|
Some(last)
|
||||||
if next.range().start < last.range().end
|
if next.range().start < last.range().end
|
||||||
@@ -61,14 +61,10 @@ pub(crate) fn merged(mut all: Vec<Selection>) -> Vec<Selection> {
|
|||||||
}
|
}
|
||||||
_ => out.push(next),
|
_ => 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);
|
let lead = out.remove(lead);
|
||||||
out.push(lead);
|
out.push(lead);
|
||||||
out
|
out
|
||||||
@@ -174,6 +170,19 @@ pub(crate) fn replace_each(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn overlapping_selections_join_and_carets_on_one_spot_become_one() {
|
fn overlapping_selections_join_and_carets_on_one_spot_become_one() {
|
||||||
let joined = merged(vec![
|
let joined = merged(vec![
|
||||||
|
|||||||
@@ -182,6 +182,9 @@ impl EntityInputHandler for CodeEditor {
|
|||||||
}
|
}
|
||||||
self.apply(vec![(range.clone(), text.to_string())], true, cx);
|
self.apply(vec![(range.clone(), text.to_string())], true, cx);
|
||||||
self.marked = (!text.is_empty()).then(|| range.start..range.start + text.len());
|
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 {
|
let caret = match selected_utf16 {
|
||||||
Some(inner) => range.start + from_utf16(text, inner.end),
|
Some(inner) => range.start + from_utf16(text, inner.end),
|
||||||
None => range.start + text.len(),
|
None => range.start + text.len(),
|
||||||
|
|||||||
@@ -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 {
|
impl CodeEditor {
|
||||||
/// Whether line `line` has a row now.
|
/// Whether line `line` has a row now.
|
||||||
fn frame_shows(&self, line: usize) -> bool {
|
fn frame_shows(&self, line: usize) -> bool {
|
||||||
|
|||||||
Reference in new issue
Block a user