/** * Wraps the current selection in CodeMirror with the given prefix and suffix. */ export function wrapSelection(view: any, prefix: string, suffix: string = prefix) { if (!view || view.updateInProgress) return; const { state } = view; view.dispatch(state.changeByRange((range: any) => { return { changes: [ {from: range.from, insert: prefix}, {from: range.to, insert: suffix} ], range: range.constructor.range(range.from + prefix.length, range.to + prefix.length) }; })); view.focus(); } /** * Inserts a prefix at the start of each line in the selection (e.g., for lists or blockquotes). */ export function toggleLinePrefix(view: any, prefix: string) { if (!view || view.updateInProgress) return; const { state } = view; view.dispatch(state.changeByRange((range: any) => { const lines = []; for (let pos = range.from; pos <= range.to; ) { const line = state.doc.lineAt(pos); lines.push(line); pos = line.to + 1; } const isAlreadyPrefixed = lines.every(l => l.text.startsWith(prefix)); const lineChanges = lines.map(l => { if (isAlreadyPrefixed) { return { from: l.from, to: l.from + prefix.length, insert: '' }; } else { return { from: l.from, insert: prefix }; } }); const newFrom = range.from + (isAlreadyPrefixed ? -prefix.length : prefix.length); const newTo = range.to + (isAlreadyPrefixed ? (-prefix.length * lines.length) : (prefix.length * lines.length)); return { changes: lineChanges, range: range.constructor.range(newFrom, Math.max(newFrom, newTo)) }; })); view.focus(); }