Skip to content
This repository was archived by the owner on Jun 15, 2022. It is now read-only.

Commit fd5a784

Browse files
author
Mo Bitar
authored
Merge pull request #22 from jonnyparris/feature/enable_gfm_markdown_features
Use gfm Codemirror mode on top of markdown
2 parents d9bfd65 + 54f240b commit fd5a784

File tree

5 files changed

+444
-2
lines changed

5 files changed

+444
-2
lines changed

Gruntfile.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ module.exports = function(grunt) {
6868
'node_modules/codemirror/lib/codemirror.js',
6969
'node_modules/sn-components-api/dist/dist.js',
7070
'vendor/modes/markdown/markdown.js',
71+
'vendor/addon/modes/overlay.js',
72+
'vendor/modes/gfm/gfm.js',
7173
'vendor/mark-selection.js'
7274
],
7375
dest: 'dist/lib.js',

dist/dist.js

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11357,6 +11357,226 @@ CodeMirror.defineMIME("text/x-markdown", "markdown");
1135711357
;// CodeMirror, copyright (c) by Marijn Haverbeke and others
1135811358
// Distributed under an MIT license: https://codemirror.net/LICENSE
1135911359

11360+
// Utility function that allows modes to be combined. The mode given
11361+
// as the base argument takes care of most of the normal mode
11362+
// functionality, but a second (typically simple) mode is used, which
11363+
// can override the style of text. Both modes get to parse all of the
11364+
// text, but when both assign a non-null style to a piece of code, the
11365+
// overlay wins, unless the combine argument was true and not overridden,
11366+
// or state.overlay.combineTokens was true, in which case the styles are
11367+
// combined.
11368+
11369+
(function(mod) {
11370+
if (typeof exports == "object" && typeof module == "object") // CommonJS
11371+
mod(require("../../lib/codemirror"));
11372+
else if (typeof define == "function" && define.amd) // AMD
11373+
define(["../../lib/codemirror"], mod);
11374+
else // Plain browser env
11375+
mod(CodeMirror);
11376+
})(function(CodeMirror) {
11377+
"use strict";
11378+
11379+
CodeMirror.overlayMode = function(base, overlay, combine) {
11380+
return {
11381+
startState: function() {
11382+
return {
11383+
base: CodeMirror.startState(base),
11384+
overlay: CodeMirror.startState(overlay),
11385+
basePos: 0, baseCur: null,
11386+
overlayPos: 0, overlayCur: null,
11387+
streamSeen: null
11388+
};
11389+
},
11390+
copyState: function(state) {
11391+
return {
11392+
base: CodeMirror.copyState(base, state.base),
11393+
overlay: CodeMirror.copyState(overlay, state.overlay),
11394+
basePos: state.basePos, baseCur: null,
11395+
overlayPos: state.overlayPos, overlayCur: null
11396+
};
11397+
},
11398+
11399+
token: function(stream, state) {
11400+
if (stream != state.streamSeen ||
11401+
Math.min(state.basePos, state.overlayPos) < stream.start) {
11402+
state.streamSeen = stream;
11403+
state.basePos = state.overlayPos = stream.start;
11404+
}
11405+
11406+
if (stream.start == state.basePos) {
11407+
state.baseCur = base.token(stream, state.base);
11408+
state.basePos = stream.pos;
11409+
}
11410+
if (stream.start == state.overlayPos) {
11411+
stream.pos = stream.start;
11412+
state.overlayCur = overlay.token(stream, state.overlay);
11413+
state.overlayPos = stream.pos;
11414+
}
11415+
stream.pos = Math.min(state.basePos, state.overlayPos);
11416+
11417+
// state.overlay.combineTokens always takes precedence over combine,
11418+
// unless set to null
11419+
if (state.overlayCur == null) return state.baseCur;
11420+
else if (state.baseCur != null &&
11421+
state.overlay.combineTokens ||
11422+
combine && state.overlay.combineTokens == null)
11423+
return state.baseCur + " " + state.overlayCur;
11424+
else return state.overlayCur;
11425+
},
11426+
11427+
indent: base.indent && function(state, textAfter, line) {
11428+
return base.indent(state.base, textAfter, line);
11429+
},
11430+
electricChars: base.electricChars,
11431+
11432+
innerMode: function(state) { return {state: state.base, mode: base}; },
11433+
11434+
blankLine: function(state) {
11435+
var baseToken, overlayToken;
11436+
if (base.blankLine) baseToken = base.blankLine(state.base);
11437+
if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay);
11438+
11439+
return overlayToken == null ?
11440+
baseToken :
11441+
(combine && baseToken != null ? baseToken + " " + overlayToken : overlayToken);
11442+
}
11443+
};
11444+
};
11445+
11446+
});
11447+
;// CodeMirror, copyright (c) by Marijn Haverbeke and others
11448+
// Distributed under an MIT license: http://codemirror.net/LICENSE
11449+
11450+
(function(mod) {
11451+
if (typeof exports == "object" && typeof module == "object") // CommonJS
11452+
mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay"));
11453+
else if (typeof define == "function" && define.amd) // AMD
11454+
define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod);
11455+
else // Plain browser env
11456+
mod(CodeMirror);
11457+
})(function(CodeMirror) {
11458+
"use strict";
11459+
11460+
var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»]))/i
11461+
11462+
CodeMirror.defineMode("gfm", function(config, modeConfig) {
11463+
var codeDepth = 0;
11464+
function blankLine(state) {
11465+
state.code = false;
11466+
return null;
11467+
}
11468+
var gfmOverlay = {
11469+
startState: function() {
11470+
return {
11471+
code: false,
11472+
codeBlock: false,
11473+
ateSpace: false
11474+
};
11475+
},
11476+
copyState: function(s) {
11477+
return {
11478+
code: s.code,
11479+
codeBlock: s.codeBlock,
11480+
ateSpace: s.ateSpace
11481+
};
11482+
},
11483+
token: function(stream, state) {
11484+
state.combineTokens = null;
11485+
11486+
// Hack to prevent formatting override inside code blocks (block and inline)
11487+
if (state.codeBlock) {
11488+
if (stream.match(/^```+/)) {
11489+
state.codeBlock = false;
11490+
return null;
11491+
}
11492+
stream.skipToEnd();
11493+
return null;
11494+
}
11495+
if (stream.sol()) {
11496+
state.code = false;
11497+
}
11498+
if (stream.sol() && stream.match(/^```+/)) {
11499+
stream.skipToEnd();
11500+
state.codeBlock = true;
11501+
return null;
11502+
}
11503+
// If this block is changed, it may need to be updated in Markdown mode
11504+
if (stream.peek() === '`') {
11505+
stream.next();
11506+
var before = stream.pos;
11507+
stream.eatWhile('`');
11508+
var difference = 1 + stream.pos - before;
11509+
if (!state.code) {
11510+
codeDepth = difference;
11511+
state.code = true;
11512+
} else {
11513+
if (difference === codeDepth) { // Must be exact
11514+
state.code = false;
11515+
}
11516+
}
11517+
return null;
11518+
} else if (state.code) {
11519+
stream.next();
11520+
return null;
11521+
}
11522+
// Check if space. If so, links can be formatted later on
11523+
if (stream.eatSpace()) {
11524+
state.ateSpace = true;
11525+
return null;
11526+
}
11527+
if (stream.sol() || state.ateSpace) {
11528+
state.ateSpace = false;
11529+
if (modeConfig.gitHubSpice !== false) {
11530+
if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
11531+
// User/Project@SHA
11532+
// User@SHA
11533+
// SHA
11534+
state.combineTokens = true;
11535+
return "link";
11536+
} else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) {
11537+
// User/Project#Num
11538+
// User#Num
11539+
// #Num
11540+
state.combineTokens = true;
11541+
return "link";
11542+
}
11543+
}
11544+
}
11545+
if (stream.match(urlRE) &&
11546+
stream.string.slice(stream.start - 2, stream.start) != "](" &&
11547+
(stream.start == 0 || /\W/.test(stream.string.charAt(stream.start - 1)))) {
11548+
// URLs
11549+
// Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
11550+
// And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
11551+
// And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL
11552+
state.combineTokens = true;
11553+
return "link";
11554+
}
11555+
stream.next();
11556+
return null;
11557+
},
11558+
blankLine: blankLine
11559+
};
11560+
11561+
var markdownConfig = {
11562+
underscoresBreakWords: false,
11563+
taskLists: true,
11564+
fencedCodeBlocks: '```',
11565+
strikethrough: true
11566+
};
11567+
for (var attr in modeConfig) {
11568+
markdownConfig[attr] = modeConfig[attr];
11569+
}
11570+
markdownConfig.name = "markdown";
11571+
return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay);
11572+
11573+
}, "markdown");
11574+
11575+
CodeMirror.defineMIME("text/x-gfm", "gfm");
11576+
});
11577+
;// CodeMirror, copyright (c) by Marijn Haverbeke and others
11578+
// Distributed under an MIT license: https://codemirror.net/LICENSE
11579+
1136011580
// Because sometimes you need to mark the selected *text*.
1136111581
//
1136211582
// Adds an option 'styleSelectedText' which, when enabled, gives
@@ -11553,7 +11773,7 @@ document.addEventListener("DOMContentLoaded", function (event) {
1155311773

1155411774
function loadEditor() {
1155511775
editor = CodeMirror.fromTextArea(document.getElementById("code"), {
11556-
mode: "markdown",
11776+
mode: "gfm",
1155711777
lineWrapping: true,
1155811778
extraKeys: {
1155911779
"Alt-F": "findPersistent"

src/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ document.addEventListener("DOMContentLoaded", function(event) {
7777

7878
function loadEditor() {
7979
editor = CodeMirror.fromTextArea(document.getElementById("code"), {
80-
mode: "markdown",
80+
mode: "gfm",
8181
lineWrapping: true,
8282
extraKeys: {"Alt-F": "findPersistent"}
8383
});

vendor/addon/modes/overlay.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// CodeMirror, copyright (c) by Marijn Haverbeke and others
2+
// Distributed under an MIT license: https://codemirror.net/LICENSE
3+
4+
// Utility function that allows modes to be combined. The mode given
5+
// as the base argument takes care of most of the normal mode
6+
// functionality, but a second (typically simple) mode is used, which
7+
// can override the style of text. Both modes get to parse all of the
8+
// text, but when both assign a non-null style to a piece of code, the
9+
// overlay wins, unless the combine argument was true and not overridden,
10+
// or state.overlay.combineTokens was true, in which case the styles are
11+
// combined.
12+
13+
(function(mod) {
14+
if (typeof exports == "object" && typeof module == "object") // CommonJS
15+
mod(require("../../lib/codemirror"));
16+
else if (typeof define == "function" && define.amd) // AMD
17+
define(["../../lib/codemirror"], mod);
18+
else // Plain browser env
19+
mod(CodeMirror);
20+
})(function(CodeMirror) {
21+
"use strict";
22+
23+
CodeMirror.overlayMode = function(base, overlay, combine) {
24+
return {
25+
startState: function() {
26+
return {
27+
base: CodeMirror.startState(base),
28+
overlay: CodeMirror.startState(overlay),
29+
basePos: 0, baseCur: null,
30+
overlayPos: 0, overlayCur: null,
31+
streamSeen: null
32+
};
33+
},
34+
copyState: function(state) {
35+
return {
36+
base: CodeMirror.copyState(base, state.base),
37+
overlay: CodeMirror.copyState(overlay, state.overlay),
38+
basePos: state.basePos, baseCur: null,
39+
overlayPos: state.overlayPos, overlayCur: null
40+
};
41+
},
42+
43+
token: function(stream, state) {
44+
if (stream != state.streamSeen ||
45+
Math.min(state.basePos, state.overlayPos) < stream.start) {
46+
state.streamSeen = stream;
47+
state.basePos = state.overlayPos = stream.start;
48+
}
49+
50+
if (stream.start == state.basePos) {
51+
state.baseCur = base.token(stream, state.base);
52+
state.basePos = stream.pos;
53+
}
54+
if (stream.start == state.overlayPos) {
55+
stream.pos = stream.start;
56+
state.overlayCur = overlay.token(stream, state.overlay);
57+
state.overlayPos = stream.pos;
58+
}
59+
stream.pos = Math.min(state.basePos, state.overlayPos);
60+
61+
// state.overlay.combineTokens always takes precedence over combine,
62+
// unless set to null
63+
if (state.overlayCur == null) return state.baseCur;
64+
else if (state.baseCur != null &&
65+
state.overlay.combineTokens ||
66+
combine && state.overlay.combineTokens == null)
67+
return state.baseCur + " " + state.overlayCur;
68+
else return state.overlayCur;
69+
},
70+
71+
indent: base.indent && function(state, textAfter, line) {
72+
return base.indent(state.base, textAfter, line);
73+
},
74+
electricChars: base.electricChars,
75+
76+
innerMode: function(state) { return {state: state.base, mode: base}; },
77+
78+
blankLine: function(state) {
79+
var baseToken, overlayToken;
80+
if (base.blankLine) baseToken = base.blankLine(state.base);
81+
if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay);
82+
83+
return overlayToken == null ?
84+
baseToken :
85+
(combine && baseToken != null ? baseToken + " " + overlayToken : overlayToken);
86+
}
87+
};
88+
};
89+
90+
});

0 commit comments

Comments
 (0)