1,037 changes: 520 additions & 517 deletions mode/rst/rst.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion mode/ruby/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ <h2>Ruby mode</h2>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "text/x-ruby",
tabMode: "indent",
matchBrackets: true,
indentUnit: 4
});
Expand Down
1 change: 1 addition & 0 deletions mode/ruby/ruby.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ CodeMirror.defineMode("ruby", function(config) {
if (ch == "`" || ch == "'" || ch == '"') {
return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state);
} else if (ch == "/" && !stream.eol() && stream.peek() != " ") {
if (stream.eat("=")) return "operator";
return chain(readQuoted(ch, "string-2", true), stream, state);
} else if (ch == "%") {
var style = "string", embed = true;
Expand Down
11 changes: 11 additions & 0 deletions mode/ruby/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(function() {
var mode = CodeMirror.getMode({indentUnit: 2}, "ruby");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }

MT("divide_equal_operator",
"[variable bar] [operator /=] [variable foo]");

MT("divide_equal_operator_no_spacing",
"[variable foo][operator /=][number 42]");

})();
3 changes: 1 addition & 2 deletions mode/rust/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ <h2>Rust mode</h2>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
tabMode: "indent"
lineNumbers: true
});
</script>

Expand Down
57 changes: 57 additions & 0 deletions mode/solr/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!doctype html>

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

<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="solr.js"></script>
<style type="text/css">
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}

.CodeMirror .cm-operator {
color: orange;
}
</style>
<div id=nav>
<a href="http://codemirror.net"><img id=logo src="../../doc/logo.png"></a>

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

<article>
<h2>Solr mode</h2>

<div>
<textarea id="code" name="code">author:Camus

title:"The Rebel" and author:Camus

philosophy:Existentialism -author:Kierkegaard

hardToSpell:Dostoevsky~

published:[194* TO 1960] and author:(Sartre or "Simone de Beauvoir")</textarea>
</div>

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

<p><strong>MIME types defined:</strong> <code>text/x-solr</code>.</p>
</article>
89 changes: 89 additions & 0 deletions mode/solr/solr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
CodeMirror.defineMode("solr", function() {
"use strict";

var isStringChar = /[^\s\|\!\+\-\*\?\~\^\&\:\(\)\[\]\{\}\^\"\\]/;
var isOperatorChar = /[\|\!\+\-\*\?\~\^\&]/;
var isOperatorString = /^(OR|AND|NOT|TO)$/i;

function isNumber(word) {
return parseFloat(word, 10).toString() === word;
}

function tokenString(quote) {
return function(stream, state) {
var escaped = false, next;
while ((next = stream.next()) != null) {
if (next == quote && !escaped) break;
escaped = !escaped && next == "\\";
}

if (!escaped) state.tokenize = tokenBase;
return "string";
};
}

function tokenOperator(operator) {
return function(stream, state) {
var style = "operator";
if (operator == "+")
style += " positive";
else if (operator == "-")
style += " negative";
else if (operator == "|")
stream.eat(/\|/);
else if (operator == "&")
stream.eat(/\&/);
else if (operator == "^")
style += " boost";

state.tokenize = tokenBase;
return style;
};
}

function tokenWord(ch) {
return function(stream, state) {
var word = ch;
while ((ch = stream.peek()) && ch.match(isStringChar) != null) {
word += stream.next();
}

state.tokenize = tokenBase;
if (isOperatorString.test(word))
return "operator";
else if (isNumber(word))
return "number";
else if (stream.peek() == ":")
return "field";
else
return "string";
};
}

function tokenBase(stream, state) {
var ch = stream.next();
if (ch == '"')
state.tokenize = tokenString(ch);
else if (isOperatorChar.test(ch))
state.tokenize = tokenOperator(ch);
else if (isStringChar.test(ch))
state.tokenize = tokenWord(ch);

return (state.tokenize != tokenBase) ? state.tokenize(stream, state) : null;
}

return {
startState: function() {
return {
tokenize: tokenBase
};
},

token: function(stream, state) {
if (stream.eatSpace()) return null;
return state.tokenize(stream, state);
}
};
});

CodeMirror.defineMIME("text/x-solr", "solr");
1 change: 0 additions & 1 deletion mode/sparql/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ <h2>SPARQL mode</h2>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "application/x-sparql-query",
tabMode: "indent",
matchBrackets: true
});
</script>
Expand Down
4 changes: 2 additions & 2 deletions mode/sql/sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
CodeMirror.defineMIME("text/x-plsql", {
name: "sql",
client: set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),
keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),
builtin: set("abs acos add_months ascii asin atan atan2 average bfile bfilename bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv varchar varchar2 variance varying vsize xml"),
keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),
builtin: set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"),
operatorChars: /^[*+\-%<>!=~]/,
dateSQL: set("date time timestamp"),
support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
Expand Down
15 changes: 7 additions & 8 deletions mode/tcl/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ <h2>Tcl mode</h2>
proc whois::311 {from key text} {
if {[regexp -- {^[^\s]+\s(.+?)\s(.+?)\s(.+?)\s\*\s\:(.+)$} $text wholematch nick ident host realname]} {
putserv "PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Host:${whois::textf} \
$nick \(${ident}@${host}\) ${whois::tagf}Realname:${whois::textf} $realname"
$nick \(${ident}@${host}\) ${whois::tagf}Realname:${whois::textf} $realname"
}
}
proc whois::multi {from key text} {
if {[regexp {\:(.*)$} $text match $key]} {
putserv "PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Note:${whois::textf} [subst $$key]"
return 1
return 1
}
}
proc whois::312 {from key text} {
Expand All @@ -98,7 +98,7 @@ <h2>Tcl mode</h2>
proc whois::317 {from key text} {
if {[regexp -- {.*\s(\d+)\s(\d+)\s\:} $text wholematch idle signon]} {
putserv "PRIVMSG $whois::channel :${whois::logo} ${whois::tagf}Connected:${whois::textf} \
[ctime $signon] ${whois::tagf}Idle:${whois::textf} [duration $idle]"
[ctime $signon] ${whois::tagf}Idle:${whois::textf} [duration $idle]"
}
}
proc whois::301 {from key text} {
Expand All @@ -108,7 +108,7 @@ <h2>Tcl mode</h2>
}
proc whois::318 {from key text} {
namespace eval whois {
variable channel ""
variable channel ""
}
variable whois::channel ""
}
Expand All @@ -118,8 +118,8 @@ <h2>Tcl mode</h2>
proc whois::list {nick host hand chan text} {
if {[lsearch -exact [channel info $chan] "+${whois::command}"] != -1} {
namespace eval whois {
variable channel ""
}
variable channel ""
}
variable whois::channel $chan
putserv "WHOIS $text"
}
Expand All @@ -129,8 +129,7 @@ <h2>Tcl mode</h2>
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
tabMode: "indent",
theme: "night",
theme: "night",
lineNumbers: true,
indentUnit: 2,
scrollPastEnd: true,
Expand Down
1 change: 0 additions & 1 deletion mode/tiddlywiki/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ <h2>TiddlyWiki mode</h2>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: 'tiddlywiki',
lineNumbers: true,
enterMode: 'keep',
matchBrackets: true
});
</script>
Expand Down
1 change: 0 additions & 1 deletion mode/turtle/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ <h2>Turtle mode</h2>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "text/turtle",
tabMode: "indent",
matchBrackets: true
});
</script>
Expand Down
3 changes: 1 addition & 2 deletions mode/vb/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ <h2>VB.NET mode</h2>
editor = CodeMirror.fromTextArea(document.getElementById("solution"), {
lineNumbers: true,
mode: "text/x-vb",
readOnly: false,
tabMode: "shift"
readOnly: false
});
runTest();
}
Expand Down
1 change: 0 additions & 1 deletion mode/velocity/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ <h2>Velocity mode</h2>
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
tabMode: "indent",
theme: "night",
lineNumbers: true,
indentUnit: 4,
Expand Down
3 changes: 2 additions & 1 deletion mode/xml/xml.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
CodeMirror.defineMode("xml", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag || true;
var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag;
if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true;

var Kludges = parserConfig.htmlMode ? {
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codemirror",
"version":"3.21.0",
"version":"3.22.0",
"main": "lib/codemirror.js",
"description": "In-browser code editing made bearable",
"licenses": [{"type": "MIT",
Expand Down
1 change: 1 addition & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ <h2>Test Suite</h2>
<script src="../mode/xml/xml.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<script src="../mode/ruby/ruby.js"></script>
<script src="../mode/ruby/test.js"></script>
<script src="../mode/haml/haml.js"></script>
<script src="../mode/haml/test.js"></script>
<script src="../mode/markdown/markdown.js"></script>
Expand Down
37 changes: 32 additions & 5 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,15 +172,15 @@ test("core_defaults", function() {
for (var opt in defs) defsCopy[opt] = defs[opt];
defs.indentUnit = 5;
defs.value = "uu";
defs.enterMode = "keep";
defs.indentWithTabs = true;
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.getOption("indentWithTabs"), true);
eq(cm.getInputField().tabIndex, 55);
}
finally {
Expand Down Expand Up @@ -1133,13 +1133,32 @@ testCM("groupMovementCommands", function(cm) {
cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight");
eqPos(cm.getCursor(), Pos(0, 20));
cm.execCommand("goGroupRight");
eqPos(cm.getCursor(), Pos(1, 0));
cm.execCommand("goGroupRight");
eqPos(cm.getCursor(), Pos(1, 2));
cm.execCommand("goGroupRight");
eqPos(cm.getCursor(), Pos(1, 5));
cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft");
eqPos(cm.getCursor(), Pos(1, 0));
cm.execCommand("goGroupLeft");
eqPos(cm.getCursor(), Pos(0, 20));
cm.execCommand("goGroupLeft");
eqPos(cm.getCursor(), Pos(0, 16));
}, {value: "booo ba---quux. ffff\n abc d"});

testCM("groupsAndWhitespace", function(cm) {
var positions = [Pos(0, 0), Pos(0, 2), Pos(0, 5), Pos(0, 9), Pos(0, 11),
Pos(1, 0), Pos(1, 2), Pos(1, 5)];
for (var i = 1; i < positions.length; i++) {
cm.execCommand("goGroupRight");
eqPos(cm.getCursor(), positions[i]);
}
for (var i = positions.length - 2; i >= 0; i--) {
cm.execCommand("goGroupLeft");
eqPos(cm.getCursor(), i == 2 ? Pos(0, 6) : positions[i]);
}
}, {value: " foo +++ \n bar"});

testCM("charMovementCommands", function(cm) {
cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft");
eqPos(cm.getCursor(), Pos(0, 0));
Expand Down Expand Up @@ -1625,8 +1644,9 @@ testCM("change_removedText", function(cm) {
testCM("lineStyleFromMode", function(cm) {
CodeMirror.defineMode("test_mode", function() {
return {token: function(stream) {
if (stream.match(/^\[[^\]]*\]/)) return "line-brackets";
if (stream.match(/^\([^\]]*\)/)) return "line-background-parens";
if (stream.match(/^\[[^\]]*\]/)) return " line-brackets ";
if (stream.match(/^\([^\)]*\)/)) return " line-background-parens ";
if (stream.match(/^<[^>]*>/)) return " span line-line line-background-bg ";
stream.match(/^\s+|^\S+/);
}};
});
Expand All @@ -1639,7 +1659,14 @@ testCM("lineStyleFromMode", function(cm) {
eq(parenElts.length, 1);
eq(parenElts[0].nodeName, "DIV");
is(!/parens.*parens/.test(parenElts[0].className));
}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: nothing"});
eq(parenElts[0].parentElement.nodeName, "DIV");

eq(byClassName(cm.getWrapperElement(), "bg").length, 1);
eq(byClassName(cm.getWrapperElement(), "line").length, 1);
var spanElts = byClassName(cm.getWrapperElement(), "cm-span");
eq(spanElts.length, 2);
is(/^\s*cm-span\s*$/.test(spanElts[0].className));
}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: <tag> <tag>"});

CodeMirror.registerHelper("xxx", "a", "A");
CodeMirror.registerHelper("xxx", "b", "B");
Expand Down
182 changes: 181 additions & 1 deletion test/vim_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ testJumplist('jumplist_skip_delted_mark<c-o>',
testJumplist('jumplist_skip_delted_mark<c-i>',
['*', 'n', 'n', 'k', 'd', 'k', '<C-o>', '<C-i>', '<C-i>'],
[1,0], [0,2]);

/**
* @param name Name of the test
* @param keys An array of keys or a string with a single key to simulate.
Expand Down Expand Up @@ -997,6 +998,40 @@ testEdit('diW_end_spc', 'foo \tbAr', /A/, 'diW', 'foo \t');
testEdit('daW_end_spc', 'foo \tbAr', /A/, 'daW', 'foo');
testEdit('diW_end_punct', 'foo \tbAr.', /A/, 'diW', 'foo \t');
testEdit('daW_end_punct', 'foo \tbAr.', /A/, 'daW', 'foo');
// Deleting text objects
// Open and close on same line
testEdit('di(_open_spc', 'foo (bAr) baz', /\(/, 'di(', 'foo () baz');
testEdit('di)_open_spc', 'foo (bAr) baz', /\(/, 'di)', 'foo () baz');
testEdit('da(_open_spc', 'foo (bAr) baz', /\(/, 'da(', 'foo baz');
testEdit('da)_open_spc', 'foo (bAr) baz', /\(/, 'da)', 'foo baz');

testEdit('di(_middle_spc', 'foo (bAr) baz', /A/, 'di(', 'foo () baz');
testEdit('di)_middle_spc', 'foo (bAr) baz', /A/, 'di)', 'foo () baz');
testEdit('da(_middle_spc', 'foo (bAr) baz', /A/, 'da(', 'foo baz');
testEdit('da)_middle_spc', 'foo (bAr) baz', /A/, 'da)', 'foo baz');

testEdit('di(_close_spc', 'foo (bAr) baz', /\)/, 'di(', 'foo () baz');
testEdit('di)_close_spc', 'foo (bAr) baz', /\)/, 'di)', 'foo () baz');
testEdit('da(_close_spc', 'foo (bAr) baz', /\)/, 'da(', 'foo baz');
testEdit('da)_close_spc', 'foo (bAr) baz', /\)/, 'da)', 'foo baz');

// Open and close on different lines, equally indented
testEdit('di{_middle_spc', 'a{\n\tbar\n}b', /r/, 'di{', 'a{}b');
testEdit('di}_middle_spc', 'a{\n\tbar\n}b', /r/, 'di}', 'a{}b');
testEdit('da{_middle_spc', 'a{\n\tbar\n}b', /r/, 'da{', 'ab');
testEdit('da}_middle_spc', 'a{\n\tbar\n}b', /r/, 'da}', 'ab');

// open and close on diff lines, open indented less than close
testEdit('di{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di{', 'a{}b');
testEdit('di}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di}', 'a{}b');
testEdit('da{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da{', 'ab');
testEdit('da}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da}', 'ab');

// open and close on diff lines, open indented more than close
testEdit('di[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di[', 'a\t[]b');
testEdit('di]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di]', 'a\t[]b');
testEdit('da[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da[', 'a\tb');
testEdit('da]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da]', 'a\tb');

// Operator-motion tests
testVim('D', function(cm, vim, helpers) {
Expand Down Expand Up @@ -1538,6 +1573,13 @@ testVim('/_case', function(cm, vim, helpers) {
helpers.doKeys('/');
helpers.assertCursorAt(1, 6);
}, { value: 'match nope match \n nope Match' });
testVim('/_2', function(cm, vim, helpers) {
cm.openDialog = helpers.fakeOpenDialog('\\(word\\)\\{2}');
helpers.doKeys('/');
helpers.assertCursorAt(1, 9);
helpers.doKeys('n');
helpers.assertCursorAt(2, 1);
}, { value: 'word\n another wordword\n wordwordword\n' });
testVim('/_nongreedy', function(cm, vim, helpers) {
cm.openDialog = helpers.fakeOpenDialog('aa');
helpers.doKeys('/');
Expand Down Expand Up @@ -2003,6 +2045,53 @@ testVim('zt==z<CR>', function(cm, vim, helpers){
eq(zVals[2], zVals[5]);
});

var moveTillCharacterSandbox =
'The quick brown fox \n'
'jumped over the lazy dog.'
testVim('moveTillCharacter', function(cm, vim, helpers){
cm.setCursor(0, 0);
// Search for the 'q'.
cm.openDialog = helpers.fakeOpenDialog('q');
helpers.doKeys('/');
eq(4, cm.getCursor().ch);
// Jump to just before the first o in the list.
helpers.doKeys('t');
helpers.doKeys('o');
eq('The quick brown fox \n', cm.getValue());
// Delete that one character.
helpers.doKeys('d');
helpers.doKeys('t');
helpers.doKeys('o');
eq('The quick bown fox \n', cm.getValue());
// Delete everything until the next 'o'.
helpers.doKeys('.');
eq('The quick box \n', cm.getValue());
// An unmatched character should have no effect.
helpers.doKeys('d');
helpers.doKeys('t');
helpers.doKeys('q');
eq('The quick box \n', cm.getValue());
// Matches should only be possible on single lines.
helpers.doKeys('d');
helpers.doKeys('t');
helpers.doKeys('z');
eq('The quick box \n', cm.getValue());
// After all that, the search for 'q' should still be active, so the 'N' command
// can run it again in reverse. Use that to delete everything back to the 'q'.
helpers.doKeys('d');
helpers.doKeys('N');
eq('The ox \n', cm.getValue());
eq(4, cm.getCursor().ch);
}, { value: moveTillCharacterSandbox});
testVim('searchForPipe', function(cm, vim, helpers){
cm.setCursor(0, 0);
// Search for the '|'.
cm.openDialog = helpers.fakeOpenDialog('|');
helpers.doKeys('/');
eq(4, cm.getCursor().ch);
}, { value: 'this|that'});


var scrollMotionSandbox =
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
Expand Down Expand Up @@ -2225,6 +2314,8 @@ testVim('ex_sort_decimal_mixed_reverse', function(cm, vim, helpers) {
helpers.doEx('sort! d');
eq('a3\nb2\nc1\nz\ny', cm.getValue());
}, { value: 'a3\nz\nc1\ny\nb2'});


testVim('ex_substitute_same_line', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('s/one/two');
Expand All @@ -2249,9 +2340,40 @@ testVim('ex_substitute_visual_range', function(cm, vim, helpers) {
}, { value: '1\n2\n3\n4\n5' });
testVim('ex_substitute_capture', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('s/(\\d+)/$1$1/')
// \n should be a backreference.
helpers.doEx('s/\\(\\d+\\)/\\1\\1/')
eq('a1111 a1212 a1313', cm.getValue());
}, { value: 'a11 a12 a13' });
testVim('ex_substitute_capture2', function(cm, vim, helpers) {
cm.setCursor(1, 0);
// \n should be a backreference, even if followed by '$'
helpers.doEx('s/\\(\\d+\\)/$\\1\\1/')
eq('a $00 b', cm.getValue());
}, { value: 'a 0 b' });
testVim('ex_substitute_javascript', function(cm, vim, helpers) {
cm.setCursor(1, 0);
// Throw all the things that javascript likes to treat as special values
// into the replace part. All should be literal (this is VIM).
helpers.doEx('s/\\(\\d+\\)/$$ $\' $` $& \\1/')
eq('a $$ $\' $` $& 0 b', cm.getValue());
}, { value: 'a 0 b' });
testVim('ex_substitute_nocapture', function(cm, vim, helpers) {
cm.setCursor(1, 0);
// $n should be literal, since that is the javascript form, not VIM.
helpers.doEx('s/\\(\\d+\\)/$1$1/')
eq('a$1$1 a$1$1 a$1$1', cm.getValue());
}, { value: 'a11 a12 a13' });
testVim('ex_substitute_nocapture2', function(cm, vim, helpers) {
cm.setCursor(1, 0);
// \$n should be literal, since that is the javascript form, not VIM.
helpers.doEx('s/\\(\\d+\\)/\\$1\\1/')
eq('a $10 b', cm.getValue());
}, { value: 'a 0 b' });
testVim('ex_substitute_nocapture', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('s/b/$/')
eq('a $ c', cm.getValue());
}, { value: 'a b c' });
testVim('ex_substitute_empty_query', function(cm, vim, helpers) {
// If the query is empty, use last query.
cm.setCursor(1, 0);
Expand All @@ -2260,6 +2382,64 @@ testVim('ex_substitute_empty_query', function(cm, vim, helpers) {
helpers.doEx('s//b');
eq('abb ab2 ab3', cm.getValue());
}, { value: 'a11 a12 a13' });
testVim('ex_substitute_slash_regex', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('%s/\\//|');
eq('one|two \n three|four', cm.getValue());
}, { value: 'one/two \n three/four'});
testVim('ex_substitute_pipe_regex', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('%s/|/,');
eq('one,two \n three,four', cm.getValue());
}, { value: 'one|two \n three|four'});
testVim('ex_substitute_or_regex', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('%s/o\\|e\\|u/a');
eq('ana|twa \n thraa|faar', cm.getValue());
}, { value: 'one|two \n three|four'});
testVim('ex_substitute_or_word_regex', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('%s/\\(one\\|two\\)/five');
eq('five|five \n three|four', cm.getValue());
}, { value: 'one|two \n three|four'});
testVim('ex_substitute_backslashslash_regex', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('%s/\\\\/,');
eq('one,two \n three,four', cm.getValue());
}, { value: 'one\\two \n three\\four'});
testVim('ex_substitute_slash_replacement', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('%s/,/\\/');
eq('one/two \n three/four', cm.getValue());
}, { value: 'one,two \n three,four'});
testVim('ex_substitute_backslash_replacement', function(cm, vim, helpers) {
helpers.doEx('%s/,/\\\\/g');
eq('one\\two \n three\\four', cm.getValue());
}, { value: 'one,two \n three,four'});
testVim('ex_substitute_multibackslash_replacement', function(cm, vim, helpers) {
helpers.doEx('%s/,/\\\\\\\\\\\\\\\\/g'); // 16 backslashes.
eq('one\\\\\\\\two \n three\\\\\\\\four', cm.getValue()); // 2*8 backslashes.
}, { value: 'one,two \n three,four'});
testVim('ex_substitute_braces_word', function(cm, vim, helpers) {
helpers.doEx('%s/\\(ab\\)\\{2\\}//g');
eq('ab abb ab{2}', cm.getValue());
}, { value: 'ababab abb ab{2}'});
testVim('ex_substitute_braces_range', function(cm, vim, helpers) {
helpers.doEx('%s/a\\{2,3\\}//g');
eq('a a', cm.getValue());
}, { value: 'a aa aaa aaaa'});
testVim('ex_substitute_braces_literal', function(cm, vim, helpers) {
helpers.doEx('%s/ab{2}//g');
eq('ababab abb ', cm.getValue());
}, { value: 'ababab abb ab{2}'});
testVim('ex_substitute_braces_char', function(cm, vim, helpers) {
helpers.doEx('%s/ab\\{2\\}//g');
eq('ababab ab{2}', cm.getValue());
}, { value: 'ababab abb ab{2}'});
testVim('ex_substitute_braces_no_escape', function(cm, vim, helpers) {
helpers.doEx('%s/ab\\{2}//g');
eq('ababab ab{2}', cm.getValue());
}, { value: 'ababab abb ab{2}'});
testVim('ex_substitute_count', function(cm, vim, helpers) {
cm.setCursor(1, 0);
helpers.doEx('s/\\d/0/i 2');
Expand Down
44 changes: 44 additions & 0 deletions theme/mdn-like.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
MDN-LIKE Theme - Mozilla
Ported to CodeMirror by Peter Kroon <plakroon@gmail.com>
Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues
GitHub: @peterkroon
The mdn-like theme is inspired on the displayed code examples at: https://developer.mozilla.org/en-US/docs/Web/CSS/animation
*/
.cm-s-mdn-like.CodeMirror { color: #999; font-family: monospace; background-color: #fff; }
.cm-s-mdn-like .CodeMirror-selected { background: #cfc !important; }

.cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; border-left: 6px solid rgba(0,83,159,0.65); color: #333; }
.cm-s-mdn-like .CodeMirror-linenumber { color: #aaa; margin-left: 3px; }
div.cm-s-mdn-like .CodeMirror-cursor { border-left: 2px solid #222; }

.cm-s-mdn-like .cm-keyword { color: #6262FF; }
.cm-s-mdn-like .cm-atom { color: #F90; }
.cm-s-mdn-like .cm-number { color: #ca7841; }
.cm-s-mdn-like .cm-def { color: #8DA6CE; }
.cm-s-mdn-like span.cm-variable-2, .cm-s-mdn-like span.cm-tag { color: #690; }
.cm-s-mdn-like span.cm-variable-3, .cm-s-mdn-like span.cm-def { color: #07a; }

.cm-s-mdn-like .cm-variable { color: #07a; }
.cm-s-mdn-like .cm-property { color: #905; }
.cm-s-mdn-like .cm-qualifier { color: #690; }

.cm-s-mdn-like .cm-operator { color: #cda869; }
.cm-s-mdn-like .cm-comment { color:#777; font-weight:normal; }
.cm-s-mdn-like .cm-string { color:#07a; font-style:italic; }
.cm-s-mdn-like .cm-string-2 { color:#bd6b18; } /*?*/
.cm-s-mdn-like .cm-meta { color: #000; } /*?*/
.cm-s-mdn-like .cm-builtin { color: #9B7536; } /*?*/
.cm-s-mdn-like .cm-tag { color: #997643; }
.cm-s-mdn-like .cm-attribute { color: #d6bb6d; } /*?*/
.cm-s-mdn-like .cm-header { color: #FF6400; }
.cm-s-mdn-like .cm-hr { color: #AEAEAE; }
.cm-s-mdn-like .cm-link { color:#ad9361; font-style:italic; text-decoration:none; }
.cm-s-mdn-like .cm-error { border-bottom: 1px solid red; }

div.cm-s-mdn-like .CodeMirror-activeline-background {background: #efefff;}
div.cm-s-mdn-like span.CodeMirror-matchingbracket {outline:1px solid grey; color: inherit;}

.cm-s-mdn-like.CodeMirror { background-image: url(data:image/png;base64,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); }
1 change: 1 addition & 0 deletions theme/solarized.css