git add stuff

This commit is contained in:
Alan
2026-02-14 19:50:25 +03:00
parent 5c3329238b
commit 3942076805
1130 changed files with 120023 additions and 6 deletions

View File

@@ -0,0 +1,19 @@
Copyright (C) 2011 by Marijn Haverbeke <marijnh@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,6 @@
# CodeMirror 2
CodeMirror 2 is a rewrite of [CodeMirror
1](http://github.com/marijnh/CodeMirror). The docs live
[here](http://codemirror.net/2/manual.html), and the project page is
[http://codemirror.net/2/](http://codemirror.net/2/).

View File

@@ -0,0 +1,77 @@
<!doctype html>
<html>
<head>
<title>CodeMirror: Compression Helper</title>
<link rel="stylesheet" type="text/css" href="css/docs.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1>
<pre class="grey">
<img src="css/baboon.png" class="logo" alt="logo"/>/* Script compression
helper */
</pre>
<p>To optimize loading CodeMirror, especially when including a
bunch of different modes, it is recommended that you combine and
minify (and preferably also gzip) the scrips. This page makes
those first two steps very easy. Simply select the version and
scripts you need in the form below, and
click <strong>Compress</strong> to download the minified script
file.</p>
<form id="form" action="http://marijnhaverbeke.nl/uglifyjs" method="post">
<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/2/">HEAD</option>
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.0;f=">2.0</option>
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=beta2;f=">beta2</option>
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=beta1;f=">beta1</option>
</select></p>
<select multiple="multiple" name="code_url" style="width: 40em;" class="field" id="files">
<optgroup label="CodeMirror Library">
<option value="http://codemirror.net/2/lib/codemirror.js" selected>codemirror.js</option>
<option value="http://codemirror.net/2/lib/overlay.js">overlay.js</option>
</optgroup>
<optgroup label="Modes">
<option value="http://codemirror.net/2/mode/javascript/javascript.js">javascript.js</option>
<option value="http://codemirror.net/2/mode/xml/xml.js">xml.js</option>
<option value="http://codemirror.net/2/mode/css/css.js">css.js</option>
<option value="http://codemirror.net/2/mode/htmlmixed/htmlmixed.js">htmlmixed.js</option>
<option value="http://codemirror.net/2/mode/clike/clike.js">clike.js</option>
<option value="http://codemirror.net/2/mode/php/php.js">php.js</option>
<option value="http://codemirror.net/2/mode/haskell/haskell.js">haskell.js</option>
<option value="http://codemirror.net/2/mode/diff/diff.js">diff.js</option>
<option value="http://codemirror.net/2/mode/stex/stex.js">stex.js</option>
</optgroup>
</select></p>
<p>
<button type="submit">Compress</button> with <a href="http://github.com/mishoo/UglifyJS/">UglifyJS</a>
</p>
<p>Custom code to add to the compressed file:<textarea name="js_code" style="width: 100%; height: 15em;" class="field"></textarea></p>
</form>
<script type="text/javascript">
function setVersion(ver) {
var urlprefix = ver.options[ver.selectedIndex].value;
var select = document.getElementById("files"), m;
for (var optgr = select.firstChild; optgr; optgr = optgr.nextSibling)
for (var opt = optgr.firstChild; opt; opt = opt.nextSibling) {
if (opt.nodeName != "OPTION")
continue;
else if (m = opt.value.match(/^http:\/\/codemirror.net\/2\/(.*)$/))
opt.value = urlprefix + m[1];
else if (m = opt.value.match(/http:\/\/marijnhaverbeke.nl\/git\/codemirror\?a=blob_plain;hb=[^;]+;f=(.*)$/))
opt.value = urlprefix + m[1];
}
}
</script>
<script type="text/javascript" src="css/font.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,158 @@
body {
font-family: Arial, sans-serif;
line-height: 1.5;
max-width: 64.3em;
margin: 3em auto;
padding: 0 1em;
}
body.droid {
font-family: Droid Sans, Arial, sans-serif;
}
h1 {
letter-spacing: -3px;
font-size: 3.23em;
font-weight: bold;
margin: 0;
}
h2 {
font-size: 1.23em;
font-weight: bold;
margin: .5em 0;
letter-spacing: -1px;
}
h3 {
font-size: 1em;
font-weight: bold;
margin: .4em 0;
}
pre {
font-family: Courier New, monospaced;
background-color: #eee;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
border-radius: 6px;
padding: 1em;
}
pre.code {
margin: 0 1em;
}
.grey {
font-size: 2em;
padding: .5em 1em;
line-height: 1.2em;
margin-top: .5em;
position: relative;
}
img.logo {
position: absolute;
right: -25px;
bottom: 4px;
}
a:link, a:visited, .quasilink {
color: #df0019;
cursor: pointer;
text-decoration: none;
}
a:hover, .quasilink:hover {
color: #800004;
}
h1 a:link, h1 a:visited, h1 a:hover {
color: black;
}
ul {
margin: 0;
padding-left: 1.2em;
}
a.download {
color: white;
background-color: #df0019;
width: 100%;
display: block;
text-align: center;
font-size: 1.23em;
font-weight: bold;
text-decoration: none;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
border-radius: 6px;
padding: .5em 0;
margin-bottom: 1em;
}
a.download:hover {
background-color: #bb0010;
}
.rel {
margin-bottom: 0;
}
.rel-note {
color: #777;
font-size: .9em;
margin-top: .1em;
}
.logo-braces {
color: #df0019;
position: relative;
top: -4px;
}
.blk {
float: left;
}
.left {
width: 37em;
padding-right: 6.53em;
padding-bottom: 1em;
}
.left1 {
width: 15.24em;
padding-right: 6.45em;
}
.left2 {
width: 15.24em;
}
.right {
width: 20.68em;
}
.leftbig {
width: 42.44em;
padding-right: 6.53em;
}
.rightsmall {
width: 15.24em;
}
.clear:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.clear { display: inline-block; }
/* start commented backslash hack \*/
* html .clear { height: 1%; }
.clear { display: block; }
/* close commented backslash hack */

View File

@@ -0,0 +1,15 @@
function waitForStyles() {
for (var i = 0; i < document.styleSheets.length; i++)
if (/googleapis/.test(document.styleSheets[i].href))
return document.body.className += " droid";
setTimeout(waitForStyles, 100);
}
setTimeout(function() {
if (/AppleWebKit/.test(navigator.userAgent) && /iP[oa]d|iPhone/.test(navigator.userAgent)) return;
var link = document.createElement("LINK");
link.type = "text/css";
link.rel = "stylesheet";
link.href = "http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold";
document.documentElement.getElementsByTagName("HEAD")[0].appendChild(link);
waitForStyles();
}, 10);

View File

@@ -0,0 +1,71 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: Active Line Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<link rel="stylesheet" href="../mode/xml/xml.css">
<script src="../mode/xml/xml.js"></script>
<link rel="stylesheet" href="../css/docs.css">
<style type="text/css">
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.activeline {background: #f0fcff !important;}
</style>
</head>
<body>
<h1>CodeMirror 2: Active Line Demo</h1>
<form><textarea id="code" name="code">
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
xmlns:georss="http://www.georss.org/georss"
xmlns:twitter="http://api.twitter.com">
<channel>
<title>Twitter / codemirror</title>
<link>http://twitter.com/codemirror</link>
<atom:link type="application/rss+xml"
href="http://twitter.com/statuses/user_timeline/242283288.rss" rel="self"/>
<description>Twitter updates from CodeMirror / codemirror.</description>
<language>en-us</language>
<ttl>40</ttl>
<item>
<title>codemirror: http://cloud-ide.com &#8212; they're springing up like mushrooms. This one
uses CodeMirror as its editor.</title>
<description>codemirror: http://cloud-ide.com &#8212; they're springing up like mushrooms. This
one uses CodeMirror as its editor.</description>
<pubDate>Thu, 17 Mar 2011 23:34:47 +0000</pubDate>
<guid>http://twitter.com/codemirror/statuses/48527733722058752</guid>
<link>http://twitter.com/codemirror/statuses/48527733722058752</link>
<twitter:source>web</twitter:source>
<twitter:place/>
</item>
<item>
<title>codemirror: Posted a description of the CodeMirror 2 internals at
http://codemirror.net/2/internals.html</title>
<description>codemirror: Posted a description of the CodeMirror 2 internals at
http://codemirror.net/2/internals.html</description>
<pubDate>Wed, 02 Mar 2011 12:15:09 +0000</pubDate>
<guid>http://twitter.com/codemirror/statuses/42920879788789760</guid>
<link>http://twitter.com/codemirror/statuses/42920879788789760</link>
<twitter:source>web</twitter:source>
<twitter:place/>
</item>
</feed></textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "application/xml",
lineNumbers: true,
onCursorActivity: function() {
editor.setLineClass(hlLine, null);
hlLine = editor.setLineClass(editor.getCursor().line, "activeline");
}
});
var hlLine = editor.setLineClass(0, "activeline");
</script>
<p>Styling the current cursor line.</p>
</body>
</html>

View File

@@ -0,0 +1,79 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: Autocomplete Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<link rel="stylesheet" href="../mode/javascript/javascript.css">
<script src="../mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="../css/docs.css">
<style type="text/css">
.completions {
position: absolute;
z-index: 10;
overflow: hidden;
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
}
.completions select {
background: #fafafa;
outline: none;
border: none;
padding: 0;
margin: 0;
font-family: monospace;
}
.CodeMirror {
border: 1px solid #eee;
}
</style>
</head>
<body>
<h1>CodeMirror 2: Autocomplete demo</h1>
<form><textarea id="code" name="code">
function getCompletions(token, context) {
var found = [], start = token.string;
function maybeAdd(str) {
if (str.indexOf(start) == 0) found.push(str);
}
function gatherCompletions(obj) {
if (typeof obj == "string") forEach(stringProps, maybeAdd);
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
for (var name in obj) maybeAdd(name);
}
if (context) {
// If this is a property, see if it belongs to some object we can
// find in the current environment.
var obj = context.pop(), base;
if (obj.className == "js-variable")
base = window[obj.string];
else if (obj.className == "js-string")
base = "";
else if (obj.className == "js-atom")
base = 1;
while (base != null && context.length)
base = base[context.pop().string];
if (base != null) gatherCompletions(base);
}
else {
// If not, just look in the window object and any local scope
// (reading into JS mode internals to get at the local variables)
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
gatherCompletions(window);
forEach(keywords, maybeAdd);
}
return found;
}
</textarea></form>
<p>Press <strong>ctrl-space</strong> to activate autocompletion. See
the <a href="complete.js">code</a> to figure out how it works.</p>
<script src="complete.js"></script>
</body>
</html>

View File

@@ -0,0 +1,150 @@
(function () {
// Minimal event-handling wrapper.
function stopEvent() {
if (this.preventDefault) {this.preventDefault(); this.stopPropagation();}
else {this.returnValue = false; this.cancelBubble = true;}
}
function addStop(event) {
if (!event.stop) event.stop = stopEvent;
return event;
}
function connect(node, type, handler) {
function wrapHandler(event) {handler(addStop(event || window.event));}
if (typeof node.addEventListener == "function")
node.addEventListener(type, wrapHandler, false);
else
node.attachEvent("on" + type, wrapHandler);
}
function forEach(arr, f) {
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
}
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
onKeyEvent: function(i, e) {
// Hook into ctrl-space
if (e.keyCode == 32 && (e.ctrlKey || e.metaKey) && !e.altKey) {
e.stop();
return startComplete();
}
}
});
function startComplete() {
// We want a single cursor position.
if (editor.somethingSelected()) return;
// Find the token at the cursor
var cur = editor.getCursor(false), token = editor.getTokenAt(cur), tprop = token;
// If it's not a 'word-style' token, ignore the token.
if (!/^[\w$_]*$/.test(token.string)) {
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
className: token.string == "." ? "js-property" : null};
}
// If it is a property, find out what it is a property of.
while (tprop.className == "js-property") {
tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
if (tprop.string != ".") return;
tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
if (!context) var context = [];
context.push(tprop);
}
var completions = getCompletions(token, context);
if (!completions.length) return;
function insert(str) {
editor.replaceRange(str, {line: cur.line, ch: token.start}, {line: cur.line, ch: token.end});
}
// When there is only one completion, use it directly.
if (completions.length == 1) {insert(completions[0]); return true;}
// Build the select widget
var complete = document.createElement("div");
complete.className = "completions";
var sel = complete.appendChild(document.createElement("select"));
sel.multiple = true;
for (var i = 0; i < completions.length; ++i) {
var opt = sel.appendChild(document.createElement("option"));
opt.appendChild(document.createTextNode(completions[i]));
}
sel.firstChild.selected = true;
sel.size = Math.min(10, completions.length);
var pos = editor.cursorCoords();
complete.style.left = pos.x + "px";
complete.style.top = pos.yBot + "px";
document.body.appendChild(complete);
// Hack to hide the scrollbar.
if (completions.length <= 10)
complete.style.width = (sel.clientWidth - 1) + "px";
var done = false;
function close() {
if (done) return;
done = true;
complete.parentNode.removeChild(complete);
}
function pick() {
insert(sel.options[sel.selectedIndex].value);
close();
setTimeout(function(){editor.focus();}, 50);
}
connect(sel, "blur", close);
connect(sel, "keydown", function(event) {
var code = event.keyCode;
// Enter and space
if (code == 13 || code == 32) {event.stop(); pick();}
// Escape
else if (code == 27) {event.stop(); close(); editor.focus();}
else if (code != 38 && code != 40) {close(); editor.focus(); setTimeout(startComplete, 50);}
});
connect(sel, "dblclick", pick);
sel.focus();
// Opera sometimes ignores focusing a freshly created node
if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100);
return true;
}
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
"toUpperCase toLowerCase split concat match replace search").split(" ");
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
var funcProps = "prototype apply call bind".split(" ");
var keywords = ("break case catch continue debugger default delete do else false finally for function " +
"if in instanceof new null return switch throw true try typeof var void while with").split(" ");
function getCompletions(token, context) {
var found = [], start = token.string;
function maybeAdd(str) {
if (str.indexOf(start) == 0) found.push(str);
}
function gatherCompletions(obj) {
if (typeof obj == "string") forEach(stringProps, maybeAdd);
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
for (var name in obj) maybeAdd(name);
}
if (context) {
// If this is a property, see if it belongs to some object we can
// find in the current environment.
var obj = context.pop(), base;
if (obj.className == "js-variable")
base = window[obj.string];
else if (obj.className == "js-string")
base = "";
else if (obj.className == "js-atom")
base = 1;
while (base != null && context.length)
base = base[context.pop().string];
if (base != null) gatherCompletions(base);
}
else {
// If not, just look in the window object and any local scope
// (reading into JS mode internals to get at the local variables)
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
gatherCompletions(window);
forEach(keywords, maybeAdd);
}
return found;
}
})();

View File

@@ -0,0 +1,53 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: Breakpoint Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<link rel="stylesheet" href="../mode/javascript/javascript.css">
<script src="../mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="../css/docs.css">
<style type="text/css">
.CodeMirror-gutter {
width: 3em;
background: white;
}
.CodeMirror {
border: 1px solid #aaa;
}
</style>
</head>
<body>
<h1>CodeMirror 2: Breakpoint demo</h1>
<form><textarea id="code" name="code">
CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
onGutterClick: function(cm, n) {
var info = cm.lineInfo(n);
if (info.markerText)
cm.clearMarker(n);
else
cm.setMarker(n, "<span style=\"color: #900\"></span> %N%");
}
});
</textarea></form>
<p>Click the line-number gutter to add or remove 'breakpoints'.</p>
<script>
CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
onGutterClick: function(cm, n) {
var info = cm.lineInfo(n);
if (info.markerText)
cm.clearMarker(n);
else
cm.setMarker(n, "<span style=\"color: #900\">●</span> %N%");
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,57 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: Overlay Parser Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../lib/overlay.js"></script>
<link rel="stylesheet" href="../mode/xml/xml.css">
<script src="../mode/xml/xml.js"></script>
<link rel="stylesheet" href="../css/docs.css">
<style type="text/css">
.CodeMirror {border: 1px solid black;}
.mustache {color: #0ca;}
</style>
</head>
<body>
<h1>CodeMirror 2: Overlay Parser Demo</h1>
<form><textarea id="code" name="code">
<html>
<body>
<h1>{{title}}</h1>
<p>These are links to {{things}}:</p>
<ul>{{#links}}
<li><a href="{{url}}">{{text}}</a></li>
{{/links}}</ul>
</body>
</html>
</textarea></form>
<script>
CodeMirror.defineMode("mustache", function(config, parserConfig) {
var mustacheOverlay = {
token: function(stream, state) {
if (stream.match("{{")) {
while ((ch = stream.next()) != null)
if (ch == "}" && stream.next() == "}") break;
return "mustache";
}
while (stream.next() != null && !stream.match("{{", false)) {}
return null;
}
};
return CodeMirror.overlayParser(CodeMirror.getMode(config, parserConfig.backdrop || "text/html"), mustacheOverlay);
});
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "mustache"});
</script>
<p>Demonstration of a mode that parses HTML, highlighting
the <a href="http://mustache.github.com/">Mustache</a> templating
directives inside of it by using the code
in <a href="../lib/overlay.js"><code>overlay.js</code></a>. View
source to see the 15 lines of code needed to accomplish this.</p>
</body>
</html>

View File

@@ -0,0 +1,38 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: Autoresize Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<link rel="stylesheet" href="../mode/css/css.css">
<script src="../mode/css/css.js"></script>
<link rel="stylesheet" href="../css/docs.css">
<style type="text/css">
.CodeMirror {
border: 1px solid #eee;
height: auto;
}
</style>
</head>
<body>
<h1>CodeMirror 2: Autoresize demo</h1>
<form><textarea id="code" name="code">
.CodeMirror {
height: auto;
}</textarea></form>
<p>By setting a single CSS property, CodeMirror can be made to
automatically resize to fit the content. Use <code>max-height</code>
to prevent it from growing past a given point (on halfway modern
browsers).</p>
<script>
CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true
});
</script>
</body>
</html>

View File

@@ -0,0 +1,106 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: Search/Replace Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<link rel="stylesheet" href="../mode/xml/xml.css">
<script src="../mode/xml/xml.js"></script>
<link rel="stylesheet" href="../css/docs.css">
<style type="text/css">
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.searched {background: yellow;}
</style>
</head>
<body>
<h1>CodeMirror 2: Search/Replace Demo</h1>
<form><textarea id="code" name="code">
<dt id="option_indentWithTabs"><code>indentWithTabs (boolean)</code></dt>
<dd>Whether, when indenting, the first N*8 spaces should be
replaced by N tabs. Default is false.</dd>
<dt id="option_tabMode"><code>tabMode (string)</code></dt>
<dd>Determines what happens when the user presses the tab key.
Must be one of the following:
<dl>
<dt><code>"classic" (the default)</code></dt>
<dd>When nothing is selected, insert a tab. Otherwise,
behave like the <code>"shift"</code> mode. (When shift is
held, this behaves like the <code>"indent"</code> mode.)</dd>
<dt><code>"shift"</code></dt>
<dd>Indent all selected lines by
one <a href="#option_indentUnit"><code>indentUnit</code></a>.
If shift was held while pressing tab, un-indent all selected
lines one unit.</dd>
<dt><code>"indent"</code></dt>
<dd>Indent the line the 'correctly', based on its syntactic
context. Only works if the
mode <a href="#indent">supports</a> it.</dd>
<dt><code>"default"</code></dt>
<dd>Do not capture tab presses, let the browser apply its
default behaviour (which usually means it skips to the next
control).</dd>
</dl></dd>
<dt id="option_enterMode"><code>enterMode (string)</code></dt>
<dd>Determines whether and how new lines are indented when the
enter key is pressed. The following modes are supported:
<dl>
<dt><code>"indent" (the default)</code></dt>
<dd>Use the mode's indentation rules to give the new line
the correct indentation.</dd>
<dt><code>"keep"</code></dt>
<dd>Indent the line the same as the previous line.</dd>
<dt><code>"flat"</code></dt>
<dd>Do not indent the new line.</dd>
</dl></dd>
</textarea></form>
<button type=button onclick="search()">Search</button>
<input type=text style="width: 5em" id=query value=indent> or
<button type=button onclick="replace()">replace</button> it by
<input type=text style="width: 5em" id=replace>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "text/html", lineNumbers: true});
var lastPos = null, lastQuery = null, marked = [];
function unmark() {
for (var i = 0; i < marked.length; ++i) marked[i]();
marked.length = 0;
}
function search() {
unmark();
var text = document.getElementById("query").value;
if (!text) return;
for (var cursor = editor.getSearchCursor(text); cursor.findNext();)
marked.push(editor.markText(cursor.from(), cursor.to(), "searched"));
if (lastQuery != text) lastPos = null;
var cursor = editor.getSearchCursor(text, lastPos || editor.getCursor());
if (!cursor.findNext()) {
cursor = editor.getSearchCursor(text);
if (!cursor.findNext()) return;
}
editor.setSelection(cursor.from(), cursor.to());
lastQuery = text; lastPos = cursor.to();
}
function replace() {
unmark();
var text = document.getElementById("query").value,
replace = document.getElementById("replace").value;
if (!text) return;
for (var cursor = editor.getSearchCursor(text); cursor.findNext();)
editor.replaceRange(replace, cursor.from(), cursor.to());
}
</script>
<p>Demonstration of search/replace functionality and marking
text.</p>
</body>
</html>

View File

@@ -0,0 +1,225 @@
<!doctype html>
<html>
<head>
<title>CodeMirror</title>
<link rel="stylesheet" type="text/css" href="css/docs.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="alternate" href="http://twitter.com/statuses/user_timeline/242283288.rss" type="application/rss+xml"/>
</head>
<body>
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1>
<pre class="grey">
<img src="css/baboon.png" class="logo" alt="logo"/>/* In-browser code editing
made bearable */
</pre>
<div class="clear"><div class="left blk">
<p style="margin-top: 0">CodeMirror is a JavaScript library that can
be used to create a relatively pleasant editor interface for
code-like content &#x2015; computer programs, HTML markup, and
similar. If a mode has been written for the language you are
editing, the code will be coloured, and the editor will optionally
help you with indentation.</p>
<p>This is the project page for CodeMirror 2, the currently more
actively developed, and recommended
version. <a href="1/index.html">CodeMirror 1</a> is still available
from here.</p>
<div class="clear"><div class="left1 blk">
<h2 style="margin-top: 0">Supported modes:</h2>
<ul>
<li><a href="mode/javascript/index.html">JavaScript</a></li>
<li><a href="mode/xml/index.html">XML/HTML</a></li>
<li><a href="mode/css/index.html">CSS</a></li>
<li><a href="mode/htmlmixed/index.html">HTML mixed-mode</a></li>
<li><a href="mode/php/index.html">PHP</a></li>
<li><a href="mode/diff/index.html">diff</a></li>
<li><a href="mode/clike/index.html">C, Java, and similar</a></li>
<li><a href="mode/stex/index.html">sTeX, LaTeX</a></li>
<li><a href="mode/haskell/index.html">Haskell</a></li>
</ul>
</div><div class="left2 blk">
<h2 style="margin-top: 0">Usage demos:</h2>
<ul>
<li><a href="demo/complete.html">Autocompletion</a></li>
<li><a href="demo/mustache.html">Mode overlays</a></li>
<li><a href="demo/search.html">Search/replace</a></li>
<li><a href="demo/resize.html">Auto-resizing editor</a></li>
<li><a href="demo/marker.html">Setting breakpoints</a></li>
<li><a href="demo/activeline.html">Highlighting the current line</a></li>
</ul>
</div></div>
<h2 id="code">Getting the code</h2>
<p>All of CodeMirror is released under a <a
href="LICENSE">MIT-style</a> license. To get it, you can download
the <a href="http://codemirror.net/codemirror.zip">latest
release</a> or the current <a
href="http://codemirror.net/codemirror-latest.zip">development
snapshot</a> as zip files. To create a custom minified script file,
you can use the <a href="compress.html">compression API</a>.</p>
<p>We use <a href="http://git-scm.com/">git</a> for version control.
The main repository can be fetched in this way:</p>
<pre class="code">git clone http://marijnhaverbeke.nl/git/codemirror2</pre>
<p>CodeMirror can also be found on GitHub at <a
href="http://github.com/marijnh/CodeMirror2">marijnh/CodeMirror2</a>.
If you plan to hack on the code and contribute patches, the best way
to do it is to create a GitHub fork, and send pull requests.</p>
<h2 id="documention">Documentation</h2>
<p>The <a href="manual.html">manual</a> is your first stop for
learning how to use this library. It starts with a quick explanation
of how to use the editor, and then describes all of the (many)
options and methods that CodeMirror exposes.</p>
<p>For those who want to learn more about the code, there is
an <a href="internals.html">overview of the internals</a> available.
The <a href="http://github.com/marijnh/CodeMirror2">source code</a>
itself is, for the most part, also well commented.</p>
<h2 id="support">Support and bug reports</h2>
<p>There is
a <a href="http://groups.google.com/group/codemirror">Google
group</a> (a sort of mailing list/newsgroup thing) for discussion
and news related to CodeMirror. Reporting bugs is best done
on <a href="http://github.com/marijnh/CodeMirror2/issues">github</a>.
You can also e-mail me
directly: <a href="mailto:marijnh@gmail.com">Marijn
Haverbeke</a>.</p>
<h2 id="supported">Supported browsers</h2>
<p>The following browsers are able to run CodeMirror:</p>
<ul>
<li>Firefox 2 or higher</li>
<li>Chrome, any version</li>
<li>Safari 3 or higher</li>
<li>Internet Explorer 6 or higher</li>
<li>Opera 9 or higher (with some key-handling problems on OS X)</li>
</ul>
<p>I am not actively testing against every new browser release, and
vendors have a habit of introducing bugs all the time, so I am
relying on the community to tell me when something breaks.
See <a href="#support">below</a> for information on how to contact
me.</p>
</div>
<div class="right blk">
<a href="http://codemirror.net/codemirror.zip" class="download">Download the latest release</a>
<h2>Make a donation</h2>
<ul>
<li><span onclick="document.getElementById('paypal').submit();" class="quasilink">Paypal</span></li>
<li><span onclick="document.getElementById('bankinfo').style.display = 'block';" class="quasilink">Bank</span></li>
</ul>
<p id="bankinfo" style="display: none;">
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>
</p>
<h2>Releases:</h2>
<p class="rel">28-03-2011: <a href="http://codemirror.net/codemirror-2.0.zip">Version 2.0</a>:</p>
<p class="rel-note">CodeMirror 2 is a complete rewrite that's
faster, smaller, simpler to use, and less dependent on browser
quirks. See <a href="internals.html">this</a>
and <a href="http://groups.google.com/group/codemirror/browse_thread/thread/5a8e894024a9f580">this</a>
for more information.</a>
<p class="rel">28-03-2011: <a href="http://codemirror.net/codemirror-1.0.zip">Version 1.0</a>:</p>
<ul class="rel-note">
<li>Fix error when debug history overflows.</li>
<li>Refine handling of C# verbatim strings.</li>
<li>Fix some issues with JavaScript indentation.</li>
</ul>
<p class="rel">22-02-2011: <a href="https://github.com/marijnh/codemirror2/tree/beta2">Version 2.0 beta 2</a>:</p>
<p class="rel-note">Somewhate more mature API, lots of bugs shaken out.</a>
<p class="rel">17-02-2011: <a href="http://codemirror.net/codemirror-0.94.zip">Version 0.94</a>:</p>
<ul class="rel-note">
<li><code>tabMode: "spaces"</code> was modified slightly (now indents when something is selected).</li>
<li>Fixes a bug that would cause the selection code to break on some IE versions.</li>
<li>Disabling spell-check on WebKit browsers now works.</li>
</ul>
<p class="rel">08-02-2011: <a href="http://codemirror.net/2/">Version 2.0 beta 1</a>:</p>
<p class="rel-note">CodeMirror 2 is a complete rewrite of
CodeMirror, no longer depending on an editable frame.</p>
<p class="rel">19-01-2011: <a href="http://codemirror.net/codemirror-0.93.zip">Version 0.93</a>:</p>
<ul class="rel-note">
<li>Added a <a href="contrib/regex/index.html">Regular Expression</a> parser.</li>
<li>Fixes to the PHP parser.</li>
<li>Support for regular expression in search/replace.</li>
<li>Add <code>save</code> method to instances created with <code>fromTextArea</code>.</li>
<li>Add support for MS T-SQL in the SQL parser.</li>
<li>Support use of CSS classes for highlighting brackets.</li>
<li>Fix yet another hang with line-numbering in hidden editors.</li>
</ul>
<p class="rel">17-12-2010: <a href="http://codemirror.net/codemirror-0.92.zip">Version 0.92</a>:</p>
<ul class="rel-note">
<li>Make CodeMirror work in XHTML documents.</li>
<li>Fix bug in handling of backslashes in Python strings.</li>
<li>The <code>styleNumbers</code> option is now officially
supported and documented.</li>
<li><code>onLineNumberClick</code> option added.</li>
<li>More consistent names <code>onLoad</code> and
<code>onCursorActivity</code> callbacks. Old names still work, but
are deprecated.</li>
<li>Add a <a href="contrib/freemarker/index.html">Freemarker</a> mode.</li>
</ul>
<p class="rel">11-11-2010: <a
href="http://codemirror.net/codemirror-0.91.zip">Version 0.91</a>:</p>
<ul class="rel-note">
<li>Adds support for <a href="contrib/java">Java</a>.</li>
<li>Small additions to the <a href="contrib/php">PHP</a> and <a href="contrib/sql">SQL</a> parsers.</li>
<li>Work around various <a href="https://bugs.webkit.org/show_bug.cgi?id=47806">Webkit</a> <a href="https://bugs.webkit.org/show_bug.cgi?id=23474">issues</a>.</li>
<li>Fix <code>toTextArea</code> to update the code in the textarea.</li>
<li>Add a <code>noScriptCaching</code> option (hack to ease development).</li>
<li>Make sub-modes of <a href="mixedtest.html">HTML mixed</a> mode configurable.</li>
</ul>
<p><a href="oldrelease.html">Older releases...</a></p>
</div></div>
<div style="height: 2em">&nbsp;</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"/>
</form>
<script type="text/javascript" src="css/font.js"></script>
</body>
</html>

View File

@@ -0,0 +1,352 @@
<!doctype html>
<html><head>
<title>CodeMirror 2: (Re-) Implementing A Syntax-Highlighting Editor in JavaScript</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="stylesheet" href="docs.css">
</head><body>
<h1>(Re-) Implementing A Syntax-Highlighting Editor in JavaScript</h1>
<p style="font-size: 85%">
<strong>Topic:</strong> JavaScript, code editor implementation<br>
<strong>Author:</strong> Marijn Haverbeke<br>
<strong>Date:</strong> March 2nd 2011
</p>
<p>This is a followup to
my <a href="http://codemirror.net/story.html">Brutal Odyssey to the
Dark Side of the DOM Tree</a> story. That one describes the
mind-bending process of implementing (what would become) CodeMirror 1.
This one describes the internals of CodeMirror 2, a complete rewrite
and rethink of the old code base. I wanted to give this piece another
Hunter Thompson copycat subtitle, but somehow that would be out of
place—the process this time around was one of straightforward
engineering, requiring no serious mind-bending whatsoever.</p>
<p>So, what is wrong with CodeMirror 1? I'd estimate, by mailing list
activity and general search-engine presence, that it has been
integrated into about a thousand systems by now. The most prominent
one, since a few weeks,
being <a href="http://googlecode.blogspot.com/2011/01/make-quick-fixes-quicker-on-google.html">Google
code's project hosting</a>. It works, and it's being used widely.</a>
<p>Still, I did not start replacing it because I was bored. CodeMirror
1 was heavily reliant on <code>designMode</code>
or <code>contentEditable</code> (depending on the browser). Neither of
these are well specified (HTML5 tries
to <a href="http://www.w3.org/TR/html5/editing.html#contenteditable">specify</a>
their basics), and, more importantly, they tend to be one of the more
obscure and buggy areas of browser functionality—CodeMirror, by using
this functionality in a non-typical way, was constantly running up
against browser bugs. WebKit wouldn't show an empty line at the end of
the document, and in some releases would suddenly get unbearably slow.
Firefox would show the cursor in the wrong place. Internet Explorer
would insist on linkifying everything that looked like a URL or email
address, a behaviour that can't be turned off. Some bugs I managed to
work around (which was often a frustrating, painful process), others,
such as the Firefox cursor placement, I gave up on, and had to tell
user after user that they were known problems, but not something I
could help.</p>
<p>Also, there is the fact that <code>designMode</code> (which seemed
to be less buggy than <code>contentEditable</code> in Webkit and
Firefox, and was thus used by CodeMirror 1 in those browsers) requires
a frame. Frames are another tricky area. It takes some effort to
prevent getting tripped up by domain restrictions, they don't
initialize synchronously, behave strangely in response to the back
button, and, on several browsers, can't be moved around the DOM
without having them re-initialize. They did provide a very nice way to
namespace the library, though—CodeMirror 1 could freely pollute the
namespace inside the frame.</p>
<p>Finally, working with an editable document means working with
selection in arbitrary DOM structures. Internet Explorer (8 and
before) has an utterly different (and awkward) selection API than all
of the other browsers, and even among the different implementations of
<code>document.selection</code>, details about how exactly a selection
is represented vary quite a bit. Add to that the fact that Opera's
selection support tended to be very buggy until recently, and you can
imagine why CodeMirror 1 contains 700 lines of selection-handling
code.</p>
<p>And that brings us to the main issue with the CodeMirror 1
code base: The proportion of browser-bug-workarounds to real
application code was getting dangerously high. By building on top of a
few dodgy features, I put the system in a vulnerable position—any
incompatibility and bugginess in these features, I had to paper over
with my own code. Not only did I have to do some serious stunt-work to
get it to work on older browsers (as detailed in the
previous <a href="http://codemirror.net/story.html">story</a>), things
also kept breaking in newly released versions, requiring me to come up
with <em>new</em> scary hacks in order to keep up. This was starting
to lose its appeal.</p>
<h2>General Approach</h2>
<p>What CodeMirror 2 does is try to sidestep most of the hairy hacks
that came up in version 1. I owe a lot to the
<a href="http://ace.ajax.org">ACE</a> editor for inspiration on how to
approach this.</p>
<p>I absolutely did not want to be completely reliant on key events to
generate my input. Every JavaScript programmer knows that key event
information is horrible and incomplete. Some people (most awesomely
Mihai Bazon with <a href="http://ymacs.org">Ymacs</a>) have been able
to build more or less functioning editors by directly reading key
events, but it takes a lot of work (the kind of never-ending, fragile
work I described earlier), and will never be able to properly support
things like multi-keystoke international character input.</p>
<p>So what I do is focus a hidden textarea, and let the browser
believe that the user is typing into that. What we show to the user is
a DOM structure we built to represent his document. If this is updated
quickly enough, and shows some kind of believable cursor, it feels
like a real text-input control.</p>
<p>Another big win is that this DOM representation does not have to
span the whole document. Some CodeMirror 1 users insisted that they
needed to put a 30 thousand line XML document into CodeMirror. Putting
all that into the DOM takes a while, especially since, for some
reason, an editable DOM tree is slower than a normal one on most
browsers. If we have full control over what we show, we must only
ensure that the visible part of the document has been added, and can
do the rest only when needed. (Fortunately, the <code>onscroll</code>
event works almost the same on all browsers, and lends itself well to
displaying things only as they are scrolled into view.)</p>
<h2>Input</h2>
<p>ACE uses its hidden textarea only as a text input shim, and does
all cursor movement and things like text deletion itself by directly
handling key events. CodeMirror's way is to let the browser do its
thing as much as possible, and not, for example, define its own set of
key bindings. One way to do this would have been to have the whole
document inside the hidden textarea, and after each key event update
the display DOM to reflect what's in that textarea.</p>
<p>That'd be simple, but it is not realistic. For even medium-sized
document the editor would be constantly munging huge strings, and get
terribly slow. What CodeMirror 2 does is put the current selection,
along with an extra line on the top and on the bottom, into the
textarea.</p>
<p>This means that the arrow keys (and their ctrl-variations), home,
end, etcetera, do not have to be handled specially. We just read the
cursor position in the textarea, and update our cursor to match it.
Also, copy and paste work pretty much for free, and people get their
native key bindings, without any special work on my part. For example,
I have emacs key bindings configured for Chrome and Firefox. There is
no way for a script to detect this.</p>
<p>Of course, since only a small part of the document sits in the
textarea, keys like page up and ctrl-end won't do the right thing.
CodeMirror is catching those events and handling them itself.</p>
<h2>Selection</h2>
<p>Getting and setting the selection range of a textarea in modern
browsers is trivial—you just use the <code>selectionStart</code>
and <code>selectionEnd</code> properties. On IE you have to do some
insane stuff with temporary ranges and compensating for the fact that
moving the selection by a 'character' will treat \r\n as a single
character, but even there it is possible to build functions that
reliably set and get the selection range.</p>
<p>But consider this typical case: When I'm somewhere in my document,
press shift, and press the up arrow, something gets selected. Then, if
I, still holding shift, press the up arrow again, the top of my
selection is adjusted. The selection remembers where its <em>head</em>
and its <em>anchor</em> are, and moves the head when we shift-move.
This is a generally accepted property of selections, and done right by
every editing component built in the past twenty years.</p>
<p>But not something that the browser selection APIs expose.</p>
<p>Great. So when someone creates an 'upside-down' selection, the next
time CodeMirror has to update the textarea, it'll re-create the
selection as an 'upside-up' selection, with the anchor at the top, and
the next cursor motion will behave in an unexpected way—our second
up-arrow press in the example above will not do anything, since it is
interpreted in exactly the same way as the first.</p>
<p>No problem. We'll just, ehm, detect that the selection is
upside-down (you can tell by the way it was created), and then, when
an upside-down selection is present, and a cursor-moving key is
pressed in combination with shift, we quickly collapse the selection
in the textarea to its start, allow the key to take effect, and then
combine its new head with its old anchor to get the <em>real</em>
selection.</p>
<p>In short, scary hacks could not be avoided entirely in CodeMirror
2.</p>
<p>And, the observant reader might ask, how do you even know that a
key combo is a cursor-moving combo, if you claim you support any
native key bindings? Well, we don't, but we can learn. The editor
keeps a set known cursor-movement combos (initialized to the
predictable defaults), and updates this set when it observes that
pressing a certain key had (only) the effect of moving the cursor.
This, of course, doesn't work if the first time the key is used was
for extending an inverted selection, but it works most of the
time.</p>
<h2>Intelligent Updating</h2>
<p>One thing that always comes up when you have a complicated internal
state that's reflected in some user-visible external representation
(in this case, the displayed code and the textarea's content) is
keeping the two in sync. The naive way is to just update the display
every time you change your state, but this is not only error prone
(you'll forget), it also easily leads to duplicate work on big,
composite operations. Then you start passing around flags indicating
whether the display should be updated in an attempt to be efficient
again and, well, at that point you might as well give up completely.</p>
<p>I did go down that road, but then switched to a much simpler model:
simply keep track of all the things that have been changed during an
action, and then, only at the end, use this information to update the
user-visible display.</p>
<p>CodeMirror uses a concept of <em>operations</em>, which start by
calling a specific set-up function that clears the state and end by
calling another function that reads this state and does the required
updating. Most event handlers, and all the user-visible methods that
change state are wrapped like this. There's a method
called <code>operation</code> that accepts a function, and returns
another function that wraps the given function as an operation.</p>
<p>It's trivial to extend this (as CodeMirror does) to detect nesting,
and, when an operation is started inside an operation, simply
increment the nesting count, and only do the updating when this count
reaches zero again.</p>
<p>If we have a set of changed ranges and know the currently shown
range, we can (with some awkward code to deal with the fact that
changes can add and remove lines, so we're dealing with a changing
coordinate system) construct a map of the ranges that were left
intact. We can then compare this map with the part of the document
that's currently visible (based on scroll offset and editor height) to
determine whether something needs to be updated.</p>
<p>CodeMirror uses two update algorithms—a full refresh, where it just
discards the whole part of the DOM that contains the edited text and
rebuilds it, and a patch algorithm, where it uses the information
about changed and intact ranges to update only the out-of-date parts
of the DOM. When more than 30 percent (which is the current heuristic,
might change) of the lines need to be updated, the full refresh is
chosen (since it's faster to do than painstakingly finding and
updating all the changed lines), in the other case it does the
patching (so that, if you scroll a line or select another character,
the whole screen doesn't have to be re-rendered).</p>
<p>All updating uses <code>innerHTML</code> rather than direct DOM
manipulation, since that still seems to be by far the fastest way to
build documents. There's a per-line function that combines the
highlighting, <a href="manual.html#markText">marking</a>, and
selection info for that line into a snippet of HTML. The patch updater
uses this to reset individual lines, the refresh updater builds an
HTML chunk for the whole visible document at once, and then uses a
single <code>innerHTML</code> update to do the refresh.</p>
<h2>Parsers can be Simple</h2>
<p>When I wrote CodeMirror 1, I
thought <a href="http://codemirror.net/story.html#parser">interruptable
parsers</a> were a hugely scary and complicated thing, and I used a
bunch of heavyweight abstractions to keep this supposed complexity
under control: parsers
were <a href="http://bob.pythonmac.org/archives/2005/07/06/iteration-in-javascript/">iterators</a>
that consumed input from another iterator, and used funny
closure-resetting tricks to copy and resume themselves.</p>
<p>This made for a rather nice system, in that parsers formed strictly
separate modules, and could be composed in predictable ways.
Unfortunately, it was quite slow (stacking three or four iterators on
top of each other), and extremely intimidating to people not used to a
functional programming style.</p>
<p>With a few small changes, however, we can keep all those
advantages, but simplify the API and make the whole thing less
indirect and inefficient. CodeMirror
2's <a href="manual.html#modeapi">mode API</a> uses explicit state
objects, and makes the parser/tokenizer a function that simply takes a
state and a character stream abstraction, advances the stream one
token, and returns the way the token should be styled. This state may
be copied, optionally in a mode-defined way, in order to be able to
continue a parse at a given point. Even someone who's never touched a
lambda in his life can understand this approach. Additionally, far
fewer objects are allocated in the course of parsing now.</p>
<p>The biggest speedup comes from the fact that the parsing no longer
has to touch the DOM though. In CodeMirror 1, on an older browser, you
could <em>see</em> the parser work its way through the document,
managing some twenty lines in each 50-millisecond time slice it got. It
was reading its input from the DOM, and updating the DOM as it went
along, which any experienced JavaScript programmer will immediately
spot as a recipe for slowness. In CodeMirror 2, the parser usually
finishes the whole document in a single 100-millisecond time slice—it
manages some 1500 lines during that time on Chrome. All it has to do
is munge strings, so there is no real reason for it to be slow
anymore.</p>
<h2>What Gives?</h2>
<p>Given all this, what can you expect from CodeMirror 2? First, the
good:</p>
<ul>
<li><strong>Small.</strong> the base library is some 32k when minified
now, 12k when gzipped. It's smaller than its own logo.</li>
<li><strong>Lightweight.</strong> CodeMirror 2 initializes very
quickly, and does almost no work when it is not focused. This means
you can treat it almost like a textarea, have multiple instances on a
page without trouble.</li>
<li><strong>Huge document support.</strong> Since highlighting is
really fast, and no DOM structure is being built for non-visible
content, you don't have to worry about locking up your browser when a
user enters a megabyte-sized document.</li>
<li><strong>Extended API.</strong> Some things kept coming up in the
mailing list, such as marking pieces of text or lines, which were
extremely hard to do with CodeMirror 1. The new version has proper
support for these built in.</li>
<li><strong>Tab support.</strong> Tabs inside editable documents were,
for some reason, a no-go. At least six different people announced they
were going to add tab support to CodeMirror 1, none survived (I mean,
none delivered a working version). CodeMirror 2 no longer removes tabs
from your document.</li>
<li><strong>Sane styling.</strong> <code>iframe</code> nodes aren't
really known for respecting document flow. Now that an editor instance
is a plain <code>div</code> element, it is much easier to size it to
fit the surrounding elements. You don't even have to make it scroll if
you do not <a href="demo/resize.html">want to</a>.</li>
</ul>
<p>Then, the bad:</p>
<ul>
<li><strong>No line-wrapping.</strong> I'd have liked to get
line-wrapping to work, but it doesn't match the model I'm using very
well. It is important that cursor movement in the textarea matches
what you see on the screen, and it seems to be impossible to have the
lines wrapped the same in the textarea and the normal DOM.</li>
<li><strong>Some cursor flakiness.</strong> The textarea hack does not
really do justice to the complexity of cursor handling—a selection is
typically more than just an offset into a string. For example, if you
use the up and down arrow keys to move to a shorter line and then
back, you'll end up in your old position in most editor controls, but
CodeMirror 2 currently doesn't remember the 'real' cursor column in
this case. These can be worked around on a case-by-case basis, but
I haven't put much energy into that yet.</li>
</ul>
</body></html>

View File

@@ -0,0 +1,53 @@
.CodeMirror {
overflow: auto;
height: 300px;
line-height: 1em;
font-family: monospace;
_position: relative; /* IE6 hack */
}
.CodeMirror-gutter {
position: absolute; left: 0; top: 0;
background-color: #f7f7f7;
border-right: 1px solid #eee;
min-width: 2em;
height: 100%;
}
.CodeMirror-gutter-text {
color: #aaa;
text-align: right;
padding: .4em .2em .4em .4em;
}
.CodeMirror-lines {
padding: .4em;
}
.CodeMirror pre {
-moz-border-radius: 0;
-webkit-border-radius: 0;
-o-border-radius: 0;
border-radius: 0;
border-width: 0; margin: 0; padding: 0; background: transparent;
font-family: inherit;
}
.CodeMirror-cursor {
z-index: 10;
position: absolute;
visibility: hidden;
border-left: 1px solid black !important;
}
.CodeMirror-focused .CodeMirror-cursor {
visibility: visible;
}
span.CodeMirror-selected {
background: #ccc !important;
color: HighlightText !important;
}
.CodeMirror-focused span.CodeMirror-selected {
background: Highlight !important;
}
.CodeMirror-matchingbracket {color: #0f0 !important;}
.CodeMirror-nonmatchingbracket {color: #f22 !important;}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
// Utility function that allows modes to be combined. The mode given
// as the base argument takes care of most of the normal mode
// functionality, but a second (typically simple) mode is used, which
// can override the style of text. Both modes get to parse all of the
// text, but when both assign a non-null style to a piece of code, the
// overlay wins, unless the combine argument was true, in which case
// the styles are combined.
CodeMirror.overlayParser = function(base, overlay, combine) {
return {
startState: function() {
return {
base: CodeMirror.startState(base),
overlay: CodeMirror.startState(overlay),
basePos: 0, baseCur: null,
overlayPos: 0, overlayCur: null
};
},
copyState: function(state) {
return {
base: CodeMirror.copyState(base, state.base),
overlay: CodeMirror.copyState(overlay, state.overlay),
basePos: state.basePos, baseCur: null,
overlayPos: state.overlayPos, overlayCur: null
};
},
token: function(stream, state) {
if (stream.start == state.basePos) {
state.baseCur = base.token(stream, state.base);
state.basePos = stream.pos;
}
if (stream.start == state.overlayPos) {
stream.pos = stream.start;
state.overlayCur = overlay.token(stream, state.overlay);
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;
else return state.overlayCur;
},
indent: function(state, textAfter) {
return base.indent(state.base, textAfter);
},
electricChars: base.electricChars
};
};

View File

@@ -0,0 +1,771 @@
<!doctype html>
<html>
<head>
<title>CodeMirror: User Manual</title>
<link rel="stylesheet" type="text/css" href="css/docs.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<style>dl dl {margin: 0;}</style>
</head>
<body>
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1>
<pre class="grey">
<img src="css/baboon.png" class="logo" alt="logo"/>/* User manual and
reference guide */
</pre>
<div class="clear"><div class="leftbig blk">
<h2 id="overview">Overview</h2>
<p>CodeMirror is a code-editor component that can be embedded in
Web pages. It provides <em>only</em> the editor component, no
accompanying buttons, auto-completion, or other IDE functionality.
It does provide a rich API on top of which such functionality can
be straightforwardly implemented.</p>
<p>CodeMirror works with language-specific modes. Modes are
JavaScript programs that help color (and optionally indent) text
written in a given language. The distribution comes with a few
modes (see the <code>mode/</code> directory), and it isn't hard
to <a href="#modeapi">write new ones</a> for other languages.</p>
<h2 id="usage">Basic Usage</h2>
<p>The easiest way to use CodeMirror is to simply load the script
and style sheet found under <code>lib/</code> in the distribution,
plus the script and style sheet for the mode(s) you want to use.
(See also <a href="compress.html">the compresion helper</a>.) For
example:</p>
<pre>&lt;script src="lib/codemirror.js">&lt;/script>
&lt;link rel="stylesheet" href="lib/codemirror.css">
&lt;script src="mode/javascript/javascript.js">&lt;/script>
&lt;link rel="stylesheet" href="mode/javascript/javascript.css"></pre>
<p>Having done this, an editor instance can be created like
this:</p>
<pre>var myCodeMirror = CodeMirror(document.body);</pre>
<p>The editor will be appended to the document body, will start
empty, and will use the mode that we loaded. To have more control
over the new editor, a configuration object can be passed
to <code>CodeMirror</code> as a second argument:</p>
<pre>var myCodeMirror = CodeMirror(document.body, {
value: "function myScript(){return 100;}\n",
mode: "javascript"
});</pre>
<p>This will initialize the editor with a piece of code already in
it, and explicitly tell it to use the JavaScript mode (which is
useful when multiple modes are loaded).
See <a href="#config">below</a> for a full discussion of the
configuration options that CodeMirror accepts.</p>
<p>In cases where you don't want to append the editor to an
element, and need more control over the way it is inserted, the
first argument to the <code>CodeMirror</code> function can also
be a function that, when given a DOM element, inserts it into the
document somewhere. This could be used to, for example, replace a
textarea with a real editor:</p>
<pre>var myCodeMirror = CodeMirror(function(elt) {
myTextArea.parentNode.replaceChild(myTextArea, elt);
}, {value: myTextArea.value});</pre>
<p>However, for this use case, which is a common way to use
CodeMirror, the library provides a much more powerful
shortcut:</p>
<pre>var myCodeMirror = CodeMirror.fromTextArea(myTextArea);</pre>
<p>This will, among other things, ensure that the textarea's value
is updated when the form (if it is part of a form) is submitted.
See the <a href="#fromTextArea">API reference</a> for a full
description of this method.</p>
<h2 id="config">Configuration</h2>
<p>Both the <code>CodeMirror</code> function and
its <code>fromTextArea</code> method take as second (optional)
argument an object containing configuration options. Any option
not supplied like this will be taken
from <code>CodeMirror.defaults</code>, an object containing the
default options. You can update this object to change the defaults
on your page.</p>
<p>Options are not checked in any way, so setting bogus options is
bound to lead to odd errors.</p>
<p>These are the supported options:</p>
<dl>
<dt id="option_value"><code>value (string)</code></dt>
<dd>The starting value of the editor.</dd>
<dt id="option_mode"><code>mode (string or object)</code></dt>
<dd>The mode to use. When not given, this will default to the
first mode that was loaded. It may be a string, which either
simply names the mode or is
a <a href="http://en.wikipedia.org/wiki/MIME">MIME</a> type
associated with the mode. Alternatively, it may be an object
containing configuration options for the mode, with
a <code>name</code> property that names the mode (for
example <code>{name: "javascript", json: true}</code>). The demo
pages for each mode contain information about what configuration
parameters the mode supports. You can ask CodeMirror which modes
and MIME types are loaded with
the <code>CodeMirror.listModes</code>
and <code>CodeMirror.listMIMEs</code> functions.</dd>
<dt id="option_indentUnit"><code>indentUnit (integer)</code></dt>
<dd>How many spaces a block (whatever that means in the edited
language) should be indented. The default is 2.</dd>
<dt id="option_indentWithTabs"><code>indentWithTabs (boolean)</code></dt>
<dd>Whether, when indenting, the first N*8 spaces should be
replaced by N tabs. Default is false.</dd>
<dt id="option_tabMode"><code>tabMode (string)</code></dt>
<dd>Determines what happens when the user presses the tab key.
Must be one of the following:
<dl>
<dt><code>"classic" (the default)</code></dt>
<dd>When nothing is selected, insert a tab. Otherwise,
behave like the <code>"shift"</code> mode. (When shift is
held, this behaves like the <code>"indent"</code> mode.)</dd>
<dt><code>"shift"</code></dt>
<dd>Indent all selected lines by
one <a href="#option_indentUnit"><code>indentUnit</code></a>.
If shift was held while pressing tab, un-indent all selected
lines one unit.</dd>
<dt><code>"indent"</code></dt>
<dd>Indent the line the 'correctly', based on its syntactic
context. Only works if the
mode <a href="#indent">supports</a> it.</dd>
<dt><code>"default"</code></dt>
<dd>Do not capture tab presses, let the browser apply its
default behaviour (which usually means it skips to the next
control).</dd>
</dl></dd>
<dt id="option_enterMode"><code>enterMode (string)</code></dt>
<dd>Determines whether and how new lines are indented when the
enter key is pressed. The following modes are supported:
<dl>
<dt><code>"indent" (the default)</code></dt>
<dd>Use the mode's indentation rules to give the new line
the correct indentation.</dd>
<dt><code>"keep"</code></dt>
<dd>Indent the line the same as the previous line.</dd>
<dt><code>"flat"</code></dt>
<dd>Do not indent the new line.</dd>
</dl></dd>
<dt id="option_electricChars"><code>electricChars (boolean)</code></dt>
<dd>Configures whether the editor should re-indent the current
line when a character is typed that might change its proper
indentation (only works if the mode supports indentation).
Default is true.</dd>
<dt id="option_lineNumbers"><code>lineNumbers (boolean)</code></dt>
<dd>Whether to show line numbers to the left of the editor.</dd>
<dt id="option_firstLineNumber"><code>firstLineNumber (integer)</code></dt>
<dd>At which number to start counting lines. Default is 1.</dd>
<dt id="option_gutter"><code>gutter (boolean)</code></dt>
<dd>Can be used to force a 'gutter' (empty space on the left of
the editor) to be shown even when no line numbers are active.
This is useful for setting <a href="#setMarker">markers</a>.</dd>
<dt id="option_readOnly"><code>readOnly (boolean)</code></dt>
<dd>This disables editing of the editor content by the user.
(Changes through API functions will still be possible.)</dd>
<dt id="option_onChange"><code>onChange (function)</code></dt>
<dd>When given, this function will be called every time the
content of the editor is changed. It will be given the editor
instance as only argument.</dd>
<dt id="option_onCursorActivity"><code>onCursorActivity (function)</code></dt>
<dd>Like <code>onChange</code>, but will also be called when the
cursor moves without any changes being made.</dd>
<dt id="option_onGutterClick"><code>onGutterClick (function)</code></dt>
<dd>When given, will be called whenever the editor gutter (the
line-number area) is clicked. Will be given the editor instance
as first argument, and the (zero-based) number of the line that
was clicked as second argument.</dd>
<dt id="option_onFocus"><code>onFocus, onBlur (function)</code></dt>
<dd>The given functions will be called whenever the editor is
focused or unfocused.</dd>
<dt id="option_onScroll"><code>onScroll (function)</code></dt>
<dd>When given, will be called whenever the editor is
scrolled.</dd>
<dt id="option_matchBrackets"><code>matchBrackets (boolean)</code></dt>
<dd>Determines whether brackets are matched whenever the cursor
is moved next to a bracket.</dd>
<dt id="option_workTime"><code>workTime, workDelay (number)</code></dt>
<dd>Highlighting is done by a pseudo background-thread that will
work for <code>workTime</code> milliseconds, and then use
timeout to sleep for <code>workDelay</code> milliseconds. The
defaults are 200 and 300, you can change these options to make
the highlighting more or less aggressive.</dd>
<dt id="option_undoDepth"><code>undoDepth (integer)</code></dt>
<dd>The maximum number of undo levels that the editor stores.
Defaults to 40.</dd>
<dt id="option_tabindex"><code>tabindex (integer)</code></dt>
<dd>The <a href="http://www.w3.org/TR/html401/interact/forms.html#adef-tabindex">tab
index</a> to assign to the editor. If not given, no tab index
will be assigned.</dd>
<dt id="option_onKeyEvent"><code>onKeyEvent (function)</code></dt>
<dd>This provides a rather low-level hook into CodeMirror's key
handling. If provided, this function will be called on
every <code>keydown</code> and <code>keypress</code> event that
CodeMirror captures. It will be passed two arguments, the editor
instance and the key event. This key event is pretty much the
raw key event, except that a <code>stop()</code> method is
always added to it. You could feed it to, for
example, <code>jQuery.Event</code> to further normalize
it.<br>This function can inspect the key event, and handle it if
it wants to. It may return true to tell CodeMirror to ignore the
event. Be wary that, on some browsers, stopping
a <code>keydown</code> does not stop the <code>keypress</code>
from firing, whereas on others it does. If you respond to an
event, you should probably inspect its <code>type</code>
property and only do something when it is <code>keydown</code>
(or <code>keypress</code> for actions that need character
data).</dd>
</dl>
<h2 id="styling">Customized Styling</h2>
<p>Up to a certain extent, CodeMirror's look can be changed by
modifying style sheet files. The style sheets supplied by modes
simply provide the colors for that mode, and can be adapted in a
very straightforward way. To style the editor itself, it is
possible to alter or override the styles defined
in <a href="lib/codemirror.css"><code>codemirror.css</code></a>.</p>
<p>Some care must be taken there, since a lot of the rules in this
file are necessary to have CodeMirror function properly. Adjusting
colors should be safe, of course, and with some care a lot of
other things can be changed as well. The CSS classes defined in
this file serve the following roles:</p>
<dl>
<dt id="class_CodeMirror"><code>CodeMirror</code></dt>
<dd>The outer element of the editor. This determines whether the
editor scrolls (<code>overflow: auto</code> + fixed height). Can
also be used to set styles that should hold for everything
inside the editor, or to set a background.</dd>
<dt id="class_CodeMirror_focused"><code>CodeMirror-focused</code></dt>
<dd>Whenever the editor is focused, the top element gets this
class. This is used to hide the cursor and give the selection a
different color when the editor is not focused.</dd>
<dt id="class_CodeMirror_gutter"><code>CodeMirror-gutter</code></dt>
<dd>Use this for giving a background or a border to the editor
gutter. Don't set any padding here,
use <code>CodeMirror-gutter-text</code> for that. By default,
the gutter is 'fluid', meaning it will adjust its width to the
maximum line number or line marker width. You can also set a
fixed width if you want.</dd>
<dt id="class_CodeMirror_gutter_text"><code>CodeMirror-gutter-text</code></dt>
<dd>Used to style the actual line numbers. For the numbers to
line up, you'll want this style to use exactly the same font and
vertical padding as normal edited text, as per
the <code>CodeMirror-lines</code> class.</dd>
<dt id="class_CodeMirror_lines"><code>CodeMirror-lines</code></dt>
<dd>The visible lines. If this has vertical
padding, <code>CodeMirror-gutter</code> should have the same
padding.</dd>
<dt id="class_CodeMirror_cursor"><code>CodeMirror-cursor</code></dt>
<dd>The cursor is a block element that is absolutely positioned.
You can make it look whichever way you want.</dd>
<dt id="class_CodeMirror_selected"><code>CodeMirror-selected</code></dt>
<dd>The selection is represented by <code>span</code> elements
with this class.</dd>
<dt id="class_CodeMirror_matchingbracket"><code>CodeMirror-matchingbracket</code>,
<code>CodeMirror-matchingbracket</code></dt>
<dd>These are used to style matched (or unmatched) brackets.</dd>
</dl>
<p>The actual lines, as well as the cursor, are represented
by <code>pre</code> elements. By default no text styling (such as
bold) that might change line height is applied. If you do want
such effects, you'll have to give <code>CodeMirror pre</code> a
fixed height. Also, you must still take care that character width
is constant.</p>
<p>If your page's style sheets do funky things to
all <code>div</code> or <code>pre</code> elements (you probably
shouldn't do that), you'll have to define rules to cancel these
effects out again for elements under the <code>CodeMirror</code>
class.</p>
<h2 id="api">Programming API</h2>
<p>A lot of CodeMirror features are only available through its API.
This has the disadvantage that you need to do work to enable them,
and the advantage that CodeMirror will fit seamlessly into your
application.</p>
<p>Whenever points in the document are represented, the API uses
objects with <code>line</code> and <code>ch</code> properties.
Both are zero-based. CodeMirror makes sure to 'clip' any positions
passed by client code so that they fit inside the document, so you
shouldn't worry too much about sanitizing your coordinates. If you
give <code>ch</code> a value of <code>null</code>, or don't
specify it, it will be replaced with the length of the specified
line.</p>
<dl>
<dt id="getValue"><code>getValue() → string</code></dt>
<dd>Get the current editor content.</dd>
<dt id="setValue"><code>setValue(string)</code></dt>
<dd>Set the editor content.</dd>
<dt id="getSelection"><code>getSelection() → string</code></dt>
<dd>Get the currently selected code.</dd>
<dt id="replaceSelection"><code>replaceSelection(string)</code></dt>
<dd>Replace the selection with the given string.</dd>
<dt id="focus"><code>focus()</code></dt>
<dd>Give the editor focus.</dd>
<dt id="setOption"><code>setOption(option, value)</code></dt>
<dd>Change the configuration of the editor. <code>option</code>
should the name of an <a href="#config">option</a>,
and <code>value</code> should be a valid value for that
option.</dd>
<dt id="getOption"><code>getOption(option) → value</code></dt>
<dd>Retrieves the current value of the given option for this
editor instance.</dd>
<dt id="cursorCoords"><code>cursorCoords(start) → object</code></dt>
<dd>Returns an <code>{x, y, yBot}</code> object containing the
coordinates of the cursor relative to the top-left corner of the
page. <code>yBot</code> is the coordinate of the bottom of the
cursor. <code>start</code> is a boolean indicating whether you
want the start or the end of the selection.</dd>
<dt id="charCoords"><code>charCoords(pos) → object</code></dt>
<dd>Like <code>cursorCoords</code>, but returns the position of
an arbitrary characters. <code>pos</code> should be
a <code>{line, ch}</code> object.</dd>
<dt id="coordsChar"><code>coordsChar(object) → pos</code></dt>
<dd>Given an <code>{x, y}</code> object (in page coordinates),
returns the <code>{line, ch}</code> position that corresponds to
it.</dd>
<dt id="undo"><code>undo()</code></dt>
<dd>Undo one edit (if any undo events are stored).</dd>
<dt id="redo"><code>redo()</code></dt>
<dd>Redo one undone edit.</dd>
<dt id="historySize"><code>historySize() → object</code></dt>
<dd>Returns an object with <code>{undo, redo}</code> properties,
both of which hold integers, indicating the amount of stored
undo and redo operations.</dd>
<dt id="indentLine"><code>indentLine(line)</code></dt>
<dd>Reset the given line's indentation to the indentation
prescribed by the mode.</dd>
<dt id="getSearchCursor"><code>getSearchCursor(query, start, caseFold) → cursor</code></dt>
<dd>Used to implement search/replace
functionality. <code>query</code> can be a regular expression or
a string (only strings will match across lines—if they contain
newlines). <code>start</code> provides the starting position of
the search. It can be a <code>{line, ch}</code> object, or can
be left off to default to the start of the
document. <code>caseFold</code> is only relevant when matching a
string. It will cause the search to be case-insensitive. A
search cursor has the following methods:
<dl>
<dt><code>findNext(), findPrevious() → boolean</code></dt>
<dd>Search forward or backward from the current position.
The return value indicates whether a match was found. If
matching a regular expression, the return value will be the
array returned by the <code>match</code> method, in case you
want to extract matched groups.</dd>
<dt><code>from(), to() → object</code></dt>
<dd>These are only valid when the last call
to <code>findNext</code> or <code>findPrevious</code> did
not return false. They will return <code>{line, ch}</code>
objects pointing at the start and end of the match.</dd>
</dl></dd>
<dt id="getTokenAt"><code>getTokenAt(pos) → object</code></dt>
<dd>Retrieves information about the token the current mode found
at the given position (a <code>{line, ch}</code> object). The
returned object has the following properties:
<dl>
<dt><code>start</code></dt><dd>The character (on the given line) at which the token starts.</dd>
<dt><code>end</code></dt><dd>The character at which the token ends.</dd>
<dt><code>string</code></dt><dd>The token's string.</dd>
<dt><code>className</code></dt><dd>The class the mode assigned
to the token. (Can be null when no class was assigned.)</dd>
</dl></dd>
<dt id="markText"><code>markText(from, to, className) → function</code></dt>
<dd>Can be used to mark a range of text with a specific CSS
class name. <code>from</code> and <code>to</code> should
be <code>{line, ch}</code> objects. The method will return a
function that can be called to remove the marking.</dd>
<dt id="setMarker"><code>setMarker(line, text, className) → lineHandle</code></dt>
<dd>Add a gutter marker for the given line. Gutter markers are
shown in the line-number area (instead of the number for this
line). Both <code>text</code> and <code>className</code> are
optional. Setting <code>text</code> to a Unicode character like
● tends to give a nice effect. To put a picture in the gutter,
set <code>text</code> to a space and <code>className</code> to
something that sets a background image. If you
specify <code>text</code>, the given text (which may contain
HTML) will, by default, replace the line number for that line.
If this is not what you want, you can include the
string <code>%N%</code> in the text, which will be replaced by
the line number.</dd>
<dt id="clearMarker"><code>clearMarker(line)</code></dt>
<dd>Clears a marker created
with <code>setMarker</code>. <code>line</code> can be either a
number or a handle returned by <code>setMarker</code> (since a
number may now refer to a different line if something was added
or deleted).</dd>
<dt id="setLineClass"><code>setLineClass(line, className) → lineHandle</code></dt>
<dd>Set a CSS class name for the given line. <code>line</code>
can be a number or a line handle (as returned
by <code>setMarker</code> or this function).
Pass <code>null</code> to clear the class for a line.</dd>
<dt id="lineInfo"><code>lineInfo(line) → object</code></dt>
<dd>Returns the line number, text content, and marker status of
the given line, which can be either a number or a handle
returned by <code>setMarker</code>. The returned object has the
structure <code>{line, text, markerText, markerClass}</code>.</dd>
<dt id="addWidget"><code>addWidget(pos, node, scrollIntoView)</code></dt>
<dd>Puts <code>node</code>, which should be an absolutely
positioned DOM node, into the editor, positioned right below the
given <code>{line, ch}</code> position.
When <code>scrollIntoView</code> is true, the editor will ensure
that the entire node is visible (if possible). To remove the
widget again, simply use DOM methods (move it somewhere else, or
call <code>removeChild</code> on its parent).</dd>
<dt id="matchBrackets"><code>matchBrackets()</code></dt>
<dd>Force matching-bracket-highlighting to happen.</dd>
<dt id="lineCount"><code>lineCount() → number</code></dt>
<dd>Get the number of lines in the editor.</dd>
<dt id="getCursor"><code>getCursor(start) → object</code></dt>
<dd><code>start</code> is a boolean indicating whether the start
or the end of the selection must be retrieved. If it is not
given, the current cursor pos, i.e. the side of the selection
that would move if you pressed an arrow key, is chosen.
A <code>{line, ch}</code> object will be returned.</dd>
<dt id="somethingSelected"><code>somethingSelected() → boolean</code></dt>
<dd>Return true if any text is selected.</dd>
<dt id="setCursor"><code>setCursor(pos)</code></dt>
<dd>Set the cursor position. You can either pass a
single <code>{line, ch}</code> object, or the line and the
character as two separate parameters.</dd>
<dt id="setSelection"><code>setSelection(start, end)</code></dt>
<dd>Set the selection range. <code>start</code>
and <code>end</code> should be <code>{line, ch}</code> objects.</dd>
<dt id="getLine"><code>getLine(n) → string</code></dt>
<dd>Get the content of line <code>n</code>.</dd>
<dt id="setLine"><code>setLine(n, text)</code></dt>
<dd>Set the content of line <code>n</code>.</dd>
<dt id="removeLine"><code>removeLine(n)</code></dt>
<dd>Remove the given line from the document.</dd>
<dt id="getRange"><code>getRange(from, to) → string</code></td>
<dd>Get the text between the given points in the editor, which
should be <code>{line, ch}</code> objects.</dd>
<dt id="replaceRange"><code>replaceRange(string, from, to)</code></dt>
<dd>Replace the part of the document between <code>from</code>
and <code>to</code> with the given string. <code>from</code>
and <code>to</code> must be <code>{line, ch}</code>
objects. <code>to</code> can be left off to simply insert the
string at position <code>from</code>.</dd>
</dl>
<p>The following are more low-level methods:</p>
<dl>
<dt id="operation"><code>operation(func) → result</code></dt>
<dd>CodeMirror internally buffers changes and only updates its
DOM structure after it has finished performing some operation.
If you need to perform a lot of operations on a CodeMirror
instance, you can call this method with a function argument. It
will call the function, buffering up all changes, and only doing
the expensive update after the function returns. This can be a
lot faster. The return value from this method will be the return
value of your function.</dd>
<dt id="refresh"><code>refresh()</code></dt>
<dd>If your code does something to change the size of the editor
element (window resizes are already listened for), or unhides
it, you should probably follow up by calling this method to
ensure CodeMirror is still looking as intended.</dd>
<dt id="getInputField"><code>getInputField() → textarea</code></dt>
<dd>Returns the hiden textarea used to read input.</dd>
<dt id="getWrapperElement"><code>getWrapperElement() → node</code></dt>
<dd>Returns the DOM node that represents the editor. Remove this
from your tree to delete an editor instance.</dd>
</dl>
<p id="fromTextArea">Finally, the <code>CodeMirror</code> object
itself has a method <code>fromTextArea</code>. This takes a
textarea DOM node as first argument and an optional configuration
object as second. It will replace the textarea with a CodeMirror
instance, and wire up the form of that textarea (if any) to make
sure the editor contents are put into the textarea when the form
is submitted. A CodeMirror instance created this way has two
additional methods:</p>
<dl>
<dt id="save"><code>save()</code></dt>
<dd>Copy the content of the editor into the textarea.</dd>
<dt id="toTextArea"><code>toTextArea()</code></dt>
<dd>Remove the editor, and restore the original textarea (with
the editor's current content).</dd>
</dl>
<h2 id="modeapi">Writing CodeMirror Modes</h2>
<p>Modes typically consist of a JavaScript file and a CSS file.
The CSS file (see, for
example <a href="mode/javascript/javascript.css"><code>javascript.css</code></a>)
defines the classes that will be used to style the syntactic
elements of the code, and the script contains the logic to
actually assign these classes to the right pieces of text.</p>
<p>You'll usually want to use some kind of prefix for your CSS
classes, so that they are unlikely to clash with other classes,
both those used by other modes and those defined by the page in
which CodeMirror is embedded.</p>
<p id="defineMode">The mode script should
call <code>CodeMirror.defineMode</code> to register itself with
CodeMirror. This function takes two arguments. The first should be
the name of the mode, for which you should use a lowercase string,
preferably one that is also the name of the files that define the
mode (i.e. <code>"xml"</code> is defined <code>xml.js</code>). The
second argument should be a function that, given a CodeMirror
configuration object (the thing passed to
the <code>CodeMirror</code> function) and a mode configuration
object (as in the <a href="#option_mode"><code>mode</code></a>
option), returns a mode object.</p>
<p>Typically, you should use this second argument
to <code>defineMode</code> as your module scope function (modes
should not leak anything into the global scope!), i.e. write your
whole mode inside this function.</p>
<p>The main responsibility of a mode script is <em>parsing</em>
the content of the editor. Depending on the language and the
amount of functionality desired, this can be done in really easy
or extremely complicated ways. Some parsers can be stateless,
meaning that they look at one element (<em>token</em>) of the code
at a time, with no memory of what came before. Most, however, will
need to remember something. This is done by using a <em>state
object</em>, which is an object that can be mutated every time a
new token is read.</p>
<p id="startState">Modes that use a state must define
a <code>startState</code> method on their mode object. This is a
function of no arguments that produces a state object to be used
at the start of a document.</p>
<p id="token">The most important part of a mode object is
its <code>token(stream, state)</code> method. All modes must
define this method. It should read one token from the stream it is
given as an argument, optionally update its state, and return a
CSS class string, or <code>null</code> for tokens that do not have
to be styled.<p>
<p id="StringStream">The stream object encapsulates a line of code
(tokens may never span lines) and our current position in that
line. It has the following API:</p>
<dl>
<dt><code>eol() → boolean</code></dt>
<dd>Returns true only if the stream is at the end of the
line.</dd>
<dt><code>sol() → boolean</code></dt>
<dd>Returns true only if the stream is at the start of the
line.</dd>
<dt><code>peek() → character</code></dt>
<dd>Returns the next character in the stream without advancing
it. Will return <code>undefined</code> at the end of the
line.</dd>
<dt><code>next() → character</code></dt>
<dd>Returns the next character in the stream and advances it.
Also returns <code>undefined</code> when no more characters are
available.</dd>
<dt><code>eat(match) → character</code></dt>
<dd><code>match</code> can be a character, a regular expression,
or a function that takes a character and returns a boolean. If
the next character in the stream 'matches' the given argument,
it is consumed and returned. Otherwise, <code>undefined</code>
is returned.</dd>
<dt><code>eatWhile(match) → boolean</code></dt>
<dd>Repeatedly calls <code>eat</code> with the given argument,
until it fails. Returns true if any characters were eaten.</dd>
<dt><code>eatSpace() → boolean</code></dt>
<dd>Shortcut for <code>eatWhile</code> when matching
white-space.</dd>
<dt><code>skipToEnd()</code></dt>
<dd>Moves the position to the end of the line.</dd>
<dt><code>skipTo(ch) → boolean</code></dt>
<dd>Skips to the next occurrence of the given character, if
found. Returns true if the character was found.</dd>
<dt><code>match(pattern, consume, caseFold) → boolean</code></dt>
<dd>Act like a
multi-character <code>eat</code>—if <code>consume</code> is true
or not given—or a look-ahead that doesn't update the stream
position—if it is false. <code>pattern</code> can be either a
string or a regular expression starting with <code>^</code>.
When it is a string, <code>caseFold</code> can be set to true to
make the match case-insensitive. When successfully matching a
regular expression, the returned value will be the array
returned by <code>match</code>, in case you need to extract
matched groups.</dd>
<dt><code>backUp(n)</code></dt>
<dd>Backs up the stream <code>n</code> characters. Backing it up
further than the start of the current token will cause things to
break, so be careful.</dd>
<dt><code>column() → integer</code></dt>
<dd>Returns the column (taking into account tabs) at which the
current token starts. Can be used to find out whether a token
starts a new line.</dd>
<dt><code>indentation() → integer</code></dt>
<dd>Tells you how far the current line has been indented, in
spaces. Corrects for tab characters.</dd>
<dt><code>current() → string</code></dt>
<dd>Get the string between the start of the current token and
the current stream position.</dd>
</dl>
<p id="copyState">Because state object are mutated, and CodeMirror
needs to keep valid versions of a state around so that it can
restart a parse at any line, copies must be made of state objects.
The default algorithm used is that a new state object is created,
which gets all the properties of the old object. Any properties
which hold arrays get a copy of these arrays (since arrays tend to
be used as mutable stacks). When this is not correct, for example
because a mode mutates non-array properties of its state object, a
mode object should define a <code>copyState</code> method,
which is given a state and should return a safe copy of that
state.</p>
<p id="indent">If you want your mode to provide smart indentation
(see <a href="#option_enterMode"><code>entermode</code></a>
and <a href="#option_tabMode"><code>tabMode</code></a> when they
have a value of <code>"indent"</code>), you must define
an <code>indent(state, textAfter)</code> method on your mode
object.</p>
<p>The indentation method should inspect the given state object,
and optionally the <code>textAfter</code> string, which contains
the text on the line that is being indented, and return an
integer, the amount of spaces to indent. It should usually take
the <a href="#option_indentUnit"><code>indentUnit</code></a>
option into account.</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
the <a href="#option_electricChars"><code>electricChars</code></a>
option.</p>
<p>So, to summarize, a mode <em>must</em> provide
a <code>token</code> method, and it <em>may</em>
provide <code>startState</code>, <code>copyState</code>,
and <code>indent</code> methods. For an example of a trivial mode,
see the <a href="mode/diff/diff.js">diff mode</a>, for a more
involved example, see
the <a href="mode/javascript/javascript.js">JavaScript
mode</a>.</p>
<p>Sometimes, it is useful for modes to <em>nest</em>—to have one
mode delegate work to another mode. An example of this kind of
mode is the <a href="mode/htmlmixed/htmlmixed.js">mixed-mode HTML
mode</a>. To implement such nesting, it is usually necessary to
create mode objects and copy states yourself. To create a mode
object, there are <code>CodeMirror.getMode(options,
parserConfig)</code>, where the first argument is a configuration
object as passed to the mode constructor function, and the second
argument is a mode specification as in
the <a href="#option_mode"><code>mode</code></a> option. To copy a
state object, call <code>CodeMirror.copyState(mode, state)</code>,
where <code>mode</code> is the mode that created the given
state.</p>
<p>To make indentation work properly in a nested parser, it is
advisable to give the <code>startState</code> method of modes that
are intended to be nested an optional argument that provides the
base indentation for the block of code. The JavaScript and CSS
parser do this, for example, to allow JavaScript and CSS code
inside the mixed-mode HTML mode to be properly indented.</p>
<p>Finally, it is possible to associate your mode, or a certain
configuration of your mode, with
a <a href="http://en.wikipedia.org/wiki/MIME">MIME</a> type. For
example, the JavaScript mode associates itself
with <code>text/javascript</code>, and its JSON variant
with <code>application/json</code>. To do this,
call <code>CodeMirror.defineMIME(mime, modeSpec)</code>,
where <code>modeSpec</code> can be a string or object specifying a
mode, as in the <a href="#option_mode"><code>mode</code></a>
option.</p>
</div><div class="rightsmall blk">
<h2>Contents</h2>
<ul>
<li><a href="#overview">Overview</a></li>
<li><a href="#usage">Basic Usage</a></li>
<li><a href="#config">Configuration</a></li>
<li><a href="#styling">Customized Styling</a></li>
<li><a href="#api">Programming API</a></li>
<li><a href="#modeapi">Writing CodeMirror Modes</a></li>
</ul>
</div></div>
<div style="height: 2em">&nbsp;</div>
<script type="text/javascript" src="css/font.js"></script>
</body>
</html>

View File

@@ -0,0 +1,6 @@
span.c-like-keyword {color: #90b;}
span.c-like-number {color: #291;}
span.c-like-comment {color: #a70;}
span.c-like-string {color: #a22;}
span.c-like-preprocessor {color: #049;}
span.c-like-var {color: #22b;}

View File

@@ -0,0 +1,181 @@
CodeMirror.defineMode("clike", function(config, parserConfig) {
var indentUnit = config.indentUnit, keywords = parserConfig.keywords,
cpp = parserConfig.useCPP, multiLineStrings = parserConfig.multiLineStrings, $vars = parserConfig.$vars;
var isOperatorChar = /[+\-*&%=<>!?|]/;
function chain(stream, state, f) {
state.tokenize = f;
return f(stream, state);
}
var type;
function ret(tp, style) {
type = tp;
return style;
}
function tokenBase(stream, state) {
var ch = stream.next();
if (ch == '"' || ch == "'")
return chain(stream, state, tokenString(ch));
else if (/[\[\]{}\(\),;\:\.]/.test(ch))
return ret(ch);
else if (ch == "#" && cpp && state.startOfLine) {
stream.skipToEnd();
return ret("directive", "c-like-preprocessor");
}
else if (/\d/.test(ch)) {
stream.eatWhile(/[\w\.]/)
return ret("number", "c-like-number");
}
else if (ch == "/") {
if (stream.eat("*")) {
return chain(stream, state, tokenComment);
}
else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "c-like-comment");
}
else {
stream.eatWhile(isOperatorChar);
return ret("operator");
}
}
else if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return ret("operator");
}
else if ($vars && ch == "$") {
stream.eatWhile(/[\w\$_]/);
return ret("word", "c-like-var");
}
else {
stream.eatWhile(/[\w\$_]/);
if (keywords && keywords.propertyIsEnumerable(stream.current())) return ret("keyword", "c-like-keyword");
return ret("word", "c-like-word");
}
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false, next, end = false;
while ((next = stream.next()) != null) {
if (next == quote && !escaped) {end = true; break;}
escaped = !escaped && next == "\\";
}
if (end || !(escaped || multiLineStrings))
state.tokenize = tokenBase;
return ret("string", "c-like-string");
};
}
function tokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "c-like-comment");
}
function Context(indented, column, type, align, prev) {
this.indented = indented;
this.column = column;
this.type = type;
this.align = align;
this.prev = prev;
}
function pushContext(state, col, type) {
return state.context = new Context(state.indented, col, type, null, state.context);
}
function popContext(state) {
return state.context = state.context.prev;
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: tokenBase,
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
indented: 0,
startOfLine: true
};
},
token: function(stream, state) {
var ctx = state.context;
if (stream.sol()) {
if (ctx.align == null) ctx.align = false;
state.indented = stream.indentation();
state.startOfLine = true;
}
if (stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
if (type == "comment") return style;
if (ctx.align == null) ctx.align = true;
if ((type == ";" || type == ":") && ctx.type == "statement") popContext(state);
else if (type == "{") pushContext(state, stream.column(), "}");
else if (type == "[") pushContext(state, stream.column(), "]");
else if (type == "(") pushContext(state, stream.column(), ")");
else if (type == "}") {
if (ctx.type == "statement") ctx = popContext(state);
if (ctx.type == "}") ctx = popContext(state);
if (ctx.type == "statement") ctx = popContext(state);
}
else if (type == ctx.type) popContext(state);
else if (ctx.type == "}") pushContext(state, stream.column(), "statement");
state.startOfLine = false;
return style;
},
indent: function(state, textAfter) {
if (state.tokenize != tokenBase) 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);
else return ctx.indented + (closing ? 0 : indentUnit);
},
electricChars: "{}"
};
});
(function() {
function keywords(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}
var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
"double static else struct entry switch extern typedef float union for unsigned " +
"goto while enum void const signed volatile";
CodeMirror.defineMIME("text/x-csrc", {
name: "clike",
useCPP: true,
keywords: keywords(cKeywords)
});
CodeMirror.defineMIME("text/x-c++src", {
name: "clike",
useCPP: true,
keywords: keywords(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
"static_cast typeid catch false operator template typename class friend private " +
"this using const_cast inline public throw virtual delete mutable protected true " +
"wchar_t")
});
CodeMirror.defineMIME("text/x-java", {
name: "clike",
keywords: keywords("abstract assert boolean break byte case catch char class const continue default " +
"do double else enum extends false final finally float for goto if implements import " +
"instanceof int interface long native new null package private protected public " +
"return short static strictfp super switch synchronized this throw throws transient " +
"true try void volatile while")
});
}());

View File

@@ -0,0 +1,101 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: C-like mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="clike.js"></script>
<link rel="stylesheet" href="clike.css">
<link rel="stylesheet" href="../../css/docs.css">
<style>.CodeMirror {border: 2px inset #dee;}</style>
</head>
<body>
<h1>CodeMirror 2: C-like mode</h1>
<form><textarea id="code" name="code">
/* C demo code */
#include <zmq.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <malloc.h>
typedef struct {
void* arg_socket;
zmq_msg_t* arg_msg;
char* arg_string;
unsigned long arg_len;
int arg_int, arg_command;
int signal_fd;
int pad;
void* context;
sem_t sem;
} acl_zmq_context;
#define p(X) (context->arg_##X)
void* zmq_thread(void* context_pointer) {
acl_zmq_context* context = (acl_zmq_context*)context_pointer;
char ok = 'K', err = 'X';
int res;
while (1) {
while ((res = sem_wait(&amp;context->sem)) == EINTR);
if (res) {write(context->signal_fd, &amp;err, 1); goto cleanup;}
switch(p(command)) {
case 0: goto cleanup;
case 1: p(socket) = zmq_socket(context->context, p(int)); break;
case 2: p(int) = zmq_close(p(socket)); break;
case 3: p(int) = zmq_bind(p(socket), p(string)); break;
case 4: p(int) = zmq_connect(p(socket), p(string)); break;
case 5: p(int) = zmq_getsockopt(p(socket), p(int), (void*)p(string), &amp;p(len)); break;
case 6: p(int) = zmq_setsockopt(p(socket), p(int), (void*)p(string), p(len)); break;
case 7: p(int) = zmq_send(p(socket), p(msg), p(int)); break;
case 8: p(int) = zmq_recv(p(socket), p(msg), p(int)); break;
case 9: p(int) = zmq_poll(p(socket), p(int), p(len)); break;
}
p(command) = errno;
write(context->signal_fd, &amp;ok, 1);
}
cleanup:
close(context->signal_fd);
free(context_pointer);
return 0;
}
void* zmq_thread_init(void* zmq_context, int signal_fd) {
acl_zmq_context* context = malloc(sizeof(acl_zmq_context));
pthread_t thread;
context->context = zmq_context;
context->signal_fd = signal_fd;
sem_init(&amp;context->sem, 1, 0);
pthread_create(&amp;thread, 0, &amp;zmq_thread, context);
pthread_detach(thread);
return context;
}
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
matchBrackets: true,
mode: "text/x-csrc"
});
</script>
<p>Simple mode that tries to handle C-like languages as well as it
can. Takes two configuration parameters: <code>keywords</code>, an
object whose property names are the keywords in the language,
and <code>useCPP</code>, which determines whether C preprocessor
directives are recognized.</p>
<p><strong>MIME types defined:</strong> <code>text/x-csrc</code>
(C code), <code>text/x-c++src</code> (C++
code), <code>text/x-java</code> (Java code).</p>
</body>
</html>

View File

@@ -0,0 +1,9 @@
span.css-at {color: #708;}
span.css-unit {color: #281;}
span.css-value {color: #708;}
span.css-identifier {color: black;}
span.css-selector {color: #11B;}
span.css-important {color: #00F;}
span.css-colorcode {color: #299;}
span.css-comment {color: #A70;}
span.css-string {color: #A22;}

View File

@@ -0,0 +1,124 @@
CodeMirror.defineMode("css", function(config) {
var indentUnit = config.indentUnit, type;
function ret(style, tp) {type = tp; return style;}
function tokenBase(stream, state) {
var ch = stream.next();
if (ch == "@") {stream.eatWhile(/\w/); return ret("css-at", stream.current());}
else if (ch == "/" && stream.eat("*")) {
state.tokenize = tokenCComment;
return tokenCComment(stream, state);
}
else if (ch == "<" && stream.eat("!")) {
state.tokenize = tokenSGMLComment;
return tokenSGMLComment(stream, state);
}
else if (ch == "=") ret(null, "compare");
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
else if (ch == "\"" || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
else if (ch == "#") {
stream.eatWhile(/\w/);
return ret("css-selector", "hash");
}
else if (ch == "!") {
stream.match(/^\s*\w*/);
return ret("css-important", "important");
}
else if (/\d/.test(ch)) {
stream.eatWhile(/[\w.%]/);
return ret("css-unit", "unit");
}
else if (/[,.+>*\/]/.test(ch)) {
return ret(null, "select-op");
}
else if (/[;{}:\[\]]/.test(ch)) {
return ret(null, ch);
}
else {
stream.eatWhile(/[\w\\\-_]/);
return ret("css-identifier", "identifier");
}
}
function tokenCComment(stream, state) {
var maybeEnd = false, ch;
while ((ch = stream.next()) != null) {
if (maybeEnd && ch == "/") {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("css-comment", "comment");
}
function tokenSGMLComment(stream, state) {
var dashes = 0, ch;
while ((ch = stream.next()) != null) {
if (dashes >= 2 && ch == ">") {
state.tokenize = tokenBase;
break;
}
dashes = (ch == "-") ? dashes + 1 : 0;
}
return ret("css-comment", "comment");
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false, ch;
while ((ch = stream.next()) != null) {
if (ch == quote && !escaped)
break;
escaped = !escaped && ch == "\\";
}
if (!escaped) state.tokenize = tokenBase;
return ret("css-string", "string");
};
}
return {
startState: function(base) {
return {tokenize: tokenBase,
baseIndent: base || 0,
stack: []};
},
token: function(stream, state) {
if (stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
var context = state.stack[state.stack.length-1];
if (type == "hash" && context == "rule") style = "css-colorcode";
else if (style == "css-identifier") {
if (context == "rule") style = "css-value";
else if (!context || context == "@media{") style = "css-selector";
}
if (context == "rule" && /^[\{\};]$/.test(type))
state.stack.pop();
if (type == "{") {
if (context == "@media") state.stack[state.stack.length-1] = "@media{";
else state.stack.push("{");
}
else if (type == "}") state.stack.pop();
else if (type == "@media") state.stack.push("@media");
else if (context != "rule" && context != "@media" && type != "comment") state.stack.push("rule");
return style;
},
indent: function(state, textAfter) {
var n = state.stack.length;
if (/^\}/.test(textAfter))
n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
return state.baseIndent + n * indentUnit;
},
electricChars: "}"
};
});
CodeMirror.defineMIME("text/css", "css");

View File

@@ -0,0 +1,56 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: CSS mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="css.js"></script>
<link rel="stylesheet" href="css.css">
<style>.CodeMirror {background: #f8f8f8;}</style>
<link rel="stylesheet" href="../../css/docs.css">
</head>
<body>
<h1>CodeMirror 2: CSS mode</h1>
<form><textarea id="code" name="code">
/* Some example CSS */
@import url("something.css");
body {
margin: 0;
padding: 3em 6em;
font-family: tahoma, arial, sans-serif;
color: #000;
}
#navigation a {
font-weight: bold;
text-decoration: none !important;
}
h1 {
font-size: 2.5em;
}
h2 {
font-size: 1.7em;
}
h1:before, h2:before {
content: "::";
}
code {
font-family: courier, monospace;
font-size: 80%;
color: #418A8A;
}
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
</script>
<p><strong>MIME types defined:</strong> <code>text/css</code>.</p>
</body>
</html>

View File

@@ -0,0 +1,3 @@
span.diff-rangeinfo {color: #a0b;}
span.diff-minus {color: #a22;}
span.diff-plus {color: #2b2;}

View File

@@ -0,0 +1,13 @@
CodeMirror.defineMode("diff", function() {
return {
token: function(stream) {
var ch = stream.next();
stream.skipToEnd();
if (ch == "+") return "diff-plus";
if (ch == "-") return "diff-minus";
if (ch == "@") return "diff-rangeinfo";
}
};
});
CodeMirror.defineMIME("text/x-diff", "diff");

View File

@@ -0,0 +1,99 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: Diff mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="diff.js"></script>
<link rel="stylesheet" href="diff.css">
<style>.CodeMirror {border-top: 1px solid #ddd; border-bottom: 1px solid #ddd;}</style>
<link rel="stylesheet" href="../../css/docs.css">
</head>
<body>
<h1>CodeMirror 2: Diff mode</h1>
<form><textarea id="code" name="code">
diff --git a/index.html b/index.html
index c1d9156..7764744 100644
--- a/index.html
+++ b/index.html
@@ -95,7 +95,8 @@ StringStream.prototype = {
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
- autoMatchBrackets: true
+ autoMatchBrackets: true,
+ onGutterClick: function(x){console.log(x);}
});
</script>
</body>
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 04646a9..9a39cc7 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -399,10 +399,16 @@ var CodeMirror = (function() {
}
function onMouseDown(e) {
- var start = posFromMouse(e), last = start;
+ var start = posFromMouse(e), last = start, target = e.target();
if (!start) return;
setCursor(start.line, start.ch, false);
if (e.button() != 1) return;
+ if (target.parentNode == gutter) {
+ if (options.onGutterClick)
+ options.onGutterClick(indexOf(gutter.childNodes, target) + showingFrom);
+ return;
+ }
+
if (!focused) onFocus();
e.stop();
@@ -808,7 +814,7 @@ var CodeMirror = (function() {
for (var i = showingFrom; i < showingTo; ++i) {
var marker = lines[i].gutterMarker;
if (marker) html.push('<div class="' + marker.style + '">' + htmlEscape(marker.text) + '</div>');
- else html.push("<div>" + (options.lineNumbers ? i + 1 : "\u00a0") + "</div>");
+ else html.push("<div>" + (options.lineNumbers ? i + options.firstLineNumber : "\u00a0") + "</div>");
}
gutter.style.display = "none"; // TODO test whether this actually helps
gutter.innerHTML = html.join("");
@@ -1371,10 +1377,8 @@ var CodeMirror = (function() {
if (option == "parser") setParser(value);
else if (option === "lineNumbers") setLineNumbers(value);
else if (option === "gutter") setGutter(value);
- else if (option === "readOnly") options.readOnly = value;
- else if (option === "indentUnit") {options.indentUnit = indentUnit = value; setParser(options.parser);}
- else if (/^(?:enterMode|tabMode|indentWithTabs|readOnly|autoMatchBrackets|undoDepth)$/.test(option)) options[option] = value;
- else throw new Error("Can't set option " + option);
+ else if (option === "indentUnit") {options.indentUnit = value; setParser(options.parser);}
+ else options[option] = value;
},
cursorCoords: cursorCoords,
undo: operation(undo),
@@ -1402,7 +1406,8 @@ var CodeMirror = (function() {
replaceRange: operation(replaceRange),
operation: function(f){return operation(f)();},
- refresh: function(){updateDisplay([{from: 0, to: lines.length}]);}
+ refresh: function(){updateDisplay([{from: 0, to: lines.length}]);},
+ getInputField: function(){return input;}
};
return instance;
}
@@ -1420,6 +1425,7 @@ var CodeMirror = (function() {
readOnly: false,
onChange: null,
onCursorActivity: null,
+ onGutterClick: null,
autoMatchBrackets: false,
workTime: 200,
workDelay: 300,
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
</script>
<p><strong>MIME types defined:</strong> <code>text/x-diff</code>.</p>
</body>
</html>

View File

@@ -0,0 +1,25 @@
span.hs-char,
span.hs-float,
span.hs-integer,
span.hs-string {color: #762;}
span.hs-comment {color: #262;font-style: italic;}
span.hs-pragma {color: #555;font-style: italic;}
span.hs-special,
span.hs-varid,
span.hs-varsym {color: #000;}
span.hs-conid,
span.hs-consym {color: #b11;}
span.hs-qualifier {color: #555;}
span.hs-reservedid,
span.hs-reservedop {color: #730;}
span.hs-prelude-varid,
span.hs-prelude-varsym {color: #30a;}
span.hs-prelude-conid {color: #b11;}
span.hs-error {background-color: #fdd;}

View File

@@ -0,0 +1,242 @@
CodeMirror.defineMode("haskell", function(cmCfg, modeCfg) {
function switchState(source, setState, f) {
setState(f);
return f(source, setState);
}
// These should all be Unicode extended, as per the Haskell 2010 report
var smallRE = /[a-z_]/;
var largeRE = /[A-Z]/;
var digitRE = /[0-9]/;
var hexitRE = /[0-9A-Fa-f]/;
var octitRE = /[0-7]/;
var idRE = /[a-z_A-Z0-9']/;
var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:]/;
var specialRE = /[(),;[\]`{}]/;
var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer
function normal(source, setState) {
if (source.eatWhile(whiteCharRE)) {
return null;
}
var ch = source.next();
if (specialRE.test(ch)) {
if (ch == '{' && source.eat('-')) {
var t = "hs-comment";
if (source.eat('#')) {
t = "hs-pragma";
}
return switchState(source, setState, ncomment(t, 1));
}
return "hs-special";
}
if (ch == '\'') {
if (source.eat('\\')) {
source.next(); // should handle other escapes here
}
else {
source.next();
}
if (source.eat('\'')) {
return "hs-char";
}
return "hs-error";
}
if (ch == '"') {
return switchState(source, setState, stringLiteral);
}
if (largeRE.test(ch)) {
source.eatWhile(idRE);
if (source.eat('.')) {
return "hs-qualifier";
}
return "hs-conid";
}
if (smallRE.test(ch)) {
source.eatWhile(idRE);
return "hs-varid";
}
if (digitRE.test(ch)) {
if (ch == '0') {
if (source.eat(/[xX]/)) {
source.eatWhile(hexitRE); // should require at least 1
return "hs-integer";
}
if (source.eat(/[oO]/)) {
source.eatWhile(octitRE); // should require at least 1
return "hs-integer";
}
}
source.eatWhile(digitRE);
var t = "hs-integer";
if (source.eat('.')) {
t = "hs-float";
source.eatWhile(digitRE); // should require at least 1
}
if (source.eat(/[eE]/)) {
t = "hs-float";
source.eat(/[-+]/);
source.eatWhile(digitRE); // should require at least 1
}
return t;
}
if (symbolRE.test(ch)) {
if (ch == '-' && source.eat(/-/)) {
source.eatWhile(/-/);
if (!source.eat(symbolRE)) {
source.skipToEnd();
return "hs-comment";
}
}
var t = "hs-varsym";
if (ch == ':') {
t = "hs-consym";
}
source.eatWhile(symbolRE);
return t;
}
return "hs-error";
}
function ncomment(type, nest) {
if (nest == 0) {
return normal;
}
return function(source, setState) {
var currNest = nest;
while (!source.eol()) {
ch = source.next();
if (ch == '{' && source.eat('-')) {
++currNest;
}
else if (ch == '-' && source.eat('}')) {
--currNest;
if (currNest == 0) {
setState(normal);
return type;
}
}
}
setState(ncomment(type, currNest));
return type;
}
}
function stringLiteral(source, setState) {
while (!source.eol()) {
var ch = source.next();
if (ch == '"') {
setState(normal);
return "hs-string";
}
if (ch == '\\') {
if (source.eol() || source.eat(whiteCharRE)) {
setState(stringGap);
return "hs-string";
}
if (source.eat('&')) {
}
else {
source.next(); // should handle other escapes here
}
}
}
setState(normal);
return "hs-error";
}
function stringGap(source, setState) {
if (source.eat('\\')) {
return switchState(source, setState, stringLiteral);
}
source.next();
setState(normal);
return "hs-error";
}
var wellKnownWords = (function() {
var wkw = {};
function setType(t) {
return function () {
for (var i = 0; i < arguments.length; i++)
wkw[arguments[i]] = t;
}
}
setType("hs-reservedid")(
"case", "class", "data", "default", "deriving", "do", "else", "foreign",
"if", "import", "in", "infix", "infixl", "infixr", "instance", "let",
"module", "newtype", "of", "then", "type", "where", "_");
setType("hs-reservedop")(
"\.\.", ":", "::", "=", "\\", "\"", "<-", "->", "@", "~", "=>");
setType("hs-prelude-varsym")(
"!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<=", "=<<",
"==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*", "**");
setType("hs-prelude-conid")(
"Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum", "Eq",
"False", "FilePath", "Float", "Floating", "Fractional", "Functor", "GT",
"IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left",
"Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read",
"ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS",
"String", "True");
setType("hs-prelude-varid")(
"abs", "acos", "acosh", "all", "and", "any", "appendFile", "asTypeOf",
"asin", "asinh", "atan", "atan2", "atanh", "break", "catch", "ceiling",
"compare", "concat", "concatMap", "const", "cos", "cosh", "curry",
"cycle", "decodeFloat", "div", "divMod", "drop", "dropWhile", "either",
"elem", "encodeFloat", "enumFrom", "enumFromThen", "enumFromThenTo",
"enumFromTo", "error", "even", "exp", "exponent", "fail", "filter",
"flip", "floatDigits", "floatRadix", "floatRange", "floor", "fmap",
"foldl", "foldl1", "foldr", "foldr1", "fromEnum", "fromInteger",
"fromIntegral", "fromRational", "fst", "gcd", "getChar", "getContents",
"getLine", "head", "id", "init", "interact", "ioError", "isDenormalized",
"isIEEE", "isInfinite", "isNaN", "isNegativeZero", "iterate", "last",
"lcm", "length", "lex", "lines", "log", "logBase", "lookup", "map",
"mapM", "mapM_", "max", "maxBound", "maximum", "maybe", "min", "minBound",
"minimum", "mod", "negate", "not", "notElem", "null", "odd", "or",
"otherwise", "pi", "pred", "print", "product", "properFraction",
"putChar", "putStr", "putStrLn", "quot", "quotRem", "read", "readFile",
"readIO", "readList", "readLn", "readParen", "reads", "readsPrec",
"realToFrac", "recip", "rem", "repeat", "replicate", "return", "reverse",
"round", "scaleFloat", "scanl", "scanl1", "scanr", "scanr1", "seq",
"sequence", "sequence_", "show", "showChar", "showList", "showParen",
"showString", "shows", "showsPrec", "significand", "signum", "sin",
"sinh", "snd", "span", "splitAt", "sqrt", "subtract", "succ", "sum",
"tail", "take", "takeWhile", "tan", "tanh", "toEnum", "toInteger",
"toRational", "truncate", "uncurry", "undefined", "unlines", "until",
"unwords", "unzip", "unzip3", "userError", "words", "writeFile", "zip",
"zip3", "zipWith", "zipWith3");
return wkw;
})();
return {
startState: function () { return { f: normal }; },
copyState: function (s) { return { f: s.f }; },
token: function(stream, state) {
var t = state.f(stream, function(s) { state.f = s; });
var w = stream.current();
return (w in wellKnownWords) ? wellKnownWords[w] : t;
}
};
});
CodeMirror.defineMIME("text/x-haskell", "haskell");

View File

@@ -0,0 +1,59 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: Haskell mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="haskell.js"></script>
<link rel="stylesheet" href="haskell.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
<link rel="stylesheet" href="../../css/docs.css">
</head>
<body>
<h1>CodeMirror 2: Haskell mode</h1>
<form><textarea id="code" name="code">
module UniquePerms (
uniquePerms
)
where
-- | Find all unique permutations of a list where there might be duplicates.
uniquePerms :: (Eq a) => [a] -> [[a]]
uniquePerms = permBag . makeBag
-- | An unordered collection where duplicate values are allowed,
-- but represented with a single value and a count.
type Bag a = [(a, Int)]
makeBag :: (Eq a) => [a] -> Bag a
makeBag [] = []
makeBag (a:as) = mix a $ makeBag as
where
mix a [] = [(a,1)]
mix a (bn@(b,n):bs) | a == b = (b,n+1):bs
| otherwise = bn : mix a bs
permBag :: Bag a -> [[a]]
permBag [] = [[]]
permBag bs = concatMap (\(f,cs) -> map (f:) $ permBag cs) . oneOfEach $ bs
where
oneOfEach [] = []
oneOfEach (an@(a,n):bs) =
let bs' = if n == 1 then bs else (a,n-1):bs
in (a,bs') : mapSnd (an:) (oneOfEach bs)
apSnd f (a,b) = (a, f b)
mapSnd = map . apSnd
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
matchBrackets: true
});
</script>
<p><strong>MIME types defined:</strong> <code>text/x-haskell</code>.</p>
</body>
</html>

View File

@@ -0,0 +1,66 @@
CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
var jsMode = CodeMirror.getMode(config, "javascript");
var cssMode = CodeMirror.getMode(config, "css");
function html(stream, state) {
var style = htmlMode.token(stream, state.htmlState);
if (style == "xml-tag" && stream.current() == ">" && state.htmlState.context) {
if (/^script$/i.test(state.htmlState.context.tagName)) {
state.token = javascript;
state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
}
else if (/^style$/i.test(state.htmlState.context.tagName)) {
state.token = css;
state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
}
}
return style;
}
function javascript(stream, state) {
if (stream.match(/^<\/\s*script\s*>/i, false)) {
state.token = html;
state.curState = null;
return html(stream, state);
}
return jsMode.token(stream, state.localState);
}
function css(stream, state) {
if (stream.match(/^<\/\s*style\s*>/i, false)) {
state.token = html;
state.localState = null;
return html(stream, state);
}
return cssMode.token(stream, state.localState);
}
return {
startState: function() {
var state = htmlMode.startState();
return {token: html, localState: null, htmlState: state};
},
copyState: function(state) {
if (state.localState)
var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
return {token: state.token, localState: local, htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
},
token: function(stream, state) {
return state.token(stream, state);
},
indent: function(state, textAfter) {
if (state.token == html || /^\s*<\//.test(textAfter))
return htmlMode.indent(state.htmlState, textAfter);
else if (state.token == javascript)
return jsMode.indent(state.localState, textAfter);
else
return cssMode.indent(state.localState, textAfter);
},
electricChars: "/{}:"
}
});
CodeMirror.defineMIME("text/html", "htmlmixed");

View File

@@ -0,0 +1,54 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: HTML mixed mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../xml/xml.js"></script>
<link rel="stylesheet" href="../xml/xml.css">
<script src="../javascript/javascript.js"></script>
<link rel="stylesheet" href="../javascript/javascript.css">
<script src="../css/css.js"></script>
<link rel="stylesheet" href="../css/css.css">
<script src="htmlmixed.js"></script>
<link rel="stylesheet" href="../../css/docs.css">
<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
</head>
<body>
<h1>CodeMirror 2: HTML mixed mode</h1>
<form><textarea id="code" name="code">
<html style="color: green">
<!-- this is a comment -->
<head>
<title>Mixed HTML Example</title>
<style type="text/css">
h1 {font-family: comic sans; color: #f0f;}
div {background: yellow !important;}
body {
max-width: 50em;
margin: 1em 2em 1em 5em;
}
</style>
</head>
<body>
<h1>Mixed HTML Example</h1>
<script>
function jsFunc(arg1, arg2) {
if (arg1 && arg2) document.body.innerHTML = "achoo";
}
</script>
</body>
</html>
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "text/html", tabMode: "indent"});
</script>
<p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>
<p><strong>MIME types defined:</strong> <code>text/html</code>
(redefined, only takes effect if you load this parser after the
XML parser).</p>
</body>
</html>

View File

@@ -0,0 +1,78 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: JavaScript mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="javascript.js"></script>
<link rel="stylesheet" href="javascript.css">
<link rel="stylesheet" href="../../css/docs.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
</head>
<body>
<h1>CodeMirror 2: JavaScript mode</h1>
<div><textarea id="code" name="code">
// Demo code (the actual new parser character stream implementation)
function StringStream(string) {
this.pos = 0;
this.string = string;
}
StringStream.prototype = {
done: function() {return this.pos >= this.string.length;},
peek: function() {return this.string.charAt(this.pos);},
next: function() {
if (this.pos &lt; this.string.length)
return this.string.charAt(this.pos++);
},
eat: function(match) {
var ch = this.string.charAt(this.pos);
if (typeof match == "string") var ok = ch == match;
else var ok = ch &amp;&amp; match.test ? match.test(ch) : match(ch);
if (ok) {this.pos++; return ch;}
},
eatWhile: function(match) {
var start = this.pos;
while (this.eat(match));
if (this.pos > start) return this.string.slice(start, this.pos);
},
backUp: function(n) {this.pos -= n;},
column: function() {return this.pos;},
eatSpace: function() {
var start = this.pos;
while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
return this.pos - start;
},
match: function(pattern, consume, caseInsensitive) {
if (typeof pattern == "string") {
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
if (consume !== false) this.pos += str.length;
return true;
}
}
else {
var match = this.string.slice(this.pos).match(pattern);
if (match &amp;&amp; consume !== false) this.pos += match[0].length;
return match;
}
}
};
</textarea></div>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
matchBrackets: true
});
</script>
<p>JavaScript mode supports a single configuration
option, <code>json</code>, which will set the mode to expect JSON
data rather than a JavaScript program.</p>
<p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>.</p>
</body>
</html>

View File

@@ -0,0 +1,6 @@
span.js-keyword {color: #90b;}
span.js-atom {color: #291;}
span.js-variabledef {color: #00f;}
span.js-localvariable {color: #049;}
span.js-comment {color: #a70;}
span.js-string {color: #a22;}

View File

@@ -0,0 +1,348 @@
CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var jsonMode = parserConfig.json;
// Tokenizer
var keywords = function(){
function kw(type) {return {type: type, style: "js-keyword"};}
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
var operator = kw("operator"), atom = {type: "atom", style: "js-atom"};
return {
"if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
"var": kw("var"), "function": kw("function"), "catch": kw("catch"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
"in": operator, "typeof": operator, "instanceof": operator,
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
};
}();
var isOperatorChar = /[+\-*&%=<>!?|]/;
function chain(stream, state, f) {
state.tokenize = f;
return f(stream, state);
}
function nextUntilUnescaped(stream, end) {
var escaped = false, next;
while ((next = stream.next()) != null) {
if (next == end && !escaped)
return false;
escaped = !escaped && next == "\\";
}
return escaped;
}
// Used as scratch variables to communicate multiple values without
// consing up tons of objects.
var type, content;
function ret(tp, style, cont) {
type = tp; content = cont;
return style;
}
function jsTokenBase(stream, state) {
var ch = stream.next();
if (ch == '"' || ch == "'")
return chain(stream, state, jsTokenString(ch));
else if (/[\[\]{}\(\),;\:\.]/.test(ch))
return ret(ch);
else if (ch == "0" && stream.eat(/x/i)) {
stream.eatWhile(/[\da-f]/i);
return ret("number", "js-atom");
}
else if (/\d/.test(ch)) {
stream.match(/^\d*(?:\.\d*)?(?:e[+\-]?\d+)?/);
return ret("number", "js-atom");
}
else if (ch == "/") {
if (stream.eat("*")) {
return chain(stream, state, jsTokenComment);
}
else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "js-comment");
}
else if (state.reAllowed) {
nextUntilUnescaped(stream, "/");
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
return ret("regexp", "js-string");
}
else {
stream.eatWhile(isOperatorChar);
return ret("operator", null, stream.current());
}
}
else if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return ret("operator", null, stream.current());
}
else {
stream.eatWhile(/[\w\$_]/);
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
return known ? ret(known.type, known.style, word) :
ret("variable", "js-variable", word);
}
}
function jsTokenString(quote) {
return function(stream, state) {
if (!nextUntilUnescaped(stream, quote))
state.tokenize = jsTokenBase;
return ret("string", "js-string");
};
}
function jsTokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = jsTokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "js-comment");
}
// Parser
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
function JSLexical(indented, column, type, align, prev, info) {
this.indented = indented;
this.column = column;
this.type = type;
this.prev = prev;
this.info = info;
if (align != null) this.align = align;
}
function inScope(state, varname) {
for (var v = state.localVars; v; v = v.next)
if (v.name == varname) return true;
}
function parseJS(state, style, type, content, stream) {
var cc = state.cc;
// Communicate our context to the combinators.
// (Less wasteful than consing up a hundred closures on every call.)
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = true;
while(true) {
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
if (combinator(type, content)) {
while(cc.length && cc[cc.length - 1].lex)
cc.pop()();
if (cx.marked) return cx.marked;
if (type == "variable" && inScope(state, content)) return "js-localvariable";
return style;
}
}
}
// Combinator utils
var cx = {state: null, column: null, marked: null, cc: null};
function pass() {
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
}
function cont() {
pass.apply(null, arguments);
return true;
}
function register(varname) {
var state = cx.state;
if (state.context) {
cx.marked = "js-variabledef";
for (var v = state.localVars; v; v = v.next)
if (v.name == varname) return;
state.localVars = {name: varname, next: state.localVars};
}
}
// Combinators
var defaultVars = {name: "this", next: {name: "arguments"}};
function pushcontext() {
if (!cx.state.context) cx.state.localVars = defaultVars;
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
}
function popcontext() {
cx.state.localVars = cx.state.context.vars;
cx.state.context = cx.state.context.prev;
}
function pushlex(type, info) {
var result = function() {
var state = cx.state;
state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
};
result.lex = true;
return result;
}
function poplex() {
var state = cx.state;
if (state.lexical.prev) {
if (state.lexical.type == ")")
state.indented = state.lexical.indented;
state.lexical = state.lexical.prev;
}
}
poplex.lex = true;
function expect(wanted) {
return function expecting(type) {
if (type == wanted) return cont();
else if (wanted == ";") return pass();
else return cont(arguments.callee);
};
}
function statement(type) {
if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex);
if (type == ";") return cont();
if (type == "function") return cont(functiondef);
if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
poplex, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel);
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
block, poplex, poplex);
if (type == "case") return cont(expression, expect(":"));
if (type == "default") return cont(expect(":"));
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
statement, poplex, popcontext);
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
function expression(type) {
if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
if (type == "function") return cont(functiondef);
if (type == "keyword c") return cont(expression);
if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
if (type == "operator") return cont(expression);
if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
return cont();
}
function maybeoperator(type, value) {
if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
if (type == "operator") return cont(expression);
if (type == ";") return;
if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
if (type == ".") return cont(property, maybeoperator);
if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
}
function maybelabel(type) {
if (type == ":") return cont(poplex, statement);
return pass(maybeoperator, expect(";"), poplex);
}
function property(type) {
if (type == "variable") {cx.marked = "js-property"; return cont();}
}
function objprop(type) {
if (type == "variable") cx.marked = "js-property";
if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
}
function commasep(what, end) {
function proceed(type) {
if (type == ",") return cont(what, proceed);
if (type == end) return cont();
return cont(expect(end));
}
return function commaSeparated(type) {
if (type == end) return cont();
else return pass(what, proceed);
};
}
function block(type) {
if (type == "}") return cont();
return pass(statement, block);
}
function vardef1(type, value) {
if (type == "variable"){register(value); return cont(vardef2);}
return cont();
}
function vardef2(type, value) {
if (value == "=") return cont(expression, vardef2);
if (type == ",") return cont(vardef1);
}
function forspec1(type) {
if (type == "var") return cont(vardef1, forspec2);
if (type == ";") return pass(forspec2);
if (type == "variable") return cont(formaybein);
return pass(forspec2);
}
function formaybein(type, value) {
if (value == "in") return cont(expression);
return cont(maybeoperator, forspec2);
}
function forspec2(type, value) {
if (type == ";") return cont(forspec3);
if (value == "in") return cont(expression);
return cont(expression, expect(";"), forspec3);
}
function forspec3(type) {
if (type != ")") cont(expression);
}
function functiondef(type, value) {
if (type == "variable") {register(value); return cont(functiondef);}
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
}
function funarg(type, value) {
if (type == "variable") {register(value); return cont();}
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: jsTokenBase,
reAllowed: true,
cc: [],
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: null,
context: null,
indented: 0
};
},
token: function(stream, state) {
if (stream.sol()) {
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = false;
state.indented = stream.indentation();
}
if (stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
if (type == "comment") return style;
state.reAllowed = type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/);
return parseJS(state, style, type, content, stream);
},
indent: function(state, textAfter) {
if (state.tokenize != jsTokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical,
type = lexical.type, closing = firstChar == type;
if (type == "vardef") return lexical.indented + 4;
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
else if (lexical.info == "switch" && !closing)
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
else return lexical.indented + (closing ? 0 : indentUnit);
},
electricChars: ":{}"
};
});
CodeMirror.defineMIME("text/javascript", "javascript");
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});

View File

@@ -0,0 +1,52 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: PHP mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../xml/xml.js"></script>
<link rel="stylesheet" href="../xml/xml.css">
<script src="../javascript/javascript.js"></script>
<link rel="stylesheet" href="../javascript/javascript.css">
<script src="../css/css.js"></script>
<link rel="stylesheet" href="../css/css.css">
<script src="../clike/clike.js"></script>
<link rel="stylesheet" href="../clike/clike.css">
<script src="php.js"></script>
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
<link rel="stylesheet" href="../../css/docs.css">
</head>
<body>
<h1>CodeMirror 2: PHP mode</h1>
<form><textarea id="code" name="code">
<?php
function hello($who) {
return "Hello " . $who;
}
?>
<p>The program says <?= hello("World") ?>.</p>
<script>
alert("And here is some JS code"); // also colored
</script>
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
matchBrackets: true,
mode: "application/x-httpd-php",
indentUnit: 8,
indentWithTabs: true,
enterMode: "keep",
tabMode: "shift"
});
</script>
<p>Simple HTML/PHP mode based on
the <a href="../clike/">C-like</a> mode. Depends on XML,
JavaScript, CSS, and C-like modes.</p>
<p><strong>MIME types defined:</strong> <code>application/x-httpd-php</code>.</p>
</body>
</html>

View File

@@ -0,0 +1,83 @@
(function() {
function keywords(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}
var phpKeywords =
keywords("abstract and array as break case catch cfunction class clone const continue declare " +
"default do else elseif enddeclare endfor endforeach endif endswitch endwhile extends " +
"final for foreach function global goto if implements interface instanceof namespace " +
"new or private protected public static switch throw try use var while xor");
CodeMirror.defineMode("php", function(config, parserConfig) {
var htmlMode = CodeMirror.getMode(config, "text/html");
var jsMode = CodeMirror.getMode(config, "text/javascript");
var cssMode = CodeMirror.getMode(config, "text/css");
var phpMode = CodeMirror.getMode(config, {name: "clike", keywords: phpKeywords, multiLineStrings: true, $vars: true});
function dispatch(stream, state) { // TODO open PHP inside text/css
if (state.curMode == htmlMode) {
var style = htmlMode.token(stream, state.curState);
if (style == "xml-processing" && /^<\?/.test(stream.current())) {
state.curMode = phpMode;
state.curState = state.php;
state.curClose = /^\?>/;
}
else if (style == "xml-tag" && stream.current() == ">" && state.curState.context) {
if (/^script$/i.test(state.curState.context.tagName)) {
state.curMode = jsMode;
state.curState = jsMode.startState(htmlMode.indent(state.curState, ""));
state.curClose = /^<\/\s*script\s*>/i;
}
else if (/^style$/i.test(state.curState.context.tagName)) {
state.curMode = cssMode;
state.curState = cssMode.startState(htmlMode.indent(state.curState, ""));
state.curClose = /^<\/\s*style\s*>/i;
}
}
return style;
}
else if (stream.match(state.curClose, false)) {
state.curMode = htmlMode;
state.curState = state.html;
state.curClose = null;
return dispatch(stream, state);
}
else return state.curMode.token(stream, state.curState);
}
return {
startState: function() {
var html = htmlMode.startState();
return {html: html,
php: phpMode.startState(),
curMode: htmlMode,
curState: html,
curClose: null}
},
copyState: function(state) {
var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
php = state.php, phpNew = CodeMirror.copyState(phpMode, php), cur;
if (state.curState == html) cur = htmlNew;
else if (state.curState == php) cur = phpNew;
else cur = CodeMirror.copyState(state.curMode, state.curState);
return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur, curClose: state.curClose};
},
token: dispatch,
indent: function(state, textAfter) {
if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
(state.curMode == phpMode && /^\?>/.test(textAfter)))
return htmlMode.indent(state.html, textAfter);
return state.curMode.indent(state.curState, textAfter);
},
electricChars: "/{}:"
}
});
})();
CodeMirror.defineMIME("application/x-httpd-php", "php");

View File

@@ -0,0 +1,96 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: sTeX mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="stex.js"></script>
<link rel="stylesheet" href="stex.css">
<style>.CodeMirror {background: #f8f8f8;}</style>
<link rel="stylesheet" href="../../css/docs.css">
</head>
<body>
<h1>CodeMirror 2: sTeX mode</h1>
<form><textarea id="code" name="code">
\begin{module}[id=bbt-size]
\importmodule[balanced-binary-trees]{balanced-binary-trees}
\importmodule[\KWARCslides{dmath/en/cardinality}]{cardinality}
\begin{frame}
\frametitle{Size Lemma for Balanced Trees}
\begin{itemize}
\item
\begin{assertion}[id=size-lemma,type=lemma]
Let $G=\tup{V,E}$ be a \termref[cd=binary-trees]{balanced binary tree}
of \termref[cd=graph-depth,name=vertex-depth]{depth}$n>i$, then the set
$\defeq{\livar{V}i}{\setst{\inset{v}{V}}{\gdepth{v} = i}}$ of
\termref[cd=graphs-intro,name=node]{nodes} at
\termref[cd=graph-depth,name=vertex-depth]{depth} $i$ has
\termref[cd=cardinality,name=cardinality]{cardinality} $\power2i$.
\end{assertion}
\item
\begin{sproof}[id=size-lemma-pf,proofend=,for=size-lemma]{via induction over the depth $i$.}
\begin{spfcases}{We have to consider two cases}
\begin{spfcase}{$i=0$}
\begin{spfstep}[display=flow]
then $\livar{V}i=\set{\livar{v}r}$, where $\livar{v}r$ is the root, so
$\eq{\card{\livar{V}0},\card{\set{\livar{v}r}},1,\power20}$.
\end{spfstep}
\end{spfcase}
\begin{spfcase}{$i>0$}
\begin{spfstep}[display=flow]
then $\livar{V}{i-1}$ contains $\power2{i-1}$ vertexes
\begin{justification}[method=byIH](IH)\end{justification}
\end{spfstep}
\begin{spfstep}
By the \begin{justification}[method=byDef]definition of a binary
tree\end{justification}, each $\inset{v}{\livar{V}{i-1}}$ is a leaf or has
two children that are at depth $i$.
\end{spfstep}
\begin{spfstep}
As $G$ is \termref[cd=balanced-binary-trees,name=balanced-binary-tree]{balanced} and $\gdepth{G}=n>i$, $\livar{V}{i-1}$ cannot contain
leaves.
\end{spfstep}
\begin{spfstep}[type=conclusion]
Thus $\eq{\card{\livar{V}i},{\atimes[cdot]{2,\card{\livar{V}{i-1}}}},{\atimes[cdot]{2,\power2{i-1}}},\power2i}$.
\end{spfstep}
\end{spfcase}
\end{spfcases}
\end{sproof}
\item
\begin{assertion}[id=fbbt,type=corollary]
A fully balanced tree of depth $d$ has $\power2{d+1}-1$ nodes.
\end{assertion}
\item
\begin{sproof}[for=fbbt,id=fbbt-pf]{}
\begin{spfstep}
Let $\defeq{G}{\tup{V,E}}$ be a fully balanced tree
\end{spfstep}
\begin{spfstep}
Then $\card{V}=\Sumfromto{i}1d{\power2i}= \power2{d+1}-1$.
\end{spfstep}
\end{sproof}
\end{itemize}
\end{frame}
\begin{note}
\begin{omtext}[type=conclusion,for=binary-tree]
This shows that balanced binary trees grow in breadth very quickly, a consequence of
this is that they are very shallow (and this compute very fast), which is the essence of
the next result.
\end{omtext}
\end{note}
\end{module}
%%% Local Variables:
%%% mode: LaTeX
%%% TeX-master: "all"
%%% End: \end{document}
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
</script>
<p><strong>MIME types defined:</strong> <code>text/stex</code>.</p>
</body>
</html>

View File

@@ -0,0 +1,20 @@
span.css-at {color: #708;}
span.css-unit {color: #281;}
span.css-value {color: #708;}
span.css-identifier {color: black;}
span.css-selector {color: #11B;}
span.css-important {color: #00F;}
span.css-colorcode {color: #299;}
span.css-comment {color: #A70;}
span.css-string {color: #A22;}
span.stex-unit { color: #281; }
span.stex-identifier { color: black; }
span.stex-slash { color: #FAA; }
span.stex-command { color: #00F; }
span.stex-comment { color: #A70; }
span.stex-import { color: #00F; }
span.stex-filepath { color: #852626; }
span.stex-module { color: #852626; }
span.stex-error { text-decoration: underline; color: red; }
span.stex-string { color: #A22; }

View File

@@ -0,0 +1,167 @@
/*
* Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de)
* Licence: MIT
*/
CodeMirror.defineMode("stex", function(cmCfg, modeCfg)
{
function pushCommand(state, command) {
state.cmdState.push(command);
}
function peekCommand(state) {
if (state.cmdState.length>0)
return state.cmdState[state.cmdState.length-1];
else
return null;
}
function popCommand(state) {
if (state.cmdState.length>0) {
var plug = state.cmdState.pop();
plug.closeBracket();
}
}
function applyMostPowerful(state) {
context = state.cmdState;
for (var i = context.length - 1; i >= 0; i--) {
var plug = context[i];
if (plug.name=="DEFAULT")
continue;
return plug.styleIdentifier();
}
return "stex-identifier";
}
function addPluginPattern(pluginName, cmdStyle, brackets, styles) {
return function () {
this.name=pluginName;
this.bracketNo = 0;
this.style=cmdStyle;
this.styles = styles;
this.brackets = brackets;
this.styleIdentifier = function(content) {
if (this.bracketNo<=this.styles.length)
return this.styles[this.bracketNo-1];
else
return null;
};
this.openBracket = function(content) {
this.bracketNo++;
return "stex-bracket";
};
this.closeBracket = function(content) {
};
}
}
var plugins = new Array();
plugins["importmodule"] = addPluginPattern("importmodule", "stex-command", "{[", ["stex-filepath", "stex-module"]);
plugins["documentclass"] = addPluginPattern("documentclass", "stex-command", "{[", ["", "stex-unit"]);
plugins["usepackage"] = addPluginPattern("documentclass", "stex-command", "[", ["stex-unit"]);
plugins["begin"] = addPluginPattern("documentclass", "stex-command", "[", ["stex-unit"]);
plugins["end"] = addPluginPattern("documentclass", "stex-command", "[", ["stex-unit"]);
plugins["DEFAULT"] = function () {
this.name="DEFAULT";
this.style="stex-command";
this.styleIdentifier = function(content) {
};
this.openBracket = function(content) {
};
this.closeBracket = function(content) {
};
};
function setState(state, f) {
state.f = f;
}
function normal(source, state) {
if (source.match(/^\\[a-z]+/)) {
cmdName = source.current();
cmdName = cmdName.substr(1, cmdName.length-1);
var plug = plugins[cmdName];
if (typeof(plug) == 'undefined') {
plug = plugins["DEFAULT"];
}
plug = new plug();
pushCommand(state, plug);
setState(state, beginParams);
return plug.style;
}
var ch = source.next();
if (ch == "%") {
setState(state, inCComment);
return "stex-comment";
}
else if (ch=='}' || ch==']') {
plug = peekCommand(state);
if (plug) {
plug.closeBracket(ch);
setState(state, beginParams);
} else
return "stex-error";
return "stex-bracket";
} else if (ch=='{' || ch=='[') {
plug = plugins["DEFAULT"];
plug = new plug();
pushCommand(state, plug);
return "stex-bracket";
}
else if (/\d/.test(ch)) {
source.eatWhile(/[\w.%]/);
return "stex-unit";
}
else {
source.eatWhile(/[\w-_]/);
return applyMostPowerful(state);
}
}
function inCComment(source, state) {
source.skipToEnd();
setState(state, normal);
return "css-comment";
}
function beginParams(source, state) {
var ch = source.peek();
if (ch == '{' || ch == '[') {
lastPlug = peekCommand(state);
style = lastPlug.openBracket(ch);
source.eat(ch);
setState(state, normal);
return "stex-bracket";
}
if (/[ \t\r]/.test(ch)) {
source.eat(ch);
return null;
}
setState(state, normal);
lastPlug = peekCommand(state);
if (lastPlug) {
popCommand(state);
}
return normal(source, state);
}
return {
startState: function() { return { f:normal, cmdState:[] }; },
copyState: function(s) { return { f: s.f, cmdState: s.cmdState.slice(0, s.cmdState.length) }; },
token: function(stream, state) {
var t = state.f(stream, state);
var w = stream.current();
return t;
}
};
});
CodeMirror.defineMIME("text/x-stex", "stex");

View File

@@ -0,0 +1,42 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: XML mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="xml.js"></script>
<link rel="stylesheet" href="xml.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
<link rel="stylesheet" href="../../css/docs.css">
</head>
<body>
<h1>CodeMirror 2: XML mode</h1>
<form><textarea id="code" name="code">
&lt;html style="color: green"&gt;
&lt;!-- this is a comment --&gt;
&lt;head&gt;
&lt;title&gt;HTML Example&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
The indentation tries to be &lt;em&gt;somewhat &amp;quot;do what
I mean&amp;quot;&lt;/em&gt;... but might not match your style.
&lt;/body&gt;
&lt;/html&gt;
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: {name: "xml", htmlMode: true}});
</script>
<p>The XML mode supports two configuration parameters:</p>
<dl>
<dt><code>htmlMode (boolean)</code></dt>
<dd>This switches the mode to parse HTML instead of XML. This
means attributes do not have to be quoted, and some elements
(such as <code>br</code>) do not require a closing tag.</dd>
<dt><code>alignCDATA (boolean)</code></dt>
<dd>Setting this to true will force the opening tag of CDATA
blocks to not be indented.</dd>
</dl>
<p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/html</code>.</p>
</body>
</html>

View File

@@ -0,0 +1,7 @@
span.xml-tag {color: #a0b;}
span.xml-attribute {color: #281;}
span.xml-attname {color: #00f;}
span.xml-comment {color: #a70;}
span.xml-cdata {color: #48a;}
span.xml-processing {color: #999;}
span.xml-entity {color: #a22;}

View File

@@ -0,0 +1,206 @@
CodeMirror.defineMode("xml", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var Kludges = parserConfig.htmlMode ? {
autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
"meta": true, "col": true, "frame": true, "base": true, "area": true},
doNotIndent: {"pre": true, "!cdata": true},
allowUnquoted: true
} : {autoSelfClosers: {}, doNotIndent: {"!cdata": true}, allowUnquoted: false};
var alignCDATA = parserConfig.alignCDATA;
// Return variables for tokenizers
var tagName, type;
function inText(stream, state) {
function chain(parser) {
state.tokenize = parser;
return parser(stream, state);
}
var ch = stream.next();
if (ch == "<") {
if (stream.eat("!")) {
if (stream.eat("[")) {
if (stream.match("[CDATA[")) return chain(inBlock("xml-cdata", "]]>"));
else return null;
}
else if (stream.match("--")) return chain(inBlock("xml-comment", "-->"));
else if (stream.match("DOCTYPE")) {
stream.eatWhile(/[\w\._\-]/);
return chain(inBlock("xml-doctype", ">"));
}
else return null;
}
else if (stream.eat("?")) {
stream.eatWhile(/[\w\._\-]/);
state.tokenize = inBlock("xml-processing", "?>");
return "xml-processing";
}
else {
type = stream.eat("/") ? "closeTag" : "openTag";
stream.eatSpace();
tagName = "";
var c;
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
state.tokenize = inTag;
return "xml-tag";
}
}
else if (ch == "&") {
stream.eatWhile(/[^;]/);
stream.eat(";");
return "xml-entity";
}
else {
stream.eatWhile(/[^&<]/);
return null;
}
}
function inTag(stream, state) {
var ch = stream.next();
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
state.tokenize = inText;
type = ch == ">" ? "endTag" : "selfcloseTag";
return "xml-tag";
}
else if (ch == "=") {
type = "equals";
return null;
}
else if (/[\'\"]/.test(ch)) {
state.tokenize = inAttribute(ch);
return state.tokenize(stream, state);
}
else {
stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
return "xml-word";
}
}
function inAttribute(quote) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.next() == quote) {
state.tokenize = inTag;
break;
}
}
return "xml-attribute";
};
}
function inBlock(style, terminator) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.match(terminator)) {
state.tokenize = inText;
break;
}
stream.next();
}
return style;
};
}
var curState, setStyle;
function pass() {
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
}
function cont() {
pass.apply(null, arguments);
return true;
}
function pushContext(tagName, startOfLine) {
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
curState.context = {
prev: curState.context,
tagName: tagName,
indent: curState.indented,
startOfLine: startOfLine,
noIndent: noIndent
};
}
function popContext() {
if (curState.context) curState.context = curState.context.prev;
}
function element(type) {
if (type == "openTag") {curState.tagName = tagName; return cont(attributes, endtag(curState.startOfLine));}
else if (type == "closeTag") {popContext(); return cont(endclosetag);}
else if (type == "xml-cdata") {
if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
if (curState.tokenize == inText) popContext();
return cont();
}
else return cont();
}
function endtag(startOfLine) {
return function(type) {
if (type == "selfcloseTag" ||
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
return cont();
if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
return cont();
};
}
function endclosetag(type) {
if (type == "endTag") return cont();
return pass();
}
function attributes(type) {
if (type == "xml-word") {setStyle = "xml-attname"; return cont(attributes);}
if (type == "equals") return cont(attvalue, attributes);
return pass();
}
function attvalue(type) {
if (type == "xml-word" && Kludges.allowUnquoted) {setStyle = "xml-attribute"; return cont();}
if (type == "xml-attribute") return cont();
return pass();
}
return {
startState: function() {
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
},
token: function(stream, state) {
if (stream.sol()) {
state.startOfLine = true;
state.indented = stream.indentation();
}
if (stream.eatSpace()) return null;
setStyle = type = tagName = null;
var style = state.tokenize(stream, state);
if ((style || type) && style != "xml-comment") {
curState = state;
while (true) {
var comb = state.cc.pop() || element;
if (comb(type || style)) break;
}
}
state.startOfLine = false;
return setStyle || style;
},
indent: function(state, textAfter) {
var context = state.context;
if (context && context.noIndent) return 0;
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
if (context && /^<\//.test(textAfter))
context = context.prev;
while (context && !context.startOfLine)
context = context.prev;
if (context) return context.indent + indentUnit;
else return 0;
},
electricChars: "/"
};
});
CodeMirror.defineMIME("application/xml", "xml");
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});

View File

@@ -0,0 +1,116 @@
<!doctype html>
<html>
<head>
<title>CodeMirror</title>
<link rel="stylesheet" type="text/css" href="css/docs.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="alternate" href="http://twitter.com/statuses/user_timeline/242283288.rss" type="application/rss+xml"/>
</head>
<body>
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1>
<pre class="grey">
<img src="css/baboon.png" class="logo" alt="logo"/>/* Old release history */
</pre>
<p class="rel">02-10-2010: <a
href="http://codemirror.net/codemirror-0.9.zip">Version 0.9</a>:</p>
<ul class="rel-note">
<li>Add support for searching backwards.</li>
<li>There are now parsers for <a href="contrib/scheme/index.html">Scheme</a>, <a href="contrib/xquery/index.html">XQuery</a>, and <a href="contrib/ometa/index.html">OmetaJS</a>.</li>
<li>Makes <code>height: "dynamic"</code> more robust.</li>
<li>Fixes bug where paste did not work on OS X.</li>
<li>Add a <code>enterMode</code> and <code>electricChars</code> options to make indentation even more customizable.</li>
<li>Add <code>firstLineNumber</code> option.</li>
<li>Fix bad handling of <code>@media</code> rules by the CSS parser.</li>
<li>Take a new, more robust approach to working around the invisible-last-line bug in WebKit.</li>
</ul>
<p class="rel">22-07-2010: <a
href="http://codemirror.net/codemirror-0.8.zip">Version 0.8</a>:</p>
<ul class="rel-note">
<li>Add a <code>cursorCoords</code> method to find the screen
coordinates of the cursor.</li>
<li>A number of fixes and support for more syntax in the PHP parser.</li>
<li>Fix indentation problem with JSON-mode JS parser in Webkit.</li>
<li>Add a <a href="compress.html">minification</a> UI.</li>
<li>Support a <code>height: dynamic</code> mode, where the editor's
height will adjust to the size of its content.</li>
<li>Better support for IME input mode.</li>
<li>Fix JavaScript parser getting confused when seeing a no-argument
function call.</li>
<li>Have CSS parser see the difference between selectors and other
identifiers.</li>
<li>Fix scrolling bug when pasting in a horizontally-scrolled
editor.</li>
<li>Support <code>toTextArea</code> method in instances created with
<code>fromTextArea</code>.</li>
<li>Work around new Opera cursor bug that causes the cursor to jump
when pressing backspace at the end of a line.</li>
</ul>
<p class="rel">27-04-2010: <a
href="http://codemirror.net/codemirror-0.67.zip">Version
0.67</a>:</p>
<p class="rel-note">More consistent page-up/page-down behaviour
across browsers. Fix some issues with hidden editors looping forever
when line-numbers were enabled. Make PHP parser parse
<code>"\\"</code> correctly. Have <code>jumpToLine</code> work on
line handles, and add <code>cursorLine</code> function to fetch the
line handle where the cursor currently is. Add new
<code>setStylesheet</code> function to switch style-sheets in a
running editor.</p>
<p class="rel">01-03-2010: <a
href="http://codemirror.net/codemirror-0.66.zip">Version
0.66</a>:</p>
<p class="rel-note">Adds <code>removeLine</code> method to API.
Introduces the <a href="contrib/plsql/index.html">PLSQL parser</a>.
Marks XML errors by adding (rather than replacing) a CSS class, so
that they can be disabled by modifying their style. Fixes several
selection bugs, and a number of small glitches.</p>
<p class="rel">12-11-2009: <a
href="http://codemirror.net/codemirror-0.65.zip">Version
0.65</a>:</p>
<p class="rel-note">Add support for having both line-wrapping and
line-numbers turned on, make paren-highlighting style customisable
(<code>markParen</code> and <code>unmarkParen</code> config
options), work around a selection bug that Opera
<em>re</em>introduced in version 10.</p>
<p class="rel">23-10-2009: <a
href="http://codemirror.net/codemirror-0.64.zip">Version
0.64</a>:</p>
<p class="rel-note">Solves some issues introduced by the
paste-handling changes from the previous release. Adds
<code>setSpellcheck</code>, <code>setTextWrapping</code>,
<code>setIndentUnit</code>, <code>setUndoDepth</code>,
<code>setTabMode</code>, and <code>setLineNumbers</code> to
customise a running editor. Introduces an <a
href="contrib/sql/index.html">SQL</a> parser. Fixes a few small
problems in the <a href="contrib/python/index.html">Python</a>
parser. And, as usual, add workarounds for various newly discovered
browser incompatibilities.</p>
<p class="rel"><em>31-08-2009</em>: <a
href="http://codemirror.net/codemirror-0.63.zip">Version
0.63</a>:</p>
<p class="rel-note"> Overhaul of paste-handling (less fragile), fixes for several
serious IE8 issues (cursor jumping, end-of-document bugs) and a number
of small problems.</p>
<p class="rel"><em>30-05-2009</em>: <a
href="http://codemirror.net/codemirror-0.62.zip">Version
0.62</a>:</p>
<p class="rel-note">Introduces <a href="contrib/python/index.html">Python</a>
and <a href="contrib/lua/index.html">Lua</a> parsers. Add
<code>setParser</code> (on-the-fly mode changing) and
<code>clearHistory</code> methods. Make parsing passes time-based
instead of lines-based (see the <code>passTime</code> option).</p>
<script type="text/javascript" src="css/font.js"></script>
</body></html>

View File

@@ -0,0 +1,30 @@
<!doctype html>
<html>
<head>
<title>CodeMirror 2: Test Suite</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<link rel="stylesheet" href="../mode/javascript/javascript.css">
<script src="../mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="../docs.css">
<style type="text/css">
.ok {color: #0e0;}
.failure {color: #e00;}
.error {color: #c90;}
</style>
</head>
<body>
<h1>CodeMirror 2: Test Suite</h1>
<p>A limited set of programmatic sanity tests for CodeMirror.</p>
<pre id=output></pre>
<div style="visibility: hidden" id=testground>
<form><textarea id="code" name="code"></textarea><input type=submit value=ok name=submit></form>
</div>
<script src="test.js"></script>
</body>
</html>

View File

@@ -0,0 +1,234 @@
var tests = [];
test("fromTextArea", function() {
var te = document.getElementById("code");
te.value = "CONTENT";
var cm = CodeMirror.fromTextArea(te);
is(!te.offsetHeight);
eq(cm.getValue(), "CONTENT");
cm.setValue("foo\nbar");
eq(cm.getValue(), "foo\nbar");
cm.save();
is(/^foo\r?\nbar$/.test(te.value));
cm.setValue("xxx");
cm.toTextArea();
is(te.offsetHeight);
eq(te.value, "xxx");
});
testCM("getRange", function(cm) {
eq(cm.getLine(0), "1234");
eq(cm.getLine(1), "5678");
eq(cm.getLine(2), null);
eq(cm.getLine(-1), null);
eq(cm.getRange({line: 0, ch: 0}, {line: 0, ch: 3}), "123");
eq(cm.getRange({line: 0, ch: -1}, {line: 0, ch: 200}), "1234");
eq(cm.getRange({line: 0, ch: 2}, {line: 1, ch: 2}), "34\n56");
eq(cm.getRange({line: 1, ch: 2}, {line: 100, ch: 0}), "78");
}, {value: "1234\n5678"});
testCM("replaceRange", function(cm) {
eq(cm.getValue(), "");
cm.replaceRange("foo\n", {line: 0, ch: 0});
eq(cm.getValue(), "foo\n");
cm.replaceRange("a\nb", {line: 0, ch: 1});
eq(cm.getValue(), "fa\nboo\n");
eq(cm.lineCount(), 3);
cm.replaceRange("xyzzy", {line: 0, ch: 0}, {line: 1, ch: 1});
eq(cm.getValue(), "xyzzyoo\n");
cm.replaceRange("abc", {line: 0, ch: 0}, {line: 10, ch: 0});
eq(cm.getValue(), "abc");
eq(cm.lineCount(), 1);
});
testCM("selection", function(cm) {
cm.setSelection({line: 0, ch: 4}, {line: 2, ch: 2});
is(cm.somethingSelected());
eq(cm.getSelection(), "11\n222222\n33");
eqPos(cm.getCursor(false), {line: 2, ch: 2});
eqPos(cm.getCursor(true), {line: 0, ch: 4});
cm.setSelection({line: 1, ch: 0});
is(!cm.somethingSelected());
eq(cm.getSelection(), "");
eqPos(cm.getCursor(true), {line: 1, ch: 0});
cm.replaceSelection("abc");
eq(cm.getSelection(), "abc");
eq(cm.getValue(), "111111\nabc222222\n333333");
cm.replaceSelection("def", "end");
eq(cm.getSelection(), "");
eqPos(cm.getCursor(true), {line: 1, ch: 3});
cm.setCursor({line: 2, ch: 1});
eqPos(cm.getCursor(true), {line: 2, ch: 1});
cm.setCursor(1, 2);
eqPos(cm.getCursor(true), {line: 1, ch: 2});
}, {value: "111111\n222222\n333333"});
testCM("lines", function(cm) {
eq(cm.getLine(0), "111111");
eq(cm.getLine(1), "222222");
eq(cm.getLine(-1), null);
cm.removeLine(1);
cm.setLine(1, "abc");
eq(cm.getValue(), "111111\nabc");
}, {value: "111111\n222222\n333333"});
testCM("indent", function(cm) {
cm.indentLine(1);
eq(cm.getLine(1), " blah();");
cm.setOption("indentUnit", 8);
cm.indentLine(1);
eq(cm.getLine(1), "\tblah();");
}, {value: "if (x) {\nblah();\n}", indentUnit: 3, indentWithTabs: true});
test("defaults", function() {
var olddefaults = CodeMirror.defaults, defs = CodeMirror.defaults = {};
for (var opt in olddefaults) defs[opt] = olddefaults[opt];
defs.indentUnit = 5;
defs.value = "uu";
defs.enterMode = "keep";
defs.tabindex = 55;
var place = document.getElementById("testground"), cm = CodeMirror(place);
try {
eq(cm.getOption("indentUnit"), 5);
cm.setOption("indentUnit", 10);
eq(defs.indentUnit, 5);
eq(cm.getValue(), "uu");
eq(cm.getOption("enterMode"), "keep");
eq(cm.getInputField().tabindex, 55);
}
finally {
CodeMirror.defaults = olddefaults;
place.removeChild(cm.getWrapperElement());
}
});
testCM("lineInfo", function(cm) {
eq(cm.lineInfo(-1), null);
var lh = cm.setMarker(1, "FOO", "bar");
var info = cm.lineInfo(1);
eq(info.text, "222222");
eq(info.markerText, "FOO");
eq(info.markerClass, "bar");
eq(info.line, 1);
eq(cm.lineInfo(2).markerText, null);
cm.clearMarker(lh);
eq(cm.lineInfo(1).markerText, null);
}, {value: "111111\n222222\n333333"});
testCM("coords", function(cm) {
cm.getWrapperElement().style.height = "100px";
var content = [];
for (var i = 0; i < 200; ++i) content.push("------------------------------" + i);
cm.setValue(content.join("\n"));
var top = cm.charCoords({line: 0, ch: 0});
var bot = cm.charCoords({line: 200, ch: 30});
is(top.x < bot.x);
is(top.y < bot.y);
is(top.y < top.yBot);
cm.getWrapperElement().scrollTop = 100;
cm.refresh();
var top2 = cm.charCoords({line: 0, ch: 0});
is(top.y > top2.y);
eq(top.x, top2.x);
});
testCM("undo", function(cm) {
cm.setLine(0, "def");
eq(cm.historySize().undo, 1);
cm.undo();
eq(cm.getValue(), "abc");
eq(cm.historySize().undo, 0);
eq(cm.historySize().redo, 1);
cm.redo();
eq(cm.getValue(), "def");
eq(cm.historySize().undo, 1);
eq(cm.historySize().redo, 0);
cm.setValue("1\n\n\n2");
eq(cm.historySize().undo, 0);
for (var i = 0; i < 20; ++i) {
cm.replaceRange("a", {line: 0, ch: 0});
cm.replaceRange("b", {line: 3, ch: 0});
}
eq(cm.historySize().undo, 40);
for (var i = 0; i < 38; ++i) cm.undo();
eq(cm.historySize().undo, 2);
eq(cm.historySize().redo, 38);
eq(cm.getValue(), "a1\n\n\nb2");
cm.setOption("undoDepth", 10);
for (var i = 0; i < 20; ++i) {
cm.replaceRange("a", {line: 0, ch: 0});
cm.replaceRange("b", {line: 3, ch: 0});
}
eq(cm.historySize().undo, 10);
}, {value: "abc"});
testCM("undoMultiLine", function(cm) {
cm.replaceRange("x", {line:0, ch: 0});
cm.replaceRange("y", {line:1, ch: 0});
cm.undo();
eq(cm.getValue(), "abc\ndef\nghi");
cm.replaceRange("y", {line:1, ch: 0});
cm.replaceRange("x", {line:0, ch: 0});
cm.undo();
eq(cm.getValue(), "abc\ndef\nghi");
cm.replaceRange("y", {line:2, ch: 0});
cm.replaceRange("x", {line:1, ch: 0});
cm.replaceRange("z", {line:2, ch: 0});
cm.undo();
eq(cm.getValue(), "abc\ndef\nghi");
}, {value: "abc\ndef\nghi"});
// Scaffolding
function htmlEscape(str) {
return str.replace(/[<&]/g, function(str) {return str == "&" ? "&amp;" : "&lt;";});
}
function forEach(arr, f) {
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
}
function Failure(why) {this.message = why;}
function test(name, run) {tests.push({name: name, func: run});}
function testCM(name, run, opts) {
test(name, function() {
var place = document.getElementById("testground"), cm = CodeMirror(place, opts);
try {run(cm);}
finally {place.removeChild(cm.getWrapperElement());}
});
}
function runTests() {
var failures = [], run = 0;
for (var i = 0; i < tests.length; ++i) {
var test = tests[i];
try {test.func();}
catch(e) {
if (e instanceof Failure)
failures.push({type: "failure", test: test.name, text: e.message});
else
failures.push({type: "error", test: test.name, text: e.toString()});
}
run++;
}
var html = [run + " tests run."];
if (failures.length)
forEach(failures, function(fail) {
html.push(fail.test + ': <span class="' + fail.type + '">' + htmlEscape(fail.text) + "</span>");
});
else html.push('<span class="ok">All passed.</span>');
document.getElementById("output").innerHTML = html.join("\n");
}
function eq(a, b, msg) {
if (a != b) throw new Failure(a + " != " + b + (msg ? " (" + msg + ")" : ""));
}
function eqPos(a, b, msg) {
eq(a.line, b.line, msg);
eq(a.ch, b.ch, msg);
}
function is(a, msg) {
if (!a) throw new Failure("assertion failed" + (msg ? " (" + msg + ")" : ""));
}
window.onload = runTests;