Просмотр исходного кода

- added ContextBasedNewLineHandlerExecutor class
- modified MarkdownListHelper and MarkdownTableHelper according to ContextBasedNewLineHandlerExecutor

Ryu Sato 8 лет назад
Родитель
Сommit
bb651eff3c

+ 29 - 0
resource/js/components/PageEditor/ContextBasedNewLineHandlerExecutor.js

@@ -0,0 +1,29 @@
+import * as codemirror from 'codemirror';
+
+import markdownListHelper from './MarkdownListHelper';
+import markdownTableHelper from './MarkdownTableHelper';
+
+/**
+ * Selector for one new line handler
+ */
+class ContextBasedNewLineHandlerExecutor {
+
+  /**
+   * select one handler from helper
+   * @param {any} editor An editor instance of CodeMirror
+   */
+  execNewLineHandler(editor) {
+    let newLineHelpers = [markdownTableHelper, markdownListHelper];
+    const helper = newLineHelpers.find( h => h.isMatchedContext(editor));
+    if (helper) {
+      helper.handleNewLine(editor);
+    } else {
+      codemirror.commands.newlineAndIndent(editor);
+    }
+  }
+}
+
+// singleton pattern
+const instance = new ContextBasedNewLineHandlerExecutor();
+Object.freeze(instance);
+export default instance;

+ 2 - 7
resource/js/components/PageEditor/Editor.js

@@ -34,8 +34,7 @@ require('codemirror/theme/twilight.css');
 import Dropzone from 'react-dropzone';
 
 import pasteHelper from './PasteHelper';
-import markdownListHelper from './MarkdownListHelper';
-import markdownTableHelper from './MarkdownTableHelper';
+import contextBasedNewLineHandlerExecutor from './ContextBasedNewLineHandlerExecutor';
 import emojiAutoCompleteHelper from './EmojiAutoCompleteHelper';
 
 export default class Editor extends React.Component {
@@ -344,11 +343,7 @@ export default class Editor extends React.Component {
               highlightFormatting: true,
               // continuelist, indentlist
               extraKeys: {
-                "Enter": function(editor) {
-                  // [TODO] encapsulation all helper in InsertNewLineHelper(TBD)
-                  markdownListHelper.newlineAndIndentContinueMarkdownList(editor);
-                  markdownTableHelper.newlineAndIndentContinueMarkdownList(editor);
-                },
+                "Enter": contextBasedNewLineHandlerExecutor.execNewLineHandler,
                 "Tab": "indentMore",
                 "Shift-Tab": "indentLess",
                 "Ctrl-Q": (cm) => { cm.foldCode(cm.getCursor()) },

+ 34 - 1
resource/js/components/PageEditor/MarkdownListHelper.js

@@ -7,6 +7,7 @@ class MarkdownListHelper {
     // https://regex101.com/r/7BN2fR/5
     this.indentAndMarkRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/;
     this.indentAndMarkOnlyRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/;
+    this.indentAndUnorderedMarkRE = /[*+-]\s/;
 
     this.newlineAndIndentContinueMarkdownList = this.newlineAndIndentContinueMarkdownList.bind(this);
     this.pasteText = this.pasteText.bind(this);
@@ -17,20 +18,52 @@ class MarkdownListHelper {
     this.getStrToEol = this.getStrToEol.bind(this);
   }
 
+  /**
+   * return whether context is matched by list
+   * @param {any} editor An editor instance of CodeMirror
+   */
+  isMatchedContext(editor) {
+    console.log('MarkdownListHelper.isMatchedContext');
+    // get strings from BOL(beginning of line) to current position
+    const strFromBol = this.getStrFromBol(editor);
+    const strToEol = this.getStrToEol(editor);
+    console.log('strToEol: ' + strToEol);
+    console.log('strFromBol: ' + strFromBol);
+    console.log('will return ' + (this.indentAndMarkRE.test(strToEol)
+                                 || this.indentAndMarkRE.test(strFromBol)
+                                 || this.indentAndMarkOnlyRE.test(strFromBol)
+                                 || this.indentAndUnorderedMarkRE.test(strFromBol) ? 'true' : 'false'));
+    return this.indentAndMarkRE.test(strToEol)
+           || this.indentAndMarkRE.test(strFromBol)
+           || this.indentAndMarkOnlyRE.test(strFromBol)
+           || this.indentAndUnorderedMarkRE.test(strFromBol);
+  }
+
+  /**
+   * handle new line
+   * @param {any} editor An editor instance of CodeMirror
+   */
+  handleNewLine(editor) {
+    console.log('MarkdownListHelper.handleNewLine');
+    this.newlineAndIndentContinueMarkdownList(editor);
+  }
+
   /**
    * wrap codemirror.commands.newlineAndIndentContinueMarkdownList
    * @param {any} editor An editor instance of CodeMirror
    */
   newlineAndIndentContinueMarkdownList(editor) {
+    console.log('MarkdownListHelper.newlineAndIndentContinueMarkdownList');
     // get strings from current position to EOL(end of line) before break the line
     const strToEol = this.getStrToEol(editor);
-
     if (this.indentAndMarkRE.test(strToEol)) {
+      console.log('MarkdownListHelper.newlineAndIndentContinueMarkdownList: abort auto indent');
       codemirror.commands.newlineAndIndent(editor);
       // replace the line with strToEol (abort auto indent)
       editor.getDoc().replaceRange(strToEol, this.getBol(editor), this.getEol(editor));
     }
     else {
+      console.log('MarkdownListHelper.newlineAndIndentContinueMarkdownList: will auto indent');
       codemirror.commands.newlineAndIndentContinueMarkdownList(editor);
     }
   }

+ 55 - 83
resource/js/components/PageEditor/MarkdownTableHelper.js

@@ -3,116 +3,72 @@ import * as codemirror from 'codemirror';
 class MarkdownTableHelper {
 
   constructor() {
-    // 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*)$/;
+    // https://stackoverflow.com/questions/9837935/regex-for-markdown-table-syntax
+    // https://regex101.com/r/7BN2fR/6
+    this.tableTitleAndHeaderAndBodyRE = /\|(?:([^\r\n\|]*)\|)+\r?\n\|(?:(\:?-+\:?)\|)+\r?\n(\|(?:([^\r\n\|]*)\|)+\r?\n)+/;
 
-    this.newlineAndIndentContinueMarkdownList = this.newlineAndIndentContinueMarkdownList.bind(this);
+    this.isMatchedContext = this.isMatchedContext.bind(this);
+    this.handleNewLine = this.handleNewLine.bind(this);
+
+    this.newlineAndIndentContinueMarkdownTable = this.newlineAndIndentContinueMarkdownTable.bind(this);
     this.pasteText = this.pasteText.bind(this);
 
     this.getBot = this.getBot.bind(this);
     this.getEot = this.getEot.bind(this);
+    this.getBol = this.getBol.bind(this);
     this.getStrFromBot = this.getStrFromBot.bind(this);
+    this.getStrFromBol = this.getStrFromBol.bind(this);
   }
 
   /**
-   * wrap codemirror.commands.newlineAndIndentContinueMarkdownList
+   * return whether context is matched by table
    * @param {any} editor An editor instance of CodeMirror
    */
-  newlineAndIndentContinueMarkdownList(editor) {
-    console.log('MarkdownTableHelper.newlineAndIndentContinueMarkdownList');
-    // get lines all of table from current position to begin of table
-    const strTableLines = this.getStrFromBot(editor);
-
-    if (strTableLines.length > 0) {
-      codemirror.commands.newlineAndIndent(editor);
-      // [TODO] Format table lines
-      strTableLinesFormated = strTableLines;
-      // replace the lines to strFormatedTableLines
-      editor.getDoc().replaceRange(strTableLinesFormated, this.getBot(editor), this.getEot(editor));
-    }
-    else {
-      codemirror.commands.newlineAndIndentContinueMarkdownList(editor);
-    }
+  isMatchedContext(editor) {
+    console.log('MarkdownTableHelper.isMatchedContext');
+    // get strings from BOL(beginning of line) to current position
+    const strFromBot = this.getStrFromBot(editor);
+    console.log('strFromBol: ' + strFromBot);
+    console.log('will return ' + (this.tableTitleAndHeaderAndBodyRE.test(strFromBot) ? 'true' : 'false'));
+    return this.tableTitleAndHeaderAndBodyRE.test(strFromBot);
   }
 
   /**
-   * paste text
+   * handle new line
    * @param {any} editor An editor instance of CodeMirror
-   * @param {any} event
-   * @param {string} text
    */
-  pasteText(editor, event, text) {
-    // [TODO] replace to formated table markdown
+  handleNewLine(editor) {
+    console.log('MarkdownTableHelper.handleNewLine');
+    this.newlineAndIndentContinueMarkdownTable(editor);
   }
 
   /**
-   * return adjusted pasted data by indentAndMark
-   *
-   * @param {string} indentAndMark
-   * @param {string} text
-   * @returns adjusted pasted data
-   *      returns null when adjustment is not necessary
+   * insert new line with auto shaping format of Markdown table
+   * @param {any} editor An editor instance of CodeMirror
    */
-  adjustPastedData(indentAndMark, text) {
-    let adjusted = null;
-
-    // list data (starts with indent and mark)
-    if (text.match(this.indentAndMarkRE)) {
-      const indent = indentAndMark.match(this.indentAndMarkRE)[1];
-
-      // splice to an array of line
-      const lines = text.match(/[^\r\n]+/g);
-      // indent
-      const replacedLines = lines.map((line) => {
-        return indent + line;
-      })
-
-      adjusted = replacedLines.join('\n');
-    }
-    // listful data
-    else if (this.isListfulData(text)) {
-      // do nothing (return null)
-    }
-    // not listful data
-    else {
-      // append `indentAndMark` at the beginning of all lines (except the first line)
-      const replacedText = text.replace(/(\r\n|\r|\n)/g, "$1" + indentAndMark);
-      // append `indentAndMark` to the first line
-      adjusted = indentAndMark + replacedText;
+  newlineAndIndentContinueMarkdownTable(editor) {
+    console.log('MarkdownTableHelper.newlineAndIndentContinueMarkdownTable');
+    if (!this.isMatchedContext(editor)) {
+      return;
     }
 
-    return adjusted;
+    // get lines all of table from current position to beginning of table
+    const strTableLines = this.getStrFromBot(editor);
+    // [TODO] Format table lines
+    strTableLinesFormated = strTableLines;
+    // replace the lines to strFormatedTableLines
+    editor.getDoc().replaceRange(strTableLinesFormated, this.getBot(editor), this.getEot(editor));
+    codemirror.commands.newline(editor);
   }
 
   /**
-   * evaluate whether `text` is list like data or not
+   * paste text
+   * @param {any} editor An editor instance of CodeMirror
+   * @param {any} event
    * @param {string} text
    */
-  isListfulData(text) {
-    // return false if includes at least one blank line
-    // see https://stackoverflow.com/a/16369725
-    if (text.match(/^\s*[\r\n]/m) != null) {
-      return false;
-    }
-
-    const lines = text.match(/[^\r\n]+/g);
-    // count lines that starts with indent and mark
-    let isListful = false;
-    let count = 0;
-    lines.forEach((line) => {
-      if (line.match(this.indentAndMarkRE)) {
-        count++;
-      }
-      // ensure to be true if it is 50% or more
-      if (count >= lines.length / 2) {
-        isListful = true;
-        return;
-      }
-    });
-
-    return isListful;
+  pasteText(editor, event, text) {
+    // [TODO] replace to formated table markdown
   }
 
   /**
@@ -134,6 +90,14 @@ class MarkdownTableHelper {
     return { line: curPos.line, ch: lineLength };
   }
 
+  /**
+   * return the postion of the BOL(beginning of line)
+   */
+  getBol(editor) {
+    const curPos = editor.getCursor();
+    return { line: curPos.line, ch: 0 };
+  }
+
   /**
    * return strings from current position to BOL(beginning of table)
    */
@@ -141,6 +105,14 @@ class MarkdownTableHelper {
     const curPos = editor.getCursor();
     return editor.getDoc().getRange(this.getBot(editor), curPos);
   }
+
+  /**
+   * return strings from BOL(beginning of line) to current position
+   */
+  getStrFromBol(editor) {
+    const curPos = editor.getCursor();
+    return editor.getDoc().getRange(this.getBol(editor), curPos);
+  }
 }
 
 // singleton pattern