MarkdownDrawioUtil.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /**
  2. * Utility for markdown drawio
  3. */
  4. class MarkdownDrawioUtil {
  5. constructor() {
  6. this.lineBeginPartOfDrawioRE = /^```(\s.*)drawio$/;
  7. this.lineEndPartOfDrawioRE = /^```$/;
  8. this.curPos = this.curPos.bind(this);
  9. this.doc = this.doc.bind(this);
  10. }
  11. // get cursor position from editor
  12. curPos(editor) {
  13. return editor.state.selection.main.head;
  14. }
  15. // get doc from editor
  16. doc(editor) {
  17. return editor.state.doc;
  18. }
  19. // get first line number
  20. firstLine() {
  21. return 1;
  22. }
  23. // get list line number
  24. lastLine(editor) {
  25. return this.doc(editor).lines;
  26. }
  27. // get line information
  28. getLine(editor, lineNum) {
  29. return this.doc(editor).line(lineNum);
  30. }
  31. /**
  32. * return the postion of the BOD(beginning of drawio)
  33. * (If the BOD is not found after the cursor or the EOD is found before the BOD, return null)
  34. */
  35. getBod(editor) {
  36. if (this.lineBeginPartOfDrawioRE.test(this.doc(editor).lineAt(this.curPos(editor))).text) {
  37. // get the beginning of the line where the cursor is located
  38. return this.doc(editor).lineAt(this.curPos(editor)).from;
  39. }
  40. let line = this.doc(editor).lineAt(this.curPos(editor)).number - 1;
  41. let isFound = false;
  42. for (; line >= this.firstLine; line--) {
  43. const strLine = this.getLine(editor, line).text;
  44. if (this.lineBeginPartOfDrawioRE.test(strLine)) {
  45. isFound = true;
  46. break;
  47. }
  48. if (this.lineEndPartOfDrawioRE.test(strLine)) {
  49. isFound = false;
  50. break;
  51. }
  52. }
  53. if (!isFound) {
  54. return null;
  55. }
  56. const botLine = Math.max(this.firstLine, line);
  57. return this.getLineText(editor, botLine).from;
  58. }
  59. /**
  60. * return the postion of the EOD(end of drawio)
  61. * (If the EOD is not found after the cursor or the BOD is found before the EOD, return null)
  62. */
  63. getEod(editor) {
  64. if (this.lineEndPartOfDrawioRE.test(this.doc(editor).lineAt(this.curPos(editor))).text) {
  65. // get the end of the line where the cursor is located
  66. return this.doc(editor).lineAt(this.curPos(editor)).to;
  67. }
  68. let line = this.doc(editor).lineAt(this.curPos(editor)).number + 1;
  69. let isFound = false;
  70. for (; line <= this.lastLine(editor); line++) {
  71. const strLine = this.getLine(editor, line).text;
  72. if (this.lineEndPartOfDrawioRE.test(strLine)) {
  73. isFound = true;
  74. break;
  75. }
  76. if (this.lineBeginPartOfDrawioRE.test(strLine)) {
  77. isFound = false;
  78. break;
  79. }
  80. }
  81. if (!isFound) {
  82. return null;
  83. }
  84. const eodLine = Math.min(line, this.lastLine(editor));
  85. return this.getLine(editor, eodLine).to;
  86. }
  87. /**
  88. * return boolean value whether the cursor position is in a drawio
  89. */
  90. isInDrawioBlock(editor) {
  91. const bod = this.getBod(editor);
  92. const eod = this.getEod(editor);
  93. if (bod === null || eod === null) {
  94. return false;
  95. }
  96. return JSON.stringify(bod) !== JSON.stringify(eod);
  97. }
  98. /**
  99. * return drawioData instance where the cursor is
  100. * (If the cursor is not in a drawio block, return null)
  101. */
  102. getMarkdownDrawioMxfile(editor) {
  103. if (this.isInDrawioBlock(editor)) {
  104. const bod = this.getBod(editor);
  105. const eod = this.getEod(editor);
  106. // skip block begin sesion("``` drawio")
  107. const bodLineNum = this.doc(editor).lineAt(bod).number + 1;
  108. const bodLine = this.getLine(editor, bodLineNum).from;
  109. // skip block end sesion("```")
  110. const eodLineNum = this.doc(editor).lineAt(eod).number - 1;
  111. const eodLine = this.getLine(editor, eodLineNum).to;
  112. return editor.state.sliceDoc(bodLine, eodLine);
  113. }
  114. return null;
  115. }
  116. replaceFocusedDrawioWithEditor(editor, drawioData) {
  117. const drawioBlock = ['``` drawio', drawioData.toString(), '```'].join('\n');
  118. let beginPos;
  119. let endPos;
  120. if (this.isInDrawioBlock(editor)) {
  121. beginPos = this.getBod(editor);
  122. endPos = this.getEod(editor);
  123. }
  124. else {
  125. beginPos = this.doc(editor).lineAt(this.curPos(editor)).from;
  126. endPos = this.doc(editor).lineAt(this.curPos(editor)).to;
  127. }
  128. editor.dispatch({
  129. changes: {
  130. from: beginPos,
  131. to: endPos,
  132. insert: drawioBlock,
  133. },
  134. });
  135. }
  136. /**
  137. * return markdown where the drawioData specified by line number params is replaced to the drawioData specified by drawioData param
  138. * @param {string} drawioData
  139. * @param {string} markdown
  140. * @param beginLineNumber
  141. * @param endLineNumber
  142. */
  143. replaceDrawioInMarkdown(drawioData, markdown, beginLineNumber, endLineNumber) {
  144. const splitMarkdown = markdown.split(/\r\n|\r|\n/);
  145. const markdownBeforeDrawio = splitMarkdown.slice(0, beginLineNumber - 1);
  146. const markdownAfterDrawio = splitMarkdown.slice(endLineNumber);
  147. let newMarkdown = '';
  148. if (markdownBeforeDrawio.length > 0) {
  149. newMarkdown += `${markdownBeforeDrawio.join('\n')}\n`;
  150. }
  151. newMarkdown += '``` drawio\n';
  152. newMarkdown += drawioData;
  153. newMarkdown += '\n```';
  154. if (markdownAfterDrawio.length > 0) {
  155. newMarkdown += `\n${markdownAfterDrawio.join('\n')}`;
  156. }
  157. return newMarkdown;
  158. }
  159. /**
  160. * return an array of the starting line numbers of the drawio sections found in markdown
  161. */
  162. findAllDrawioSection(editor) {
  163. const lineNumbers = [];
  164. const lastLine = this.lastLine(editor);
  165. let firstLine = this.firstLine;
  166. // refs: https://github.com/codemirror/CodeMirror/blob/5.64.0/addon/fold/foldcode.js#L106-L111
  167. for (firstLine, lastLine; firstLine <= lastLine; firstLine++) {
  168. const lineText = this.getLine(editor, firstLine + 1).text;
  169. const match = this.lineBeginPartOfDrawioRE.exec(lineText);
  170. if (match) {
  171. lineNumbers.push(firstLine);
  172. }
  173. }
  174. return lineNumbers;
  175. }
  176. }
  177. // singleton pattern
  178. const instance = new MarkdownDrawioUtil();
  179. Object.freeze(instance);
  180. export default instance;