1 import {MarkdownEditorInput, MarkdownEditorInputSelection} from "./interface";
2 import {EditorView} from "@codemirror/view";
3 import {ChangeSpec, TransactionSpec} from "@codemirror/state";
6 export class CodemirrorInput implements MarkdownEditorInput {
7 protected cm: EditorView;
9 constructor(cm: EditorView) {
18 if (!this.cm.hasFocus) {
23 getSelection(): MarkdownEditorInputSelection {
24 return this.cm.state.selection.main;
27 getSelectionText(selection?: MarkdownEditorInputSelection): string {
28 selection = selection || this.getSelection();
29 return this.cm.state.sliceDoc(selection.from, selection.to);
32 setSelection(selection: MarkdownEditorInputSelection, scrollIntoView: boolean = false) {
34 selection: {anchor: selection.from, head: selection.to},
40 return this.cm.state.doc.toString();
43 getTextAboveView(): string {
44 const blockInfo = this.cm.lineBlockAtHeight(this.cm.scrollDOM.scrollTop);
45 return this.cm.state.sliceDoc(0, blockInfo.from);
48 setText(text: string, selection?: MarkdownEditorInputSelection) {
49 selection = selection || this.getSelection();
50 const newDoc = this.cm.state.toText(text);
51 const newSelectFrom = Math.min(selection.from, newDoc.length);
52 const scrollTop = this.cm.scrollDOM.scrollTop;
53 this.dispatchChange(0, this.cm.state.doc.length, text, newSelectFrom);
55 window.requestAnimationFrame(() => {
56 this.cm.scrollDOM.scrollTop = scrollTop;
60 spliceText(from: number, to: number, newText: string, selection: Partial<MarkdownEditorInputSelection> | null = null) {
61 const end = (selection?.from === selection?.to) ? null : selection?.to;
62 this.dispatchChange(from, to, newText, selection?.from, end)
65 appendText(text: string) {
66 const end = this.cm.state.doc.length;
67 this.dispatchChange(end, end, `\n${text}`);
70 getLineText(lineIndex: number = -1): string {
71 const index = lineIndex > -1 ? lineIndex : this.getSelection().from;
72 return this.cm.state.doc.lineAt(index).text;
75 eventToPosition(event: MouseEvent): MarkdownEditorInputSelection {
76 const cursorPos = this.cm.posAtCoords({x: event.screenX, y: event.screenY}, false);
77 return {from: cursorPos, to: cursorPos};
80 getLineRangeFromPosition(position: number): MarkdownEditorInputSelection {
81 const line = this.cm.state.doc.lineAt(position);
82 return {from: line.from, to: line.to};
85 searchForLineContaining(text: string): MarkdownEditorInputSelection | null {
86 const docText = this.cm.state.doc;
88 let scrollToLine = -1;
89 for (const line of docText.iterLines()) {
90 if (line.includes(text)) {
91 scrollToLine = lineCount;
97 if (scrollToLine === -1) {
101 const line = docText.line(scrollToLine);
102 return {from: line.from, to: line.to};
106 * Dispatch changes to the editor.
108 protected dispatchChange(from: number, to: number|null = null, text: string|null = null, selectFrom: number|null = null, selectTo: number|null = null): void {
109 const change: ChangeSpec = {from};
114 change.insert = text;
116 const tr: TransactionSpec = {changes: change};
119 tr.selection = {anchor: selectFrom};
121 tr.selection.head = selectTo;
125 this.cm.dispatch(tr);