| 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);
|
|
|
|
|
| if (range.from === range.to) {
|
| cb(range.from, undefined, changes, line);
|
| }
|
|
|
| else if (range.from < range.to && range.to <= line.to) {
|
| cb(range.from, range.to, changes, line);
|
| } else {
|
| let atLine = -1;
|
|
|
|
|
| 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)),
|
| };
|
| });
|
| }
|
|
|