Spaces:
Running
Running
import { indentLess } from '@codemirror/commands'; | |
import { indentUnit } from '@codemirror/language'; | |
import { EditorSelection, EditorState, Line, type ChangeSpec } from '@codemirror/state'; | |
import { EditorView, type KeyBinding } from '@codemirror/view'; | |
export const indentKeyBinding: KeyBinding = { | |
key: 'Tab', | |
run: indentMore, | |
shift: indentLess, | |
}; | |
function indentMore({ state, dispatch }: EditorView) { | |
if (state.readOnly) { | |
return false; | |
} | |
dispatch( | |
state.update( | |
changeBySelectedLine(state, (from, to, changes) => { | |
changes.push({ from, to, insert: state.facet(indentUnit) }); | |
}), | |
{ userEvent: 'input.indent' }, | |
), | |
); | |
return true; | |
} | |
function changeBySelectedLine( | |
state: EditorState, | |
cb: (from: number, to: number | undefined, changes: ChangeSpec[], line: Line) => void, | |
) { | |
return state.changeByRange((range) => { | |
const changes: ChangeSpec[] = []; | |
const line = state.doc.lineAt(range.from); | |
// just insert single indent unit at the current cursor position | |
if (range.from === range.to) { | |
cb(range.from, undefined, changes, line); | |
} | |
// handle the case when multiple characters are selected in a single line | |
else if (range.from < range.to && range.to <= line.to) { | |
cb(range.from, range.to, changes, line); | |
} else { | |
let atLine = -1; | |
// handle the case when selection spans multiple lines | |
for (let pos = range.from; pos <= range.to; ) { | |
const line = state.doc.lineAt(pos); | |
if (line.number > atLine && (range.empty || range.to > line.from)) { | |
cb(line.from, undefined, changes, line); | |
atLine = line.number; | |
} | |
pos = line.to + 1; | |
} | |
} | |
const changeSet = state.changes(changes); | |
return { | |
changes, | |
range: EditorSelection.range(changeSet.mapPos(range.anchor, 1), changeSet.mapPos(range.head, 1)), | |
}; | |
}); | |
} | |