]> BookStack Code Mirror - bookstack/blobdiff - resources/js/markdown/inputs/codemirror.ts
Deps: Updated PHP composer dependancy versions, fixed test namespaces
[bookstack] / resources / js / markdown / inputs / codemirror.ts
index 8fc6b686374a75b972a5e52f18740ab10624d096..827068238e0e70c400525a55a484966a05a95cb5 100644 (file)
 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<MarkdownEditorInputSelection> | 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.
      */