gfm.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: http://codemirror.net/LICENSE
  3. (function(mod) {
  4. if (typeof exports == "object" && typeof module == "object") // CommonJS
  5. mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay"));
  6. else if (typeof define == "function" && define.amd) // AMD
  7. define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod);
  8. else // Plain browser env
  9. mod(CodeMirror);
  10. })(function(CodeMirror) {
  11. "use strict";
  12. CodeMirror.defineMode("gfm", function(config, modeConfig) {
  13. var codeDepth = 0;
  14. function blankLine(state) {
  15. state.code = false;
  16. return null;
  17. }
  18. var gfmOverlay = {
  19. startState: function() {
  20. return {
  21. code: false,
  22. codeBlock: false,
  23. ateSpace: false
  24. };
  25. },
  26. copyState: function(s) {
  27. return {
  28. code: s.code,
  29. codeBlock: s.codeBlock,
  30. ateSpace: s.ateSpace
  31. };
  32. },
  33. token: function(stream, state) {
  34. state.combineTokens = null;
  35. // Hack to prevent formatting override inside code blocks (block and inline)
  36. if (state.codeBlock) {
  37. if (stream.match(/^```/)) {
  38. state.codeBlock = false;
  39. return null;
  40. }
  41. stream.skipToEnd();
  42. return null;
  43. }
  44. if (stream.sol()) {
  45. state.code = false;
  46. }
  47. if (stream.sol() && stream.match(/^```/)) {
  48. stream.skipToEnd();
  49. state.codeBlock = true;
  50. return null;
  51. }
  52. // If this block is changed, it may need to be updated in Markdown mode
  53. if (stream.peek() === '`') {
  54. stream.next();
  55. var before = stream.pos;
  56. stream.eatWhile('`');
  57. var difference = 1 + stream.pos - before;
  58. if (!state.code) {
  59. codeDepth = difference;
  60. state.code = true;
  61. } else {
  62. if (difference === codeDepth) { // Must be exact
  63. state.code = false;
  64. }
  65. }
  66. return null;
  67. } else if (state.code) {
  68. stream.next();
  69. return null;
  70. }
  71. // Check if space. If so, links can be formatted later on
  72. if (stream.eatSpace()) {
  73. state.ateSpace = true;
  74. return null;
  75. }
  76. if (stream.sol() || state.ateSpace) {
  77. state.ateSpace = false;
  78. if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
  79. // User/Project@SHA
  80. // User@SHA
  81. // SHA
  82. state.combineTokens = true;
  83. return "link";
  84. } else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) {
  85. // User/Project#Num
  86. // User#Num
  87. // #Num
  88. state.combineTokens = true;
  89. return "link";
  90. }
  91. }
  92. if (stream.match(/^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i) &&
  93. stream.string.slice(stream.start - 2, stream.start) != "](") {
  94. // URLs
  95. // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
  96. // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
  97. state.combineTokens = true;
  98. return "link";
  99. }
  100. stream.next();
  101. return null;
  102. },
  103. blankLine: blankLine
  104. };
  105. var markdownConfig = {
  106. underscoresBreakWords: false,
  107. taskLists: true,
  108. fencedCodeBlocks: true,
  109. strikethrough: true
  110. };
  111. for (var attr in modeConfig) {
  112. markdownConfig[attr] = modeConfig[attr];
  113. }
  114. markdownConfig.name = "markdown";
  115. CodeMirror.defineMIME("gfmBase", markdownConfig);
  116. return CodeMirror.overlayMode(CodeMirror.getMode(config, "gfmBase"), gfmOverlay);
  117. }, "markdown");
  118. });