markdowncodeblocks.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. /**
  4. * The namespace for code block functions which help
  5. * in extract code from markdown text
  6. */
  7. export
  8. namespace MarkdownCodeBlocks {
  9. export
  10. const markdownMarkers: string[] = ["```", "~~~~", "`"]
  11. const markdownExtensions: string[] = [
  12. '.markdown',
  13. '.mdown',
  14. '.mkdn',
  15. '.md',
  16. '.mkd',
  17. '.mdwn',
  18. '.mdtxt',
  19. '.mdtext',
  20. '.text',
  21. '.txt',
  22. '.Rmd'
  23. ];
  24. export
  25. class MarkdownCodeBlock {
  26. startLine: number;
  27. endLine: number;
  28. code: string;
  29. constructor(startLine: number) {
  30. this.startLine = startLine;
  31. this.code = "";
  32. this.endLine = -1;
  33. }
  34. }
  35. /**
  36. * Check whether the given file extension is a markdown extension
  37. * @param extension - A file extension
  38. *
  39. * @returns true/false depending on whether this is a supported markdown extension
  40. */
  41. export
  42. function isMarkdown(extension: string): boolean {
  43. return markdownExtensions.indexOf(extension) > -1
  44. }
  45. /**
  46. * Construct all code snippets from current text
  47. * (this could be potentially optimized if we can cache and detect differences)
  48. * @param text - A string to parse codeblocks from
  49. *
  50. * @returns An array of MarkdownCodeBlocks.
  51. */
  52. export
  53. function findMarkdownCodeBlocks(text: string): MarkdownCodeBlock[] {
  54. if (!text || text == '') {
  55. return [];
  56. }
  57. const lines = text.split("\n");
  58. const codeSnippets: MarkdownCodeBlock[] = [];
  59. var currentCode = null;
  60. for (var lineIndex = 0; lineIndex < lines.length; lineIndex++) {
  61. const line = lines[lineIndex];
  62. const marker = findNextMarker(line);
  63. const lineContainsMarker = marker != '';
  64. const constructingSnippet = currentCode != null;
  65. //skip this line if it is not part of any code snippet and doesn't contain a marker
  66. if (!lineContainsMarker && !constructingSnippet) {
  67. continue;
  68. }
  69. //check if we are already constructing a code snippet
  70. if (!constructingSnippet) {
  71. //start constructing
  72. currentCode = new MarkdownCodeBlock(lineIndex);
  73. //check whether this is a single line code snippet
  74. const firstIndex = line.indexOf(marker);
  75. const lastIndex = line.lastIndexOf(marker);
  76. const isSingleLine = firstIndex != lastIndex
  77. if (isSingleLine) {
  78. currentCode.code = line.substring(firstIndex + marker.length, lastIndex);
  79. currentCode.endLine = lineIndex;
  80. codeSnippets.push(currentCode);
  81. currentCode = null;
  82. } else {
  83. currentCode.code = line.substring(firstIndex + marker.length);
  84. }
  85. } else {
  86. //already constructing
  87. if (lineContainsMarker) {
  88. currentCode.code += "\n" + line.substring(0, line.indexOf(marker));
  89. currentCode.endLine = lineIndex;
  90. codeSnippets.push(currentCode);
  91. currentCode = null;
  92. } else {
  93. currentCode.code += "\n" + line;
  94. }
  95. }
  96. }
  97. return codeSnippets;
  98. }
  99. function findNextMarker(text: string) {
  100. for (let marker of markdownMarkers) {
  101. const index = text.indexOf(marker);
  102. if (index > -1) {
  103. return marker;
  104. }
  105. }
  106. return '';
  107. }
  108. }