MarkdownTableHelper.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import * as codemirror from 'codemirror';
  2. class MarkdownTableHelper {
  3. constructor() {
  4. // https://github.com/codemirror/CodeMirror/blob/c7853a989c77bb9f520c9c530cbe1497856e96fc/addon/edit/continuelist.js#L14
  5. // https://regex101.com/r/7BN2fR/5
  6. this.indentAndMarkRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/;
  7. this.indentAndMarkOnlyRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/;
  8. this.newlineAndIndentContinueMarkdownList = this.newlineAndIndentContinueMarkdownList.bind(this);
  9. this.pasteText = this.pasteText.bind(this);
  10. this.getBot = this.getBot.bind(this);
  11. this.getEot = this.getEot.bind(this);
  12. this.getStrFromBot = this.getStrFromBot.bind(this);
  13. }
  14. /**
  15. * wrap codemirror.commands.newlineAndIndentContinueMarkdownList
  16. * @param {any} editor An editor instance of CodeMirror
  17. */
  18. newlineAndIndentContinueMarkdownList(editor) {
  19. console.log('MarkdownTableHelper.newlineAndIndentContinueMarkdownList');
  20. // get lines all of table from current position to begin of table
  21. const strTableLines = this.getStrFromBot(editor);
  22. if (strTableLines.length > 0) {
  23. codemirror.commands.newlineAndIndent(editor);
  24. // [TODO] Format table lines
  25. strTableLinesFormated = strTableLines;
  26. // replace the lines to strFormatedTableLines
  27. editor.getDoc().replaceRange(strTableLinesFormated, this.getBot(editor), this.getEot(editor));
  28. }
  29. else {
  30. codemirror.commands.newlineAndIndentContinueMarkdownList(editor);
  31. }
  32. }
  33. /**
  34. * paste text
  35. * @param {any} editor An editor instance of CodeMirror
  36. * @param {any} event
  37. * @param {string} text
  38. */
  39. pasteText(editor, event, text) {
  40. // [TODO] replace to formated table markdown
  41. }
  42. /**
  43. * return adjusted pasted data by indentAndMark
  44. *
  45. * @param {string} indentAndMark
  46. * @param {string} text
  47. * @returns adjusted pasted data
  48. * returns null when adjustment is not necessary
  49. */
  50. adjustPastedData(indentAndMark, text) {
  51. let adjusted = null;
  52. // list data (starts with indent and mark)
  53. if (text.match(this.indentAndMarkRE)) {
  54. const indent = indentAndMark.match(this.indentAndMarkRE)[1];
  55. // splice to an array of line
  56. const lines = text.match(/[^\r\n]+/g);
  57. // indent
  58. const replacedLines = lines.map((line) => {
  59. return indent + line;
  60. })
  61. adjusted = replacedLines.join('\n');
  62. }
  63. // listful data
  64. else if (this.isListfulData(text)) {
  65. // do nothing (return null)
  66. }
  67. // not listful data
  68. else {
  69. // append `indentAndMark` at the beginning of all lines (except the first line)
  70. const replacedText = text.replace(/(\r\n|\r|\n)/g, "$1" + indentAndMark);
  71. // append `indentAndMark` to the first line
  72. adjusted = indentAndMark + replacedText;
  73. }
  74. return adjusted;
  75. }
  76. /**
  77. * evaluate whether `text` is list like data or not
  78. * @param {string} text
  79. */
  80. isListfulData(text) {
  81. // return false if includes at least one blank line
  82. // see https://stackoverflow.com/a/16369725
  83. if (text.match(/^\s*[\r\n]/m) != null) {
  84. return false;
  85. }
  86. const lines = text.match(/[^\r\n]+/g);
  87. // count lines that starts with indent and mark
  88. let isListful = false;
  89. let count = 0;
  90. lines.forEach((line) => {
  91. if (line.match(this.indentAndMarkRE)) {
  92. count++;
  93. }
  94. // ensure to be true if it is 50% or more
  95. if (count >= lines.length / 2) {
  96. isListful = true;
  97. return;
  98. }
  99. });
  100. return isListful;
  101. }
  102. /**
  103. * return the postion of the BOT(beginning of table)
  104. */
  105. getBot(editor) {
  106. // [TODO] return the postion of the BOT(beginning of table)
  107. const curPos = editor.getCursor();
  108. return { line: curPos.line, ch: 0 };
  109. }
  110. /**
  111. * return the postion of the EOT(end of table)
  112. */
  113. getEot(editor) {
  114. // [TODO] return the postion of the EOT(end of table)
  115. const curPos = editor.getCursor();
  116. const lineLength = editor.getDoc().getLine(curPos.line).length;
  117. return { line: curPos.line, ch: lineLength };
  118. }
  119. /**
  120. * return strings from current position to BOL(beginning of table)
  121. */
  122. getStrFromBot(editor) {
  123. const curPos = editor.getCursor();
  124. return editor.getDoc().getRange(this.getBot(editor), curPos);
  125. }
  126. }
  127. // singleton pattern
  128. const instance = new MarkdownTableHelper();
  129. Object.freeze(instance);
  130. export default instance;