8 changes: 7 additions & 1 deletion demo/variableheight.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
<script src="../mode/xml/xml.js"></script>
<style type="text/css">
.CodeMirror {border: 1px solid silver; border-width: 1px 2px; }
.cm-header { font-size: 150%; font-family: arial; }
.cm-header { font-family: arial; }
.cm-header1 { font-size: 150%; }
.cm-header2 { font-size: 130%; }
.cm-header3 { font-size: 120%; }
.cm-header4 { font-size: 110%; }
.cm-header5 { font-size: 100%; }
.cm-header6 { font-size: 90%; }
.cm-strong { font-size: 140%; }
</style>
<div id=nav>
Expand Down
3 changes: 3 additions & 0 deletions doc/compress.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ <h2>Script compression helper</h2>
<input type="hidden" id="download" name="download" value="codemirror-compressed.js"/>
<p>Version: <select id="version" onchange="setVersion(this);" style="padding: 1px;">
<option value="http://codemirror.net/">HEAD</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=3.20.0;f=">3.20</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=3.19.0;f=">3.19</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=3.18.0;f=">3.18</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=3.16.0;f=">3.16</option>
Expand Down Expand Up @@ -107,6 +108,7 @@ <h2>Script compression helper</h2>
<option value="http://codemirror.net/mode/jade/jade.js">jade.js</option>
<option value="http://codemirror.net/mode/javascript/javascript.js">javascript.js</option>
<option value="http://codemirror.net/mode/jinja2/jinja2.js">jinja2.js</option>
<option value="http://codemirror.net/mode/julia/julia.js">jinja2.js</option>
<option value="http://codemirror.net/mode/less/less.js">less.js</option>
<option value="http://codemirror.net/mode/livescript/livescript.js">livescript.js</option>
<option value="http://codemirror.net/mode/lua/lua.js">lua.js</option>
Expand All @@ -117,6 +119,7 @@ <h2>Script compression helper</h2>
<option value="http://codemirror.net/mode/ocaml/ocaml.js">ocaml.js</option>
<option value="http://codemirror.net/mode/octave/octave.js">octave.js</option>
<option value="http://codemirror.net/mode/pascal/pascal.js">pascal.js</option>
<option value="http://codemirror.net/mode/pegjs/pegjs.js">pegjs.js</option>
<option value="http://codemirror.net/mode/perl/perl.js">perl.js</option>
<option value="http://codemirror.net/mode/php/php.js">php.js</option>
<option value="http://codemirror.net/mode/pig/pig.js">pig.js</option>
Expand Down
1 change: 0 additions & 1 deletion doc/docs.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ p { margin-top: 0; }

h2, h3 {
font-weight: normal;
text-decoration: underline;
margin-bottom: .7em;
}
h2 { font-size: 120%; }
Expand Down
44 changes: 32 additions & 12 deletions doc/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,19 @@ <h2>Configuration</h2>
indentation (only works if the mode supports indentation).
Default is true.</dd>

<dt id="option_specialChars"><code><strong>specialChars</strong>: RegExp</code></dt>
<dd>A regular expression used to determine which characters
should be replaced by a
special <a href="#option_specialCharPlaceholder">placeholder</a>.
Mostly useful for non-printing special characters. The default
is <code>/[\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/</code>.</dd>
<dt id="option_specialCharPlaceholder"><code><strong>specialCharPlaceholder</strong>: function(char) → Element</code></dt>
<dd>A function that, given a special character identified by
the <a href="#option_specialChars"><code>specialChars</code></a>
option, produces a DOM node that is used to represent the
character. By default, a red dot (<span style="color: red"></span>)
is shown, with a title tooltip to indicate the character code.</dd>

<dt id="option_rtlMoveVisually"><code><strong>rtlMoveVisually</strong>: boolean</code></dt>
<dd>Determines whether horizontal cursor movement through
right-to-left (Arabic, Hebrew) text is visual (pressing the left
Expand Down Expand Up @@ -1576,6 +1589,9 @@ <h3 id="api_misc">Miscellaneous methods</h3>
given an argument), or sets the overwrite mode to a specific
state (when given an argument).</dd>

<dt id="execCommand"><code><strong>cm.execCommand</strong>(name: string)</code></dt>
<dd>Runs the <a href="#commands">command</a> with the given name on the editor.</dd>

<dt id="posFromIndex"><code><strong>doc.posFromIndex</strong>(index: integer) → {line, ch}</code></dt>
<dd>Calculates and returns a <code>{line, ch}</code> object for a
zero-based <code>index</code> who's value is relative to the start of the
Expand Down Expand Up @@ -1615,13 +1631,14 @@ <h3 id="api_static">Static properties</h3>

<dt id="fromTextArea"><code><strong>CodeMirror.fromTextArea</strong>(textArea: TextAreaElement, ?config: object)</code></dt>
<dd>
The method provides another way to initialize an editor. It takes a
textarea DOM node as first argument and an optional configuration
object as second. It will replace the textarea with a CodeMirror
instance, and wire up the form of that textarea (if any) to make
sure the editor contents are put into the textarea when the form
is submitted. A CodeMirror instance created this way has three
additional methods:
The method provides another way to initialize an editor. It
takes a textarea DOM node as first argument and an optional
configuration object as second. It will replace the textarea
with a CodeMirror instance, and wire up the form of that
textarea (if any) to make sure the editor contents are put
into the textarea when the form is submitted. The text in the
textarea will provide the content for the editor. A CodeMirror
instance created this way has three additional methods:
<dl>
<dt id="save"><code><strong>cm.save</strong>()</code></dt>
<dd>Copy the content of the editor into the textarea.</dd>
Expand Down Expand Up @@ -1704,11 +1721,14 @@ <h2>Addons</h2>
<dl>
<dt id="addon_dialog"><a href="../addon/dialog/dialog.js"><code>dialog/dialog.js</code></a></dt>
<dd>Provides a very simple way to query users for text input.
Adds an <code>openDialog</code> method to CodeMirror instances,
which can be called with an HTML fragment that provides the
prompt (should include an <code>input</code> tag), and a
callback function that is called when text has been entered.
Depends on <code>addon/dialog/dialog.css</code>.</dd>
Adds an <strong><code>openDialog</code></strong> method to
CodeMirror instances, which can be called with an HTML fragment
or a detached DOM node that provides the prompt (should include
an <code>input</code> tag), and a callback function that is called
when text has been entered. Also adds
an <strong><code>openNotification</code></strong> function that
simply shows an HTML fragment as a notification. Depends
on <code>addon/dialog/dialog.css</code>.</dd>

<dt id="addon_searchcursor"><a href="../addon/search/searchcursor.js"><code>search/searchcursor.js</code></a></dt>
<dd>Adds the <code>getSearchCursor(query, start, caseFold) →
Expand Down
4 changes: 3 additions & 1 deletion doc/realworld.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ <h2>CodeMirror real-world uses</h2>
<li><a href="http://www.communitycodecamp.com/">Community Code Camp</a> (code snippet sharing)</li>
<li><a href="http://www.compilejava.net/">compilejava.net</a> (online Java sandbox)</li>
<li><a href="http://www.ckwnc.com/">CKWNC</a> (UML editor)</li>
<li><a href="http://www.crudzilla.com/">Crudzilla</a> (self-hosted web IDE)</li>
<li><a href="http://cssdeck.com/">CSSDeck</a> (CSS showcase)</li>
<li><a href="http://ireneros.com/deck/deck.js-codemirror/introduction/#textarea-code">Deck.js integration</a> (slides with editors)</li>
<li><a href="http://www.dbninja.com">DbNinja</a> (MySQL access interface)</li>
Expand All @@ -68,7 +69,7 @@ <h2>CodeMirror real-world uses</h2>
<li><a href="https://github.com/github/android">GitHub's Android app</a></li>
<li><a href="https://script.google.com/">Google Apps Script</a></li>
<li><a href="http://web.uvic.ca/~siefkenj/graphit/graphit.html">Graphit</a> (function graphing)</li>
<li><a href="http://www.handcraft2.com/">Handcraft 2</a> (HTML prototyping)</li>
<li><a href="http://www.handcraft.com/">Handcraft</a> (HTML prototyping)</li>
<li><a href="http://try.haxe.org">Haxe</a> (Haxe Playground) </li>
<li><a href="http://haxpad.com/">HaxPad</a> (editor for Win RT)</li>
<li><a href="http://megafonweblab.github.com/histone-javascript/">Histone template engine playground</a></li>
Expand All @@ -87,6 +88,7 @@ <h2>CodeMirror real-world uses</h2>
<li><a href="http://www.mergely.com/">Mergely</a> (interactive diffing)</li>
<li><a href="http://www.iunbug.com/mihtool">MIHTool</a> (iOS web-app debugging tool)</li>
<li><a href="http://mongo-mapreduce-webbrowser.opensagres.cloudbees.net/">Mongo MapReduce WebBrowser</a></li>
<li><a href="http://mvcplayground.apphb.com/">MVC Playground</a></li>
<li><a href="https://www.my2ndgeneration.com/">My2ndGeneration</a> (social coding)</li>
<li><a href="http://www.navigatecms.com">Navigate CMS</a></li>
<li><a href="https://github.com/soliton4/nodeMirror">nodeMirror</a> (IDE project)</li>
Expand Down
11 changes: 11 additions & 0 deletions doc/releases.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ <h2>Release notes and version history</h2>

<h2>Version 3.x</h2>

<p class="rel">21-11-2013: <a href="http://codemirror.net/codemirror-3.20.zip">Version 3.20</a>:</p>

<ul class="rel-note">
<li>New modes: <a href="../mode/julia/index.html">Julia</a> and <a href="../mode/pegjs/index.html">PEG.js</a>.</li>
<li>Support ECMAScript 6 in the <a href="../mode/javascript/index.html">JavaScript mode</a>.</li>
<li>Improved indentation for the <a href="../mode/coffeescript/index.html">CoffeeScript mode</a>.</li>
<li>Make non-printable-character representation <a href="manual.html#option_specialChars">configurable</a>.</li>
<li>Add ‘notification’ functionality to <a href="manual.html#addon_dialog">dialog</a> addon.</li>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/3.19.0...3.20.0">list of patches</a>.</li>
</ul>

<p class="rel">21-10-2013: <a href="http://codemirror.net/codemirror-3.19.zip">Version 3.19</a>:</p>

<ul class="rel-note">
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ <h2>This is CodeMirror</h2>
</script>
<div style="position: relative; margin: 1em 0;">
<a class="bigbutton left" href="http://codemirror.net/codemirror.zip">DOWNLOAD LATEST RELEASE</a>
<div><strong>version 3.19</strong> (<a href="doc/releases.html">Release notes</a>)</div>
<div><strong>version 3.20</strong> (<a href="doc/releases.html">Release notes</a>)</div>
<div>or use the <a href="doc/compress.html">minification helper</a></div>
<div style="position: absolute; top: 0; right: 0; text-align: right">
<span class="bigbutton right" onclick="document.getElementById('paypal').submit();">DONATE WITH PAYPAL</span>
Expand Down
7 changes: 3 additions & 4 deletions keymap/vim.js
Original file line number Diff line number Diff line change
Expand Up @@ -2726,10 +2726,9 @@
return regexp;
}
function showConfirm(cm, text) {
if (cm.openConfirm) {
cm.openConfirm('<span style="color: red">' + text +
'</span> <button type="button">OK</button>', function() {},
{bottom: true});
if (cm.openNotification) {
cm.openNotification('<span style="color: red">' + text + '</span>',
{bottom: true, duration: 5000});
} else {
alert(text);
}
Expand Down
88 changes: 61 additions & 27 deletions lib/codemirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ window.CodeMirror = (function() {
// Crude, but necessary to handle a number of hard-to-feature-detect
// bugs and behavior differences.
var gecko = /gecko\/\d/i.test(navigator.userAgent);
// IE11 currently doesn't count as 'ie', since it has almost none of
// the same bugs as earlier versions. Use ie_gt10 to handle
// incompatibilities in that version.
var ie = /MSIE \d/.test(navigator.userAgent);
var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
var ie_gt10 = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent);
var webkit = /WebKit\//.test(navigator.userAgent);
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
var chrome = /Chrome\//.test(navigator.userAgent);
Expand Down Expand Up @@ -906,7 +910,7 @@ window.CodeMirror = (function() {
doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
if (doc.frontier >= cm.display.showingFrom) { // Visible
var oldStyles = line.styles;
line.styles = highlightLine(cm, line, state);
line.styles = highlightLine(cm, line, state, true);
var ischange = !oldStyles || oldStyles.length != line.styles.length;
for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
if (ischange) {
Expand All @@ -915,7 +919,7 @@ window.CodeMirror = (function() {
}
line.stateAfter = copyState(doc.mode, state);
} else {
processLine(cm, line, state);
processLine(cm, line.text, state);
line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
}
++doc.frontier;
Expand Down Expand Up @@ -959,7 +963,7 @@ window.CodeMirror = (function() {
if (!state) state = startState(doc.mode);
else state = copyState(doc.mode, state);
doc.iter(pos, n, function(line) {
processLine(cm, line, state);
processLine(cm, line.text, state);
var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
line.stateAfter = save ? copyState(doc.mode, state) : null;
++pos;
Expand Down Expand Up @@ -1383,8 +1387,10 @@ window.CodeMirror = (function() {
}
if (!updated && op.selectionChanged) updateSelection(cm);
if (op.updateScrollPos) {
display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, newScrollPos.scrollTop));
var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, newScrollPos.scrollLeft));
display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left;
alignHorizontally(cm);
if (op.scrollToPos)
scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
Expand Down Expand Up @@ -1903,7 +1909,6 @@ window.CodeMirror = (function() {
if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
cm.replaceSelection(text, null, "paste");
focusInput(cm);
onFocus(cm);
}
}
catch(e){}
Expand Down Expand Up @@ -2761,6 +2766,8 @@ window.CodeMirror = (function() {

if (indentString != curSpaceString)
replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
else if (doc.sel.head.line == n && doc.sel.head.ch < curSpaceString.length)
setSelection(doc, Pos(n, curSpaceString.length), Pos(n, curSpaceString.length), 1);
line.stateAfter = null;
}

Expand Down Expand Up @@ -2861,7 +2868,7 @@ window.CodeMirror = (function() {

CodeMirror.prototype = {
constructor: CodeMirror,
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
focus: function(){window.focus(); focusInput(this); fastPoll(this);},

setOption: function(option, value) {
var options = this.options, old = options[option];
Expand Down Expand Up @@ -3274,8 +3281,14 @@ window.CodeMirror = (function() {
clearCaches(cm);
regChange(cm);
}, true);
option("specialChars", /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/g, function(cm, val) {
cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
cm.refresh();
}, true);
option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
option("electricChars", true);
option("rtlMoveVisually", !windows);
option("wholeLineUpdateBefore", true);

option("theme", "default", function(cm) {
themeChanged(cm);
Expand Down Expand Up @@ -3308,8 +3321,14 @@ window.CodeMirror = (function() {
option("resetSelectionOnContextMenu", true);

option("readOnly", false, function(cm, val) {
if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
else if (!val) resetInput(cm, true);
if (val == "nocursor") {
onBlur(cm);
cm.display.input.blur();
cm.display.disabled = true;
} else {
cm.display.disabled = false;
if (!val) resetInput(cm, true);
}
});
option("dragDrop", true);

Expand Down Expand Up @@ -3850,7 +3869,9 @@ window.CodeMirror = (function() {
if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);

var marker = new TextMarker(doc, type);
if (type == "range" && !posLess(from, to)) return marker;
if (posLess(to, from) || posEq(from, to) && type == "range" &&
!(options.inclusiveLeft && options.inclusiveRight))
return marker;
if (options) copyObj(options, marker);
if (marker.replacedWith) {
marker.collapsed = true;
Expand Down Expand Up @@ -3966,7 +3987,9 @@ window.CodeMirror = (function() {
if (old) for (var i = 0, nw; i < old.length; ++i) {
var span = old[i], marker = span.marker;
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
if (startsBefore ||
(marker.inclusiveLeft && marker.inclusiveRight || marker.type == "bookmark") &&
span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
(nw || (nw = [])).push({from: span.from,
to: endsAfter ? null : span.to,
Expand Down Expand Up @@ -4239,6 +4262,7 @@ window.CodeMirror = (function() {
this.height = estimateHeight ? estimateHeight(this) : 1;
};
eventMixin(Line);
Line.prototype.lineNo = function() { return lineNo(this); };

function updateLine(line, text, markedSpans, estimateHeight) {
line.text = text;
Expand All @@ -4259,7 +4283,7 @@ window.CodeMirror = (function() {
// Run the given mode's parser over a line, update the styles
// array, which contains alternating fragments of text and CSS
// classes.
function runMode(cm, text, mode, state, f) {
function runMode(cm, text, mode, state, f, forceToEnd) {
var flattenSpans = mode.flattenSpans;
if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
var curStart = 0, curStyle = null;
Expand All @@ -4268,6 +4292,7 @@ window.CodeMirror = (function() {
while (!stream.eol()) {
if (stream.pos > cm.options.maxHighlightLength) {
flattenSpans = false;
if (forceToEnd) processLine(cm, text, state, stream.pos);
stream.pos = text.length;
style = null;
} else {
Expand All @@ -4287,12 +4312,14 @@ window.CodeMirror = (function() {
}
}

function highlightLine(cm, line, state) {
function highlightLine(cm, line, state, forceToEnd) {
// A styles array always starts with a number identifying the
// mode/overlays that it is based on (for easy invalidation).
var st = [cm.state.modeGen];
// Compute the base array of styles
runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
st.push(end, style);
}, forceToEnd);

// Run overlays, adjust style array.
for (var o = 0; o < cm.state.overlays.length; ++o) {
Expand Down Expand Up @@ -4331,10 +4358,11 @@ window.CodeMirror = (function() {

// Lightweight form of highlight -- proceed over this line and
// update state, but don't save a style array.
function processLine(cm, line, state) {
function processLine(cm, text, state, startAt) {
var mode = cm.doc.mode;
var stream = new StringStream(line.text, cm.options.tabSize);
if (line.text == "" && mode.blankLine) mode.blankLine(state);
var stream = new StringStream(text, cm.options.tabSize);
stream.start = stream.pos = startAt || 0;
if (text == "" && mode.blankLine) mode.blankLine(state);
while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
mode.token(stream, state);
stream.start = stream.pos;
Expand Down Expand Up @@ -4391,7 +4419,7 @@ window.CodeMirror = (function() {
// Work around problem with the reported dimensions of single-char
// direction spans on IE (issue #1129). See also the comment in
// cursorCoords.
if (measure && ie && (order = getOrder(line))) {
if (measure && (ie || ie_gt10) && (order = getOrder(line))) {
var l = order.length - 1;
if (order[l].from == order[l].to) --l;
var last = order[l], prev = order[l - 1];
Expand All @@ -4409,17 +4437,23 @@ window.CodeMirror = (function() {
return builder;
}

var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
function defaultSpecialCharPlaceholder(ch) {
var token = elt("span", "\u2022", "cm-invalidchar");
token.title = "\\u" + ch.charCodeAt(0).toString(16);
return token;
}

function buildToken(builder, text, style, startStyle, endStyle, title) {
if (!text) return;
if (!tokenSpecialChars.test(text)) {
var special = builder.cm.options.specialChars;
if (!special.test(text)) {
builder.col += text.length;
var content = document.createTextNode(text);
} else {
var content = document.createDocumentFragment(), pos = 0;
while (true) {
tokenSpecialChars.lastIndex = pos;
var m = tokenSpecialChars.exec(text);
special.lastIndex = pos;
var m = special.exec(text);
var skipped = m ? m.index - pos : text.length - pos;
if (skipped) {
content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
Expand All @@ -4432,8 +4466,7 @@ window.CodeMirror = (function() {
content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
builder.col += tabWidth;
} else {
var token = elt("span", "\u2022", "cm-invalidchar");
token.title = "\\u" + m[0].charCodeAt(0).toString(16);
var token = builder.cm.options.specialCharPlaceholder(m[0]);
content.appendChild(token);
builder.col += 1;
}
Expand Down Expand Up @@ -4586,7 +4619,8 @@ window.CodeMirror = (function() {
var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;

// First adjust the line structure
if (from.ch == 0 && to.ch == 0 && lastText == "") {
if (from.ch == 0 && to.ch == 0 && lastText == "" &&
(!doc.cm || doc.cm.options.wholeLineUpdateBefore)) {
// This is a whole-line replace. Treated specially to make
// sure line objects move the way they are supposed to.
for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
Expand Down Expand Up @@ -5473,7 +5507,7 @@ window.CodeMirror = (function() {
return true;
}

var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\u1DC0\u1DFF\u20D0\u20FF\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff\uFE20\uFE2F]/;

// DOM UTILITIES

Expand Down Expand Up @@ -5902,7 +5936,7 @@ window.CodeMirror = (function() {

// THE END

CodeMirror.version = "3.19.0";
CodeMirror.version = "3.20.0";

return CodeMirror;
})();
4 changes: 2 additions & 2 deletions mode/clike/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,12 @@ <h2>Java example</h2>
</textarea></div>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("c-code"), {
var cEditor = CodeMirror.fromTextArea(document.getElementById("c-code"), {
lineNumbers: true,
matchBrackets: true,
mode: "text/x-csrc"
});
var editor = CodeMirror.fromTextArea(document.getElementById("cpp-code"), {
var cppEditor = CodeMirror.fromTextArea(document.getElementById("cpp-code"), {
lineNumbers: true,
matchBrackets: true,
mode: "text/x-c++src"
Expand Down
21 changes: 13 additions & 8 deletions mode/coffeescript/coffeescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ CodeMirror.defineMode("coffeescript", function(conf) {
if (type !== "coffee") {
align = null;
alignOffset = stream.column() + stream.current().length;
} else if (state.scope.align) {
state.scope.align = false;
}
state.scope = {
offset: offset,
Expand Down Expand Up @@ -268,7 +270,6 @@ CodeMirror.defineMode("coffeescript", function(conf) {
}
if (((current === "->" || current === "=>") &&
!state.lambda &&
state.scope.type == "coffee" &&
!stream.peek())
|| style === "indent") {
indent(stream, state);
Expand All @@ -292,9 +293,10 @@ CodeMirror.defineMode("coffeescript", function(conf) {
}
delimiter_index = "])}".indexOf(current);
if (delimiter_index !== -1) {
if (dedent(stream, state)) {
return ERRORCLASS;
}
while (state.scope.type == "coffee" && state.scope.prev)
state.scope = state.scope.prev;
if (state.scope.type == current)
state.scope = state.scope.prev;
}
if (state.dedent > 0 && stream.eol() && state.scope.type == "coffee") {
if (state.scope.prev) state.scope = state.scope.prev;
Expand Down Expand Up @@ -333,11 +335,14 @@ CodeMirror.defineMode("coffeescript", function(conf) {

indent: function(state, text) {
if (state.tokenize != tokenBase) return 0;
var closes = state.scope.type === (text && text.charAt(0));
if (state.scope.align)
return state.scope.alignOffset - (closes ? 1 : 0);
var scope = state.scope;
var closer = text && "])}".indexOf(text.charAt(0)) > -1;
if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
var closes = closer && scope.type === text.charAt(0);
if (scope.align)
return scope.alignOffset - (closes ? 1 : 0);
else
return (closes ? state.scope.prev : state.scope).offset;
return (closes ? scope.prev : scope).offset;
},

lineComment: "#",
Expand Down
6 changes: 3 additions & 3 deletions mode/css/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
// Pop off end of array until { is reached
while(state.stack.length){
var removed = state.stack.pop();
if(removed.indexOf("{") > -1){
if(removed.indexOf("{") > -1 || removed == "block" || removed == "rule"){
break;
}
}
Expand Down Expand Up @@ -609,8 +609,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
}
return ["variable", "variable"];
},
",": function(_stream, state) {
if (state.stack[state.stack.length - 1] == "propertyValue") {
",": function(stream, state) {
if (state.stack[state.stack.length - 1] == "propertyValue" && stream.match(/^ *\$/, false)) {
return ["operator", ";"];
}
},
Expand Down
9 changes: 9 additions & 0 deletions mode/css/scss_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,13 @@

IT('mixin',
"@mixin container[1 (][2 $a: 10][1 , ][2 $b: 10][1 , ][2 $c: 10]) [1 {]}");

IT('nested',
"foo [1 { bar ][2 { ][1 } ]}");

IT('comma',
"foo [1 { font-family][2 : verdana, sans-serif][1 ; ]}");

IT('parentheses',
"foo [1 { color][2 : darken][3 ($blue, 9%][2 )][1 ; ]}");
})();
12 changes: 12 additions & 0 deletions mode/css/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,16 @@

IT("tagSelector",
"strong, em [1 { background][2 : rgba][3 (255, 255, 0, .2][2 )][1 ;]}");

IT("atMedia",
"[1 @media { foo ][2 { ][1 } ]}");

IT("comma",
"foo [1 { font-family][2 : verdana, sans-serif][1 ; ]}");

IT("parentheses",
"foo [1 { background][2 : url][3 (\"bar\"][2 )][1 ; ]}");

IT("pseudo",
"foo:before [1 { ]}");
})();
2 changes: 1 addition & 1 deletion mode/go/go.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ CodeMirror.defineMode("go", function(config) {
else return ctx.indented + (closing ? 0 : indentUnit);
},

electricChars: "{}:",
electricChars: "{}):",
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//"
Expand Down
2 changes: 1 addition & 1 deletion mode/haskell/haskell.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) {
token: function(stream, state) {
var t = state.f(stream, function(s) { state.f = s; });
var w = stream.current();
return (w in wellKnownWords) ? wellKnownWords[w] : t;
return wellKnownWords.hasOwnProperty(w) ? wellKnownWords[w] : t;
},

blockCommentStart: "{-",
Expand Down
2 changes: 1 addition & 1 deletion mode/htmlmixed/htmlmixed.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
if (close > -1) stream.backUp(cur.length - close);
else if (m = cur.match(/<\/?$/)) {
stream.backUp(cur.length);
if (!stream.match(pat, false)) stream.match(cur[0]);
if (!stream.match(pat, false)) stream.match(cur);
}
return style;
}
Expand Down
2 changes: 2 additions & 0 deletions mode/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ <h2>Language modes</h2>
<li><a href="jade/index.html">Jade</a></li>
<li><a href="javascript/index.html">JavaScript</a></li>
<li><a href="jinja2/index.html">Jinja2</a></li>
<li><a href="julia/index.html">Julia</a></li>
<li><a href="less/index.html">LESS</a></li>
<li><a href="livescript/index.html">LiveScript</a></li>
<li><a href="lua/index.html">Lua</a></li>
Expand All @@ -70,6 +71,7 @@ <h2>Language modes</h2>
<li><a href="ocaml/index.html">OCaml</a></li>
<li><a href="octave/index.html">Octave</a> (MATLAB)</li>
<li><a href="pascal/index.html">Pascal</a></li>
<li><a href="pegjs/index.html">PEG.js</a></li>
<li><a href="perl/index.html">Perl</a></li>
<li><a href="php/index.html">PHP</a></li>
<li><a href="pig/index.html">Pig Latin</a></li>
Expand Down
295 changes: 216 additions & 79 deletions mode/javascript/javascript.js

Large diffs are not rendered by default.

62 changes: 62 additions & 0 deletions mode/javascript/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,66 @@

MT("comma-and-binop",
"[keyword function](){ [keyword var] [def x] = [number 1] + [number 2], [def y]; }");

MT("destructuring",
"([keyword function]([def a], [[[def b], [def c] ]]) {",
" [keyword let] {[def d], [property foo]: [def c]=[number 10], [def x]} = [variable foo]([variable-2 a]);",
" [[[variable-2 c], [variable y] ]] = [variable-2 c];",
"})();");

MT("class",
"[keyword class] [variable Point] [keyword extends] [variable SuperThing] {",
" [[ [string-2 /expr/] ]]: [number 24],",
" [property constructor]([def x], [def y]) {",
" [keyword super]([string 'something']);",
" [keyword this].[property x] = [variable-2 x];",
" }",
"}");

MT("module",
"[keyword module] [string 'foo'] {",
" [keyword export] [keyword let] [def x] = [number 42];",
" [keyword export] [keyword *] [keyword from] [string 'somewhere'];",
"}");

MT("import",
"[keyword function] [variable foo]() {",
" [keyword import] [def $] [keyword from] [string 'jquery'];",
" [keyword module] [def crypto] [keyword from] [string 'crypto'];",
" [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];",
"}");

MT("const",
"[keyword function] [variable f]() {",
" [keyword const] [[ [def a], [def b] ]] = [[ [number 1], [number 2] ]];",
"}");

MT("for/of",
"[keyword for]([keyword let] [variable of] [keyword of] [variable something]) {}");

MT("generator",
"[keyword function*] [variable repeat]([def n]) {",
" [keyword for]([keyword var] [def i] = [number 0]; [variable-2 i] < [variable-2 n]; ++[variable-2 i])",
" [keyword yield] [variable-2 i];",
"}");

MT("fatArrow",
"[variable array].[property filter]([def a] => [variable-2 a] + [number 1]);",
"[variable a];", // No longer in scope
"[keyword let] [variable f] = ([[ [def a], [def b] ]], [def c]) => [variable-2 a] + [variable-2 c];",
"[variable c];");

MT("spread",
"[keyword function] [variable f]([def a], [meta ...][def b]) {",
" [variable something]([variable-2 a], [meta ...][variable-2 b]);",
"}");

MT("comprehension",
"[keyword function] [variable f]() {",
" [[ [variable x] + [number 1] [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];",
" ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] === [string 'blue']));",
"}");

MT("quasi",
"[variable re][string-2 `fofdlakj${][variable x] + ([variable re][string-2 `foo`]) + [number 1][string-2 }fdsa`] + [number 2]");
})();
187 changes: 187 additions & 0 deletions mode/julia/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<!doctype html>

<title>CodeMirror: Julia mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">

<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="julia.js"></script>
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
<div id=nav>
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>

<ul>
<li><a href="../../index.html">Home</a>
<li><a href="../../doc/manual.html">Manual</a>
<li><a href="https://github.com/marijnh/codemirror">Code</a>
</ul>
<ul>
<li><a href="../index.html">Language modes</a>
<li><a class=active href="#">Julia</a>
</ul>
</div>

<article>
<h2>Julia mode</h2>

<div><textarea id="code" name="code">
#numbers
1234
1234im
.234
.234im
2.23im
2.3f3
23e2
0x234

#strings
'a'
"asdf"
r"regex"
b"bytestring"

"""
multiline string
"""

#identifiers
a
as123
function_name!

#literal identifier multiples
3x
4[1, 2, 3]

#dicts and indexing
x=[1, 2, 3]
x[end-1]
x={"julia"=>"language of technical computing"}

#exception handling
try
f()
catch
@printf "Error"
finally
g()
end

#types
immutable Color{T<:Number}
r::T
g::T
b::T
end

#functions
function change!(x::Vector{Float64})
for i = 1:length(x)
x[i] *= 2
end
end

#function invocation
f('b', (2, 3)...)

#operators
|=
&=
^=
\-
%=
*=
+=
-=
<=
>=
!=
==
%
*
+
-
<
>
!
=
|
&
^
\
?
~
:
$
<:
.<
.>
<<
<<=
>>
>>>>
>>=
>>>=
<<=
<<<=
.<=
.>=
.==
->
//
in
...
//
:=
.//=
.*=
./=
.^=
.%=
.+=
.-=
\=
\\=
||
===
&&
|=
.|=
<:
>:
|>
<|
::
x ? y : z

#macros
@spawnat 2 1+1
@eval(:x)

#keywords and operators
if else elseif while for
begin let end do
try catch finally return break continue
global local const
export import importall using
function macro module baremodule
type immutable quote
true false enumerate


</textarea></div>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: {name: "julia",
},
lineNumbers: true,
indentUnit: 4,
tabMode: "shift",
matchBrackets: true
});
</script>

<p><strong>MIME types defined:</strong> <code>text/x-julia</code>.</p>
</article>
262 changes: 262 additions & 0 deletions mode/julia/julia.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
CodeMirror.defineMode("julia", function(_conf, parserConf) {
var ERRORCLASS = 'error';

function wordRegexp(words) {
return new RegExp("^((" + words.join(")|(") + "))\\b");
}

var operators = parserConf.operators || /^(?:\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|<:|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b|\.{3})/;
var delimiters = parserConf.delimiters || /^[;,()[\]{}]/;
var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*!*/;
var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch"];
var blockClosers = ["end", "else", "elseif", "catch", "finally"];
var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype', 'ccall'];
var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf'];

//var stringPrefixes = new RegExp("^[br]?('|\")")
var stringPrefixes = /^[br]?('|"{3}|")/;
var keywords = wordRegexp(keywordList);
var builtins = wordRegexp(builtinList);
var openers = wordRegexp(blockOpeners);
var closers = wordRegexp(blockClosers);
var macro = /@[_A-Za-z][_A-Za-z0-9]*!*/;
var indentInfo = null;

function in_array(state) {
var ch = cur_scope(state);
if(ch=="[" || ch=="{") {
return true;
}
else {
return false;
}
}

function cur_scope(state) {
if(state.scopes.length==0) {
return null;
}
return state.scopes[state.scopes.length - 1];
}

// tokenizers
function tokenBase(stream, state) {
// Handle scope changes
var leaving_expr = state.leaving_expr;
state.leaving_expr = false;
if(leaving_expr) {
if(stream.match(/^'+/)) {
return 'operator';
}
if(stream.match("...")) {
return 'operator';
}
}

if (stream.eatSpace()) {
return null;
}

var ch = stream.peek();
// Handle Comments
if (ch === '#') {
stream.skipToEnd();
return 'comment';
}
if(ch==='[') {
state.scopes.push("[");
}

if(ch==='{') {
state.scopes.push("{");
}

var scope=cur_scope(state);

if(scope==='[' && ch===']') {
state.scopes.pop();
state.leaving_expr=true;
}

if(scope==='{' && ch==='}') {
state.scopes.pop();
state.leaving_expr=true;
}

var match;
if(match=stream.match(openers, false)) {
state.scopes.push(match);
}

if(!in_array(state) && stream.match(closers, false)) {
state.scopes.pop();
}

if(in_array(state)) {
if(stream.match("end")) {
return 'number';
}

}
if(stream.match("=>")) {
return 'operator';
}
// Handle Number Literals
if (stream.match(/^[0-9\.]/, false)) {
var imMatcher = RegExp(/^im\b/);
var floatLiteral = false;
// Floats
if (stream.match(/^\d*\.\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; }
if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
if (stream.match(/^\.\d+/)) { floatLiteral = true; }
if (floatLiteral) {
// Float literals may be "imaginary"
stream.match(imMatcher);
return 'number';
}
// Integers
var intLiteral = false;
// Hex
if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
// Binary
if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
// Octal
if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
// Decimal
if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
// Decimal literals may be "imaginary"
stream.eat(/J/i);
// TODO - Can you have imaginary longs?
intLiteral = true;
}
// Zero by itself with no other piece of number.
if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
if (intLiteral) {
// Integer literals may be "long"
stream.match(imMatcher);
return 'number';
}
}

// Handle Strings
if (stream.match(stringPrefixes)) {
state.tokenize = tokenStringFactory(stream.current());
return state.tokenize(stream, state);
}

// Handle operators and Delimiters
if (stream.match(operators)) {
return 'operator';
}

if (stream.match(delimiters)) {
return null;
}

if (stream.match(keywords)) {
return 'keyword';
}

if (stream.match(builtins)) {
return 'builtin';
}

if (stream.match(macro)) {
return 'meta';
}

if (stream.match(identifiers)) {
state.leaving_expr=true;
return 'variable';
}
// Handle non-detected items
stream.next();
return ERRORCLASS;
}

function tokenStringFactory(delimiter) {
while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
delimiter = delimiter.substr(1);
}
var singleline = delimiter.length == 1;
var OUTCLASS = 'string';

function tokenString(stream, state) {
while (!stream.eol()) {
stream.eatWhile(/[^'"\\]/);
if (stream.eat('\\')) {
stream.next();
if (singleline && stream.eol()) {
return OUTCLASS;
}
} else if (stream.match(delimiter)) {
state.tokenize = tokenBase;
return OUTCLASS;
} else {
stream.eat(/['"]/);
}
}
if (singleline) {
if (parserConf.singleLineStringErrors) {
return ERRORCLASS;
} else {
state.tokenize = tokenBase;
}
}
return OUTCLASS;
}
tokenString.isString = true;
return tokenString;
}

function tokenLexer(stream, state) {
indentInfo = null;
var style = state.tokenize(stream, state);
var current = stream.current();

// Handle '.' connected identifiers
if (current === '.') {
style = stream.match(identifiers, false) ? null : ERRORCLASS;
if (style === null && state.lastStyle === 'meta') {
// Apply 'meta' style to '.' connected identifiers when
// appropriate.
style = 'meta';
}
return style;
}

return style;
}

var external = {
startState: function() {
return {
tokenize: tokenBase,
scopes: [],
leaving_expr: false
};
},

token: function(stream, state) {
var style = tokenLexer(stream, state);
state.lastStyle = style;
return style;
},

indent: function(state, textAfter) {
var delta = 0;
if(textAfter=="end" || textAfter=="]" || textAfter=="}" || textAfter=="else" || textAfter=="elseif" || textAfter=="catch" || textAfter=="finally") {
delta = -1;
}
return (state.scopes.length + delta) * 2;
},

lineComment: "#",
fold: "indent",
electricChars: "edlsifyh]}"
};
return external;
});


CodeMirror.defineMIME("text/x-julia", "julia");
6 changes: 4 additions & 2 deletions mode/less/less.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ CodeMirror.defineMode("less", function(config) {
stream.eatWhile(/[\a-zA-Z0-9\-_]/);
if(stream.peek() === " ")stream.eatSpace();
if(stream.peek() === ")" || type === ":")return ret("number", "unit");//rgba(0,0,0,.25);
else if(state.stack[state.stack.length-1] === "rule" && stream.peek().match(/{|,|\+|\(/) === null)return ret("number", "unit");
else if(stream.current().length >1){
if(state.stack[state.stack.length-1] === "rule" && stream.peek().match(/{|,|\+|\(/) === null)return ret("number", "unit");
}
return ret("tag", "tag");
} else if (ch == "#") {
//we don't eat white-space, we want the hex color and or id only
Expand Down Expand Up @@ -204,7 +206,7 @@ CodeMirror.defineMode("less", function(config) {
else if(type === "unit" && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");
else if(type === "unit" && state.stack[state.stack.length-1] === ";")return ret(null, "unit");
else if(type === ")" && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");
else if(type.match("@") !== null && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");
else if(type && type.match("@") !== null && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");
//else if(type === "unit" && state.stack[state.stack.length-1] === "rule")return ret(null, stream.current());

else if((type === ";" || type === "}" || type === ",") && state.stack[state.stack.length-1] === ";")return ret("tag", stream.current());
Expand Down
23 changes: 16 additions & 7 deletions mode/markdown/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
, ulRE = /^[*\-+]\s+/
, olRE = /^[0-9]+\.\s+/
, taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
, headerRE = /^(?:\={1,}|-{1,})$/
, atxHeaderRE = /^#+/
, setextHeaderRE = /^(?:\={1,}|-{1,})$/
, textRE = /^[^!\[\]*_\\<>` "'(]+/;

function switchInline(stream, state, f) {
Expand Down Expand Up @@ -127,14 +128,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.listDepth = 0;
}

var match = null;
if (state.indentationDiff >= 4) {
state.indentation -= 4;
stream.skipToEnd();
return code;
} else if (stream.eatSpace()) {
return null;
} else if (stream.peek() === '#' || (state.prevLineHasContent && stream.match(headerRE)) ) {
state.header = true;
} else if (match = stream.match(atxHeaderRE)) {
state.header = match[0].length <= 6 ? match[0].length : 6;
} else if (state.prevLineHasContent && (match = stream.match(setextHeaderRE))) {
state.header = match[0].charAt(0) == '=' ? 1 : 2;
} else if (stream.eat('>')) {
state.indentation++;
state.quote = 1;
Expand Down Expand Up @@ -207,7 +211,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {

if (state.code) { styles.push(code); }

if (state.header) { styles.push(header); }
if (state.header) { styles.push(header); styles.push(header + state.header); }
if (state.quote) { styles.push(state.quote % 2 ? quote1 : quote2); }
if (state.list !== false) {
var listMod = (state.listDepth - 1) % 3;
Expand Down Expand Up @@ -257,6 +261,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.taskOpen = false;
state.taskClosed = false;

// Get sol() value now, before character is consumed
var sol = stream.sol();

var ch = stream.next();

if (ch === '\\') {
Expand Down Expand Up @@ -355,7 +362,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
var t = getType(state);
if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
if (sol && stream.peek() === ' ') {
// Do nothing, surrounded by newline and space
} else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
state.strong = false;
return t;
} else if (!state.strong && stream.eat(ch)) { // Add STRONG
Expand Down Expand Up @@ -466,7 +475,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
linkTitle: false,
em: false,
strong: false,
header: false,
header: 0,
taskList: false,
list: false,
listDepth: 0,
Expand Down Expand Up @@ -517,7 +526,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}

// Reset state.header
state.header = false;
state.header = 0;

// Reset state.taskList
state.taskList = false;
Expand Down
30 changes: 19 additions & 11 deletions mode/markdown/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,27 +87,31 @@
// http://daringfireball.net/projects/markdown/syntax#header

MT("atxH1",
"[header # foo]");
"[header&header1 # foo]");

MT("atxH2",
"[header ## foo]");
"[header&header2 ## foo]");

MT("atxH3",
"[header ### foo]");
"[header&header3 ### foo]");

MT("atxH4",
"[header #### foo]");
"[header&header4 #### foo]");

MT("atxH5",
"[header ##### foo]");
"[header&header5 ##### foo]");

MT("atxH6",
"[header ###### foo]");
"[header&header6 ###### foo]");

// H6 - 7x '#' should still be H6, per Dingus
// http://daringfireball.net/projects/markdown/dingus
MT("atxH6NotH7",
"[header ####### foo]");
"[header&header6 ####### foo]");

// Inline styles should be parsed inside headers
MT("atxH1inline",
"[header&header1 # foo ][header&header1&em *bar*]");

// Setext headers - H1, H2
// Per documentation, "Any number of underlining =’s or -’s will work."
Expand All @@ -119,22 +123,22 @@
// Check if single underlining = works
MT("setextH1",
"foo",
"[header =]");
"[header&header1 =]");

// Check if 3+ ='s work
MT("setextH1",
"foo",
"[header ===]");
"[header&header1 ===]");

// Check if single underlining - works
MT("setextH2",
"foo",
"[header -]");
"[header&header2 -]");

// Check if 3+ -'s work
MT("setextH2",
"foo",
"[header ---]");
"[header&header2 ---]");

// Single-line blockquote with trailing space
MT("blockquoteSpace",
Expand Down Expand Up @@ -577,6 +581,10 @@
MT("emEscapedBySpaceOut",
"foo _ bar[em _hello_]world");

MT("emEscapedByNewline",
"foo",
"_ bar[em _hello_]world");

// Unclosed emphasis characters
// Instead of simply marking as EM / STRONG, it would be nice to have an
// incomplete flag for EM and STRONG, that is styled slightly different.
Expand Down
4 changes: 3 additions & 1 deletion mode/meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ CodeMirror.modeInfo = [
{name: 'JSON', mime: 'application/x-json', mode: 'javascript'},
{name: 'JSON', mime: 'application/json', mode: 'javascript'},
{name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'},
{name: 'Jinja2', mime: 'jinja2', mode: 'jinja2'},
{name: 'Jinja2', mime: null, mode: 'jinja2'},
{name: 'Julia', mime: 'text/x-julia', mode: 'julia'},
{name: 'LESS', mime: 'text/x-less', mode: 'less'},
{name: 'LiveScript', mime: 'text/x-livescript', mode: 'livescript'},
{name: 'Lua', mime: 'text/x-lua', mode: 'lua'},
Expand All @@ -47,6 +48,7 @@ CodeMirror.modeInfo = [
{name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'},
{name: 'Octave', mime: 'text/x-octave', mode: 'octave'},
{name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'},
{name: 'PEG.js', mime: null, mode: 'pegjs'},
{name: 'Perl', mime: 'text/x-perl', mode: 'perl'},
{name: 'PHP', mime: 'text/x-php', mode: 'php'},
{name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'},
Expand Down
66 changes: 66 additions & 0 deletions mode/pegjs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!doctype html>
<html>
<head>
<title>CodeMirror: PEG.js Mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">

<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../javascript/javascript.js"></script>
<script src="pegjs.js"></script>
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
</head>
<body>
<div id=nav>
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>

<ul>
<li><a href="../../index.html">Home</a>
<li><a href="../../doc/manual.html">Manual</a>
<li><a href="https://github.com/marijnh/codemirror">Code</a>
</ul>
<ul>
<li><a href="../index.html">Language modes</a>
<li><a class=active href="#">PEG.js Mode</a>
</ul>
</div>

<article>
<h2>PEG.js Mode</h2>
<form><textarea id="code" name="code">
/*
* Classic example grammar, which recognizes simple arithmetic expressions like
* "2*(3+4)". The parser generated from this grammar then computes their value.
*/

start
= additive

additive
= left:multiplicative "+" right:additive { return left + right; }
/ multiplicative

multiplicative
= left:primary "*" right:multiplicative { return left * right; }
/ primary

primary
= integer
/ "(" additive:additive ")" { return additive; }

integer "integer"
= digits:[0-9]+ { return parseInt(digits.join(""), 10); }

letter = [a-z]+</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: {name: "pegjs"},
lineNumbers: true
});
</script>
<h3>The PEG.js Mode</h3>
<p> Created by Forbes Lindesay.</p>
</article>
</body>
</html>
103 changes: 103 additions & 0 deletions mode/pegjs/pegjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
CodeMirror.defineMode("pegjs", function (config) {
var jsMode = CodeMirror.getMode(config, "javascript");

function identifier(stream) {
return stream.match(/^[a-zA-Z_][a-zA-Z0-9_]*/);
}

return {
startState: function () {
return {
inString: false,
stringType: null,
inComment: false,
inChracterClass: false,
braced: 0,
lhs: true,
localState: null
};
},
token: function (stream, state) {
if (stream)

//check for state changes
if (!state.inString && !state.inComment && ((stream.peek() == '"') || (stream.peek() == "'"))) {
state.stringType = stream.peek();
stream.next(); // Skip quote
state.inString = true; // Update state
}
if (!state.inString && !state.inComment && stream.match(/^\/\*/)) {
state.inComment = true;
}

//return state
if (state.inString) {
while (state.inString && !stream.eol()) {
if (stream.peek() === state.stringType) {
stream.next(); // Skip quote
state.inString = false; // Clear flag
} else if (stream.peek() === '\\') {
stream.next();
stream.next();
} else {
stream.match(/^.[^\\\"\']*/);
}
}
return state.lhs ? "property string" : "string"; // Token style
} else if (state.inComment) {
while (state.inComment && !stream.eol()) {
if (stream.match(/\*\//)) {
state.inComment = false; // Clear flag
} else {
stream.match(/^.[^\*]*/);
}
}
return "comment";
} else if (state.inChracterClass) {
if (stream.match(/^[^\]\\]+/)) {
return;
} else if (stream.match(/^\\./)) {
return;
} else {
stream.next();
state.inChracterClass = false;
return 'bracket';
}
} else if (stream.peek() === '[') {
stream.next();
state.inChracterClass = true;
return 'bracket';
} else if (stream.match(/^\/\//)) {
stream.skipToEnd();
return "comment";
} else if (state.braced || stream.peek() === '{') {
if (state.localState === null) {
state.localState = jsMode.startState();
}
var token = jsMode.token(stream, state.localState);
var text = stream.current();
if (!token) {
for (var i = 0; i < text.length; i++) {
if (text[i] === '{') {
state.braced++;
} else if (text[i] === '}') {
state.braced--;
}
};
}
return token;
} else if (identifier(stream)) {
if (stream.peek() === ':') {
return 'variable';
}
return 'variable-2';
} else if (['[', ']', '(', ')'].indexOf(stream.peek()) != -1) {
stream.next();
return 'bracket';
} else if (!stream.eatSpace()) {
stream.next();
}
return null;
}
};
}, "javascript");
2 changes: 1 addition & 1 deletion mode/pig/pig.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ CodeMirror.defineMode("pig", function(_config, parserConfig) {
+ "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL "
+ "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE "
+ "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE "
+ "NEQ MATCHES TRUE FALSE ";
+ "NEQ MATCHES TRUE FALSE DUMP";

// data types
var pTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP ";
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codemirror",
"version":"3.19.0",
"version":"3.20.0",
"main": "lib/codemirror.js",
"description": "In-browser code editing made bearable",
"licenses": [{"type": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion theme/ambiance.css
4 changes: 3 additions & 1 deletion theme/mbo.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@

.cm-s-mbo .CodeMirror-activeline-background {background: #494b41 !important;}
.cm-s-mbo .CodeMirror-matchingbracket {
text-decoration: underline;
text-decoration: underline;
color: #f5e107 !important;
}

.cm-s-mbo .CodeMirror-matchingtag {background: #4e4e4e;}

div.CodeMirror span.CodeMirror-searching {
background-color: none;
Expand Down