From 02732037436f547717ec53073ce8b295329c9bd8 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Sat, 23 Apr 2022 13:20:46 +0200 Subject: [PATCH] app,io/router: expand IME snippets if a new range overlaps the old Instead of cmpletely replacing the IME snippet for every update, expand the old range if there is overlap. This change avoids never-ending restarts of the IME on Android where snippets are expanded in two calls, one for expanding before the selection and one for exanding after the selection. Signed-off-by: Elias Naur --- app/ime_test.go | 21 +++++++++++++++++++++ app/window.go | 1 - io/router/router.go | 29 ++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/app/ime_test.go b/app/ime_test.go index ec17d1e0..ac763f7f 100644 --- a/app/ime_test.go +++ b/app/ime_test.go @@ -108,6 +108,27 @@ func FuzzIME(f *testing.F) { newState := r.EditorState() // We don't track caret position. state.Selection.Caret = newState.Selection.Caret + // Expanded snippets are ok. + their, our := newState.Snippet, state.EditorState.Snippet + beforeLen := 0 + for before := our.Start - their.Start; before > 0; before-- { + _, n := utf8.DecodeRuneInString(their.Text[beforeLen:]) + beforeLen += n + } + afterLen := 0 + for after := their.End - our.End; after > 0; after-- { + _, n := utf8.DecodeLastRuneInString(their.Text[:len(their.Text)-afterLen]) + afterLen += n + } + if beforeLen > 0 { + our.Text = their.Text[:beforeLen] + our.Text + our.Start = their.Start + } + if afterLen > 0 { + our.Text = our.Text + their.Text[len(their.Text)-afterLen:] + our.End = their.End + } + state.EditorState.Snippet = our if newState != state.EditorState { t.Errorf("IME state: %+v\neditor state: %+v", state.EditorState, newState) } diff --git a/app/window.go b/app/window.go index f947cbea..30447cd0 100644 --- a/app/window.go +++ b/app/window.go @@ -492,7 +492,6 @@ func (c *callbacks) EditorInsert(text string) { func (c *callbacks) EditorReplace(r key.Range, text string) { c.w.imeState.Replace(r, text) c.Event(key.EditEvent{Range: r, Text: text}) - c.Event(key.SnippetEvent(c.w.imeState.Snippet.Range)) } func (c *callbacks) SetEditorSelection(r key.Range) { diff --git a/io/router/router.go b/io/router/router.go index d51ebf02..c7b5a7b7 100644 --- a/io/router/router.go +++ b/io/router/router.go @@ -142,7 +142,20 @@ func (q *Router) Queue(events ...event.Event) bool { q.pointer.queue.Push(e, &q.handlers) case key.Event: q.queueKeyEvent(e) - case key.EditEvent, key.FocusEvent, key.SnippetEvent, key.SelectionEvent: + case key.SnippetEvent: + // Expand existing, overlapping snippet. + if r := q.key.queue.content.Snippet.Range; rangeOverlaps(r, key.Range(e)) { + if e.Start > r.Start { + e.Start = r.Start + } + if e.End < r.End { + e.End = r.End + } + } + if f := q.key.queue.focus; f != nil { + q.handlers.Add(f, e) + } + case key.EditEvent, key.FocusEvent, key.SelectionEvent: if f := q.key.queue.focus; f != nil { q.handlers.Add(f, e) } @@ -153,6 +166,20 @@ func (q *Router) Queue(events ...event.Event) bool { return q.handlers.HadEvents() } +func rangeOverlaps(r1, r2 key.Range) bool { + r1 = rangeNorm(r1) + r2 = rangeNorm(r2) + return r1.Start <= r2.Start && r2.Start < r1.End || + r1.Start <= r2.End && r2.End < r1.End +} + +func rangeNorm(r key.Range) key.Range { + if r.End < r.Start { + r.End, r.Start = r.Start, r.End + } + return r +} + func (q *Router) queueKeyEvent(e key.Event) { kq := &q.key.queue f := q.key.queue.focus