Skip to content

Commit d5df151

Browse files
committed
Improve handling of text input in atomic ranges
FIX: Move the cursor out of atomic ranges when text input happens. See https://discuss.codemirror.net/t/cursor-trapped-in-atomic-range/9512
1 parent ac872ce commit d5df151

File tree

1 file changed

+21
-11
lines changed

1 file changed

+21
-11
lines changed

src/domchange.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import browser from "./browser"
55
import {DOMReader, DOMPoint, LineBreakPlaceholder} from "./domreader"
66
import {findCompositionNode} from "./docview"
77
import {EditorSelection, Text, Transaction, TransactionSpec} from "@codemirror/state"
8-
import {skipAtomsForSelection} from "./cursor"
8+
import {skipAtomsForSelection, skipAtomicRanges} from "./cursor"
99

1010
export class DOMChange {
1111
bounds: {
@@ -175,10 +175,20 @@ export function applyDOMChangeInner(
175175

176176
function applyDefaultInsert(view: EditorView, change: {from: number, to: number, insert: Text},
177177
newSel: EditorSelection | null): Transaction {
178-
let tr: TransactionSpec, startState = view.state, sel = startState.selection.main
179-
if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
180-
(!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
181-
view.inputState.composing < 0) {
178+
let tr: TransactionSpec, startState = view.state, sel = startState.selection.main, inAtomic = -1
179+
if (change.from == change.to && change.from < sel.from || change.from > sel.to) {
180+
let side: -1 | 1 = change.from < sel.from ? -1 : 1, pos = side < 0 ? sel.from : sel.to
181+
let moved = skipAtomicRanges(startState.facet(atomicRanges).map(f => f(view)), pos, side)
182+
if (change.from == moved) inAtomic = moved
183+
}
184+
if (inAtomic > -1) {
185+
tr = {
186+
changes: change,
187+
selection: EditorSelection.cursor(change.from + change.insert.length, -1)
188+
}
189+
} else if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
190+
(!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
191+
view.inputState.composing < 0) {
182192
let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : ""
183193
let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : ""
184194
tr = startState.replaceSelection(view.state.toText(
@@ -188,7 +198,7 @@ function applyDefaultInsert(view: EditorView, change: {from: number, to: number,
188198
let mainSel = newSel && newSel.main.to <= changes.newLength ? newSel.main : undefined
189199
// Try to apply a composition change to all cursors
190200
if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
191-
change.to <= sel.to && change.to >= sel.to - 10) {
201+
change.to <= sel.to && change.to >= sel.to - 10) {
192202
let replaced = view.state.sliceDoc(change.from, change.to)
193203
let compositionRange: {from: number, to: number}, composition = newSel && findCompositionNode(view, newSel.main.head)
194204
if (composition) {
@@ -203,11 +213,11 @@ function applyDefaultInsert(view: EditorView, change: {from: number, to: number,
203213
return {changes, range: mainSel || range.map(changes)}
204214
let to = range.to - offset, from = to - replaced.length
205215
if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced ||
206-
// Unfortunately, there's no way to make multiple
207-
// changes in the same node work without aborting
208-
// composition, so cursors in the composition range are
209-
// ignored.
210-
range.to >= compositionRange.from && range.from <= compositionRange.to)
216+
// Unfortunately, there's no way to make multiple
217+
// changes in the same node work without aborting
218+
// composition, so cursors in the composition range are
219+
// ignored.
220+
range.to >= compositionRange.from && range.from <= compositionRange.to)
211221
return {range}
212222
let rangeChanges = startState.changes({from, to, insert: change!.insert}), selOff = range.to - sel.to
213223
return {

0 commit comments

Comments
 (0)