Skip to content

Commit 90d7ec6

Browse files
committed
Shrink EditContext changes down to the minimum text change
FIX: Avoid firing text changes that cover unchanged text on Android. See https://discuss.codemirror.net/t/autocomplete-gets-stuck-and-permanently-breaks-enter-key-when-quickly-backspacing-on-android/9514
1 parent b7bf9d5 commit 90d7ec6

File tree

2 files changed

+11
-7
lines changed

2 files changed

+11
-7
lines changed

src/domchange.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ function applyDefaultInsert(view: EditorView, change: {from: number, to: number,
246246
return startState.update(tr, {userEvent, scrollIntoView: true})
247247
}
248248

249-
function findDiff(a: string, b: string, preferredPos: number, preferredSide: string | null)
250-
: {from: number, toA: number, toB: number} | null {
249+
export function findDiff(a: string, b: string, preferredPos: number, preferredSide: string | null):
250+
{from: number, toA: number, toB: number} | null {
251251
let minLen = Math.min(a.length, b.length)
252252
let from = 0
253253
while (from < minLen && a.charCodeAt(from) == b.charCodeAt(from)) from++

src/domobserver.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {ContentView, ViewFlag} from "./contentview"
33
import {EditorView} from "./editorview"
44
import {editable, ViewUpdate, setEditContextFormatting, MeasureRequest} from "./extension"
55
import {hasSelection, getSelection, DOMSelectionState, isEquivalentPosition, dispatchKey, atElementStart} from "./dom"
6-
import {DOMChange, applyDOMChange, applyDOMChangeInner} from "./domchange"
6+
import {DOMChange, applyDOMChange, applyDOMChangeInner, findDiff} from "./domchange"
77
import type {EditContext} from "./editcontext"
88
import {Decoration} from "./decoration"
99
import {Text, EditorSelection, EditorState} from "@codemirror/state"
@@ -557,18 +557,22 @@ class EditContextManager {
557557
let from = this.toEditorPos(e.updateRangeStart), to = this.toEditorPos(e.updateRangeEnd)
558558
if (view.inputState.composing >= 0 && !this.composing)
559559
this.composing = {contextBase: e.updateRangeStart, editorBase: from, drifted: false}
560-
let change = {from, to, insert: Text.of(e.text.split("\n"))}
560+
let deletes = to - from > e.text.length
561561
// If the window doesn't include the anchor, assume changes
562562
// adjacent to a side go up to the anchor.
563-
if (change.from == this.from && anchor < this.from) change.from = anchor
564-
else if (change.to == this.to && anchor > this.to) change.to = anchor
563+
if (from == this.from && anchor < this.from) from = anchor
564+
else if (to == this.to && anchor > this.to) to = anchor
565565

566+
let diff = findDiff(view.state.sliceDoc(from, to), e.text, (deletes ? main.from : main.to) - from, deletes ? "end" : null)
566567
// Edit contexts sometimes fire empty changes
567-
if (change.from == change.to && !change.insert.length) {
568+
if (!diff) {
568569
let newSel = EditorSelection.single(this.toEditorPos(e.selectionStart), this.toEditorPos(e.selectionEnd))
569570
if (!newSel.main.eq(main)) view.dispatch({selection: newSel, userEvent: "select"})
570571
return
571572
}
573+
574+
let change = {from: diff.from + from, to: diff.toA + from,
575+
insert: Text.of(e.text.slice(diff.from, diff.toB).split("\n"))}
572576
if ((browser.mac || browser.android) && change.from == head - 1 &&
573577
/^\. ?$/.test(e.text) && view.contentDOM.getAttribute("autocorrect") == "off")
574578
change = {from, to, insert: Text.of([e.text.replace(".", " ")])}

0 commit comments

Comments
 (0)