| 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)), |
| }; |
| }); |
| } |
|
|