Files
yaak/src-web/components/core/Editor/singleLine.ts
Gregory Schier f745435d26 Add comment
2025-07-10 11:47:26 -07:00

48 lines
1.6 KiB
TypeScript

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