1 change: 1 addition & 0 deletions addon/lint/javascript-lint.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"Unclosed string", "Stopping, unable to continue" ];

function validator(text, options) {
if (!window.JSHINT) return [];
JSHINT(text, options);
var errors = JSHINT.data().errors, result = [];
if (errors) parseErrors(errors, result);
Expand Down
25 changes: 15 additions & 10 deletions addon/merge/merge.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
}
};

function ensureDiff(dv) {
if (dv.diffOutOfDate) {
dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue());
dv.diffOutOfDate = false;
CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
}
}

function registerUpdate(dv) {
var edit = {from: 0, to: 0, marked: []};
var orig = {from: 0, to: 0, marked: []};
Expand All @@ -65,11 +73,7 @@
clearMarks(dv.orig, orig.marked, dv.classes);
edit.from = edit.to = orig.from = orig.to = 0;
}
if (dv.diffOutOfDate) {
dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue());
dv.diffOutOfDate = false;
CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
}
ensureDiff(dv);
if (dv.showDifferences) {
updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);
updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
Expand Down Expand Up @@ -165,7 +169,7 @@
var mark = arr[i];
if (mark instanceof CodeMirror.TextMarker) {
mark.clear();
} else {
} else if (mark.parent) {
editor.removeLineClass(mark, "background", classes.chunk);
editor.removeLineClass(mark, "background", classes.start);
editor.removeLineClass(mark, "background", classes.end);
Expand Down Expand Up @@ -362,10 +366,10 @@
if (this.left) this.left.setShowDifferences(val);
},
rightChunks: function() {
return this.right && getChunks(this.right.diff);
return this.right && getChunks(this.right);
},
leftChunks: function() {
return this.left && getChunks(this.left.diff);
return this.left && getChunks(this.left);
}
};

Expand Down Expand Up @@ -416,9 +420,10 @@
f(startOrig, orig.line + 1, startEdit, edit.line + 1);
}

function getChunks(diff) {
function getChunks(dv) {
ensureDiff(dv);
var collect = [];
iterateChunks(diff, function(topOrig, botOrig, topEdit, botEdit) {
iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) {
collect.push({origFrom: topOrig, origTo: botOrig,
editFrom: topEdit, editTo: botEdit});
});
Expand Down
10 changes: 8 additions & 2 deletions addon/mode/overlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
base: CodeMirror.startState(base),
overlay: CodeMirror.startState(overlay),
basePos: 0, baseCur: null,
overlayPos: 0, overlayCur: null
overlayPos: 0, overlayCur: null,
lineSeen: null
};
},
copyState: function(state) {
Expand All @@ -36,6 +37,12 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
},

token: function(stream, state) {
if (stream.sol() || stream.string != state.lineSeen ||
Math.min(state.basePos, state.overlayPos) < stream.start) {
state.lineSeen = stream.string;
state.basePos = state.overlayPos = stream.start;
}

if (stream.start == state.basePos) {
state.baseCur = base.token(stream, state.base);
state.basePos = stream.pos;
Expand All @@ -46,7 +53,6 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
state.overlayPos = stream.pos;
}
stream.pos = Math.min(state.basePos, state.overlayPos);
if (stream.eol()) state.basePos = state.overlayPos = 0;

if (state.overlayCur == null) return state.baseCur;
if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
Expand Down
1 change: 1 addition & 0 deletions addon/runmode/runmode-standalone.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ CodeMirror.runMode = function (string, modespec, callback, options) {
for (var i = 0, e = lines.length; i < e; ++i) {
if (i) callback("\n");
var stream = new CodeMirror.StringStream(lines[i]);
if (!stream.string && mode.blankLine) mode.blankLine();
while (!stream.eol()) {
var style = mode.token(stream, state);
callback(stream.current(), style, i, stream.start, state);
Expand Down
1 change: 1 addition & 0 deletions addon/runmode/runmode.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ CodeMirror.runMode = function(string, modespec, callback, options) {
for (var i = 0, e = lines.length; i < e; ++i) {
if (i) callback("\n");
var stream = new CodeMirror.StringStream(lines[i]);
if (!stream.string && mode.blankLine) mode.blankLine();
while (!stream.eol()) {
var style = mode.token(stream, state);
callback(stream.current(), style, i, stream.start, state);
Expand Down
1 change: 1 addition & 0 deletions addon/runmode/runmode.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ exports.runMode = function(string, modespec, callback, options) {
for (var i = 0, e = lines.length; i < e; ++i) {
if (i) callback("\n");
var stream = new exports.StringStream(lines[i]);
if (!stream.string && mode.blankLine) mode.blankLine();
while (!stream.eol()) {
var style = mode.token(stream, state);
callback(stream.current(), style, i, stream.start, state);
Expand Down
28 changes: 14 additions & 14 deletions addon/search/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@
})(function(CodeMirror) {
"use strict";
function searchOverlay(query, caseInsensitive) {
var startChar;
if (typeof query == "string") {
startChar = query.charAt(0);
query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"),
caseInsensitive ? "i" : "");
} else {
query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : "");
}
if (typeof query == "string")
query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
else if (!query.global)
query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");

return {token: function(stream) {
if (stream.match(query)) return "searching";
while (!stream.eol()) {
stream.next();
if (startChar && !caseInsensitive)
stream.skipTo(startChar) || stream.skipToEnd();
if (stream.match(query, false)) break;
query.lastIndex = stream.pos;
var match = query.exec(stream.string);
if (match && match.index == stream.pos) {
stream.pos += match[0].length;
return "searching";
} else if (match) {
stream.pos = match.index;
} else {
stream.skipToEnd();
}
}};
}
Expand Down
2 changes: 1 addition & 1 deletion demo/bidi.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ <h2>Bi-directional Text Demo</h2>
<dt id=option_theme><code>theme (string)</code></dt>
<dd>موضوع لنمط المحرر مع. يجب عليك التأكد من الملف CSS تحديد
المقابلة <code>.cm-s-[name]</code> يتم تحميل أنماط (انظر
<a href=../theme/><code>theme</code></a> الدليل في التوزيع).
<a href="../theme/"><code>theme</code></a> الدليل في التوزيع).
الافتراضي هو <code>"default"</code> ، والتي تم تضمينها في
الألوان <code>codemirror.css</code>. فمن الممكن استخدام فئات متعددة
في تطبيق السمات مرة واحدة على سبيل المثال <code>"foo bar"</code>
Expand Down
26 changes: 26 additions & 0 deletions demo/merge.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,31 @@ <h2>merge view demo</h2>
.replace("white", "purple;\n font: comic sans;\n text-decoration: underline;\n height: 15em");
initUI(2);
};

function mergeViewHeight(mergeView) {
function editorHeight(editor) {
if (!editor) return 0;
return editor.getScrollInfo().height;
}
return Math.max(editorHeight(mergeView.leftOriginal()),
editorHeight(mergeView.editor()),
editorHeight(mergeView.rightOriginal()));
}

function resize(mergeView) {
var height = mergeViewHeight(mergeView);
for(;;) {
if (mergeView.leftOriginal())
mergeView.leftOriginal().setSize(null, height);
mergeView.editor().setSize(null, height);
if (mergeView.rightOriginal())
mergeView.rightOriginal().setSize(null, height);

var newHeight = mergeViewHeight(mergeView);
if (newHeight >= height) break;
else height = newHeight;
}
mergeView.wrap.style.height = height + "px";
}
</script>
</article>
12 changes: 9 additions & 3 deletions demo/vim.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<script src="../addon/dialog/dialog.js"></script>
<script src="../addon/search/searchcursor.js"></script>
<script src="../mode/clike/clike.js"></script>
<script src="../addon/edit/matchbrackets.js"></script>
<script src="../keymap/vim.js"></script>
<style type="text/css">
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;}
Expand Down Expand Up @@ -51,22 +52,27 @@ <h2>Vim bindings demo</h2>

<p>The vim keybindings are enabled by
including <a href="../keymap/vim.js">keymap/vim.js</a> and setting
the <code>keyMap</code> option to <code>"vim"</code>. Because
CodeMirror's internal API is quite different from Vim, they are only
a loose approximation of actual vim bindings, though.</p>
the <code>vimMode</code> option to <code>true</code>. This will also
automatically change the <code>keyMap</code> option to <code>"vim"</code>.</p>

<p>Note that while the vim mode tries to emulate the most useful features of
vim as faithfully as possible, it does not strive to become a complete vim
implementation</p>

<script>
CodeMirror.commands.save = function(){ alert("Saving"); };
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "text/x-csrc",
vimMode: true,
matchBrackets: true,
showCursorWhenSelecting: true
});
var editor2 = CodeMirror.fromTextArea(document.getElementById("code2"), {
lineNumbers: true,
mode: "text/x-csrc",
vimMode: true,
matchBrackets: true,
showCursorWhenSelecting: true
});
</script>
Expand Down
4 changes: 4 additions & 0 deletions demo/xmlcomplete.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ <h2>XML Autocomplete Demo</h2>

var tags = {
"!top": ["top"],
"!attrs": {
id: null,
class: ["A", "B", "C"]
},
top: {
attrs: {
lang: ["en", "de", "fr", "nl"],
Expand Down
4 changes: 4 additions & 0 deletions doc/compress.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ <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=4.1.0;f=">4.1</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=4.0.3;f=">4.0</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=3.23.0;f=">3.23</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=3.22.0;f=">3.22</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=3.21.0;f=">3.21</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=3.20.0;f=">3.20</option>
Expand Down Expand Up @@ -92,7 +94,9 @@ <h2>Script compression helper</h2>
<option value="http://codemirror.net/mode/css/css.js">css.js</option>
<option value="http://codemirror.net/mode/d/d.js">d.js</option>
<option value="http://codemirror.net/mode/diff/diff.js">diff.js</option>
<option value="http://codemirror.net/mode/django/django.js">django.js</option>
<option value="http://codemirror.net/mode/dtd/dtd.js">dtd.js</option>
<option value="http://codemirror.net/mode/dylan/dylan.js">dylan.js</option>
<option value="http://codemirror.net/mode/ecl/ecl.js">ecl.js</option>
<option value="http://codemirror.net/mode/eiffel/eiffel.js">eiffel.js</option>
<option value="http://codemirror.net/mode/erlang/erlang.js">erlang.js</option>
Expand Down
4 changes: 2 additions & 2 deletions doc/docs.css
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ section.first {
z-index: 25;
}

#bankinfo {
.bankinfo {
text-align: left;
display: none;
padding: 0 .5em;
Expand All @@ -151,7 +151,7 @@ section.first {
left: 30px;
}

#bankinfo_close {
.bankinfo_close {
position: absolute;
top: 0; right: 6px;
font-weight: bold;
Expand Down
24 changes: 18 additions & 6 deletions doc/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
<section class=first id=overview>
<h2 style="position: relative">
User manual and reference guide
<span style="color: #888; font-size: 1rem; position: absolute; right: 0; bottom: 0">version 4.0.3</span>
<span style="color: #888; font-size: 1rem; position: absolute; right: 0; bottom: 0">version 4.1.0</span>
</h2>

<p>CodeMirror is a code-editor component that can be embedded in
Expand Down Expand Up @@ -910,6 +910,10 @@ <h2>Commands</h2>
<dt class=command id=command_insertTab><code><strong>insertTab</strong></code></dt>
<dd>Insert a tab character at the cursor.</dd>

<dt class=command id=command_insertSoftTab><code><strong>insertSoftTab</strong></code></dt>
<dd>Insert the amount of spaces that match the width a tab at
the cursor position would have.</dd>

<dt class=command id=command_defaultTab><code><strong>defaultTab</strong></code><span class=keybinding>Tab</span></dt>
<dd>If something is selected, indent it by
one <a href="#option_indentUnit">indent unit</a>. If nothing is
Expand Down Expand Up @@ -2800,12 +2804,20 @@ <h2>Writing CodeMirror Modes</h2>
continued lines in a block comment). All of these are
optional.</p>

<p id="electricChars">Finally, a mode may define
an <code>electricChars</code> property, which should hold a string
containing all the characters that should trigger the behaviour
described for
<p id="electricChars">Finally, a mode may define either
an <code>electricChars</code> or an <code>electricInput</code>
property, which are used to automatically reindent the line when
certain patterns are typed and
the <a href="#option_electricChars"><code>electricChars</code></a>
option.</p>
option is enabled. <code>electricChars</code> may be a string, and
will trigger a reindent whenever one of the characters in that
string are typed. Often, it is more appropriate to
use <code>electricInput</code>, which should hold a regular
expression, and will trigger indentation when the part of the
line <em>before</em> the cursor matches the expression. It should
usually end with a <code>$</code> character, so that it only
matches when the indentation-changing pattern was just typed, not when something was
typed after the pattern.</p>

<p>So, to summarize, a mode <em>must</em> provide
a <code>token</code> method, and it <em>may</em>
Expand Down
2 changes: 2 additions & 0 deletions doc/realworld.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ <h2>CodeMirror real-world uses</h2>
<li><a href="https://metacpan.org/module/Farabi">Farabi</a> (modern Perl IDE)</li>
<li><a href="http://blog.pamelafox.org/2012/02/interactive-html5-slides-with-fathomjs.html">FathomJS integration</a> (slides with editors, again)</li>
<li><a href="http://fiddlesalad.com/">Fiddle Salad</a> (web development environment)</li>
<li><a href="https://github.com/simogeo/Filemanager">Filemanager</a></li>
<li><a href="https://hacks.mozilla.org/2013/11/firefox-developer-tools-episode-27-edit-as-html-codemirror-more/">Firefox Developer Tools</a></li>
<li><a href="http://www.firepad.io">Firepad</a> (collaborative text editor)</li>
<li><a href="https://code.google.com/p/gerrit/">Gerrit</a>'s diff view</a></li>
Expand Down Expand Up @@ -119,6 +120,7 @@ <h2>CodeMirror real-world uses</h2>
<li><a href="http://snippets.pro/">Snippets.pro</a> (code snippet sharing)</li>
<li><a href="http://www.solidshops.com/">SolidShops</a> (hosted e-commerce platform)</li>
<li><a href="http://sqlfiddle.com">SQLFiddle</a> (SQL playground)</li>
<li><a href="http://www.tagspaces.org/">TagSpaces</a> (personal data manager)</li>
<li><a href="https://thefiletree.com">The File Tree</a> (collab editor)</li>
<li><a href="http://www.mapbox.com/tilemill/">TileMill</a> (map design tool)</li>
<li><a href="http://www.toolsverse.com/products/data-explorer/">Toolsverse Data Explorer</a> (database management)</li>
Expand Down
38 changes: 35 additions & 3 deletions doc/releases.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
<li><a href="https://github.com/marijnh/codemirror">Code</a>
</ul>
<ul>
<li><a class=active data-default="true" href="#v3">Version 3.x</a>
<li><a class=active data-default="true" href="#v4">Version 4.x</a>
<li><a href="#v3">Version 3.x</a>
<li><a href="#v2">Version 2.x</a>
<li><a href="#v1">Version 0.x</a>
</ul>
Expand All @@ -26,7 +27,22 @@ <h2>Release notes and version history</h2>

<section id=v3 class=first>

<h2>Version 4.x</h2>
<h2 id="v4">Version 4.x</h2>

<p class="rel">22-04-2014: <a href="http://codemirror.net/codemirror-4.1.zip">Version 4.1</a>:</p>

<ul class="rel-note">
<li><em>Slightly incompatible</em>:
The <a href="manual.html#event_cursorActivity"><code>"cursorActivity"</code></a>
event now fires after all other events for the operation (and only
for handlers that were actually registered at the time the
activity happened).</li>
<li>New command: <a href="manual.html#command_insertSoftTab"><code>insertSoftTab</code></a>.</li>
<li>New mode: <a href="../mode/django/index.html">Django</a>.</li>
<li>Improved modes: <a href="../mode/verilog/index.html">Verilog</a> (rewritten), <a href="../mode/jinja2/index.html">Jinja2</a>, <a href="../mode/haxe/index.html">Haxe</a>, <a href="../mode/php/index.html">PHP</a> (string interpolation highlighted), <a href="../mode/javascript/index.html">JavaScript</a> (indentation of trailing else, template strings), <a href="../mode/livescript/index.html">LiveScript</a> (multi-line strings).</li>
<li>Many small issues from the 3.x→4.x transition were found and fixed.</li>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/4.0.3...4.1.0">list of patches</a>.</li>
</ul>

<p class="rel">20-03-2014: <a href="http://codemirror.net/codemirror-4.0.zip">Version 4.0</a>:</p>

Expand All @@ -44,7 +60,23 @@ <h2>Version 4.x</h2>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/3.23.0...4.0.3">list of patches</a>.</li>
</ul>

<h2>Version 3.x</h2>
<h2 id="v3">Version 3.x</h2>

<p class="rel">22-04-2014: <a href="http://codemirror.net/codemirror-3.24.zip">Version 3.24</a>:</p>

<p class="rel-note">Merges the improvements from 4.1 that could
easily be applied to the 3.x code. Also improves the way the editor
size is updated when line widgets change.</p>

<p class="rel">20-03-2014: <a href="http://codemirror.net/codemirror-3.23.zip">Version 3.23</a>:</p>

<ul class="rel-note">
<li>In the <a href="../mode/xml/index.html">XML mode</a>,
add <code>brackets</code> style to angle brackets, fix
case-sensitivity of tags for HTML.</li>
<li>New mode: <a href="../mode/dylan/index.html">Dylan</a>.</li>
<li>Many improvements to the <a href="../demo/vim.html">Vim bindings</a>.</li>
</ul>

<p class="rel">21-02-2014: <a href="http://codemirror.net/codemirror-3.22.zip">Version 3.22</a>:</p>

Expand Down
3 changes: 0 additions & 3 deletions doc/upgrade_v4.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@

<h2 id=upgrade>Upgrading to version 4</h2>

<p><strong>Note:</strong> Version 4 hasn't been released yet. The
information here is provisional and might still change.</p>

<p>CodeMirror 4's interface is <em>very</em> close version 3, but it
does fix a few awkward details in a backwards-incompatible ways. At
least skim the text below before upgrading.</p>
Expand Down
11 changes: 8 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,23 +85,28 @@ <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 4.0</strong> (<a href="doc/releases.html">Release notes</a>)</div>
<div><strong>version 4.1</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>
<div style="position: relative">
or <span onclick="document.getElementById('bankinfo').style.display = 'block';" class=quasilink>Bank</span>,
<span onclick="document.getElementById('bcinfo').style.display = 'block';" class=quasilink>Bitcoin</span>,
<a href="https://www.gittip.com/marijnh">Gittip</a>,
<a href="https://flattr.com/profile/marijnh">Flattr</a><br>
<div id=bankinfo>
<span id=bankinfo_close onclick="document.getElementById('bankinfo').style.display = '';">×</span>
<div id=bankinfo class=bankinfo>
<span class=bankinfo_close onclick="document.getElementById('bankinfo').style.display = '';">×</span>
Bank: <i>Rabobank</i><br/>
Country: <i>Netherlands</i><br/>
SWIFT: <i>RABONL2U</i><br/>
Account: <i>147850770</i><br/>
Name: <i>Marijn Haverbeke</i><br/>
IBAN: <i>NL26 RABO 0147 8507 70</i>
</div>
<div id=bcinfo class=bankinfo>
<span class=bankinfo_close onclick="document.getElementById('bcinfo').style.display = '';">×</span>
Bitcoin address: 1HVnnU8E9yLPeFyNgNtUPB5deXBvUmZ6Nx
</div>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" id="paypal">
<input type="hidden" name="cmd" value="_s-xclick"/>
<input type="hidden" name="hosted_button_id" value="3FVHS5FGUY7CC"/>
Expand Down
16 changes: 14 additions & 2 deletions keymap/sublime.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,22 @@
cmds[map["Alt-Right"] = "goSubwordRight"] = function(cm) { moveSubword(cm, 1); };

cmds[map[ctrl + "Up"] = "scrollLineUp"] = function(cm) {
cm.scrollTo(null, cm.getScrollInfo().top - cm.defaultTextHeight());
var info = cm.getScrollInfo();
if (!cm.somethingSelected()) {
var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
if (cm.getCursor().line >= visibleBottomLine)
cm.execCommand("goLineUp");
}
cm.scrollTo(null, info.top - cm.defaultTextHeight());
};
cmds[map[ctrl + "Down"] = "scrollLineDown"] = function(cm) {
cm.scrollTo(null, cm.getScrollInfo().top + cm.defaultTextHeight());
var info = cm.getScrollInfo();
if (!cm.somethingSelected()) {
var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
if (cm.getCursor().line <= visibleTopLine)
cm.execCommand("goLineDown");
}
cm.scrollTo(null, info.top + cm.defaultTextHeight());
};

cmds[map["Shift-" + ctrl + "L"] = "splitSelectionByLine"] = function(cm) {
Expand Down
353 changes: 212 additions & 141 deletions keymap/vim.js

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions lib/codemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,12 @@ div.CodeMirror-overwrite div.CodeMirror-cursor {}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable {color: black;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-property {color: black;}
.cm-s-default .cm-operator {color: black;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
Expand Down Expand Up @@ -250,6 +251,7 @@ div.CodeMirror-cursors {

.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }

.cm-searching {
background: #ffa;
Expand Down
417 changes: 302 additions & 115 deletions lib/codemirror.js

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions mode/clike/clike.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
};
});

(function() {
function words(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
Expand Down Expand Up @@ -251,6 +250,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
}

function def(mimes, mode) {
if (typeof mimes == "string") mimes = [mimes];
var words = [];
function add(obj) {
if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
Expand Down Expand Up @@ -295,7 +295,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
},
modeProps: {fold: ["brace", "include"]}
});
CodeMirror.defineMIME("text/x-java", {
def("text/x-java", {
name: "clike",
keywords: words("abstract assert boolean break byte case catch char class const continue default " +
"do double else enum extends final finally float for goto if implements import " +
Expand All @@ -312,7 +312,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
},
modeProps: {fold: ["brace", "import"]}
});
CodeMirror.defineMIME("text/x-csharp", {
def("text/x-csharp", {
name: "clike",
keywords: words("abstract as base break case catch checked class const continue" +
" default delegate do else enum event explicit extern finally fixed for" +
Expand All @@ -338,7 +338,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
}
}
});
CodeMirror.defineMIME("text/x-scala", {
def("text/x-scala", {
name: "clike",
keywords: words(

Expand Down Expand Up @@ -432,6 +432,5 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
hooks: {"#": cppHook},
modeProps: {fold: ["brace", "include"]}
});
}());

});
4 changes: 4 additions & 0 deletions mode/clike/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<link rel="stylesheet" href="../../addon/hint/show-hint.css">
<script src="../../addon/hint/show-hint.js"></script>
<script src="clike.js"></script>
<style>.CodeMirror {border: 2px inset #dee;}</style>
<div id=nav>
Expand Down Expand Up @@ -185,6 +187,8 @@ <h2>Java example</h2>
matchBrackets: true,
mode: "text/x-java"
});
var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;
CodeMirror.keyMap.default[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";
</script>

<p>Simple mode that tries to handle C-like languages as well as it
Expand Down
2 changes: 1 addition & 1 deletion mode/cobol/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ <h2>COBOL mode</h2>
function selectFontsize() {
var fontSizeInput = document.getElementById("selectFontSize");
var fontSize = fontSizeInput.options[fontSizeInput.selectedIndex].innerHTML;
editor.getWrapperElement().style["font-size"] = fontSize;
editor.getWrapperElement().style.fontSize = fontSize;
editor.refresh();
}
function selectReadOnly() {
Expand Down
29 changes: 23 additions & 6 deletions mode/css/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
mediaTypes = parserConfig.mediaTypes || {},
mediaFeatures = parserConfig.mediaFeatures || {},
propertyKeywords = parserConfig.propertyKeywords || {},
nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
colorKeywords = parserConfig.colorKeywords || {},
valueKeywords = parserConfig.valueKeywords || {},
fontProperties = parserConfig.fontProperties || {},
Expand Down Expand Up @@ -91,7 +92,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {

function tokenParenthesized(stream, state) {
stream.next(); // Must be '('
if (!stream.match(/\s*[\"\']/, false))
if (!stream.match(/\s*[\"\')]/, false))
state.tokenize = tokenString(")");
else
state.tokenize = null;
Expand Down Expand Up @@ -170,9 +171,13 @@ CodeMirror.defineMode("css", function(config, parserConfig) {

states.block = function(type, stream, state) {
if (type == "word") {
if (propertyKeywords.hasOwnProperty(stream.current().toLowerCase())) {
var word = stream.current().toLowerCase();
if (propertyKeywords.hasOwnProperty(word)) {
override = "property";
return "maybeprop";
} else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
override = "string-2";
return "maybeprop";
} else if (allowNested) {
override = stream.match(/^\s*:/, false) ? "property" : "tag";
return "block";
Expand Down Expand Up @@ -444,19 +449,27 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"vertical-align", "visibility", "voice-balance", "voice-duration",
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
"voice-volume", "volume", "white-space", "widows", "width", "word-break",
"word-spacing", "word-wrap", "z-index", "zoom",
"word-spacing", "word-wrap", "z-index",
// SVG-specific
"clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
"flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
"color-interpolation", "color-interpolation-filters", "color-profile",
"color-interpolation", "color-interpolation-filters",
"color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
"marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
"stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
"stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
"baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
"glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
"glyph-orientation-vertical", "text-anchor", "writing-mode"
], propertyKeywords = keySet(propertyKeywords_);

var nonStandardPropertyKeywords = [
"scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
"scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
"scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
"searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
"searchfield-results-decoration", "zoom"
], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords);

var colorKeywords_ = [
"aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
"bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
Expand Down Expand Up @@ -576,7 +589,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"font-stretch", "font-weight", "font-style"
], fontProperties = keySet(fontProperties_);

var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_).concat(colorKeywords_).concat(valueKeywords_);
var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_)
.concat(nonStandardPropertyKeywords).concat(colorKeywords_).concat(valueKeywords_);
CodeMirror.registerHelper("hintWords", "css", allWords);

function tokenCComment(stream, state) {
Expand Down Expand Up @@ -605,6 +619,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
mediaTypes: mediaTypes,
mediaFeatures: mediaFeatures,
propertyKeywords: propertyKeywords,
nonStandardPropertyKeywords: nonStandardPropertyKeywords,
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
fontProperties: fontProperties,
Expand All @@ -627,6 +642,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
mediaTypes: mediaTypes,
mediaFeatures: mediaFeatures,
propertyKeywords: propertyKeywords,
nonStandardPropertyKeywords: nonStandardPropertyKeywords,
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
fontProperties: fontProperties,
Expand Down Expand Up @@ -667,6 +683,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
mediaTypes: mediaTypes,
mediaFeatures: mediaFeatures,
propertyKeywords: propertyKeywords,
nonStandardPropertyKeywords: nonStandardPropertyKeywords,
colorKeywords: colorKeywords,
valueKeywords: valueKeywords,
fontProperties: fontProperties,
Expand Down
3 changes: 3 additions & 0 deletions mode/css/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,7 @@
" [property src]: [atom url]([string http://blah]),",
" [atom url]([string http://foo]);",
"}");

MT("empty_url",
"[def @import] [tag url]() [tag screen];");
})();
64 changes: 64 additions & 0 deletions mode/django/django.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"),
require("../../addon/mode/overlay"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../htmlmixed/htmlmixed",
"../../addon/mode/overlay"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("django:inner", function() {
var keywords = ["block", "endblock", "for", "endfor", "in", "true", "false",
"loop", "none", "self", "super", "if", "endif", "as", "not", "and",
"else", "import", "with", "endwith", "without", "context", "ifequal", "endifequal",
"ifnotequal", "endifnotequal", "extends", "include", "load", "length", "comment",
"endcomment", "empty"];
keywords = new RegExp("^((" + keywords.join(")|(") + "))\\b");

function tokenBase (stream, state) {
stream.eatWhile(/[^\{]/);
var ch = stream.next();
if (ch == "{") {
if (ch = stream.eat(/\{|%|#/)) {
state.tokenize = inTag(ch);
return "tag";
}
}
}
function inTag (close) {
if (close == "{") {
close = "}";
}
return function (stream, state) {
var ch = stream.next();
if ((ch == close) && stream.eat("}")) {
state.tokenize = tokenBase;
return "tag";
}
if (stream.match(keywords)) {
return "keyword";
}
return close == "#" ? "comment" : "string";
};
}
return {
startState: function () {
return {tokenize: tokenBase};
},
token: function (stream, state) {
return state.tokenize(stream, state);
}
};
});

CodeMirror.defineMode("django", function(config) {
var htmlBase = CodeMirror.getMode(config, "text/html");
var djangoInner = CodeMirror.getMode(config, "django:inner");
return CodeMirror.overlayMode(htmlBase, djangoInner);
});

CodeMirror.defineMIME("text/x-django", "django");
});
63 changes: 63 additions & 0 deletions mode/django/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!doctype html>

<title>CodeMirror: Django template 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="../../addon/mode/overlay.js"></script>
<script src="../xml/xml.js"></script>
<script src="../htmlmixed/htmlmixed.js"></script>
<script src="django.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="#">Django</a>
</ul>
</div>

<article>
<h2>Django template mode</h2>
<form><textarea id="code" name="code">
<!doctype html>
<html>
<head>
<title>My Django web application</title>
</head>
<body>
<h1>
{{ page.title }}
</h1>
<ul class="my-list">
{% for item in items %}
<li>{% item.name %}</li>
{% empty %}
<li>You have no items in your list.</li>
{% endfor %}
</ul>
</body>
</html>
</textarea></form>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "django",
indentUnit: 4,
indentWithTabs: true
});
</script>

<p>Mode for HTML with embedded Django template markup.</p>

<p><strong>MIME types defined:</strong> <code>text/x-django</code></p>
</article>
284 changes: 284 additions & 0 deletions mode/dylan/dylan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
CodeMirror.defineMode("dylan", function(_config) {
// Words
var words = {
// Words that introduce unnamed definitions like "define interface"
unnamedDefinition: ["interface"],

// Words that introduce simple named definitions like "define library"
namedDefinition: ["module", "library", "macro",
"C-struct", "C-union",
"C-function", "C-callable-wrapper"
],

// Words that introduce type definitions like "define class".
// These are also parameterized like "define method" and are
// appended to otherParameterizedDefinitionWords
typeParameterizedDefinition: ["class", "C-subtype", "C-mapped-subtype"],

// Words that introduce trickier definitions like "define method".
// These require special definitions to be added to startExpressions
otherParameterizedDefinition: ["method", "function",
"C-variable", "C-address"
],

// Words that introduce module constant definitions.
// These must also be simple definitions and are
// appended to otherSimpleDefinitionWords
constantSimpleDefinition: ["constant"],

// Words that introduce module variable definitions.
// These must also be simple definitions and are
// appended to otherSimpleDefinitionWords
variableSimpleDefinition: ["variable"],

// Other words that introduce simple definitions
// (without implicit bodies).
otherSimpleDefinition: ["generic", "domain",
"C-pointer-type",
"table"
],

// Words that begin statements with implicit bodies.
statement: ["if", "block", "begin", "method", "case",
"for", "select", "when", "unless", "until",
"while", "iterate", "profiling", "dynamic-bind"
],

// Patterns that act as separators in compound statements.
// This may include any general pattern that must be indented
// specially.
separator: ["finally", "exception", "cleanup", "else",
"elseif", "afterwards"
],

// Keywords that do not require special indentation handling,
// but which should be highlighted
other: ["above", "below", "by", "from", "handler", "in",
"instance", "let", "local", "otherwise", "slot",
"subclass", "then", "to", "keyed-by", "virtual"
],

// Condition signaling function calls
signalingCalls: ["signal", "error", "cerror",
"break", "check-type", "abort"
]
};

words["otherDefinition"] =
words["unnamedDefinition"]
.concat(words["namedDefinition"])
.concat(words["otherParameterizedDefinition"]);

words["definition"] =
words["typeParameterizedDefinition"]
.concat(words["otherDefinition"]);

words["parameterizedDefinition"] =
words["typeParameterizedDefinition"]
.concat(words["otherParameterizedDefinition"]);

words["simpleDefinition"] =
words["constantSimpleDefinition"]
.concat(words["variableSimpleDefinition"])
.concat(words["otherSimpleDefinition"]);

words["keyword"] =
words["statement"]
.concat(words["separator"])
.concat(words["other"]);

// Patterns
var symbolPattern = "[-_a-zA-Z?!*@<>$%]+";
var symbol = new RegExp("^" + symbolPattern);
var patterns = {
// Symbols with special syntax
symbolKeyword: symbolPattern + ":",
symbolClass: "<" + symbolPattern + ">",
symbolGlobal: "\\*" + symbolPattern + "\\*",
symbolConstant: "\\$" + symbolPattern
};
var patternStyles = {
symbolKeyword: "atom",
symbolClass: "tag",
symbolGlobal: "variable-2",
symbolConstant: "variable-3"
};

// Compile all patterns to regular expressions
for (var patternName in patterns)
if (patterns.hasOwnProperty(patternName))
patterns[patternName] = new RegExp("^" + patterns[patternName]);

// Names beginning "with-" and "without-" are commonly
// used as statement macro
patterns["keyword"] = [/^with(?:out)?-[-_a-zA-Z?!*@<>$%]+/];

var styles = {};
styles["keyword"] = "keyword";
styles["definition"] = "def";
styles["simpleDefinition"] = "def";
styles["signalingCalls"] = "builtin";

// protected words lookup table
var wordLookup = {};
var styleLookup = {};

[
"keyword",
"definition",
"simpleDefinition",
"signalingCalls"
].forEach(function(type) {
words[type].forEach(function(word) {
wordLookup[word] = type;
styleLookup[word] = styles[type];
});
});


function chain(stream, state, f) {
state.tokenize = f;
return f(stream, state);
}

var type, content;

function ret(_type, style, _content) {
type = _type;
content = _content;
return style;
}

function tokenBase(stream, state) {
// String
var ch = stream.peek();
if (ch == "'" || ch == '"') {
stream.next();
return chain(stream, state, tokenString(ch, "string", "string"));
}
// Comment
else if (ch == "/") {
stream.next();
if (stream.eat("*")) {
return chain(stream, state, tokenComment);
} else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "comment");
} else {
stream.skipTo(" ");
return ret("operator", "operator");
}
}
// Decimal
else if (/\d/.test(ch)) {
stream.match(/^\d*(?:\.\d*)?(?:e[+\-]?\d+)?/);
return ret("number", "number");
}
// Hash
else if (ch == "#") {
stream.next();
// Symbol with string syntax
ch = stream.peek();
if (ch == '"') {
stream.next();
return chain(stream, state, tokenString('"', "symbol", "string-2"));
}
// Binary number
else if (ch == "b") {
stream.next();
stream.eatWhile(/[01]/);
return ret("number", "number");
}
// Hex number
else if (ch == "x") {
stream.next();
stream.eatWhile(/[\da-f]/i);
return ret("number", "number");
}
// Octal number
else if (ch == "o") {
stream.next();
stream.eatWhile(/[0-7]/);
return ret("number", "number");
}
// Hash symbol
else {
stream.eatWhile(/[-a-zA-Z]/);
return ret("hash", "keyword");
}
} else if (stream.match("end")) {
return ret("end", "keyword");
}
for (var name in patterns) {
if (patterns.hasOwnProperty(name)) {
var pattern = patterns[name];
if ((pattern instanceof Array && pattern.some(function(p) {
return stream.match(p);
})) || stream.match(pattern))
return ret(name, patternStyles[name], stream.current());
}
}
if (stream.match("define")) {
return ret("definition", "def");
} else {
stream.eatWhile(/[\w\-]/);
// Keyword
if (wordLookup[stream.current()]) {
return ret(wordLookup[stream.current()], styleLookup[stream.current()], stream.current());
} else if (stream.current().match(symbol)) {
return ret("variable", "variable");
} else {
stream.next();
return ret("other", "variable-2");
}
}
}

function tokenComment(stream, state) {
var maybeEnd = false,
ch;
while ((ch = stream.next())) {
if (ch == "/" && maybeEnd) {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "comment");
}

function tokenString(quote, type, style) {
return function(stream, state) {
var next, end = false;
while ((next = stream.next()) != null) {
if (next == quote) {
end = true;
break;
}
}
if (end)
state.tokenize = tokenBase;
return ret(type, style);
};
}

// Interface
return {
startState: function() {
return {
tokenize: tokenBase,
currentIndent: 0
};
},
token: function(stream, state) {
if (stream.eatSpace())
return null;
var style = state.tokenize(stream, state);
return style;
},
blockCommentStart: "/*",
blockCommentEnd: "*/"
};
});

CodeMirror.defineMIME("text/x-dylan", "dylan");
407 changes: 407 additions & 0 deletions mode/dylan/index.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion mode/erlang/erlang.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
switch (type) {
case "atom": return "atom";
case "attribute": return "attribute";
case "boolean": return "special";
case "boolean": return "atom";
case "builtin": return "builtin";
case "close_paren": return null;
case "colon": return null;
Expand Down
1 change: 1 addition & 0 deletions mode/go/go.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ CodeMirror.defineMode("go", function(config) {
},

electricChars: "{}):",
fold: "brace",
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//"
Expand Down
11 changes: 3 additions & 8 deletions mode/haml/haml.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
})(function(CodeMirror) {
"use strict";

(function() {
"use strict";

// full haml mode. This handled embeded ruby and html fragments too
CodeMirror.defineMode("haml", function(config) {
var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});
Expand Down Expand Up @@ -75,18 +72,18 @@
// donot handle --> as valid ruby, make it HTML close comment instead
if (state.startOfLine && !stream.match("-->", false) && (ch == "=" || ch == "-" )) {
state.tokenize = ruby;
return null;
return state.tokenize(stream, state);
}

if (state.previousToken.style == "hamlTag" ||
state.previousToken.style == "closeAttributeTag" ||
state.previousToken.style == "hamlAttribute") {
if (ch == "(") {
state.tokenize = rubyInQuote(")");
return null;
return state.tokenize(stream, state);
} else if (ch == "{") {
state.tokenize = rubyInQuote("}");
return null;
return state.tokenize(stream, state);
}
}

Expand Down Expand Up @@ -156,6 +153,4 @@
}, "htmlmixed", "ruby");

CodeMirror.defineMIME("text/x-haml", "haml");
})();

});
2 changes: 1 addition & 1 deletion mode/haml/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"[tag %a]([variable title][operator =][string \"test\"]){[atom :title] [operator =>] [string \"test\"]}");

MT("htmlCode",
"[tag <h1>]Title[tag </h1>]");
"[tag&bracket <][tag h1][tag&bracket >]Title[tag&bracket </][tag h1][tag&bracket >]");

MT("rubyBlock",
"[operator =][variable-2 @item]");
Expand Down
78 changes: 75 additions & 3 deletions mode/haxe/haxe.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) {
"function": kw("function"), "catch": kw("catch"), "untyped": kw("untyped"), "callback": kw("cb"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
"in": operator, "never": kw("property_access"), "trace":kw("trace"),
"class": type, "enum":type, "interface":type, "typedef":type, "extends":type, "implements":type, "dynamic":type,
"class": type, "abstract":type, "enum":type, "interface":type, "typedef":type, "extends":type, "implements":type, "dynamic":type,
"true": atom, "false": atom, "null": atom
};
}();
Expand Down Expand Up @@ -316,12 +316,13 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) {

function importdef (type, value) {
if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
else if(type == "variable" || type == "property" || type == ".") return cont(importdef);
else if(type == "variable" || type == "property" || type == "." || value == "*") return cont(importdef);
}

function typedef (type, value)
{
if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
else if (type == "type" && /[A-Z]/.test(value.charAt(0))) { return cont(); }
}

function maybelabel(type) {
Expand Down Expand Up @@ -433,10 +434,81 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) {
else return lexical.indented + (closing ? 0 : indentUnit);
},

electricChars: "{}"
electricChars: "{}",
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//"
};
});

CodeMirror.defineMIME("text/x-haxe", "haxe");

CodeMirror.defineMode("hxml", function () {

return {
startState: function () {
return {
define: false,
inString: false
};
},
token: function (stream, state) {
var ch = stream.peek();
var sol = stream.sol();

///* comments */
if (ch == "#") {
stream.skipToEnd();
return "comment";
}
if (sol && ch == "-") {
var style = "variable-2";

stream.eat(/-/);

if (stream.peek() == "-") {
stream.eat(/-/);
style = "keyword a";
}

if (stream.peek() == "D") {
stream.eat(/[D]/);
style = "keyword c";
state.define = true;
}

stream.eatWhile(/[A-Z]/i);
return style;
}

var ch = stream.peek();

if (state.inString == false && ch == "'") {
state.inString = true;
ch = stream.next();
}

if (state.inString == true) {
if (stream.skipTo("'")) {

} else {
stream.skipToEnd();
}

if (stream.peek() == "'") {
stream.next();
state.inString = false;
}

return "string";
}

stream.next();
return null;
}
};
});

CodeMirror.defineMIME("text/x-hxml", "hxml");

});
29 changes: 25 additions & 4 deletions mode/haxe/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<h2>Haxe mode</h2>


<div><textarea id="code" name="code">
<div><p><textarea id="code-haxe" name="code">
import one.two.Three;

@attr("test")
Expand Down Expand Up @@ -89,15 +89,36 @@ <h2>Haxe mode</h2>
grey( v : Int );
rgb (r:Int,g:Int,b:Int);
}
</textarea></div>
</textarea></p>

<p>Hxml mode:</p>

<p><textarea id="code-hxml">
-cp test
-js path/to/file.js
#-remap nme:flash
--next
-D source-map-content
-cmd 'test'
-lib lime
</textarea></p>
</div>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
var editor = CodeMirror.fromTextArea(document.getElementById("code-haxe"), {
mode: "haxe",
lineNumbers: true,
indentUnit: 4,
indentWithTabs: true
});

editor = CodeMirror.fromTextArea(document.getElementById("code-hxml"), {
mode: "hxml",
lineNumbers: true,
indentUnit: 4,
indentWithTabs: true
});
</script>

<p><strong>MIME types defined:</strong> <code>text/x-haxe</code>.</p>
<p><strong>MIME types defined:</strong> <code>text/x-haxe, text/x-hxml</code>.</p>
</article>
4 changes: 3 additions & 1 deletion mode/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ <h2>Language modes</h2>
<li><a href="css/index.html">CSS</a></li>
<li><a href="python/index.html">Cython</a></li>
<li><a href="d/index.html">D</a></li>
<li><a href="django/index.html">Django</a> (templating language)</li>
<li><a href="diff/index.html">diff</a></li>
<li><a href="dtd/index.html">DTD</a></li>
<li><a href="dylan/index.html">Dylan</a></li>
<li><a href="ecl/index.html">ECL</a></li>
<li><a href="eiffel/index.html">Eiffel</a></li>
<li><a href="erlang/index.html">Erlang</a></li>
Expand Down Expand Up @@ -106,7 +108,7 @@ <h2>Language modes</h2>
<li><a href="vb/index.html">VB.NET</a></li>
<li><a href="vbscript/index.html">VBScript</a></li>
<li><a href="velocity/index.html">Velocity</a></li>
<li><a href="verilog/index.html">Verilog</a></li>
<li><a href="verilog/index.html">Verilog/SystemVerilog</a></li>
<li><a href="xml/index.html">XML/HTML</a></li>
<li><a href="xquery/index.html">XQuery</a></li>
<li><a href="yaml/index.html">YAML</a></li>
Expand Down
22 changes: 14 additions & 8 deletions mode/javascript/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex);
if (type == ";") return cont();
if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse);
if (type == "if") {
if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
cx.state.cc.pop()();
return cont(pushlex("form"), expression, statement, poplex, maybeelse);
}
if (type == "function") return cont(functiondef);
if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel);
Expand Down Expand Up @@ -356,12 +360,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {

var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
if (type == "function") return cont(functiondef);
if (type == "function") return cont(functiondef, maybeop);
if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
if (type == "{") return contCommasep(objprop, "}", null, maybeop);
if (type == "quasi") { return pass(quasi, maybeop); }
return cont();
}
function maybeexpression(type) {
Expand All @@ -386,21 +391,22 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (value == "?") return cont(expression, expect(":"), expr);
return cont(expr);
}
if (type == "quasi") { cx.cc.push(me); return quasi(value); }
if (type == "quasi") { return pass(quasi, me); }
if (type == ";") return;
if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
if (type == ".") return cont(property, me);
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
}
function quasi(value) {
if (value.slice(value.length - 2) != "${") return cont();
function quasi(type, value) {
if (type != "quasi") return pass();
if (value.slice(value.length - 2) != "${") return cont(quasi);
return cont(expression, continueQuasi);
}
function continueQuasi(type) {
if (type == "}") {
cx.marked = "string-2";
cx.state.tokenize = tokenQuasi;
return cont();
return cont(quasi);
}
}
function arrowBody(type) {
Expand Down Expand Up @@ -493,7 +499,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == ",") return cont(vardef);
}
function maybeelse(type, value) {
if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
}
function forspec(type) {
if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
Expand Down Expand Up @@ -606,7 +612,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (state.tokenize != tokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
// Kludge to prevent 'maybelse' from blocking lexical scope pops
for (var i = state.cc.length - 1; i >= 0; --i) {
if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
var c = state.cc[i];
if (c == poplex) lexical = lexical.prev;
else if (c != maybeelse) break;
Expand Down
19 changes: 19 additions & 0 deletions mode/javascript/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
MT("quasi",
"[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");

MT("quasi_no_function",
"[variable x] [operator =] [string-2 `fofdlakj${][variable x] [operator +] [string-2 `foo`] [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");

MT("indent_statement",
"[keyword var] [variable x] [operator =] [number 10]",
"[variable x] [operator +=] [variable y] [operator +]",
Expand Down Expand Up @@ -104,6 +107,22 @@
" [keyword debugger];",
"}");

MT("indent_else",
"[keyword for] (;;)",
" [keyword if] ([variable foo])",
" [keyword if] ([variable bar])",
" [number 1];",
" [keyword else]",
" [number 2];",
" [keyword else]",
" [number 3];");

MT("indent_below_if",
"[keyword for] (;;)",
" [keyword if] ([variable foo])",
" [number 1];",
"[number 2];");

MT("multilinestring",
"[keyword var] [variable x] [operator =] [string 'foo\\]",
"[string bar'];");
Expand Down
36 changes: 20 additions & 16 deletions mode/jinja2/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,26 @@
<article>
<h2>Jinja2 mode</h2>
<form><textarea id="code" name="code">
&lt;html style="color: green"&gt;
&lt;!-- this is a comment --&gt;
&lt;head&gt;
&lt;title&gt;Jinja2 Example&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;ul&gt;
{# this is a comment #}
{%- for item in li -%}
&lt;li&gt;
{{ item.label }}
&lt;/li&gt;
{% endfor -%}
&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;
{# this is a comment #}
{%- for item in li -%}
&lt;li&gt;{{ item.label }}&lt;/li&gt;
{% endfor -%}
{{ item.sand == true and item.keyword == false ? 1 : 0 }}
{{ app.get(55, 1.2, true) }}
{% if app.get(&#39;_route&#39;) == (&#39;_home&#39;) %}home{% endif %}
{% if app.session.flashbag.has(&#39;message&#39;) %}
{% for message in app.session.flashbag.get(&#39;message&#39;) %}
{{ message.content }}
{% endfor %}
{% endif %}
{{ path(&#39;_home&#39;, {&#39;section&#39;: app.request.get(&#39;section&#39;)}) }}
{{ path(&#39;_home&#39;, {
&#39;section&#39;: app.request.get(&#39;section&#39;),
&#39;boolean&#39;: true,
&#39;number&#39;: 55.33
})
}}
{% include (&#39;test.incl.html.twig&#39;) %}
</textarea></form>
<script>
var editor =
Expand Down
146 changes: 111 additions & 35 deletions mode/jinja2/jinja2.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,123 @@

CodeMirror.defineMode("jinja2", function() {
var keywords = ["and", "as", "block", "endblock", "by", "cycle", "debug", "else", "elif",
"extends", "filter", "endfilter", "firstof", "for",
"endfor", "if", "endif", "ifchanged", "endifchanged",
"ifequal", "endifequal", "ifnotequal",
"endifnotequal", "in", "include", "load", "not", "now", "or",
"parsed", "regroup", "reversed", "spaceless",
"endspaceless", "ssi", "templatetag", "openblock",
"closeblock", "openvariable", "closevariable",
"openbrace", "closebrace", "opencomment",
"closecomment", "widthratio", "url", "with", "endwith",
"get_current_language", "trans", "noop", "blocktrans",
"endblocktrans", "get_available_languages",
"get_current_language_bidi", "plural"];
keywords = new RegExp("^((" + keywords.join(")|(") + "))\\b");
"extends", "filter", "endfilter", "firstof", "for",
"endfor", "if", "endif", "ifchanged", "endifchanged",
"ifequal", "endifequal", "ifnotequal",
"endifnotequal", "in", "include", "load", "not", "now", "or",
"parsed", "regroup", "reversed", "spaceless",
"endspaceless", "ssi", "templatetag", "openblock",
"closeblock", "openvariable", "closevariable",
"openbrace", "closebrace", "opencomment",
"closecomment", "widthratio", "url", "with", "endwith",
"get_current_language", "trans", "endtrans", "noop", "blocktrans",
"endblocktrans", "get_available_languages",
"get_current_language_bidi", "plural"],
operator = /^[+\-*&%=<>!?|~^]/,
sign = /^[:\[\(\{]/,
atom = ["true", "false"],
number = /^(\d[+\-\*\/])?\d+(\.\d+)?/;

keywords = new RegExp("((" + keywords.join(")|(") + "))\\b");
atom = new RegExp("((" + atom.join(")|(") + "))\\b");

function tokenBase (stream, state) {
var ch = stream.next();
if (ch == "{") {
if (ch = stream.eat(/\{|%|#/)) {
stream.eat("-");
state.tokenize = inTag(ch);
return "tag";
var ch = stream.peek();

//Comment
if (state.incomment) {
if(!stream.skipTo("#}")) {
stream.skipToEnd();
} else {
stream.eatWhile(/\#|}/);
state.incomment = false;
}
}
}
function inTag (close) {
if (close == "{") {
close = "}";
}
return function (stream, state) {
var ch = stream.next();
if ((ch == close || (ch == "-" && stream.eat(close)))
&& stream.eat("}")) {
state.tokenize = tokenBase;
return "comment";
//Tag
} else if (state.intag) {
//After operator
if(state.operator) {
state.operator = false;
if(stream.match(atom)) {
return "atom";
}
if(stream.match(number)) {
return "number";
}
}
//After sign
if(state.sign) {
state.sign = false;
if(stream.match(atom)) {
return "atom";
}
if(stream.match(number)) {
return "number";
}
}

if(state.instring) {
if(ch == state.instring) {
state.instring = false;
}
stream.next();
return "string";
} else if(ch == "'" || ch == '"') {
state.instring = ch;
stream.next();
return "string";
} else if(stream.match(state.intag + "}") || stream.eat("-") && stream.match(state.intag + "}")) {
state.intag = false;
return "tag";
} else if(stream.match(operator)) {
state.operator = true;
return "operator";
} else if(stream.match(sign)) {
state.sign = true;
} else {
if(stream.eat(" ") || stream.sol()) {
if(stream.match(keywords)) {
return "keyword";
}
if(stream.match(atom)) {
return "atom";
}
if(stream.match(number)) {
return "number";
}
if(stream.sol()) {
stream.next();
}
} else {
stream.next();
}

}
if (stream.match(keywords)) {
return "keyword";
return "variable";
} else if (stream.eat("{")) {
if (ch = stream.eat("#")) {
state.incomment = true;
if(!stream.skipTo("#}")) {
stream.skipToEnd();
} else {
stream.eatWhile(/\#|}/);
state.incomment = false;
}
return "comment";
//Open tag
} else if (ch = stream.eat(/\{|%/)) {
//Cache close tag
state.intag = ch;
if(ch == "{") {
state.intag = "}";
}
stream.eat("-");
return "tag";
}
return close == "#" ? "comment" : "string";
};
}
}
stream.next();
};

return {
startState: function () {
return {tokenize: tokenBase};
Expand Down
8 changes: 4 additions & 4 deletions mode/livescript/livescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
for (i$ = 0, len$ = nr.length; i$ < len$; ++i$) {
r = nr[i$];
if (r.regex && (m = stream.match(r.regex))) {
state.next = r.next;
state.next = r.next || state.next;
return r.token;
}
}
Expand Down Expand Up @@ -203,7 +203,7 @@
next: 'start'
}, {
token: 'text',
regex: '.',
regex: '',
next: 'start'
}
],
Expand Down Expand Up @@ -265,11 +265,11 @@
if (Array.isArray(r)) {
for (var i = 0, len = r.length; i < len; ++i) {
var rr = r[i];
if (rr.regex) {
if (typeof rr.regex === 'string') {
Rules[idx][i].regex = new RegExp('^' + rr.regex);
}
}
} else if (r.regex) {
} else if (typeof rr.regex === 'string') {
Rules[idx].regex = new RegExp('^' + r.regex);
}
}
Expand Down
7 changes: 5 additions & 2 deletions mode/markdown/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {

if (forceBlankLine) {
state.prevLineHasContent = false;
return blankLine(state);
blankLine(state);
return this.token(stream, state);
} else {
state.prevLineHasContent = state.thisLineHasContent;
state.thisLineHasContent = true;
Expand Down Expand Up @@ -737,7 +738,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.indentation = adjustedIndentation;
if (indentation > 0) return null;
}
return state.f(stream, state);
var result = state.f(stream, state);
if (stream.start == stream.pos) return this.token(stream, state);
else return result;
},

innerMode: function(state) {
Expand Down
2 changes: 2 additions & 0 deletions mode/meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ CodeMirror.modeInfo = [
{name: 'D', mime: 'text/x-d', mode: 'd'},
{name: 'diff', mime: 'text/x-diff', mode: 'diff'},
{name: 'DTD', mime: 'application/xml-dtd', mode: 'dtd'},
{name: 'Dylan', mime: 'text/x-dylan', mode: 'dylan'},
{name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'},
{name: 'Eiffel', mime: 'text/x-eiffel', mode: 'eiffel'},
{name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'},
Expand Down Expand Up @@ -88,6 +89,7 @@ CodeMirror.modeInfo = [
{name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'},
{name: 'sTeX', mime: 'text/x-stex', mode: 'stex'},
{name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'},
{name: 'SystemVerilog', mime: 'text/x-systemverilog', mode: 'verilog'},
{name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'},
{name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'},
{name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'},
Expand Down
6 changes: 5 additions & 1 deletion mode/php/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@
<h2>PHP mode</h2>
<form><textarea id="code" name="code">
<?php
$a = array('a' => 1, 'b' => 2, 3 => 'c');

echo "$a[a] ${a[3] /* } comment */} {$a[b]} \$a[a]";

function hello($who) {
return "Hello " . $who;
return "Hello $who!";
}
?>
<p>The program says <?= hello("World") ?>.</p>
Expand Down
108 changes: 99 additions & 9 deletions mode/php/php.js

Large diffs are not rendered by default.

145 changes: 145 additions & 0 deletions mode/php/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
(function() {
var mode = CodeMirror.getMode({indentUnit: 2}, "php");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }

MT('simple_test',
'[meta <?php] ' +
'[keyword echo] [string "aaa"]; ' +
'[meta ?>]');

MT('variable_interpolation_non_alphanumeric',
'[meta <?php]',
'[keyword echo] [string "aaa$~$!$@$#$$$%$^$&$*$($)$.$<$>$/$\\$}$\\\"$:$;$?$|$[[$]]$+$=aaa"]',
'[meta ?>]');

MT('variable_interpolation_digits',
'[meta <?php]',
'[keyword echo] [string "aaa$1$2$3$4$5$6$7$8$9$0aaa"]',
'[meta ?>]');

MT('variable_interpolation_simple_syntax_1',
'[meta <?php]',
'[keyword echo] [string "aaa][variable-2 $aaa][string .aaa"];',
'[meta ?>]');

MT('variable_interpolation_simple_syntax_2',
'[meta <?php]',
'[keyword echo] [string "][variable-2 $aaaa][[','[number 2]', ']][string aa"];',
'[keyword echo] [string "][variable-2 $aaaa][[','[number 2345]', ']][string aa"];',
'[keyword echo] [string "][variable-2 $aaaa][[','[number 2.3]', ']][string aa"];',
'[keyword echo] [string "][variable-2 $aaaa][[','[variable aaaaa]', ']][string aa"];',
'[keyword echo] [string "][variable-2 $aaaa][[','[variable-2 $aaaaa]',']][string aa"];',

'[keyword echo] [string "1aaa][variable-2 $aaaa][[','[number 2]', ']][string aa"];',
'[keyword echo] [string "aaa][variable-2 $aaaa][[','[number 2345]', ']][string aa"];',
'[keyword echo] [string "aaa][variable-2 $aaaa][[','[number 2.3]', ']][string aa"];',
'[keyword echo] [string "aaa][variable-2 $aaaa][[','[variable aaaaa]', ']][string aa"];',
'[keyword echo] [string "aaa][variable-2 $aaaa][[','[variable-2 $aaaaa]',']][string aa"];',
'[meta ?>]');

MT('variable_interpolation_simple_syntax_3',
'[meta <?php]',
'[keyword echo] [string "aaa][variable-2 $aaaa]->[variable aaaaa][string .aaaaaa"];',
'[keyword echo] [string "aaa][variable-2 $aaaa][string ->][variable-2 $aaaaa][string .aaaaaa"];',
'[keyword echo] [string "aaa][variable-2 $aaaa]->[variable aaaaa][string [[2]].aaaaaa"];',
'[keyword echo] [string "aaa][variable-2 $aaaa]->[variable aaaaa][string ->aaaa2.aaaaaa"];',
'[meta ?>]');

MT('variable_interpolation_escaping',
'[meta <?php] [comment /* Escaping */]',
'[keyword echo] [string "aaa\\$aaaa->aaa.aaa"];',
'[keyword echo] [string "aaa\\$aaaa[[2]]aaa.aaa"];',
'[keyword echo] [string "aaa\\$aaaa[[asd]]aaa.aaa"];',
'[keyword echo] [string "aaa{\\$aaaa->aaa.aaa"];',
'[keyword echo] [string "aaa{\\$aaaa[[2]]aaa.aaa"];',
'[keyword echo] [string "aaa{\\aaaaa[[asd]]aaa.aaa"];',
'[keyword echo] [string "aaa\\${aaaa->aaa.aaa"];',
'[keyword echo] [string "aaa\\${aaaa[[2]]aaa.aaa"];',
'[keyword echo] [string "aaa\\${aaaa[[asd]]aaa.aaa"];',
'[meta ?>]');

MT('variable_interpolation_complex_syntax_1',
'[meta <?php]',
'[keyword echo] [string "aaa][variable-2 $]{[variable aaaa]}[string ->aaa.aaa"];',
'[keyword echo] [string "aaa][variable-2 $]{[variable-2 $aaaa]}[string ->aaa.aaa"];',
'[keyword echo] [string "aaa][variable-2 $]{[variable-2 $aaaa][[',' [number 42]',']]}[string ->aaa.aaa"];',
'[keyword echo] [string "aaa][variable-2 $]{[variable aaaa][meta ?>]aaaaaa');

MT('variable_interpolation_complex_syntax_2',
'[meta <?php] [comment /* Monsters */]',
'[keyword echo] [string "][variable-2 $]{[variable aaa][comment /*}?>} $aaa<?php } */]}[string ->aaa.aaa"];',
'[keyword echo] [string "][variable-2 $]{[variable aaa][comment /*}?>*/][[',' [string "aaa][variable-2 $aaa][string {}][variable-2 $]{[variable aaa]}[string "]',']]}[string ->aaa.aaa"];',
'[keyword echo] [string "][variable-2 $]{[variable aaa][comment /*} } $aaa } */]}[string ->aaa.aaa"];');


function build_recursive_monsters(nt, t, n){
var monsters = [t];
for (var i = 1; i <= n; ++i)
monsters[i] = nt.join(monsters[i - 1]);
return monsters;
}

var m1 = build_recursive_monsters(
['[string "][variable-2 $]{[variable aaa] [operator +] ', '}[string "]'],
'[comment /* }?>} */] [string "aaa][variable-2 $aaa][string .aaa"]',
10
);

MT('variable_interpolation_complex_syntax_3_1',
'[meta <?php] [comment /* Recursive monsters */]',
'[keyword echo] ' + m1[4] + ';',
'[keyword echo] ' + m1[7] + ';',
'[keyword echo] ' + m1[8] + ';',
'[keyword echo] ' + m1[5] + ';',
'[keyword echo] ' + m1[1] + ';',
'[keyword echo] ' + m1[6] + ';',
'[keyword echo] ' + m1[9] + ';',
'[keyword echo] ' + m1[0] + ';',
'[keyword echo] ' + m1[10] + ';',
'[keyword echo] ' + m1[2] + ';',
'[keyword echo] ' + m1[3] + ';',
'[keyword echo] [string "end"];',
'[meta ?>]');

var m2 = build_recursive_monsters(
['[string "a][variable-2 $]{[variable aaa] [operator +] ', ' [operator +] ', '}[string .a"]'],
'[comment /* }?>{{ */] [string "a?>}{{aa][variable-2 $aaa][string .a}a?>a"]',
5
);

MT('variable_interpolation_complex_syntax_3_2',
'[meta <?php] [comment /* Recursive monsters 2 */]',
'[keyword echo] ' + m2[0] + ';',
'[keyword echo] ' + m2[1] + ';',
'[keyword echo] ' + m2[5] + ';',
'[keyword echo] ' + m2[4] + ';',
'[keyword echo] ' + m2[2] + ';',
'[keyword echo] ' + m2[3] + ';',
'[keyword echo] [string "end"];',
'[meta ?>]');

function build_recursive_monsters_2(mf1, mf2, nt, t, n){
var monsters = [t];
for (var i = 1; i <= n; ++i)
monsters[i] = nt[0] + mf1[i - 1] + nt[1] + mf2[i - 1] + nt[2] + monsters[i - 1] + nt[3];
return monsters;
}

var m3 = build_recursive_monsters_2(
m1,
m2,
['[string "a][variable-2 $]{[variable aaa] [operator +] ', ' [operator +] ', ' [operator +] ', '}[string .a"]'],
'[comment /* }?>{{ */] [string "a?>}{{aa][variable-2 $aaa][string .a}a?>a"]',
4
);

MT('variable_interpolation_complex_syntax_3_3',
'[meta <?php] [comment /* Recursive monsters 2 */]',
'[keyword echo] ' + m3[4] + ';',
'[keyword echo] ' + m3[0] + ';',
'[keyword echo] ' + m3[3] + ';',
'[keyword echo] ' + m3[1] + ';',
'[keyword echo] ' + m3[2] + ';',
'[keyword echo] [string "end"];',
'[meta ?>]');
})();
3 changes: 1 addition & 2 deletions mode/r/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ <h2>R mode</h2>
<p><strong>MIME types defined:</strong> <code>text/x-rsrc</code>.</p>

<p>Development of the CodeMirror R mode was kindly sponsored
by <a href="http://ubalo.com/">Ubalo</a>, who hold
the <a href="LICENSE">license</a>.</p>
by <a href="http://ubalo.com/">Ubalo</a>.</p>

</article>
4 changes: 3 additions & 1 deletion mode/r/r.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ CodeMirror.defineMode("r", function(config) {
if (ctx.type == "block") return ctx.indent + (firstChar == "{" ? 0 : config.indentUnit);
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
else return ctx.indent + (closing ? 0 : config.indentUnit);
}
},

lineComment: "#"
};
});

Expand Down
190 changes: 89 additions & 101 deletions mode/verilog/index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<!doctype html>

<title>CodeMirror: Verilog mode</title>
<title>CodeMirror: Verilog/SystemVerilog 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="../../addon/edit/matchbrackets.js"></script>
<script src="verilog.js"></script>
<style>.CodeMirror {border: 2px inset #dee;}</style>
<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>

Expand All @@ -18,115 +19,102 @@
</ul>
<ul>
<li><a href="../index.html">Language modes</a>
<li><a class=active href="#">Verilog</a>
<li><a class=active href="#">Verilog/SystemVerilog</a>
</ul>
</div>

<article>
<h2>Verilog mode</h2>
<form><textarea id="code" name="code">
/* Verilog demo code */
<h2>SystemVerilog mode</h2>

module butterfly
#(
parameter WIDTH = 32,
parameter MWIDTH = 1
)
(
input wire clk,
input wire rst_n,
// m_in contains data that passes through this block with no change.
input wire [MWIDTH-1:0] m_in,
// The twiddle factor.
input wire signed [WIDTH-1:0] w,
// XA
input wire signed [WIDTH-1:0] xa,
// XB
input wire signed [WIDTH-1:0] xb,
// Set to 1 when new data is present on inputs.
input wire x_nd,
// delayed version of m_in.
output reg [MWIDTH-1:0] m_out,
// YA = XA + W*XB
// YB = XA - W*XB
output wire signed [WIDTH-1:0] ya,
output wire signed [WIDTH-1:0] yb,
output reg y_nd,
output reg error
);
<div><textarea id="code" name="code">
// Literals
1'b0
1'bx
1'bz
16'hDC78
'hdeadbeef
'b0011xxzz
1234
32'd5678
3.4e6
-128.7

// Set wire to the real and imag parts for convenience.
wire signed [WIDTH/2-1:0] xa_re;
wire signed [WIDTH/2-1:0] xa_im;
assign xa_re = xa[WIDTH-1:WIDTH/2];
assign xa_im = xa[WIDTH/2-1:0];
wire signed [WIDTH/2-1: 0] ya_re;
wire signed [WIDTH/2-1: 0] ya_im;
assign ya = {ya_re, ya_im};
wire signed [WIDTH/2-1: 0] yb_re;
wire signed [WIDTH/2-1: 0] yb_im;
assign yb = {yb_re, yb_im};
// Macro definition
`define BUS_WIDTH = 8;

// Delayed stuff.
reg signed [WIDTH/2-1:0] xa_re_z;
reg signed [WIDTH/2-1:0] xa_im_z;
// Output of multiplier
wire signed [WIDTH-1:0] xbw;
wire signed [WIDTH/2-1:0] xbw_re;
wire signed [WIDTH/2-1:0] xbw_im;
assign xbw_re = xbw[WIDTH-1:WIDTH/2];
assign xbw_im = xbw[WIDTH/2-1:0];
// Do summing
// I don't think we should get overflow here because of the
// size of the twiddle factors.
// If we do testing should catch it.
assign ya_re = xa_re_z + xbw_re;
assign ya_im = xa_im_z + xbw_im;
assign yb_re = xa_re_z - xbw_re;
assign yb_im = xa_im_z - xbw_im;

// Create the multiply module.
multiply_complex #(WIDTH) multiply_complex_0
(.clk(clk),
.rst_n(rst_n),
.x(xb),
.y(w),
.z(xbw)
);
// Module definition
module block(
input clk,
input rst_n,
input [`BUS_WIDTH-1:0] data_in,
output [`BUS_WIDTH-1:0] data_out
);

always @(posedge clk or negedge rst_n) begin

always @ (posedge clk)
begin
if (!rst_n)
begin
y_nd <= 1'b0;
error <= 1'b0;
end
else
begin
// Set delay for x_nd_old and m.
y_nd <= x_nd;
m_out <= m_in;
if (x_nd)
begin
xa_re_z <= xa_re/2;
xa_im_z <= xa_im/2;
end
end
if (~rst_n) begin
data_out <= 8'b0;
end else begin
data_out <= data_in;
end


if (~rst_n)
data_out <= 8'b0;
else
data_out <= data_in;

if (~rst_n)
begin
data_out <= 8'b0;
end
else
begin
data_out <= data_in;
end

end

endmodule
</textarea></form>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "text/x-verilog"
});
</script>
// Class definition
class test;

/**
* Sum two integers
*/
function int sum(int a, int b);
int result = a + b;
string msg = $sformatf("%d + %d = %d", a, b, result);
$display(msg);
return result;
endfunction

task delay(int num_cycles);
repeat(num_cycles) #1;
endtask

endclass

</textarea></div>

<p>Simple mode that tries to handle Verilog-like languages as well as it
can. Takes one configuration parameters: <code>keywords</code>, an
object whose property names are the keywords in the language.</p>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
matchBrackets: true,
mode: {
name: "verilog",
noIndentKeywords: ["package"]
}
});
</script>

<p>
Syntax highlighting and indentation for the Verilog and SystemVerilog languages (IEEE 1800).
<h2>Configuration options:</h2>
<ul>
<li><strong>noIndentKeywords</strong> - List of keywords which should not cause identation to increase. E.g. ["package", "module"]. Default: None</li>
</ul>
</p>

<p><strong>MIME types defined:</strong> <code>text/x-verilog</code> (Verilog code).</p>
</article>
<p><strong>MIME types defined:</strong> <code>text/x-verilog</code> and <code>text/x-systemverilog</code>.</p>
</article>
114 changes: 114 additions & 0 deletions mode/verilog/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
(function() {
var mode = CodeMirror.getMode({indentUnit: 4}, "verilog");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }

MT("Binary literals",
"[number 1'b0]",
"[number 1'b1]",
"[number 1'bx]",
"[number 1'bz]",
"[number 1'bX]",
"[number 1'bZ]",
"[number 1'B0]",
"[number 1'B1]",
"[number 1'Bx]",
"[number 1'Bz]",
"[number 1'BX]",
"[number 1'BZ]",
"[number 1'b0]",
"[number 1'b1]",
"[number 2'b01]",
"[number 2'bxz]",
"[number 2'b11]",
"[number 2'b10]",
"[number 2'b1Z]",
"[number 12'b0101_0101_0101]",
"[number 1'b 0]",
"[number 'b0101]"
);

MT("Octal literals",
"[number 3'o7]",
"[number 3'O7]",
"[number 3'so7]",
"[number 3'SO7]"
);

MT("Decimal literals",
"[number 0]",
"[number 1]",
"[number 7]",
"[number 123_456]",
"[number 'd33]",
"[number 8'd255]",
"[number 8'D255]",
"[number 8'sd255]",
"[number 8'SD255]",
"[number 32'd123]",
"[number 32 'd123]",
"[number 32 'd 123]"
);

MT("Hex literals",
"[number 4'h0]",
"[number 4'ha]",
"[number 4'hF]",
"[number 4'hx]",
"[number 4'hz]",
"[number 4'hX]",
"[number 4'hZ]",
"[number 32'hdc78]",
"[number 32'hDC78]",
"[number 32 'hDC78]",
"[number 32'h DC78]",
"[number 32 'h DC78]",
"[number 32'h44x7]",
"[number 32'hFFF?]"
);

MT("Real number literals",
"[number 1.2]",
"[number 0.1]",
"[number 2394.26331]",
"[number 1.2E12]",
"[number 1.2e12]",
"[number 1.30e-2]",
"[number 0.1e-0]",
"[number 23E10]",
"[number 29E-2]",
"[number 236.123_763_e-12]"
);

MT("Operators",
"[meta ^]"
);

MT("Keywords",
"[keyword logic]",
"[keyword logic] [variable foo]",
"[keyword reg] [variable abc]"
);

MT("Variables",
"[variable _leading_underscore]",
"[variable _if]",
"[number 12] [variable foo]",
"[variable foo] [number 14]"
);

MT("Tick defines",
"[def `FOO]",
"[def `foo]",
"[def `FOO_bar]"
);

MT("System calls",
"[meta $display]",
"[meta $vpi_printf]"
);

MT("Line comment", "[comment // Hello world]");



})();
315 changes: 237 additions & 78 deletions mode/verilog/verilog.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,141 @@
"use strict";

CodeMirror.defineMode("verilog", function(config, parserConfig) {

var indentUnit = config.indentUnit,
keywords = parserConfig.keywords || {},
blockKeywords = parserConfig.blockKeywords || {},
atoms = parserConfig.atoms || {},
hooks = parserConfig.hooks || {},
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
dontAlignCalls = parserConfig.dontAlignCalls,
noIndentKeywords = parserConfig.noIndentKeywords || [],
multiLineStrings = parserConfig.multiLineStrings;
var isOperatorChar = /[&|~><!\)\(*#%@+\/=?\:;}{,\.\^\-\[\]]/;

function words(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}

/**
* Keywords from IEEE 1800-2012
*/
var keywords = words(
"accept_on alias always always_comb always_ff always_latch and assert assign assume automatic before begin bind " +
"bins binsof bit break buf bufif0 bufif1 byte case casex casez cell chandle checker class clocking cmos config " +
"const constraint context continue cover covergroup coverpoint cross deassign default defparam design disable " +
"dist do edge else end endcase endchecker endclass endclocking endconfig endfunction endgenerate endgroup " +
"endinterface endmodule endpackage endprimitive endprogram endproperty endspecify endsequence endtable endtask " +
"enum event eventually expect export extends extern final first_match for force foreach forever fork forkjoin " +
"function generate genvar global highz0 highz1 if iff ifnone ignore_bins illegal_bins implements implies import " +
"incdir include initial inout input inside instance int integer interconnect interface intersect join join_any " +
"join_none large let liblist library local localparam logic longint macromodule matches medium modport module " +
"nand negedge nettype new nexttime nmos nor noshowcancelled not notif0 notif1 null or output package packed " +
"parameter pmos posedge primitive priority program property protected pull0 pull1 pulldown pullup " +
"pulsestyle_ondetect pulsestyle_onevent pure rand randc randcase randsequence rcmos real realtime ref reg " +
"reject_on release repeat restrict return rnmos rpmos rtran rtranif0 rtranif1 s_always s_eventually s_nexttime " +
"s_until s_until_with scalared sequence shortint shortreal showcancelled signed small soft solve specify " +
"specparam static string strong strong0 strong1 struct super supply0 supply1 sync_accept_on sync_reject_on " +
"table tagged task this throughout time timeprecision timeunit tran tranif0 tranif1 tri tri0 tri1 triand trior " +
"trireg type typedef union unique unique0 unsigned until until_with untyped use uwire var vectored virtual void " +
"wait wait_order wand weak weak0 weak1 while wildcard wire with within wor xnor xor");

/** Operators from IEEE 1800-2012
unary_operator ::=
+ | - | ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~
binary_operator ::=
+ | - | * | / | % | == | != | === | !== | ==? | !=? | && | || | **
| < | <= | > | >= | & | | | ^ | ^~ | ~^ | >> | << | >>> | <<<
| -> | <->
inc_or_dec_operator ::= ++ | --
unary_module_path_operator ::=
! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~
binary_module_path_operator ::=
== | != | && | || | & | | | ^ | ^~ | ~^
*/
var isOperatorChar = /[\+\-\*\/!~&|^%=?:]/;
var isBracketChar = /[\[\]{}()]/;

var unsignedNumber = /\d[0-9_]*/;
var decimalLiteral = /\d*\s*'s?d\s*\d[0-9_]*/i;
var binaryLiteral = /\d*\s*'s?b\s*[xz01][xz01_]*/i;
var octLiteral = /\d*\s*'s?o\s*[xz0-7][xz0-7_]*/i;
var hexLiteral = /\d*\s*'s?h\s*[0-9a-fxz?][0-9a-fxz?_]*/i;
var realLiteral = /(\d[\d_]*(\.\d[\d_]*)?E-?[\d_]+)|(\d[\d_]*\.\d[\d_]*)/i;

var closingBracketOrWord = /^((\w+)|[)}\]])/;
var closingBracket = /[)}\]]/;

var curPunc;
var curKeyword;

// Block openings which are closed by a matching keyword in the form of ("end" + keyword)
// E.g. "task" => "endtask"
var blockKeywords = words(
"case checker class clocking config function generate group interface module package" +
"primitive program property specify sequence table task"
);

// Opening/closing pairs
var openClose = {};
for (var keyword in blockKeywords) {
openClose[keyword] = "end" + keyword;
}
openClose["begin"] = "end";
openClose["casex"] = "endcase";
openClose["casez"] = "endcase";
openClose["do" ] = "while";
openClose["fork" ] = "join;join_any;join_none";

for (var i in noIndentKeywords) {
var keyword = noIndentKeywords[i];
if (openClose[keyword]) {
openClose[keyword] = undefined;
}
}

var statementKeywords = words("always always_comb always_ff always_latch assert assign assume else for foreach forever if initial repeat while");

function tokenBase(stream, state) {
var ch = stream.next();
if (hooks[ch]) {
var result = hooks[ch](stream, state);
if (result !== false) return result;
var ch = stream.peek();
if (/[,;:\.]/.test(ch)) {
curPunc = stream.next();
return null;
}
if (isBracketChar.test(ch)) {
curPunc = stream.next();
return "bracket";
}
// Macros (tick-defines)
if (ch == '`') {
stream.next();
if (stream.eatWhile(/[\w\$_]/)) {
return "def";
} else {
return null;
}
}
// System calls
if (ch == '$') {
stream.next();
if (stream.eatWhile(/[\w\$_]/)) {
return "meta";
} else {
return null;
}
}
// Time literals
if (ch == '#') {
stream.next();
stream.eatWhile(/[\d_.]/);
return "def";
}
// Strings
if (ch == '"') {
stream.next();
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
curPunc = ch;
return null;
}
if (/[\d']/.test(ch)) {
stream.eatWhile(/[\w\.']/);
return "number";
}
// Comments
if (ch == "/") {
stream.next();
if (stream.eat("*")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
Expand All @@ -46,19 +152,43 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
stream.skipToEnd();
return "comment";
}
stream.backUp(1);
}

// Numeric literals
if (stream.match(realLiteral) ||
stream.match(decimalLiteral) ||
stream.match(binaryLiteral) ||
stream.match(octLiteral) ||
stream.match(hexLiteral) ||
stream.match(unsignedNumber) ||
stream.match(realLiteral)) {
return "number";
}
if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return "operator";

// Operators
if (stream.eatWhile(isOperatorChar)) {
return "meta";
}
stream.eatWhile(/[\w\$_]/);
var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
return "keyword";

// Keywords / plain variables
if (stream.eatWhile(/[\w\$_]/)) {
var cur = stream.current();
if (keywords[cur]) {
if (openClose[cur]) {
curPunc = "newblock";
}
if (statementKeywords[cur]) {
curPunc = "newstatement";
}
curKeyword = cur;
return "keyword";
}
return "variable";
}
if (atoms.propertyIsEnumerable(cur)) return "atom";
return "variable";

stream.next();
return null;
}

function tokenString(quote) {
Expand Down Expand Up @@ -94,18 +224,56 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
this.prev = prev;
}
function pushContext(state, col, type) {
return state.context = new Context(state.indented, col, type, null, state.context);
var indent = state.indented;
var c = new Context(indent, col, type, null, state.context);
return state.context = c;
}
function popContext(state) {
var t = state.context.type;
if (t == ")" || t == "]" || t == "}")
if (t == ")" || t == "]" || t == "}") {
state.indented = state.context.indented;
}
return state.context = state.context.prev;
}

// Interface
function isClosing(text, contextClosing) {
if (text == contextClosing) {
return true;
} else {
// contextClosing may be mulitple keywords separated by ;
var closingKeywords = contextClosing.split(";");
for (var i in closingKeywords) {
if (text == closingKeywords[i]) {
return true;
}
}
return false;
}
}

function buildElectricInputRegEx() {
// Reindentation should occur on any bracket char: {}()[]
// or on a match of any of the block closing keywords, at
// the end of a line
var allClosings = [];
for (var i in openClose) {
if (openClose[i]) {
var closings = openClose[i].split(";");
for (var j in closings) {
allClosings.push(closings[j]);
}
}
}
var re = new RegExp("[{}()\\[\\]]|(" + allClosings.join("|") + ")$");
return re;
}

// Interface
return {

// Regex to force current line to reindent
electricInput: buildElectricInputRegEx(),

startState: function(basecolumn) {
return {
tokenize: null,
Expand All @@ -124,69 +292,60 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
}
if (stream.eatSpace()) return null;
curPunc = null;
curKeyword = null;
var style = (state.tokenize || tokenBase)(stream, state);
if (style == "comment" || style == "meta") return style;
if (style == "comment" || style == "meta" || style == "variable") return style;
if (ctx.align == null) ctx.align = true;

if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
else if (curPunc == "{") pushContext(state, stream.column(), "}");
else if (curPunc == "[") pushContext(state, stream.column(), "]");
else if (curPunc == "(") pushContext(state, stream.column(), ")");
else if (curPunc == "}") {
while (ctx.type == "statement") ctx = popContext(state);
if (ctx.type == "}") ctx = popContext(state);
while (ctx.type == "statement") ctx = popContext(state);
if (curPunc == ctx.type) {
popContext(state);
}
else if ((curPunc == ";" && ctx.type == "statement") ||
(ctx.type && isClosing(curKeyword, ctx.type))) {
ctx = popContext(state);
while (ctx && ctx.type == "statement") ctx = popContext(state);
}
else if (curPunc == ctx.type) popContext(state);
else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
else if (curPunc == "{") { pushContext(state, stream.column(), "}"); }
else if (curPunc == "[") { pushContext(state, stream.column(), "]"); }
else if (curPunc == "(") { pushContext(state, stream.column(), ")"); }
else if (ctx && ctx.type == "endcase" && curPunc == ":") { pushContext(state, stream.column(), "statement"); }
else if (curPunc == "newstatement") {
pushContext(state, stream.column(), "statement");
} else if (curPunc == "newblock") {
var close = openClose[curKeyword];
pushContext(state, stream.column(), close);
}

state.startOfLine = false;
return style;
},

indent: function(state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return 0;
var firstChar = textAfter && textAfter.charAt(0), ctx = state.context, closing = firstChar == ctx.type;
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit);
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
var closing = false;
var possibleClosing = textAfter.match(closingBracketOrWord);
if (possibleClosing) {
closing = isClosing(possibleClosing[0], ctx.type);
}
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
else if (closingBracket.test(ctx.type) && ctx.align && !dontAlignCalls) return ctx.column + (closing ? 0 : 1);
else if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
else return ctx.indented + (closing ? 0 : indentUnit);
},

electricChars: "{}"
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//"
};
});

function words(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}

var verilogKeywords = "always and assign automatic begin buf bufif0 bufif1 case casex casez cell cmos config " +
"deassign default defparam design disable edge else end endcase endconfig endfunction endgenerate endmodule " +
"endprimitive endspecify endtable endtask event for force forever fork function generate genvar highz0 " +
"highz1 if ifnone incdir include initial inout input instance integer join large liblist library localparam " +
"macromodule medium module nand negedge nmos nor noshowcancelled not notif0 notif1 or output parameter pmos " +
"posedge primitive pull0 pull1 pulldown pullup pulsestyle_onevent pulsestyle_ondetect rcmos real realtime " +
"reg release repeat rnmos rpmos rtran rtranif0 rtranif1 scalared showcancelled signed small specify specparam " +
"strong0 strong1 supply0 supply1 table task time tran tranif0 tranif1 tri tri0 tri1 triand trior trireg " +
"unsigned use vectored wait wand weak0 weak1 while wire wor xnor xor";

var verilogBlockKeywords = "begin bufif0 bufif1 case casex casez config else end endcase endconfig endfunction " +
"endgenerate endmodule endprimitive endspecify endtable endtask for forever function generate if ifnone " +
"macromodule module primitive repeat specify table task while";

function metaHook(stream) {
stream.eatWhile(/[\w\$_]/);
return "meta";
}

CodeMirror.defineMIME("text/x-verilog", {
name: "verilog",
keywords: words(verilogKeywords),
blockKeywords: words(verilogBlockKeywords),
atoms: words("null"),
hooks: {"`": metaHook, "$": metaHook}
});
CodeMirror.defineMIME("text/x-verilog", {
name: "verilog"
});
CodeMirror.defineMIME("text/x-systemverilog", {
name: "systemverilog"
});

});
48 changes: 48 additions & 0 deletions mode/xml/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
(function() {
var mode = CodeMirror.getMode({indentUnit: 2}, "xml"), mname = "xml";
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), mname); }

MT("matching",
"[tag&bracket <][tag top][tag&bracket >]",
" text",
" [tag&bracket <][tag inner][tag&bracket />]",
"[tag&bracket </][tag top][tag&bracket >]");

MT("nonmatching",
"[tag&bracket <][tag top][tag&bracket >]",
" [tag&bracket <][tag inner][tag&bracket />]",
" [tag&bracket </][tag&error tip][tag&bracket&error >]");

MT("doctype",
"[meta <!doctype foobar>]",
"[tag&bracket <][tag top][tag&bracket />]");

MT("cdata",
"[tag&bracket <][tag top][tag&bracket >]",
" [atom <![CDATA[foo]",
"[atom barbazguh]]]]>]",
"[tag&bracket </][tag top][tag&bracket >]");

// HTML tests
mode = CodeMirror.getMode({indentUnit: 2}, "text/html");

MT("selfclose",
"[tag&bracket <][tag html][tag&bracket >]",
" [tag&bracket <][tag link] [attribute rel]=[string stylesheet] [attribute href]=[string \"/foobar\"][tag&bracket >]",
"[tag&bracket </][tag html][tag&bracket >]");

MT("list",
"[tag&bracket <][tag ol][tag&bracket >]",
" [tag&bracket <][tag li][tag&bracket >]one",
" [tag&bracket <][tag li][tag&bracket >]two",
"[tag&bracket </][tag ol][tag&bracket >]");

MT("valueless",
"[tag&bracket <][tag input] [attribute type]=[string checkbox] [attribute checked][tag&bracket />]");

MT("pThenArticle",
"[tag&bracket <][tag p][tag&bracket >]",
" foo",
"[tag&bracket <][tag article][tag&bracket >]bar");

})();
Loading