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