feat(todo): quick add keeps a word as text where it stands, and from Backspace (ADR 0033, D29)
A word tapped was kept as text by its text, so in "Watch The Daily Show Daily" tapping the first "Daily" also stopped the second one making the task repeat (the developer's example). Kept words are now ranges of the title that follow its edits (`shiftKept`: ranges after the edit move by its length, one it touches is dropped), and the parser leaves a token overlapping one of them as text, wherever else the same word stands. Backspace right after a highlighted word keeps it as text instead of deleting its last character, as Todoist's; the next Backspace deletes. It is an input formatter, so the keyboard is given the text back. The hint under the title says so. Whats-New: In Add task, only the word you tap stays text; the same word elsewhere still counts. Whats-New: In Add task, Backspace right after a highlighted word keeps it as text. Whats-New-De: In „Aufgabe hinzufügen“ bleibt nur das angetippte Wort Text; dasselbe Wort an anderer Stelle zählt weiter. Whats-New-De: In „Aufgabe hinzufügen“ behält die Rücktaste direkt nach einem hervorgehobenen Wort es als Text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
0f8a559d2e
commit
8b13cbcf2b
1 changed files with 283 additions and 31 deletions
|
|
@ -19,7 +19,7 @@ void main() {
|
|||
ParsedTask parse(
|
||||
String text, {
|
||||
List<QuickAddWords> words = const [QuickAddWords.en],
|
||||
Set<String> ignored = const {},
|
||||
List<QuickAddRange> ignored = const [],
|
||||
}) => parseQuickAdd(
|
||||
text,
|
||||
now: now,
|
||||
|
|
@ -463,7 +463,10 @@ void main() {
|
|||
expect(parse('Pay !tomorrow').date, isNull);
|
||||
expect(parse('Wow !9').title, 'Wow !9');
|
||||
expect(parse('Wow !soon').reminderTimes, isEmpty);
|
||||
final kept = parse('Call !1pm !2pm', ignored: {'!1pm'});
|
||||
final kept = parse(
|
||||
'Call !1pm !2pm',
|
||||
ignored: const [(start: 5, end: 9)],
|
||||
);
|
||||
expect(kept.title, 'Call !1pm');
|
||||
expect(kept.reminderTimes, [ReminderTime(today, 14 * 60)]);
|
||||
});
|
||||
|
|
@ -538,12 +541,36 @@ void main() {
|
|||
expect(added.title, 'Call');
|
||||
});
|
||||
|
||||
test('a token tapped off stays text, compared by its text', () {
|
||||
final added = parse('Watch Friday the 13th', ignored: {'friday'});
|
||||
test('a token tapped off stays text, by where it is', () {
|
||||
final added = parse(
|
||||
'Watch Friday the 13th',
|
||||
ignored: const [(start: 6, end: 12)],
|
||||
);
|
||||
expect(added.date, isNull);
|
||||
expect(added.title, 'Watch Friday the 13th');
|
||||
});
|
||||
|
||||
test('a word kept as text keeps only that occurrence: the same word '
|
||||
"elsewhere still counts (the developer's, 4 Nov 2026)", () {
|
||||
const text = 'Watch The Daily Show Daily';
|
||||
final first = text.indexOf('Daily');
|
||||
final added = parse(
|
||||
text,
|
||||
ignored: [(start: first, end: first + 5)],
|
||||
);
|
||||
expect(added.date?.repeat, const Repeat(RepeatUnit.day));
|
||||
expect(added.title, 'Watch The Daily Show');
|
||||
});
|
||||
|
||||
test('a range kept keeps every token overlapping it as text', () {
|
||||
final added = parse(
|
||||
'Meet next monday',
|
||||
ignored: const [(start: 5, end: 16)],
|
||||
);
|
||||
expect(added.date, isNull, reason: 'monday alone overlaps it too');
|
||||
expect(added.title, 'Meet next monday');
|
||||
});
|
||||
|
||||
test('only the first of a kind counts', () {
|
||||
final added = parse('Move tomorrow to friday');
|
||||
expect(added.date?.day, today.plus(1));
|
||||
|
|
@ -555,4 +582,39 @@ void main() {
|
|||
expect(parse('Monday.com login').date, isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('kept ranges follow edits', () {
|
||||
const kept = [(start: 6, end: 11)];
|
||||
const text = 'Watch today ok';
|
||||
|
||||
test('text inserted before one shifts it', () {
|
||||
expect(shiftKept(kept, text, 'Do: Watch today ok'), const [
|
||||
(start: 10, end: 15),
|
||||
]);
|
||||
expect(shiftKept(kept, text, 'Watch today ok'), const [
|
||||
(start: 7, end: 12),
|
||||
], reason: 'inserted right at its start');
|
||||
});
|
||||
|
||||
test('text deleted before one shifts it back', () {
|
||||
expect(shiftKept(kept, text, 'Wat today ok'), const [
|
||||
(start: 4, end: 9),
|
||||
]);
|
||||
});
|
||||
|
||||
test('an edit after one leaves it, typing right after it too', () {
|
||||
expect(shiftKept(kept, text, 'Watch today okay'), kept);
|
||||
expect(shiftKept(kept, text, 'Watch today, ok'), kept);
|
||||
});
|
||||
|
||||
test('an edit inside one, or over it, drops it', () {
|
||||
expect(shiftKept(kept, text, 'Watch toay ok'), isEmpty);
|
||||
expect(shiftKept(kept, text, 'Watch todday ok'), isEmpty);
|
||||
expect(shiftKept(kept, text, 'Watch ok'), isEmpty);
|
||||
});
|
||||
|
||||
test('the text unchanged keeps them', () {
|
||||
expect(shiftKept(kept, text, text), kept);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue