Why Range
in StateField
does not move after RangeSet.map(tr.changes)
when symbols are inserted right before the range, e.g.:
- Text
“Lorem ipsum|” - Create range [1, 3]
“Lorem ipsum|” - Move selector to [1]
“L|orem ipsum” - Type symbols
“LAA|orem ipsum” - Range would map to [1, 6] not to [3, 5]
Code is below
import * as cmView from '@codemirror/view'
import * as cmRangeSet from '@codemirror/rangeset'
import * as cmState from "@codemirror/state"
/* CONSTs */
const TAG_PREFIX = '/* A:'
const TAG_POSTFIX = '*/'
const TAG = TAG_PREFIX + ' ' + TAG_POSTFIX
/* EFFECTs and FIELDs */
const effectStart = cmState.StateEffect.define()
const effectAddRange = cmState.StateEffect.define()
const fieldBlocks = cmState.StateField.define({
create: () => cmRangeSet.RangeSet.empty,
update: (value, tr) => {
value = value.map(tr.changes)
for (let iter = value.iter(); iter.value !== null; iter.next())
console.log('range:\t', iter.from, ' - ', iter.to)
let newBlock = tr.effects.find(e => e.is(effectAddRange))
if (newBlock) {
value = value.update({
add: [new cmRangeSet.Range(
newBlock.value.from,
newBlock.value.to,
{ type: null, complete: false, params: [] },
)]
})
}
return value
},
})
function transactionFilter(tr) {
let rangesBlocks = tr.startState.field(fieldBlocks)
// Add new block with comment
const newBlocksEffects = tr.effects.filter(i =>
i.is(effectStart) &&
(rangesBlocks.update({ filter: (from, to) => from <= i.value.head && i.value.head <= to })).size === 0
)
const newBlocksTr = newBlocksEffects.map((
{ value }) => ({
changes: { from: value.head, insert: TAG },
effects: effectAddRange.of({ from: value.head, to: value.head + TAG.length }),
selection: cmState.EditorSelection.cursor(value.head + TAG_PREFIX.length + 1)
})
)
return [tr, ...newBlocksTr]
}
/* COMMANDs */
function commandStartAssist(view) {
const range = view.state.selection.main
if (view.state.readOnly || !range.empty) return false
view.dispatch({ effects: effectStart.of({ head: range.head }) })
return true
}
/* KEYMAPs */
const keymap = cmView.keymap.of([{
key: 'Ctrl-a',
preventDefault: true,
run: commandStartAssist
}])
export const assist = [
fieldBlocks,
keymap,
cmState.EditorState.transactionFilter.of(transactionFilter)
]