import type { Extension, TransactionSpec } from '@codemirror/state'; import { EditorSelection, EditorState, Transaction } from '@codemirror/state'; /** * A CodeMirror extension that forces single-line input by stripping * all newline characters from user input, including pasted content. * * This extension uses a transaction filter to intercept user input, * removes any newline characters, and adjusts the selection to the end * of the inserted text. * * IME composition events are ignored to preserve proper input behavior * for non-Latin languages. * * @returns A CodeMirror extension that enforces single-line editing. */ export function singleLineExtensions(): Extension { return EditorState.transactionFilter.of( (tr: Transaction): TransactionSpec | readonly TransactionSpec[] => { if (!tr.isUserEvent('input') || tr.isUserEvent('input.type.compose')) return tr; const changes: { from: number; to: number; insert: string }[] = []; tr.changes.iterChanges((_fromA, toA, fromB, _toB, inserted) => { let insert = ''; for (const line of inserted.iterLines()) { insert += line.replace(/\n/g, ''); } if (insert !== inserted.toString()) { changes.push({ from: fromB, to: toA, insert }); } }); const lastChange = changes[changes.length - 1]; if (lastChange == null) return tr; const selection = EditorSelection.cursor(lastChange.from + lastChange.insert.length); return { changes, selection, userEvent: tr.annotation(Transaction.userEvent) ?? undefined, }; }, ); }