diff --git a/packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.test.ts b/packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.test.ts index 229d1cdaad..0796774fc6 100644 --- a/packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.test.ts +++ b/packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.test.ts @@ -185,4 +185,29 @@ describe('CodeMirror5Emulation', () => { expect(lastThis).toBe(codeMirror); }); + + it('.markText should support specifying ranges outside the document', () => { + const codeMirror = makeCodeMirrorEmulation('Test...'); + + const testClassName = 'out-of-range-test-mark'; + codeMirror.markText( + // In range + { line: 0, ch: 4 }, + // Out of range + { line: 0, ch: 1002 }, + { className: testClassName }, + ); + + const dom = codeMirror.editor.dom; + expect(dom.querySelectorAll(`.${testClassName}`)).toHaveLength(1); + }); + + it('.markText should throw when given non-integer boundaries', () => { + const codeMirror = makeCodeMirrorEmulation('Test...'); + + expect(() => codeMirror.markText( + { line: 0, ch: 4.2 }, + { line: 0, ch: 5 }, + )).toThrow(/is not an integer/i); + }); }); diff --git a/packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.ts b/packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.ts index 7e57e35330..16a2c8c818 100644 --- a/packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.ts +++ b/packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.ts @@ -37,6 +37,10 @@ interface DocumentPositionRange { to: DocumentPosition; } +const clampPositionToDoc = (doc: Text, pos: number) => { + return Math.min(Math.max(0, pos), doc.length); +}; + const documentPositionFromPos = (doc: Text, pos: number): DocumentPosition => { const line = doc.lineAt(pos); return { @@ -48,7 +52,12 @@ const documentPositionFromPos = (doc: Text, pos: number): DocumentPosition => { const posFromDocumentPosition = (doc: Text, pos: DocumentPosition) => { const line = doc.line(pos.line + 1); - return line.from + pos.ch; + const result = line.from + pos.ch; + + if (!Number.isInteger(result)) { + throw new Error(`Document position ${result} (${pos.line}:${pos.ch}) is not an integer`); + } + return result; }; export default class CodeMirror5Emulation extends BaseCodeMirror5Emulation { @@ -420,8 +429,8 @@ export default class CodeMirror5Emulation extends BaseCodeMirror5Emulation { const doc = this.editor.state.doc; return this._decorator.markText( - posFromDocumentPosition(doc, from), - posFromDocumentPosition(doc, to), + clampPositionToDoc(doc, posFromDocumentPosition(doc, from)), + clampPositionToDoc(doc, posFromDocumentPosition(doc, to)), options, ); }