فهرست منبع

able to insert list number as one

soumaeda 2 سال پیش
والد
کامیت
4900176267

+ 61 - 17
apps/app/src/components/PageEditor/MarkdownListUtil.js

@@ -7,31 +7,75 @@ class MarkdownListUtil {
     // https://github.com/codemirror/CodeMirror/blob/c7853a989c77bb9f520c9c530cbe1497856e96fc/addon/edit/continuelist.js#L14
     // https://regex101.com/r/7BN2fR/5
     this.indentAndMarkRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/;
-    this.indentAndMarkOnlyRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/;
+    this.indentAndMarkOnlyRE = /^(\d+)[.)](\s*)$/;
 
     this.newlineAndIndentContinueMarkdownList = this.newlineAndIndentContinueMarkdownList.bind(this);
     this.pasteText = this.pasteText.bind(this);
   }
 
+  insertText(editor, text) {
+    editor.dispatch({
+      changes: {
+        insert: text,
+      },
+    });
+  }
+
   /**
-   * Self Implementation with AbstractEditor interface
-   * @param {AbstractEditor} editor An instance of AbstractEditor
+   * return the postion of the BOL(beginning of line)
    */
-  newlineAndIndentContinueMarkdownList(editor) {
-    const strFromBol = editor.getStrFromBol();
+  getBol(editor) {
+    const curPos = editor.state.selection.main.head;
+    return editor.state.doc.lineAt(curPos).from;
+  }
 
-    if (this.indentAndMarkOnlyRE.test(strFromBol)) {
-      // clear current line and end list
-      editor.replaceBolToCurrentPos('\n');
-    }
-    else if (this.indentAndMarkRE.test(strFromBol)) {
-      // continue list
-      const indentAndMark = strFromBol.match(this.indentAndMarkRE)[0];
-      editor.insertText(`\n${indentAndMark}`);
+  getStrFromBol(editor) {
+    const curPos = editor.state.selection.main.head;
+    return editor.state.sliceDoc(this.getBol(), curPos);
+  }
+
+  /**
+   * select the upper position of pos1 and pos2
+   * @param {{line: number, ch: number}} pos1
+   * @param {{line: number, ch: number}} pos2
+   */
+  selectUpperPos(editor, pos1, pos2) {
+    // if both is in same line
+    if (editor.state.doc.lineAt(pos1) === editor.state.doc.lineAt(pos2)) {
+      return (editor.state.doc.lineAt(pos1).from < editor.state.doc.lineAt(pos1).to) ? pos1 : pos2;
     }
-    else {
-      editor.insertLinebreak();
+    return (editor.state.doc.lineAt(pos1) < editor.state.doc.lineAt(pos2)) ? pos1 : pos2;
+  }
+
+  /**
+   * select the lower position of pos1 and pos2
+   * @param {{line: number, ch: number}} pos1
+   * @param {{line: number, ch: number}} pos2
+   */
+  selectLowerPos(editor, pos1, pos2) {
+    // if both is in same line
+    if (editor.state.doc.lineAt(pos1).number === editor.state.doc.lineAt(pos2).number) {
+      return (editor.state.doc.lineAt(pos1).from < editor.state.doc.lineAt(pos1).to) ? pos2 : pos1;
     }
+    return (editor.state.doc.lineAt(pos1) < editor.state.doc.lineAt(pos2)) ? pos2 : pos1;
+  }
+
+  replaceBolToCurrentPos(editor, text) {
+    const curPos = editor.state.selection.main.head;
+    const pos = this.selectLowerPos(editor.state.doc.lineAt(curPos).from, editor.state.doc.lineAt(curPos).to);
+    editor.dispatch({
+      changes: {
+        from: this.getBol(editor),
+        to: pos,
+        insert: text,
+      },
+    });
+  }
+
+  getStrFromBolToSelectedUpperPos(editor) {
+    const curPos = editor.state.selection.main.head;
+    const pos = this.selectUpperPos(editor.state.doc.lineAt(curPos).from, editor.state.doc.lineAt(curPos).to);
+    return editor.state.sliceDoc(this.getBol(), pos);
   }
 
   /**
@@ -42,7 +86,7 @@ class MarkdownListUtil {
    */
   pasteText(editor, event, text) {
     // get strings from BOL(beginning of line) to current position
-    const strFromBol = editor.getStrFromBolToSelectedUpperPos();
+    const strFromBol = this.getStrFromBolToSelectedUpperPos();
 
     // when match indentAndMarkOnlyRE
     // (this means the current position is the beginning of the list item)
@@ -52,7 +96,7 @@ class MarkdownListUtil {
       // replace
       if (adjusted != null) {
         event.preventDefault();
-        editor.replaceBolToCurrentPos(adjusted);
+        this.replaceBolToCurrentPos(editor, adjusted);
       }
     }
   }

+ 60 - 0
packages/editor/src/components/CodeMirrorEditor/CodeMirrorEditor.tsx

@@ -46,6 +46,66 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
   }, [onChange]);
   const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(editorKey, containerRef.current, cmProps);
 
+  /**
+   * return the postion of the BOL(beginning of line)
+   */
+  const getBol = (editor: EditorView) => {
+    const curPos = editor.state.selection.main.head;
+    return editor.state.doc.lineAt(curPos).from;
+  };
+
+  const getStrFromBol = (editor: EditorView) => {
+    const curPos = editor.state.selection.main.head;
+    return editor.state.sliceDoc(getBol(editor), curPos);
+  };
+
+  const selectLowerPos = (editor: EditorView, pos1: number, pos2: number) => {
+    // if both is in same line
+    if (editor.state.doc.lineAt(pos1).number === editor.state.doc.lineAt(pos2).number) {
+      return (editor.state.doc.lineAt(pos1).from < editor.state.doc.lineAt(pos1).to) ? pos2 : pos1;
+    }
+    return (editor.state.doc.lineAt(pos1) < editor.state.doc.lineAt(pos2)) ? pos2 : pos1;
+  };
+
+  const replaceBolToCurrentPos = (editor: EditorView, text: string) => {
+    const curPos = editor.state.selection.main.head;
+    const pos = selectLowerPos(editor, editor.state.doc.lineAt(curPos).from, editor.state.doc.lineAt(curPos).to);
+    editor.dispatch({
+      changes: {
+        from: getBol(editor),
+        to: pos,
+        insert: text,
+      },
+    });
+  };
+
+  const indentAndMarkOnlyRE = /^(\d+)[.)](\s*)$/;
+
+  // eslint-disable-next-line react-hooks/exhaustive-deps
+  const newlineAndIndentContinueMarkdownList = (editor: EditorView) => {
+    const strFromBol = getStrFromBol(editor);
+
+    if (indentAndMarkOnlyRE.test(strFromBol)) {
+      replaceBolToCurrentPos(editor, '1. ');
+    }
+  };
+
+  useEffect(() => {
+    const handleEnterKey = (event: KeyboardEvent) => {
+      if (event.key === 'Enter') {
+        event.preventDefault();
+        const editor = codeMirrorEditor?.view;
+        newlineAndIndentContinueMarkdownList(editor);
+      }
+    };
+
+    codeMirrorEditor?.view?.dom.addEventListener('keydown', handleEnterKey);
+
+    return () => {
+      codeMirrorEditor?.view?.dom.removeEventListener('keydown', handleEnterKey);
+    };
+  }, [codeMirrorEditor, newlineAndIndentContinueMarkdownList]);
+
   useEffect(() => {
     if (indentSize == null) {
       return;