X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/ec07793cda66d319bf716ef3dcbbe2fe19f0c355..HEAD:/resources/js/markdown/inputs/codemirror.ts diff --git a/resources/js/markdown/inputs/codemirror.ts b/resources/js/markdown/inputs/codemirror.ts index 8fc6b6863..827068238 100644 --- a/resources/js/markdown/inputs/codemirror.ts +++ b/resources/js/markdown/inputs/codemirror.ts @@ -1,99 +1,107 @@ import {MarkdownEditorInput, MarkdownEditorInputSelection} from "./interface"; -import {MarkdownEditor} from "../index.mjs"; import {EditorView} from "@codemirror/view"; -import {ChangeSpec, SelectionRange, TransactionSpec} from "@codemirror/state"; +import {ChangeSpec, TransactionSpec} from "@codemirror/state"; export class CodemirrorInput implements MarkdownEditorInput { - - protected editor: MarkdownEditor; protected cm: EditorView; constructor(cm: EditorView) { this.cm = cm; } + teardown(): void { + this.cm.destroy(); + } + focus(): void { - if (!this.editor.cm.hasFocus) { - this.editor.cm.focus(); + if (!this.cm.hasFocus) { + this.cm.focus(); } } getSelection(): MarkdownEditorInputSelection { - return this.editor.cm.state.selection.main; + return this.cm.state.selection.main; } - getSelectionText(selection: MarkdownEditorInputSelection|null = null): string { + getSelectionText(selection?: MarkdownEditorInputSelection): string { selection = selection || this.getSelection(); - return this.editor.cm.state.sliceDoc(selection.from, selection.to); + return this.cm.state.sliceDoc(selection.from, selection.to); } setSelection(selection: MarkdownEditorInputSelection, scrollIntoView: boolean = false) { - this.editor.cm.dispatch({ + this.cm.dispatch({ selection: {anchor: selection.from, head: selection.to}, scrollIntoView, }); } getText(): string { - return this.editor.cm.state.doc.toString(); + return this.cm.state.doc.toString(); } getTextAboveView(): string { - const blockInfo = this.editor.cm.lineBlockAtHeight(scrollEl.scrollTop); - return this.editor.cm.state.sliceDoc(0, blockInfo.from); + const blockInfo = this.cm.lineBlockAtHeight(this.cm.scrollDOM.scrollTop); + return this.cm.state.sliceDoc(0, blockInfo.from); } - setText(text: string, selection: MarkdownEditorInputSelection | null = null) { + setText(text: string, selection?: MarkdownEditorInputSelection) { selection = selection || this.getSelection(); - const newDoc = this.editor.cm.state.toText(text); + const newDoc = this.cm.state.toText(text); const newSelectFrom = Math.min(selection.from, newDoc.length); - const scrollTop = this.editor.cm.scrollDOM.scrollTop; - this.dispatchChange(0, this.editor.cm.state.doc.length, text, newSelectFrom); + const scrollTop = this.cm.scrollDOM.scrollTop; + this.dispatchChange(0, this.cm.state.doc.length, text, newSelectFrom); this.focus(); window.requestAnimationFrame(() => { - this.editor.cm.scrollDOM.scrollTop = scrollTop; + this.cm.scrollDOM.scrollTop = scrollTop; }); } - spliceText(from: number, to: number, newText: string, selection: MarkdownEditorInputSelection | null = null) { + spliceText(from: number, to: number, newText: string, selection: Partial | null = null) { const end = (selection?.from === selection?.to) ? null : selection?.to; this.dispatchChange(from, to, newText, selection?.from, end) } appendText(text: string) { - const end = this.editor.cm.state.doc.length; + const end = this.cm.state.doc.length; this.dispatchChange(end, end, `\n${text}`); } getLineText(lineIndex: number = -1): string { const index = lineIndex > -1 ? lineIndex : this.getSelection().from; - return this.editor.cm.state.doc.lineAt(index).text; - } - - wrapLine(start: string, end: string) { - const selectionRange = this.getSelection(); - const line = this.editor.cm.state.doc.lineAt(selectionRange.from); - const lineContent = line.text; - let newLineContent; - let lineOffset = 0; - - if (lineContent.startsWith(start) && lineContent.endsWith(end)) { - newLineContent = lineContent.slice(start.length, lineContent.length - end.length); - lineOffset = -(start.length); - } else { - newLineContent = `${start}${lineContent}${end}`; - lineOffset = start.length; - } - - this.dispatchChange(line.from, line.to, newLineContent, selectionRange.from + lineOffset); + return this.cm.state.doc.lineAt(index).text; } - coordsToSelection(x: number, y: number): MarkdownEditorInputSelection { - const cursorPos = this.editor.cm.posAtCoords({x, y}, false); + eventToPosition(event: MouseEvent): MarkdownEditorInputSelection { + const cursorPos = this.cm.posAtCoords({x: event.screenX, y: event.screenY}, false); return {from: cursorPos, to: cursorPos}; } + getLineRangeFromPosition(position: number): MarkdownEditorInputSelection { + const line = this.cm.state.doc.lineAt(position); + return {from: line.from, to: line.to}; + } + + searchForLineContaining(text: string): MarkdownEditorInputSelection | null { + const docText = this.cm.state.doc; + let lineCount = 1; + let scrollToLine = -1; + for (const line of docText.iterLines()) { + if (line.includes(text)) { + scrollToLine = lineCount; + break; + } + lineCount += 1; + } + + if (scrollToLine === -1) { + return null; + } + + const line = docText.line(scrollToLine); + return {from: line.from, to: line.to}; + } + /** * Dispatch changes to the editor. */