CodeMirror 5.2.1 update

* keep our custom css-lint.js, no changes in upstream
* use newer show-hint.js - {completeSingle: true} was broken in 5.2.0
* use newer search.js - replace() didn't use previous search query in 5.2.0
This commit is contained in:
tophf 2015-04-23 05:44:04 +03:00
parent 2e02b73c77
commit d25531977e
30 changed files with 644 additions and 335 deletions

View File

@ -1,6 +1,8 @@
/node_modules /node_modules
/npm-debug.log /npm-debug.log
test.html /test*.html
.tern-* .tern-*
*~ *~
*.swp *.swp
.idea
*.iml

View File

@ -1,3 +1,3 @@
language: node_js language: node_js
node_js: node_js:
- 0.8 - 0.10

View File

@ -25,6 +25,7 @@ Alexandre Bique
alexey-k alexey-k
Alex Piggott Alex Piggott
Aliaksei Chapyzhenka Aliaksei Chapyzhenka
Amin Shali
Amsul Amsul
amuntean amuntean
Amy Amy
@ -81,12 +82,16 @@ Cheah Chu Yeow
Chris Coyier Chris Coyier
Chris Granger Chris Granger
Chris Houseknecht Chris Houseknecht
Chris Lohfink
Chris Morgan Chris Morgan
Christian Oyarzun Christian Oyarzun
Christian Petrov Christian Petrov
Christopher Brown Christopher Brown
Christopher Mitchell
Christopher Pfohl
ciaranj ciaranj
CodeAnimal CodeAnimal
coderaiser
ComFreek ComFreek
Curtis Gagliardi Curtis Gagliardi
dagsta dagsta
@ -105,6 +110,7 @@ Danny Yoo
darealshinji darealshinji
Darius Roberts Darius Roberts
Dave Myers Dave Myers
David Barnett
David Mignot David Mignot
David Pathakjee David Pathakjee
David Vázquez David Vázquez
@ -181,6 +187,7 @@ ilvalle
Ingo Richter Ingo Richter
Irakli Gozalishvili Irakli Gozalishvili
Ivan Kurnosov Ivan Kurnosov
Ivoah
Jacob Lee Jacob Lee
Jakob Miland Jakob Miland
Jakub Vrana Jakub Vrana
@ -212,6 +219,7 @@ John Connor
John Lees-Miller John Lees-Miller
John Snelson John Snelson
John Van Der Loo John Van Der Loo
Jonas Döbertin
Jonathan Malmaud Jonathan Malmaud
jongalloway jongalloway
Jon Malmaud Jon Malmaud
@ -222,6 +230,7 @@ Joshua Newman
Josh Watzman Josh Watzman
jots jots
jsoojeon jsoojeon
ju1ius
Juan Benavides Romero Juan Benavides Romero
Jucovschi Constantin Jucovschi Constantin
Juho Vuori Juho Vuori
@ -230,6 +239,7 @@ jwallers@gmail.com
kaniga kaniga
Ken Newman Ken Newman
Ken Rockot Ken Rockot
Kevin Earls
Kevin Sawicki Kevin Sawicki
Kevin Ushey Kevin Ushey
Klaus Silveira Klaus Silveira
@ -248,6 +258,8 @@ Leonid Khachaturov
Leon Sorokin Leon Sorokin
Leonya Khachaturov Leonya Khachaturov
Liam Newman Liam Newman
Libo Cannici
LloydMilligan
LM LM
lochel lochel
Lorenzo Stoakes Lorenzo Stoakes
@ -272,6 +284,7 @@ Marko Bonaci
Martin Balek Martin Balek
Martín Gaitán Martín Gaitán
Martin Hasoň Martin Hasoň
Martin Hunt
Mason Malone Mason Malone
Mateusz Paprocki Mateusz Paprocki
Mathias Bynens Mathias Bynens
@ -290,6 +303,7 @@ Max Xiantu
mbarkhau mbarkhau
Metatheos Metatheos
Micah Dubinko Micah Dubinko
Michael Grey
Michael Lehenbauer Michael Lehenbauer
Michael Zhou Michael Zhou
Mighty Guava Mighty Guava
@ -306,6 +320,7 @@ misfo
mloginov mloginov
Moritz Schwörer Moritz Schwörer
mps mps
ms
mtaran-google mtaran-google
Narciso Jaramillo Narciso Jaramillo
Nathan Williams Nathan Williams
@ -317,6 +332,7 @@ nguillaumin
Ng Zhi An Ng Zhi An
Nicholas Bollweg Nicholas Bollweg
Nicholas Bollweg (Nick) Nicholas Bollweg (Nick)
Nick Kreeger
Nick Small Nick Small
Niels van Groningen Niels van Groningen
nightwing nightwing
@ -331,6 +347,7 @@ pablo
Page Page
Panupong Pasupat Panupong Pasupat
paris paris
Paris
Patil Arpith Patil Arpith
Patrick Stoica Patrick Stoica
Patrick Strawderman Patrick Strawderman
@ -351,6 +368,7 @@ Randall Mason
Randy Burden Randy Burden
Randy Edmunds Randy Edmunds
Rasmus Erik Voel Jensen Rasmus Erik Voel Jensen
ray ratchup
Ray Ratchup Ray Ratchup
Richard van der Meer Richard van der Meer
Richard Z.H. Wang Richard Z.H. Wang

View File

@ -1,7 +1,7 @@
# CodeMirror # CodeMirror
[![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror) [![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror)
[![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror) [![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror)
[Funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png)](https://marijnhaverbeke.nl/fund/) [Funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?again)](https://marijnhaverbeke.nl/fund/)
CodeMirror is a JavaScript component that provides a code editor in CodeMirror is a JavaScript component that provides a code editor in
the browser. When a mode is available for the language you are coding the browser. When a mode is available for the language you are coding

View File

@ -1,11 +1,11 @@
.CodeMirror-dialog { .CodeMirror-dialog {
position: absolute; position: absolute;
left: 0; right: 0; left: 0; right: 0;
background: white; background: inherit;
z-index: 15; z-index: 15;
padding: .1em .8em; padding: .1em .8em;
overflow: hidden; overflow: hidden;
color: #333; color: inherit;
} }
.CodeMirror-dialog-top { .CodeMirror-dialog-top {

View File

@ -58,8 +58,10 @@
if (inp) { if (inp) {
if (options.value) { if (options.value) {
inp.value = options.value; inp.value = options.value;
if (options.selectValueOnOpen !== false) {
inp.select(); inp.select();
} }
}
if (options.onInput) if (options.onInput)
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);}); CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});

View File

@ -52,7 +52,7 @@
function isFolded(cm, line) { function isFolded(cm, line) {
var marks = cm.findMarksAt(Pos(line)); var marks = cm.findMarksAt(Pos(line));
for (var i = 0; i < marks.length; ++i) for (var i = 0; i < marks.length; ++i)
if (marks[i].__isFold && marks[i].find().from.line == line) return true; if (marks[i].__isFold && marks[i].find().from.line == line) return marks[i];
} }
function marker(spec) { function marker(spec) {
@ -98,7 +98,9 @@
if (!state) return; if (!state) return;
var opts = state.options; var opts = state.options;
if (gutter != opts.gutter) return; if (gutter != opts.gutter) return;
cm.foldCode(Pos(line, 0), opts.rangeFinder); var folded = isFolded(cm, line);
if (folded) folded.clear();
else cm.foldCode(Pos(line, 0), opts.rangeFinder);
} }
function onChange(cm) { function onChange(cm) {

View File

@ -20,6 +20,10 @@
var inner = CodeMirror.innerMode(cm.getMode(), token.state); var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "css") return; if (inner.mode.name != "css") return;
if (token.type == "keyword" && "!important".indexOf(token.string) == 0)
return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start),
to: CodeMirror.Pos(cur.line, token.end)};
var start = token.start, end = cur.ch, word = token.string.slice(0, end - start); var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);
if (/[^\w$_-]/.test(word)) { if (/[^\w$_-]/.test(word)) {
word = ""; start = end = cur.ch; word = ""; start = end = cur.ch;

View File

@ -24,44 +24,44 @@
return cm.showHint(newOpts); return cm.showHint(newOpts);
}; };
var asyncRunID = 0;
function retrieveHints(getter, cm, options, then) {
if (getter.async) {
var id = ++asyncRunID;
getter(cm, function(hints) {
if (asyncRunID == id) then(hints);
}, options);
} else {
then(getter(cm, options));
}
}
CodeMirror.defineExtension("showHint", function(options) { CodeMirror.defineExtension("showHint", function(options) {
// We want a single cursor position. // We want a single cursor position.
if (this.listSelections().length > 1 || this.somethingSelected()) return; if (this.listSelections().length > 1 || this.somethingSelected()) return;
if (this.state.completionActive) this.state.completionActive.close(); if (this.state.completionActive) this.state.completionActive.close();
var completion = this.state.completionActive = new Completion(this, options); var completion = this.state.completionActive = new Completion(this, options);
var getHints = completion.options.hint; if (!completion.options.hint) return;
if (!getHints) return;
CodeMirror.signal(this, "startCompletion", this); CodeMirror.signal(this, "startCompletion", this);
return retrieveHints(getHints, this, completion.options, function(hints) { completion.showHints(hints); }); completion.update(true);
}); });
function Completion(cm, options) { function Completion(cm, options) {
this.cm = cm; this.cm = cm;
this.options = this.buildOptions(options); this.options = this.buildOptions(options);
this.widget = this.onClose = null; this.widget = null;
this.debounce = 0;
this.tick = 0;
this.startPos = this.cm.getCursor();
this.startLen = this.cm.getLine(this.startPos.line).length;
var self = this;
cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
} }
var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
return setTimeout(fn, 1000/60);
};
var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
Completion.prototype = { Completion.prototype = {
close: function() { close: function() {
if (!this.active()) return; if (!this.active()) return;
this.cm.state.completionActive = null; this.cm.state.completionActive = null;
this.tick = null;
this.cm.off("cursorActivity", this.activityFunc);
if (this.widget) this.widget.close(); if (this.widget) this.widget.close();
if (this.onClose) this.onClose();
CodeMirror.signal(this.cm, "endCompletion", this.cm); CodeMirror.signal(this.cm, "endCompletion", this.cm);
}, },
@ -78,70 +78,46 @@
this.close(); this.close();
}, },
showHints: function(data) { cursorActivity: function() {
if (!data || !data.list.length || !this.active()) return this.close(); if (this.debounce) {
cancelAnimationFrame(this.debounce);
this.debounce = 0;
}
if (this.options.completeSingle && data.list.length == 1) var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
this.pick(data, 0); if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
else pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
this.showWidget(data); (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
this.close();
} else {
var self = this;
this.debounce = requestAnimationFrame(function() {self.update();});
if (this.widget) this.widget.disable();
}
}, },
showWidget: function(data) { update: function(first) {
this.widget = new Widget(this, data); if (this.tick == null) return;
CodeMirror.signal(data, "shown"); if (this.data) CodeMirror.signal(this.data, "update");
if (!this.options.hint.async) {
var debounce = 0, completion = this, finished; this.finishUpdate(this.options.hint(this.cm, this.options), first);
var closeOn = this.options.closeCharacters;
var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length;
var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
return setTimeout(fn, 1000/60);
};
var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
function done() {
if (finished) return;
finished = true;
completion.close();
completion.cm.off("cursorActivity", activity);
if (data) CodeMirror.signal(data, "close");
}
function update() {
if (finished) return;
CodeMirror.signal(data, "update");
retrieveHints(completion.options.hint, completion.cm, completion.options, finishUpdate);
}
function finishUpdate(data_) {
data = data_;
if (finished) return;
if (!data || !data.list.length) return done();
if (completion.widget) completion.widget.close();
completion.widget = new Widget(completion, data);
}
function clearDebounce() {
if (debounce) {
cancelAnimationFrame(debounce);
debounce = 0;
}
}
function activity() {
clearDebounce();
var pos = completion.cm.getCursor(), line = completion.cm.getLine(pos.line);
if (pos.line != startPos.line || line.length - pos.ch != startLen - startPos.ch ||
pos.ch < startPos.ch || completion.cm.somethingSelected() ||
(pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) {
completion.close();
} else { } else {
debounce = requestAnimationFrame(update); var myTick = ++this.tick, self = this;
if (completion.widget) completion.widget.close(); this.options.hint(this.cm, function(data) {
if (self.tick == myTick) self.finishUpdate(data, first);
}, this.options);
} }
},
finishUpdate: function(data, first) {
this.data = data;
var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
if (this.widget) this.widget.close();
if (data && data.list.length) {
if (picked && data.list.length == 1) this.pick(data, 0);
else this.widget = new Widget(this, data);
} }
this.cm.on("cursorActivity", activity);
this.onClose = done;
}, },
buildOptions: function(options) { buildOptions: function(options) {
@ -206,6 +182,7 @@
function Widget(completion, data) { function Widget(completion, data) {
this.completion = completion; this.completion = completion;
this.data = data; this.data = data;
this.picked = false;
var widget = this, cm = completion.cm; var widget = this, cm = completion.cm;
var hints = this.hints = document.createElement("ul"); var hints = this.hints = document.createElement("ul");
@ -320,6 +297,13 @@
cm.off("scroll", this.onScroll); cm.off("scroll", this.onScroll);
}, },
disable: function() {
this.completion.cm.removeKeyMap(this.keyMap);
var widget = this;
this.keyMap = {Enter: function() { widget.picked = true; }};
this.completion.cm.addKeyMap(this.keyMap);
},
pick: function() { pick: function() {
this.completion.pick(this.data, this.selectedHint); this.completion.pick(this.data, this.selectedHint);
}, },

View File

@ -163,6 +163,7 @@
function onChange(cm) { function onChange(cm) {
var state = cm.state.lint; var state = cm.state.lint;
if (!state) return;
clearTimeout(state.timeout); clearTimeout(state.timeout);
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
} }
@ -188,6 +189,7 @@
clearMarks(cm); clearMarks(cm);
cm.off("change", onChange); cm.off("change", onChange);
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
clearTimeout(cm.state.lint.timeout);
delete cm.state.lint; delete cm.state.lint;
} }

View File

@ -68,15 +68,30 @@
var cm = this.cm, hScale = this.hScale; var cm = this.cm, hScale = this.hScale;
var frag = document.createDocumentFragment(), anns = this.annotations; var frag = document.createDocumentFragment(), anns = this.annotations;
var wrapping = cm.getOption("lineWrapping");
var singleLineH = wrapping && cm.defaultTextHeight() * 1.5;
var curLine = null, curLineObj = null;
function getY(pos, top) {
if (curLine != pos.line) {
curLine = pos.line;
curLineObj = cm.getLineHandle(curLine);
}
if (wrapping && curLineObj.height > singleLineH)
return cm.charCoords(pos, "local")[top ? "top" : "bottom"];
var topY = cm.heightAtLine(curLineObj, "local");
return topY + (top ? 0 : curLineObj.height);
}
if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) { if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {
var ann = anns[i]; var ann = anns[i];
var top = nextTop || cm.charCoords(ann.from, "local").top * hScale; var top = nextTop || getY(ann.from, true) * hScale;
var bottom = cm.charCoords(ann.to, "local").bottom * hScale; var bottom = getY(ann.to, false) * hScale;
while (i < anns.length - 1) { while (i < anns.length - 1) {
nextTop = cm.charCoords(anns[i + 1].from, "local").top * hScale; nextTop = getY(anns[i + 1].from, true) * hScale;
if (nextTop > bottom + .9) break; if (nextTop > bottom + .9) break;
ann = anns[++i]; ann = anns[++i];
bottom = cm.charCoords(ann.to, "local").bottom * hScale; bottom = getY(ann.to, false) * hScale;
} }
if (bottom == top) continue; if (bottom == top) continue;
var height = Math.max(bottom - top, 3); var height = Math.max(bottom - top, 3);

View File

@ -19,6 +19,7 @@
function SearchAnnotation(cm, query, caseFold, options) { function SearchAnnotation(cm, query, caseFold, options) {
this.cm = cm; this.cm = cm;
this.options = options;
var annotateOptions = {listenForChanges: false}; var annotateOptions = {listenForChanges: false};
for (var prop in options) annotateOptions[prop] = options[prop]; for (var prop in options) annotateOptions[prop] = options[prop];
if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match"; if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
@ -46,11 +47,12 @@
if (match.to.line >= this.gap.from) this.matches.splice(i--, 1); if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
} }
var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), this.caseFold); var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), this.caseFold);
var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;
while (cursor.findNext()) { while (cursor.findNext()) {
var match = {from: cursor.from(), to: cursor.to()}; var match = {from: cursor.from(), to: cursor.to()};
if (match.from.line >= this.gap.to) break; if (match.from.line >= this.gap.to) break;
this.matches.splice(i++, 0, match); this.matches.splice(i++, 0, match);
if (this.matches.length > MAX_MATCHES) break; if (this.matches.length > maxMatches) break;
} }
this.gap = null; this.gap = null;
}; };

View File

@ -39,7 +39,7 @@
} }
function SearchState() { function SearchState() {
this.posFrom = this.posTo = this.query = null; this.posFrom = this.posTo = this.lastQuery = this.query = null;
this.overlay = null; this.overlay = null;
} }
function getSearchState(cm) { function getSearchState(cm) {
@ -53,7 +53,7 @@
return cm.getSearchCursor(query, pos, queryCaseInsensitive(query)); return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
} }
function dialog(cm, text, shortText, deflt, f) { function dialog(cm, text, shortText, deflt, f) {
if (cm.openDialog) cm.openDialog(text, f, {value: deflt}); if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
else f(prompt(shortText, deflt)); else f(prompt(shortText, deflt));
} }
function confirmDialog(cm, text, shortText, fs) { function confirmDialog(cm, text, shortText, fs) {
@ -75,7 +75,8 @@
function doSearch(cm, rev) { function doSearch(cm, rev) {
var state = getSearchState(cm); var state = getSearchState(cm);
if (state.query) return findNext(cm, rev); if (state.query) return findNext(cm, rev);
dialog(cm, queryDialog, "Search for:", cm.getSelection(), function(query) { var q = cm.getSelection() || state.lastQuery;
dialog(cm, queryDialog, "Search for:", q, function(query) {
cm.operation(function() { cm.operation(function() {
if (!query || state.query) return; if (!query || state.query) return;
state.query = parseQuery(query); state.query = parseQuery(query);
@ -104,6 +105,7 @@
});} });}
function clearSearch(cm) {cm.operation(function() { function clearSearch(cm) {cm.operation(function() {
var state = getSearchState(cm); var state = getSearchState(cm);
state.lastQuery = state.query;
if (!state.query) return; if (!state.query) return;
state.query = null; state.query = null;
cm.removeOverlay(state.overlay); cm.removeOverlay(state.overlay);
@ -116,7 +118,8 @@
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>"; var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
function replace(cm, all) { function replace(cm, all) {
if (cm.getOption("readOnly")) return; if (cm.getOption("readOnly")) return;
dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) { var query = cm.getSelection() || getSearchState(cm).lastQuery;
dialog(cm, replaceQueryDialog, "Replace:", query, function(query) {
if (!query) return; if (!query) return;
query = parseQuery(query); query = parseQuery(query);
dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) { dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {

View File

@ -148,10 +148,10 @@
from: function() {if (this.atOccurrence) return this.pos.from;}, from: function() {if (this.atOccurrence) return this.pos.from;},
to: function() {if (this.atOccurrence) return this.pos.to;}, to: function() {if (this.atOccurrence) return this.pos.to;},
replace: function(newText) { replace: function(newText, origin) {
if (!this.atOccurrence) return; if (!this.atOccurrence) return;
var lines = CodeMirror.splitLines(newText); var lines = CodeMirror.splitLines(newText);
this.doc.replaceRange(lines, this.pos.from, this.pos.to); this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin);
this.pos.to = Pos(this.pos.from.line + lines.length - 1, this.pos.to = Pos(this.pos.from.line + lines.length - 1,
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)); lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "codemirror", "name": "codemirror",
"version":"5.0.0", "version":"5.2.1",
"main": ["lib/codemirror.js", "lib/codemirror.css"], "main": ["lib/codemirror.js", "lib/codemirror.css"],
"ignore": [ "ignore": [
"**/.*", "**/.*",
@ -11,6 +11,8 @@
"doc", "doc",
"test", "test",
"index.html", "index.html",
"package.json" "package.json",
"mode/*/*test.js",
"mode/*/*.html"
] ]
} }

View File

@ -96,7 +96,7 @@
</div> </div>
</div> </div>
<div class=actionsleft> <div class=actionsleft>
Get the current version: <a href="http://codemirror.net/codemirror.zip">5.0</a>.<br> Get the current version: <a href="http://codemirror.net/codemirror.zip">5.2</a>.<br>
You can see the <a href="https://github.com/codemirror/codemirror" title="Github repository">code</a> or<br> You can see the <a href="https://github.com/codemirror/codemirror" title="Github repository">code</a> or<br>
read the <a href="doc/releases.html">release notes</a>.<br> read the <a href="doc/releases.html">release notes</a>.<br>
There is a <a href="doc/compress.html">minification helper</a>. There is a <a href="doc/compress.html">minification helper</a>.
@ -119,7 +119,7 @@
<section id=features> <section id=features>
<h2>Features</h2> <h2>Features</h2>
<ul> <ul>
<li>Support for <a href="mode/index.html">over 90 languages</a> out of the box <li>Support for <a href="mode/index.html">over 100 languages</a> out of the box
<li>A powerful, <a href="mode/htmlmixed/index.html">composable</a> language mode <a href="doc/manual.html#modeapi">system</a> <li>A powerful, <a href="mode/htmlmixed/index.html">composable</a> language mode <a href="doc/manual.html#modeapi">system</a>
<li><a href="doc/manual.html#addon_show-hint">Autocompletion</a> (<a href="demo/xmlcomplete.html">XML</a>) <li><a href="doc/manual.html#addon_show-hint">Autocompletion</a> (<a href="demo/xmlcomplete.html">XML</a>)
<li><a href="doc/manual.html#addon_foldcode">Code folding</a> <li><a href="doc/manual.html#addon_foldcode">Code folding</a>

View File

@ -409,6 +409,19 @@
map[cK + ctrl + "Backspace"] = "delLineLeft"; map[cK + ctrl + "Backspace"] = "delLineLeft";
cmds[map["Backspace"] = "smartBackspace"] = function(cm) {
if (cm.somethingSelected()) return CodeMirror.Pass;
var cursor = cm.getCursor();
var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
if (toStartOfLine && !/\S/.test(toStartOfLine) && column % cm.getOption("indentUnit") == 0)
return cm.indentSelection("subtract");
else
return CodeMirror.Pass;
};
cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) { cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) {
cm.operation(function() { cm.operation(function() {
var ranges = cm.listSelections(); var ranges = cm.listSelections();

View File

@ -3,42 +3,16 @@
/** /**
* Supported keybindings: * Supported keybindings:
* Too many to list. Refer to defaultKeyMap below.
* *
* Motion: * Supported Ex commands:
* h, j, k, l * Refer to defaultExCommandMap below.
* gj, gk
* e, E, w, W, b, B, ge, gE
* f<character>, F<character>, t<character>, T<character>
* $, ^, 0, -, +, _
* gg, G
* %
* '<character>, `<character>
*
* Operator:
* d, y, c
* dd, yy, cc
* g~, g~g~
* >, <, >>, <<
*
* Operator-Motion:
* x, X, D, Y, C, ~
*
* Action:
* a, i, s, A, I, S, o, O
* zz, z., z<CR>, zt, zb, z-
* J
* u, Ctrl-r
* m<character>
* r<character>
*
* Modes:
* ESC - leave insert mode, visual mode, and clear input state.
* Ctrl-[, Ctrl-c - same as ESC.
* *
* Registers: unnamed, -, a-z, A-Z, 0-9 * Registers: unnamed, -, a-z, A-Z, 0-9
* (Does not respect the special case for number registers when delete * (Does not respect the special case for number registers when delete
* operator is made with these commands: %, (, ), , /, ?, n, N, {, } ) * operator is made with these commands: %, (, ), , /, ?, n, N, {, } )
* TODO: Implement the remaining registers. * TODO: Implement the remaining registers.
*
* Marks: a-z, A-Z, and 0-9 * Marks: a-z, A-Z, and 0-9
* TODO: Implement the remaining special marks. They have more complex * TODO: Implement the remaining special marks. They have more complex
* behavior. * behavior.
@ -57,6 +31,7 @@
* 6. Motion, operator, and action implementations * 6. Motion, operator, and action implementations
* 7. Helper functions for the key handler, motions, operators, and actions * 7. Helper functions for the key handler, motions, operators, and actions
* 8. Set up Vim to work as a keymap for CodeMirror. * 8. Set up Vim to work as a keymap for CodeMirror.
* 9. Ex command implementations.
*/ */
(function(mod) { (function(mod) {
@ -227,6 +202,34 @@
{ keys: ':', type: 'ex' } { keys: ':', type: 'ex' }
]; ];
/**
* Ex commands
* Care must be taken when adding to the default Ex command map. For any
* pair of commands that have a shared prefix, at least one of their
* shortNames must not match the prefix of the other command.
*/
var defaultExCommandMap = [
{ name: 'colorscheme', shortName: 'colo' },
{ name: 'map' },
{ name: 'imap', shortName: 'im' },
{ name: 'nmap', shortName: 'nm' },
{ name: 'vmap', shortName: 'vm' },
{ name: 'unmap' },
{ name: 'write', shortName: 'w' },
{ name: 'undo', shortName: 'u' },
{ name: 'redo', shortName: 'red' },
{ name: 'set', shortName: 'se' },
{ name: 'set', shortName: 'se' },
{ name: 'setlocal', shortName: 'setl' },
{ name: 'setglobal', shortName: 'setg' },
{ name: 'sort', shortName: 'sor' },
{ name: 'substitute', shortName: 's', possiblyAsync: true },
{ name: 'nohlsearch', shortName: 'noh' },
{ name: 'delmarks', shortName: 'delm' },
{ name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
{ name: 'global', shortName: 'g' }
];
var Pos = CodeMirror.Pos; var Pos = CodeMirror.Pos;
var Vim = function() { var Vim = function() {
@ -336,7 +339,11 @@
} }
var numberRegex = /[\d]/; var numberRegex = /[\d]/;
var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)]; var wordCharTest = [CodeMirror.isWordChar, function(ch) {
return ch && !CodeMirror.isWordChar(ch) && !/\s/.test(ch);
}], bigWordCharTest = [function(ch) {
return /\S/.test(ch);
}];
function makeKeyRange(start, size) { function makeKeyRange(start, size) {
var keys = []; var keys = [];
for (var i = start; i < start + size; i++) { for (var i = start; i < start + size; i++) {
@ -378,18 +385,30 @@
} }
var options = {}; var options = {};
function defineOption(name, defaultValue, type) { function defineOption(name, defaultValue, type, aliases, callback) {
if (defaultValue === undefined) { throw Error('defaultValue is required'); } if (defaultValue === undefined && !callback) {
throw Error('defaultValue is required unless callback is provided');
}
if (!type) { type = 'string'; } if (!type) { type = 'string'; }
options[name] = { options[name] = {
type: type, type: type,
defaultValue: defaultValue defaultValue: defaultValue,
callback: callback
}; };
if (aliases) {
for (var i = 0; i < aliases.length; i++) {
options[aliases[i]] = options[name];
}
}
if (defaultValue) {
setOption(name, defaultValue); setOption(name, defaultValue);
} }
}
function setOption(name, value) { function setOption(name, value, cm, cfg) {
var option = options[name]; var option = options[name];
cfg = cfg || {};
var scope = cfg.scope;
if (!option) { if (!option) {
throw Error('Unknown option: ' + name); throw Error('Unknown option: ' + name);
} }
@ -401,16 +420,59 @@
value = true; value = true;
} }
} }
if (option.callback) {
if (scope !== 'local') {
option.callback(value, undefined);
}
if (scope !== 'global' && cm) {
option.callback(value, cm);
}
} else {
if (scope !== 'local') {
option.value = option.type == 'boolean' ? !!value : value; option.value = option.type == 'boolean' ? !!value : value;
} }
if (scope !== 'global' && cm) {
cm.state.vim.options[name] = {value: value};
}
}
}
function getOption(name) { function getOption(name, cm, cfg) {
var option = options[name]; var option = options[name];
cfg = cfg || {};
var scope = cfg.scope;
if (!option) { if (!option) {
throw Error('Unknown option: ' + name); throw Error('Unknown option: ' + name);
} }
return option.value; if (option.callback) {
var local = cm && option.callback(undefined, cm);
if (scope !== 'global' && local !== undefined) {
return local;
} }
if (scope !== 'local') {
return option.callback();
}
return;
} else {
var local = (scope !== 'global') && (cm && cm.state.vim.options[name]);
return (local || (scope !== 'local') && option || {}).value;
}
}
defineOption('filetype', undefined, 'string', ['ft'], function(name, cm) {
// Option is local. Do nothing for global.
if (cm === undefined) {
return;
}
// The 'filetype' option proxies to the CodeMirror 'mode' option.
if (name === undefined) {
var mode = cm.getOption('mode');
return mode == 'null' ? '' : mode;
} else {
var mode = name == '' ? 'null' : name;
cm.setOption('mode', mode);
}
});
var createCircularJumpList = function() { var createCircularJumpList = function() {
var size = 100; var size = 100;
@ -564,8 +626,9 @@
visualBlock: false, visualBlock: false,
lastSelection: null, lastSelection: null,
lastPastedText: null, lastPastedText: null,
sel: { sel: {},
} // Buffer-local/window-local values of vim options.
options: {}
}; };
} }
return cm.state.vim; return cm.state.vim;
@ -623,6 +686,8 @@
// Add user defined key bindings. // Add user defined key bindings.
exCommandDispatcher.map(lhs, rhs, ctx); exCommandDispatcher.map(lhs, rhs, ctx);
}, },
// TODO: Expose setOption and getOption as instance methods. Need to decide how to namespace
// them, or somehow make them work with the existing CodeMirror setOption/getOption API.
setOption: setOption, setOption: setOption,
getOption: getOption, getOption: getOption,
defineOption: defineOption, defineOption: defineOption,
@ -1035,12 +1100,10 @@
break; break;
case 'search': case 'search':
this.processSearch(cm, vim, command); this.processSearch(cm, vim, command);
clearInputState(cm);
break; break;
case 'ex': case 'ex':
case 'keyToEx': case 'keyToEx':
this.processEx(cm, vim, command); this.processEx(cm, vim, command);
clearInputState(cm);
break; break;
default: default:
break; break;
@ -1133,6 +1196,7 @@
updateSearchQuery(cm, query, ignoreCase, smartCase); updateSearchQuery(cm, query, ignoreCase, smartCase);
} catch (e) { } catch (e) {
showConfirm(cm, 'Invalid regex: ' + query); showConfirm(cm, 'Invalid regex: ' + query);
clearInputState(cm);
return; return;
} }
commandDispatcher.processMotion(cm, vim, { commandDispatcher.processMotion(cm, vim, {
@ -1175,15 +1239,21 @@
} }
function onPromptKeyDown(e, query, close) { function onPromptKeyDown(e, query, close) {
var keyName = CodeMirror.keyName(e); var keyName = CodeMirror.keyName(e);
if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') { if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||
(keyName == 'Backspace' && query == '')) {
vimGlobalState.searchHistoryController.pushInput(query); vimGlobalState.searchHistoryController.pushInput(query);
vimGlobalState.searchHistoryController.reset(); vimGlobalState.searchHistoryController.reset();
updateSearchQuery(cm, originalQuery); updateSearchQuery(cm, originalQuery);
clearSearchHighlight(cm); clearSearchHighlight(cm);
cm.scrollTo(originalScrollPos.left, originalScrollPos.top); cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
CodeMirror.e_stop(e); CodeMirror.e_stop(e);
clearInputState(cm);
close(); close();
cm.focus(); cm.focus();
} else if (keyName == 'Ctrl-U') {
// Ctrl-U clears input.
CodeMirror.e_stop(e);
close('');
} }
} }
switch (command.searchArgs.querySrc) { switch (command.searchArgs.querySrc) {
@ -1244,10 +1314,12 @@
} }
function onPromptKeyDown(e, input, close) { function onPromptKeyDown(e, input, close) {
var keyName = CodeMirror.keyName(e), up; var keyName = CodeMirror.keyName(e), up;
if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') { if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||
(keyName == 'Backspace' && input == '')) {
vimGlobalState.exCommandHistoryController.pushInput(input); vimGlobalState.exCommandHistoryController.pushInput(input);
vimGlobalState.exCommandHistoryController.reset(); vimGlobalState.exCommandHistoryController.reset();
CodeMirror.e_stop(e); CodeMirror.e_stop(e);
clearInputState(cm);
close(); close();
cm.focus(); cm.focus();
} }
@ -1255,6 +1327,10 @@
up = keyName == 'Up' ? true : false; up = keyName == 'Up' ? true : false;
input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || ''; input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || '';
close(input); close(input);
} else if (keyName == 'Ctrl-U') {
// Ctrl-U clears input.
CodeMirror.e_stop(e);
close('');
} else { } else {
if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift') if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
vimGlobalState.exCommandHistoryController.reset(); vimGlobalState.exCommandHistoryController.reset();
@ -1284,8 +1360,8 @@
var registerName = inputState.registerName; var registerName = inputState.registerName;
var sel = vim.sel; var sel = vim.sel;
// TODO: Make sure cm and vim selections are identical outside visual mode. // TODO: Make sure cm and vim selections are identical outside visual mode.
var origHead = copyCursor(vim.visualMode ? sel.head: cm.getCursor('head')); var origHead = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.head): cm.getCursor('head'));
var origAnchor = copyCursor(vim.visualMode ? sel.anchor : cm.getCursor('anchor')); var origAnchor = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.anchor) : cm.getCursor('anchor'));
var oldHead = copyCursor(origHead); var oldHead = copyCursor(origHead);
var oldAnchor = copyCursor(origAnchor); var oldAnchor = copyCursor(origAnchor);
var newHead, newAnchor; var newHead, newAnchor;
@ -1851,10 +1927,11 @@
var anchor = ranges[0].anchor, var anchor = ranges[0].anchor,
head = ranges[0].head; head = ranges[0].head;
text = cm.getRange(anchor, head); text = cm.getRange(anchor, head);
if (!isWhiteSpaceString(text)) { var lastState = vim.lastEditInputState || {};
if (lastState.motion == "moveByWords" && !isWhiteSpaceString(text)) {
// Exclude trailing whitespace if the range is not all whitespace. // Exclude trailing whitespace if the range is not all whitespace.
var match = (/\s+$/).exec(text); var match = (/\s+$/).exec(text);
if (match) { if (match && lastState.motionArgs && lastState.motionArgs.forward) {
head = offsetCursor(head, 0, - match[0].length); head = offsetCursor(head, 0, - match[0].length);
text = text.slice(0, - match[0].length); text = text.slice(0, - match[0].length);
} }
@ -2628,9 +2705,6 @@
function lineLength(cm, lineNum) { function lineLength(cm, lineNum) {
return cm.getLine(lineNum).length; return cm.getLine(lineNum).length;
} }
function reverse(s){
return s.split('').reverse().join('');
}
function trim(s) { function trim(s) {
if (s.trim) { if (s.trim) {
return s.trim(); return s.trim();
@ -2944,59 +3018,38 @@
// Seek to first word or non-whitespace character, depending on if // Seek to first word or non-whitespace character, depending on if
// noSymbol is true. // noSymbol is true.
var textAfterIdx = line.substring(idx); var test = noSymbol ? wordCharTest[0] : bigWordCharTest [0];
var firstMatchedChar; while (!test(line.charAt(idx))) {
if (noSymbol) { idx++;
firstMatchedChar = textAfterIdx.search(/\w/); if (idx >= line.length) { return null; }
} else {
firstMatchedChar = textAfterIdx.search(/\S/);
} }
if (firstMatchedChar == -1) {
return null;
}
idx += firstMatchedChar;
textAfterIdx = line.substring(idx);
var textBeforeIdx = line.substring(0, idx);
var matchRegex;
// Greedy matchers for the "word" we are trying to expand.
if (bigWord) { if (bigWord) {
matchRegex = /^\S+/; test = bigWordCharTest[0];
} else { } else {
if ((/\w/).test(line.charAt(idx))) { test = wordCharTest[0];
matchRegex = /^\w+/; if (!test(line.charAt(idx))) {
} else { test = wordCharTest[1];
matchRegex = /^[^\w\s]+/;
} }
} }
var wordAfterRegex = matchRegex.exec(textAfterIdx); var end = idx, start = idx;
var wordStart = idx; while (test(line.charAt(end)) && end < line.length) { end++; }
var wordEnd = idx + wordAfterRegex[0].length; while (test(line.charAt(start)) && start >= 0) { start--; }
// TODO: Find a better way to do this. It will be slow on very long lines. start++;
var revTextBeforeIdx = reverse(textBeforeIdx);
var wordBeforeRegex = matchRegex.exec(revTextBeforeIdx);
if (wordBeforeRegex) {
wordStart -= wordBeforeRegex[0].length;
}
if (inclusive) { if (inclusive) {
// If present, trim all whitespace after word. // If present, include all whitespace after word.
// Otherwise, trim all whitespace before word. // Otherwise, include all whitespace before word, except indentation.
var textAfterWordEnd = line.substring(wordEnd); var wordEnd = end;
var whitespacesAfterWord = textAfterWordEnd.match(/^\s*/)[0].length; while (/\s/.test(line.charAt(end)) && end < line.length) { end++; }
if (whitespacesAfterWord > 0) { if (wordEnd == end) {
wordEnd += whitespacesAfterWord; var wordStart = start;
} else { while (/\s/.test(line.charAt(start - 1)) && start > 0) { start--; }
var revTrim = revTextBeforeIdx.length - wordStart; if (!start) { start = wordStart; }
var textBeforeWordStart = revTextBeforeIdx.substring(revTrim);
var whitespacesBeforeWord = textBeforeWordStart.match(/^\s*/)[0].length;
wordStart -= whitespacesBeforeWord;
} }
} }
return { start: Pos(cur.line, start), end: Pos(cur.line, end) };
return { start: Pos(cur.line, wordStart),
end: Pos(cur.line, wordEnd) };
} }
function recordJumpPosition(cm, oldCur, newCur) { function recordJumpPosition(cm, oldCur, newCur) {
@ -3154,7 +3207,7 @@
var pos = cur.ch; var pos = cur.ch;
var line = cm.getLine(lineNum); var line = cm.getLine(lineNum);
var dir = forward ? 1 : -1; var dir = forward ? 1 : -1;
var regexps = bigWord ? bigWordRegexp : wordRegexp; var charTests = bigWord ? bigWordCharTest: wordCharTest;
if (emptyLineIsWord && line == '') { if (emptyLineIsWord && line == '') {
lineNum += dir; lineNum += dir;
@ -3174,11 +3227,11 @@
// Find bounds of next word. // Find bounds of next word.
while (pos != stop) { while (pos != stop) {
var foundWord = false; var foundWord = false;
for (var i = 0; i < regexps.length && !foundWord; ++i) { for (var i = 0; i < charTests.length && !foundWord; ++i) {
if (regexps[i].test(line.charAt(pos))) { if (charTests[i](line.charAt(pos))) {
wordStart = pos; wordStart = pos;
// Advance to end of word. // Advance to end of word.
while (pos != stop && regexps[i].test(line.charAt(pos))) { while (pos != stop && charTests[i](line.charAt(pos))) {
pos += dir; pos += dir;
} }
wordEnd = pos; wordEnd = pos;
@ -3510,7 +3563,8 @@
function dialog(cm, template, shortText, onClose, options) { function dialog(cm, template, shortText, onClose, options) {
if (cm.openDialog) { if (cm.openDialog) {
cm.openDialog(template, onClose, { bottom: true, value: options.value, cm.openDialog(template, onClose, { bottom: true, value: options.value,
onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp }); onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp,
selectValueOnOpen: false});
} }
else { else {
onClose(prompt(shortText, '')); onClose(prompt(shortText, ''));
@ -3862,32 +3916,18 @@
return {top: from.line, bottom: to.line}; return {top: from.line, bottom: to.line};
} }
// Ex command handling
// Care must be taken when adding to the default Ex command map. For any
// pair of commands that have a shared prefix, at least one of their
// shortNames must not match the prefix of the other command.
var defaultExCommandMap = [
{ name: 'map' },
{ name: 'imap', shortName: 'im' },
{ name: 'nmap', shortName: 'nm' },
{ name: 'vmap', shortName: 'vm' },
{ name: 'unmap' },
{ name: 'write', shortName: 'w' },
{ name: 'undo', shortName: 'u' },
{ name: 'redo', shortName: 'red' },
{ name: 'set', shortName: 'set' },
{ name: 'sort', shortName: 'sor' },
{ name: 'substitute', shortName: 's', possiblyAsync: true },
{ name: 'nohlsearch', shortName: 'noh' },
{ name: 'delmarks', shortName: 'delm' },
{ name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
{ name: 'global', shortName: 'g' }
];
var ExCommandDispatcher = function() { var ExCommandDispatcher = function() {
this.buildCommandMap_(); this.buildCommandMap_();
}; };
ExCommandDispatcher.prototype = { ExCommandDispatcher.prototype = {
processCommand: function(cm, input, opt_params) { processCommand: function(cm, input, opt_params) {
var that = this;
cm.operation(function () {
cm.curOp.isVimOp = true;
that._processCommand(cm, input, opt_params);
});
},
_processCommand: function(cm, input, opt_params) {
var vim = cm.state.vim; var vim = cm.state.vim;
var commandHistoryRegister = vimGlobalState.registerController.getRegister(':'); var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');
var previousCommand = commandHistoryRegister.toString(); var previousCommand = commandHistoryRegister.toString();
@ -4100,6 +4140,13 @@
}; };
var exCommands = { var exCommands = {
colorscheme: function(cm, params) {
if (!params.args || params.args.length < 1) {
showConfirm(cm, cm.getOption('theme'));
return;
}
cm.setOption('theme', params.args[0]);
},
map: function(cm, params, ctx) { map: function(cm, params, ctx) {
var mapArgs = params.args; var mapArgs = params.args;
if (!mapArgs || mapArgs.length < 2) { if (!mapArgs || mapArgs.length < 2) {
@ -4133,6 +4180,9 @@
}, },
set: function(cm, params) { set: function(cm, params) {
var setArgs = params.args; var setArgs = params.args;
// Options passed through to the setOption/getOption calls. May be passed in by the
// local/global versions of the set command
var setCfg = params.setCfg || {};
if (!setArgs || setArgs.length < 1) { if (!setArgs || setArgs.length < 1) {
if (cm) { if (cm) {
showConfirm(cm, 'Invalid mapping: ' + params.input); showConfirm(cm, 'Invalid mapping: ' + params.input);
@ -4156,23 +4206,34 @@
optionName = optionName.substring(2); optionName = optionName.substring(2);
value = false; value = false;
} }
var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean'; var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean';
if (optionIsBoolean && value == undefined) { if (optionIsBoolean && value == undefined) {
// Calling set with a boolean option sets it to true. // Calling set with a boolean option sets it to true.
value = true; value = true;
} }
if (!optionIsBoolean && !value || forceGet) {
var oldValue = getOption(optionName);
// If no value is provided, then we assume this is a get. // If no value is provided, then we assume this is a get.
if (!optionIsBoolean && value === undefined || forceGet) {
var oldValue = getOption(optionName, cm, setCfg);
if (oldValue === true || oldValue === false) { if (oldValue === true || oldValue === false) {
showConfirm(cm, ' ' + (oldValue ? '' : 'no') + optionName); showConfirm(cm, ' ' + (oldValue ? '' : 'no') + optionName);
} else { } else {
showConfirm(cm, ' ' + optionName + '=' + oldValue); showConfirm(cm, ' ' + optionName + '=' + oldValue);
} }
} else { } else {
setOption(optionName, value); setOption(optionName, value, cm, setCfg);
} }
}, },
setlocal: function (cm, params) {
// setCfg is passed through to setOption
params.setCfg = {scope: 'local'};
this.set(cm, params);
},
setglobal: function (cm, params) {
// setCfg is passed through to setOption
params.setCfg = {scope: 'global'};
this.set(cm, params);
},
registers: function(cm, params) { registers: function(cm, params) {
var regArgs = params.args; var regArgs = params.args;
var registers = vimGlobalState.registerController.registers; var registers = vimGlobalState.registerController.registers;
@ -4795,7 +4856,7 @@
} }
function updateFakeCursor(cm) { function updateFakeCursor(cm) {
var vim = cm.state.vim; var vim = cm.state.vim;
var from = copyCursor(vim.sel.head); var from = clipCursorToContent(cm, copyCursor(vim.sel.head));
var to = offsetCursor(from, 0, 1); var to = offsetCursor(from, 0, 1);
if (vim.fakeCursor) { if (vim.fakeCursor) {
vim.fakeCursor.clear(); vim.fakeCursor.clear();
@ -4806,7 +4867,7 @@
var anchor = cm.getCursor('anchor'); var anchor = cm.getCursor('anchor');
var head = cm.getCursor('head'); var head = cm.getCursor('head');
// Enter or exit visual mode to match mouse selection. // Enter or exit visual mode to match mouse selection.
if (vim.visualMode && cursorEqual(head, anchor) && lineLength(cm, head.line) > head.ch) { if (vim.visualMode && !cm.somethingSelected()) {
exitVisualMode(cm, false); exitVisualMode(cm, false);
} else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) { } else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {
vim.visualMode = true; vim.visualMode = true;

View File

@ -33,8 +33,7 @@
min-width: 20px; min-width: 20px;
text-align: right; text-align: right;
color: #999; color: #999;
-moz-box-sizing: content-box; white-space: nowrap;
box-sizing: content-box;
} }
.CodeMirror-guttermarker { color: black; } .CodeMirror-guttermarker { color: black; }
@ -127,6 +126,8 @@ div.CodeMirror-overwrite div.CodeMirror-cursor {}
.cm-s-default .cm-error {color: #f00;} .cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;} .cm-invalidchar {color: #f00;}
.CodeMirror-composing { border-bottom: 2px solid; }
/* Default styles for common addons */ /* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
@ -154,14 +155,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
height: 100%; height: 100%;
outline: none; /* Prevent dragging from highlighting the element */ outline: none; /* Prevent dragging from highlighting the element */
position: relative; position: relative;
-moz-box-sizing: content-box;
box-sizing: content-box;
} }
.CodeMirror-sizer { .CodeMirror-sizer {
position: relative; position: relative;
border-right: 30px solid transparent; border-right: 30px solid transparent;
-moz-box-sizing: content-box;
box-sizing: content-box;
} }
/* The fake, visible scrollbars. Used to force redraw during scrolling /* The fake, visible scrollbars. Used to force redraw during scrolling
@ -196,8 +193,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-gutter { .CodeMirror-gutter {
white-space: normal; white-space: normal;
height: 100%; height: 100%;
-moz-box-sizing: content-box;
box-sizing: content-box;
display: inline-block; display: inline-block;
margin-bottom: -30px; margin-bottom: -30px;
/* Hack to make IE7 behave */ /* Hack to make IE7 behave */
@ -265,6 +260,16 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
outline: none; outline: none;
} }
/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-measure { .CodeMirror-measure {
position: absolute; position: absolute;
width: 100%; width: 100%;

View File

@ -82,12 +82,15 @@
keyMaps: [], // stores maps added by addKeyMap keyMaps: [], // stores maps added by addKeyMap
overlays: [], // highlighting overlays, as added by addOverlay overlays: [], // highlighting overlays, as added by addOverlay
modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
overwrite: false, focused: false, overwrite: false,
delayingBlurEvent: false,
focused: false,
suppressEdits: false, // used to disable editing during key handlers when in readOnly mode suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
draggingText: false, draggingText: false,
highlight: new Delayed(), // stores highlight worker timeout highlight: new Delayed(), // stores highlight worker timeout
keySeq: null // Unfinished key sequence keySeq: null, // Unfinished key sequence
specialChars: null
}; };
var cm = this; var cm = this;
@ -591,7 +594,7 @@
"CodeMirror-linenumber CodeMirror-gutter-elt")); "CodeMirror-linenumber CodeMirror-gutter-elt"));
var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
display.lineGutter.style.width = ""; display.lineGutter.style.width = "";
display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding); display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
display.lineNumWidth = display.lineNumInnerWidth + padding; display.lineNumWidth = display.lineNumInnerWidth + padding;
display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
display.lineGutter.style.width = display.lineNumWidth + "px"; display.lineGutter.style.width = display.lineNumWidth + "px";
@ -1076,7 +1079,7 @@
// was made out of. // was made out of.
var lastCopied = null; var lastCopied = null;
function applyTextInput(cm, inserted, deleted, sel) { function applyTextInput(cm, inserted, deleted, sel, origin) {
var doc = cm.doc; var doc = cm.doc;
cm.display.shift = false; cm.display.shift = false;
if (!sel) sel = doc.sel; if (!sel) sel = doc.sel;
@ -1102,7 +1105,7 @@
} }
var updateInput = cm.curOp.updateInput; var updateInput = cm.curOp.updateInput;
var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"}; origin: origin || (cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
makeChange(cm.doc, changeEvent); makeChange(cm.doc, changeEvent);
signalLater(cm, "inputRead", cm, changeEvent); signalLater(cm, "inputRead", cm, changeEvent);
// When an 'electric' character is inserted, immediately trigger a reindent // When an 'electric' character is inserted, immediately trigger a reindent
@ -1111,16 +1114,18 @@
(!i || sel.ranges[i - 1].head.line != range.head.line)) { (!i || sel.ranges[i - 1].head.line != range.head.line)) {
var mode = cm.getModeAt(range.head); var mode = cm.getModeAt(range.head);
var end = changeEnd(changeEvent); var end = changeEnd(changeEvent);
var indented = false;
if (mode.electricChars) { if (mode.electricChars) {
for (var j = 0; j < mode.electricChars.length; j++) for (var j = 0; j < mode.electricChars.length; j++)
if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
indentLine(cm, end.line, "smart"); indented = indentLine(cm, end.line, "smart");
break; break;
} }
} else if (mode.electricInput) { } else if (mode.electricInput) {
if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch))) if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch)))
indentLine(cm, end.line, "smart"); indented = indentLine(cm, end.line, "smart");
} }
if (indented) signalLater(cm, "electricInput", cm, end.line);
} }
} }
ensureCursorVisible(cm); ensureCursorVisible(cm);
@ -1164,6 +1169,7 @@
this.inaccurateSelection = false; this.inaccurateSelection = false;
// Used to work around IE issue with selection being forgotten when focus moves away from textarea // Used to work around IE issue with selection being forgotten when focus moves away from textarea
this.hasSelection = false; this.hasSelection = false;
this.composing = null;
}; };
function hiddenTextarea() { function hiddenTextarea() {
@ -1228,6 +1234,8 @@
te.value = lastCopied.join("\n"); te.value = lastCopied.join("\n");
selectInput(te); selectInput(te);
} }
} else if (!cm.options.lineWiseCopyCut) {
return;
} else { } else {
var ranges = copyableRanges(cm); var ranges = copyableRanges(cm);
lastCopied = ranges.text; lastCopied = ranges.text;
@ -1254,6 +1262,21 @@
on(display.lineSpace, "selectstart", function(e) { on(display.lineSpace, "selectstart", function(e) {
if (!eventInWidget(display, e)) e_preventDefault(e); if (!eventInWidget(display, e)) e_preventDefault(e);
}); });
on(te, "compositionstart", function() {
var start = cm.getCursor("from");
input.composing = {
start: start,
range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
};
});
on(te, "compositionend", function() {
if (input.composing) {
input.poll();
input.composing.range.clear();
input.composing = null;
}
});
}, },
prepareSelection: function() { prepareSelection: function() {
@ -1381,19 +1404,29 @@
return false; return false;
} }
if (text.charCodeAt(0) == 0x200b && cm.doc.sel == cm.display.selForContextMenu && !prevInput) if (cm.doc.sel == cm.display.selForContextMenu) {
prevInput = "\u200b"; var first = text.charCodeAt(0);
if (first == 0x200b && !prevInput) prevInput = "\u200b";
if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
}
// Find the part of the input that is actually new // Find the part of the input that is actually new
var same = 0, l = Math.min(prevInput.length, text.length); var same = 0, l = Math.min(prevInput.length, text.length);
while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
var self = this; var self = this;
runInOp(cm, function() { runInOp(cm, function() {
applyTextInput(cm, text.slice(same), prevInput.length - same); applyTextInput(cm, text.slice(same), prevInput.length - same,
null, self.composing ? "*compose" : null);
// Don't leave long text in the textarea, since it makes further polling slow // Don't leave long text in the textarea, since it makes further polling slow
if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = ""; if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
else self.prevInput = text; else self.prevInput = text;
if (self.composing) {
self.composing.range.clear();
self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
{className: "CodeMirror-composing"});
}
}); });
return true; return true;
}, },
@ -1440,7 +1473,9 @@
function prepareSelectAllHack() { function prepareSelectAllHack() {
if (te.selectionStart != null) { if (te.selectionStart != null) {
var selected = cm.somethingSelected(); var selected = cm.somethingSelected();
var extval = te.value = "\u200b" + (selected ? te.value : ""); var extval = "\u200b" + (selected ? te.value : "");
te.value = "\u21da"; // Used to catch context-menu undo
te.value = extval;
input.prevInput = selected ? "" : "\u200b"; input.prevInput = selected ? "" : "\u200b";
te.selectionStart = 1; te.selectionEnd = extval.length; te.selectionStart = 1; te.selectionEnd = extval.length;
// Re-set this, in case some other handler touched the // Re-set this, in case some other handler touched the
@ -1458,7 +1493,8 @@
if (te.selectionStart != null) { if (te.selectionStart != null) {
if (!ie || (ie && ie_version < 9)) prepareSelectAllHack(); if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
var i = 0, poll = function() { var i = 0, poll = function() {
if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0) if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
te.selectionEnd > 0 && input.prevInput == "\u200b")
operation(cm, commands.selectAll)(cm); operation(cm, commands.selectAll)(cm);
else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500); else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
else display.input.reset(); else display.input.reset();
@ -1491,6 +1527,7 @@
this.cm = cm; this.cm = cm;
this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
this.polling = new Delayed(); this.polling = new Delayed();
this.gracePeriod = false;
} }
ContentEditableInput.prototype = copyObj({ ContentEditableInput.prototype = copyObj({
@ -1552,6 +1589,8 @@
if (cm.somethingSelected()) { if (cm.somethingSelected()) {
lastCopied = cm.getSelections(); lastCopied = cm.getSelections();
if (e.type == "cut") cm.replaceSelection("", null, "cut"); if (e.type == "cut") cm.replaceSelection("", null, "cut");
} else if (!cm.options.lineWiseCopyCut) {
return;
} else { } else {
var ranges = copyableRanges(cm); var ranges = copyableRanges(cm);
lastCopied = ranges.text; lastCopied = ranges.text;
@ -1625,10 +1664,21 @@
sel.removeAllRanges(); sel.removeAllRanges();
sel.addRange(rng); sel.addRange(rng);
if (old && sel.anchorNode == null) sel.addRange(old); if (old && sel.anchorNode == null) sel.addRange(old);
else if (gecko) this.startGracePeriod();
} }
this.rememberSelection(); this.rememberSelection();
}, },
startGracePeriod: function() {
var input = this;
clearTimeout(this.gracePeriod);
this.gracePeriod = setTimeout(function() {
input.gracePeriod = false;
if (input.selectionChanged())
input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
}, 20);
},
showMultipleSelections: function(info) { showMultipleSelections: function(info) {
removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
@ -1671,12 +1721,15 @@
this.polling.set(this.cm.options.pollInterval, poll); this.polling.set(this.cm.options.pollInterval, poll);
}, },
pollSelection: function() { selectionChanged: function() {
if (this.composing) return; var sel = window.getSelection();
return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
},
pollSelection: function() {
if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
var sel = window.getSelection(), cm = this.cm; var sel = window.getSelection(), cm = this.cm;
if (sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset) {
this.rememberSelection(); this.rememberSelection();
var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
var head = domToPos(cm, sel.focusNode, sel.focusOffset); var head = domToPos(cm, sel.focusNode, sel.focusOffset);
@ -2895,6 +2948,7 @@
updateMaxLine: false, // Set when the widest line needs to be determined anew updateMaxLine: false, // Set when the widest line needs to be determined anew
scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
scrollToPos: null, // Used to scroll to a specific position scrollToPos: null, // Used to scroll to a specific position
focus: false,
id: ++nextOpId // Unique ID id: ++nextOpId // Unique ID
}; };
if (operationGroup) { if (operationGroup) {
@ -3012,6 +3066,7 @@
if (cm.state.focused && op.updateInput) if (cm.state.focused && op.updateInput)
cm.display.input.reset(op.typing); cm.display.input.reset(op.typing);
if (op.focus && op.focus == activeElt()) ensureFocus(op.cm);
} }
function endOperation_finish(op) { function endOperation_finish(op) {
@ -3376,15 +3431,11 @@
// Prevent wrapper from ever scrolling // Prevent wrapper from ever scrolling
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
function drag_(e) { d.dragFunctions = {
if (!signalDOMEvent(cm, e)) e_stop(e); simple: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
} start: function(e){onDragStart(cm, e);},
if (cm.options.dragDrop) { drop: operation(cm, onDrop)
on(d.scroller, "dragstart", function(e){onDragStart(cm, e);}); };
on(d.scroller, "dragenter", drag_);
on(d.scroller, "dragover", drag_);
on(d.scroller, "drop", operation(cm, onDrop));
}
var inp = d.input.getField(); var inp = d.input.getField();
on(inp, "keyup", function(e) { onKeyUp.call(cm, e); }); on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
@ -3394,6 +3445,18 @@
on(inp, "blur", bind(onBlur, cm)); on(inp, "blur", bind(onBlur, cm));
} }
function dragDropChanged(cm, value, old) {
var wasOn = old && old != CodeMirror.Init;
if (!value != !wasOn) {
var funcs = cm.display.dragFunctions;
var toggle = value ? on : off;
toggle(cm.display.scroller, "dragstart", funcs.start);
toggle(cm.display.scroller, "dragenter", funcs.simple);
toggle(cm.display.scroller, "dragover", funcs.simple);
toggle(cm.display.scroller, "drop", funcs.drop);
}
}
// Called when the window resizes // Called when the window resizes
function onResize(cm) { function onResize(cm) {
var d = cm.display; var d = cm.display;
@ -3475,6 +3538,7 @@
break; break;
case 3: case 3:
if (captureRightClick) onContextMenu(cm, e); if (captureRightClick) onContextMenu(cm, e);
else delayBlurEvent(cm);
break; break;
} }
} }
@ -3482,7 +3546,7 @@
var lastClick, lastDoubleClick; var lastClick, lastDoubleClick;
function leftButtonDown(cm, e, start) { function leftButtonDown(cm, e, start) {
if (ie) setTimeout(bind(ensureFocus, cm), 0); if (ie) setTimeout(bind(ensureFocus, cm), 0);
else ensureFocus(cm); else cm.curOp.focus = activeElt();
var now = +new Date, type; var now = +new Date, type;
if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
@ -3507,7 +3571,7 @@
// Start a text drag. When it ends, see if any dragging actually // Start a text drag. When it ends, see if any dragging actually
// happen, and treat as a click if it didn't. // happen, and treat as a click if it didn't.
function leftButtonStartDrag(cm, e, start, modifier) { function leftButtonStartDrag(cm, e, start, modifier) {
var display = cm.display; var display = cm.display, startTime = +new Date;
var dragEnd = operation(cm, function(e2) { var dragEnd = operation(cm, function(e2) {
if (webkit) display.scroller.draggable = false; if (webkit) display.scroller.draggable = false;
cm.state.draggingText = false; cm.state.draggingText = false;
@ -3515,12 +3579,13 @@
off(display.scroller, "drop", dragEnd); off(display.scroller, "drop", dragEnd);
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
e_preventDefault(e2); e_preventDefault(e2);
if (!modifier) if (!modifier && +new Date - 200 < startTime)
extendSelection(cm.doc, start); extendSelection(cm.doc, start);
display.input.focus(); // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
// Work around unexplainable focus problem in IE9 (#2127) if (webkit || ie && ie_version == 9)
if (ie && ie_version == 9)
setTimeout(function() {document.body.focus(); display.input.focus();}, 20); setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
else
display.input.focus();
} }
}); });
// Let the drag handler handle this. // Let the drag handler handle this.
@ -3546,6 +3611,7 @@
ourRange = new Range(start, start); ourRange = new Range(start, start);
} else { } else {
ourRange = doc.sel.primary(); ourRange = doc.sel.primary();
ourIndex = doc.sel.primIndex;
} }
if (e.altKey) { if (e.altKey) {
@ -3577,7 +3643,7 @@
ourIndex = ranges.length; ourIndex = ranges.length;
setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
{scroll: false, origin: "*mouse"}); {scroll: false, origin: "*mouse"});
} else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single") { } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0)); setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0));
startSel = doc.sel; startSel = doc.sel;
} else { } else {
@ -3640,7 +3706,7 @@
var cur = posFromMouse(cm, e, true, type == "rect"); var cur = posFromMouse(cm, e, true, type == "rect");
if (!cur) return; if (!cur) return;
if (cmp(cur, lastPos) != 0) { if (cmp(cur, lastPos) != 0) {
ensureFocus(cm); cm.curOp.focus = activeElt();
extendTo(cur); extendTo(cur);
var visible = visibleLines(display, doc); var visible = visibleLines(display, doc);
if (cur.line >= visible.to || cur.line < visible.from) if (cur.line >= visible.to || cur.line < visible.from)
@ -3743,7 +3809,7 @@
try { try {
var text = e.dataTransfer.getData("Text"); var text = e.dataTransfer.getData("Text");
if (text) { if (text) {
if (cm.state.draggingText && !(mac ? e.metaKey : e.ctrlKey)) if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
var selected = cm.listSelections(); var selected = cm.listSelections();
setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
if (selected) for (var i = 0; i < selected.length; ++i) if (selected) for (var i = 0; i < selected.length; ++i)
@ -3998,7 +4064,7 @@
var lastStoppedKey = null; var lastStoppedKey = null;
function onKeyDown(e) { function onKeyDown(e) {
var cm = this; var cm = this;
ensureFocus(cm); cm.curOp.focus = activeElt();
if (signalDOMEvent(cm, e)) return; if (signalDOMEvent(cm, e)) return;
// IE does strange things with escape. // IE does strange things with escape.
if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false; if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
@ -4050,7 +4116,19 @@
// FOCUS/BLUR EVENTS // FOCUS/BLUR EVENTS
function delayBlurEvent(cm) {
cm.state.delayingBlurEvent = true;
setTimeout(function() {
if (cm.state.delayingBlurEvent) {
cm.state.delayingBlurEvent = false;
onBlur(cm);
}
}, 100);
}
function onFocus(cm) { function onFocus(cm) {
if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
if (cm.options.readOnly == "nocursor") return; if (cm.options.readOnly == "nocursor") return;
if (!cm.state.focused) { if (!cm.state.focused) {
signal(cm, "focus", cm); signal(cm, "focus", cm);
@ -4068,6 +4146,8 @@
restartBlink(cm); restartBlink(cm);
} }
function onBlur(cm) { function onBlur(cm) {
if (cm.state.delayingBlurEvent) return;
if (cm.state.focused) { if (cm.state.focused) {
signal(cm, "blur", cm); signal(cm, "blur", cm);
cm.state.focused = false; cm.state.focused = false;
@ -4572,6 +4652,8 @@
if (indentString != curSpaceString) { if (indentString != curSpaceString) {
replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
line.stateAfter = null;
return true;
} else { } else {
// Ensure that, if the cursor was in the whitespace at the start // Ensure that, if the cursor was in the whitespace at the start
// of the line, it is moved to the end of that space. // of the line, it is moved to the end of that space.
@ -4584,7 +4666,6 @@
} }
} }
} }
line.stateAfter = null;
} }
// Utility for applying a change to a line by handle or number, // Utility for applying a change to a line by handle or number,
@ -4824,7 +4905,7 @@
getHelpers: function(pos, type) { getHelpers: function(pos, type) {
var found = []; var found = [];
if (!helpers.hasOwnProperty(type)) return helpers; if (!helpers.hasOwnProperty(type)) return found;
var help = helpers[type], mode = this.getModeAt(pos); var help = helpers[type], mode = this.getModeAt(pos);
if (typeof mode[type] == "string") { if (typeof mode[type] == "string") {
if (help[mode[type]]) found.push(help[mode[type]]); if (help[mode[type]]) found.push(help[mode[type]]);
@ -4874,10 +4955,15 @@
return lineAtHeight(this.doc, height + this.display.viewOffset); return lineAtHeight(this.doc, height + this.display.viewOffset);
}, },
heightAtLine: function(line, mode) { heightAtLine: function(line, mode) {
var end = false, last = this.doc.first + this.doc.size - 1; var end = false, lineObj;
if (typeof line == "number") {
var last = this.doc.first + this.doc.size - 1;
if (line < this.doc.first) line = this.doc.first; if (line < this.doc.first) line = this.doc.first;
else if (line > last) { line = last; end = true; } else if (line > last) { line = last; end = true; }
var lineObj = getLine(this.doc, line); lineObj = getLine(this.doc, line);
} else {
lineObj = line;
}
return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
(end ? this.doc.height - heightAtLine(lineObj) : 0); (end ? this.doc.height - heightAtLine(lineObj) : 0);
}, },
@ -4906,12 +4992,6 @@
}); });
}), }),
addLineWidget: methodOp(function(handle, node, options) {
return addLineWidget(this, handle, node, options);
}),
removeLineWidget: function(widget) { widget.clear(); },
lineInfo: function(line) { lineInfo: function(line) {
if (typeof line == "number") { if (typeof line == "number") {
if (!isLine(this.doc, line)) return null; if (!isLine(this.doc, line)) return null;
@ -5186,10 +5266,10 @@
clearCaches(cm); clearCaches(cm);
regChange(cm); regChange(cm);
}, true); }, true);
option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val) { option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
cm.refresh(); if (old != CodeMirror.Init) cm.refresh();
}, true); });
option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true); option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
option("electricChars", true); option("electricChars", true);
option("inputStyle", mobile ? "contenteditable" : "textarea", function() { option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
@ -5235,6 +5315,7 @@
option("showCursorWhenSelecting", false, updateSelection, true); option("showCursorWhenSelecting", false, updateSelection, true);
option("resetSelectionOnContextMenu", true); option("resetSelectionOnContextMenu", true);
option("lineWiseCopyCut", true);
option("readOnly", false, function(cm, val) { option("readOnly", false, function(cm, val) {
if (val == "nocursor") { if (val == "nocursor") {
@ -5247,7 +5328,7 @@
} }
}); });
option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true); option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);
option("dragDrop", true); option("dragDrop", true, dragDropChanged);
option("cursorBlinkRate", 530); option("cursorBlinkRate", 530);
option("cursorScrollMargin", 0); option("cursorScrollMargin", 0);
@ -6431,10 +6512,10 @@
// Line widgets are block elements displayed above or below a line. // Line widgets are block elements displayed above or below a line.
var LineWidget = CodeMirror.LineWidget = function(cm, node, options) { var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
if (options) for (var opt in options) if (options.hasOwnProperty(opt)) if (options) for (var opt in options) if (options.hasOwnProperty(opt))
this[opt] = options[opt]; this[opt] = options[opt];
this.cm = cm; this.doc = doc;
this.node = node; this.node = node;
}; };
eventMixin(LineWidget); eventMixin(LineWidget);
@ -6445,52 +6526,55 @@
} }
LineWidget.prototype.clear = function() { LineWidget.prototype.clear = function() {
var cm = this.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
if (no == null || !ws) return; if (no == null || !ws) return;
for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
if (!ws.length) line.widgets = null; if (!ws.length) line.widgets = null;
var height = widgetHeight(this); var height = widgetHeight(this);
runInOp(cm, function() { updateLineHeight(line, Math.max(0, line.height - height));
if (cm) runInOp(cm, function() {
adjustScrollWhenAboveVisible(cm, line, -height); adjustScrollWhenAboveVisible(cm, line, -height);
regLineChange(cm, no, "widget"); regLineChange(cm, no, "widget");
updateLineHeight(line, Math.max(0, line.height - height));
}); });
}; };
LineWidget.prototype.changed = function() { LineWidget.prototype.changed = function() {
var oldH = this.height, cm = this.cm, line = this.line; var oldH = this.height, cm = this.doc.cm, line = this.line;
this.height = null; this.height = null;
var diff = widgetHeight(this) - oldH; var diff = widgetHeight(this) - oldH;
if (!diff) return; if (!diff) return;
runInOp(cm, function() { updateLineHeight(line, line.height + diff);
if (cm) runInOp(cm, function() {
cm.curOp.forceUpdate = true; cm.curOp.forceUpdate = true;
adjustScrollWhenAboveVisible(cm, line, diff); adjustScrollWhenAboveVisible(cm, line, diff);
updateLineHeight(line, line.height + diff);
}); });
}; };
function widgetHeight(widget) { function widgetHeight(widget) {
if (widget.height != null) return widget.height; if (widget.height != null) return widget.height;
var cm = widget.doc.cm;
if (!cm) return 0;
if (!contains(document.body, widget.node)) { if (!contains(document.body, widget.node)) {
var parentStyle = "position: relative;"; var parentStyle = "position: relative;";
if (widget.coverGutter) if (widget.coverGutter)
parentStyle += "margin-left: -" + widget.cm.display.gutters.offsetWidth + "px;"; parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;";
if (widget.noHScroll) if (widget.noHScroll)
parentStyle += "width: " + widget.cm.display.wrapper.clientWidth + "px;"; parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, parentStyle)); removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
} }
return widget.height = widget.node.offsetHeight; return widget.height = widget.node.offsetHeight;
} }
function addLineWidget(cm, handle, node, options) { function addLineWidget(doc, handle, node, options) {
var widget = new LineWidget(cm, node, options); var widget = new LineWidget(doc, node, options);
if (widget.noHScroll) cm.display.alignWidgets = true; var cm = doc.cm;
changeLine(cm.doc, handle, "widget", function(line) { if (cm && widget.noHScroll) cm.display.alignWidgets = true;
changeLine(doc, handle, "widget", function(line) {
var widgets = line.widgets || (line.widgets = []); var widgets = line.widgets || (line.widgets = []);
if (widget.insertAt == null) widgets.push(widget); if (widget.insertAt == null) widgets.push(widget);
else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
widget.line = line; widget.line = line;
if (!lineIsHidden(cm.doc, line)) { if (cm && !lineIsHidden(doc, line)) {
var aboveVisible = heightAtLine(line) < cm.doc.scrollTop; var aboveVisible = heightAtLine(line) < doc.scrollTop;
updateLineHeight(line, line.height + widgetHeight(widget)); updateLineHeight(line, line.height + widgetHeight(widget));
if (aboveVisible) addToScrollPos(cm, null, widget.height); if (aboveVisible) addToScrollPos(cm, null, widget.height);
cm.curOp.forceUpdate = true; cm.curOp.forceUpdate = true;
@ -6710,7 +6794,9 @@
// is needed on Webkit to be able to get line-level bounding // is needed on Webkit to be able to get line-level bounding
// rectangles for it (in measureChar). // rectangles for it (in measureChar).
var content = elt("span", null, null, webkit ? "padding-right: .1px" : null); var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
var builder = {pre: elt("pre", [content]), content: content, col: 0, pos: 0, cm: cm}; var builder = {pre: elt("pre", [content]), content: content,
col: 0, pos: 0, cm: cm,
splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
lineView.measure = {}; lineView.measure = {};
// Iterate over the logical lines that make up this visual line. // Iterate over the logical lines that make up this visual line.
@ -6720,8 +6806,6 @@
builder.addToken = buildToken; builder.addToken = buildToken;
// Optionally wire in some hacks into the token-rendering // Optionally wire in some hacks into the token-rendering
// algorithm, to deal with browser quirks. // algorithm, to deal with browser quirks.
if ((ie || webkit) && cm.getOption("lineWrapping"))
builder.addToken = buildTokenSplitSpaces(builder.addToken);
if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line))) if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
builder.addToken = buildTokenBadBidi(builder.addToken, order); builder.addToken = buildTokenBadBidi(builder.addToken, order);
builder.map = []; builder.map = [];
@ -6770,10 +6854,11 @@
// the line map. Takes care to render special characters separately. // the line map. Takes care to render special characters separately.
function buildToken(builder, text, style, startStyle, endStyle, title, css) { function buildToken(builder, text, style, startStyle, endStyle, title, css) {
if (!text) return; if (!text) return;
var special = builder.cm.options.specialChars, mustWrap = false; var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
var special = builder.cm.state.specialChars, mustWrap = false;
if (!special.test(text)) { if (!special.test(text)) {
builder.col += text.length; builder.col += text.length;
var content = document.createTextNode(text); var content = document.createTextNode(displayText);
builder.map.push(builder.pos, builder.pos + text.length, content); builder.map.push(builder.pos, builder.pos + text.length, content);
if (ie && ie_version < 9) mustWrap = true; if (ie && ie_version < 9) mustWrap = true;
builder.pos += text.length; builder.pos += text.length;
@ -6784,7 +6869,7 @@
var m = special.exec(text); var m = special.exec(text);
var skipped = m ? m.index - pos : text.length - pos; var skipped = m ? m.index - pos : text.length - pos;
if (skipped) { if (skipped) {
var txt = document.createTextNode(text.slice(pos, pos + skipped)); var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
else content.appendChild(txt); else content.appendChild(txt);
builder.map.push(builder.pos, builder.pos + skipped, txt); builder.map.push(builder.pos, builder.pos + skipped, txt);
@ -6821,22 +6906,17 @@
builder.content.appendChild(content); builder.content.appendChild(content);
} }
function buildTokenSplitSpaces(inner) { function splitSpaces(old) {
function split(old) {
var out = " "; var out = " ";
for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0"; for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
out += " "; out += " ";
return out; return out;
} }
return function(builder, text, style, startStyle, endStyle, title) {
inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
};
}
// Work around nonsense dimensions being reported for stretches of // Work around nonsense dimensions being reported for stretches of
// right-to-left text. // right-to-left text.
function buildTokenBadBidi(inner, order) { function buildTokenBadBidi(inner, order) {
return function(builder, text, style, startStyle, endStyle, title) { return function(builder, text, style, startStyle, endStyle, title, css) {
style = style ? style + " cm-force-border" : "cm-force-border"; style = style ? style + " cm-force-border" : "cm-force-border";
var start = builder.pos, end = start + text.length; var start = builder.pos, end = start + text.length;
for (;;) { for (;;) {
@ -6845,8 +6925,8 @@
var part = order[i]; var part = order[i];
if (part.to > start && part.from <= start) break; if (part.to > start && part.from <= start) break;
} }
if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title); if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
inner(builder, text.slice(0, part.to - start), style, startStyle, null, title); inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
startStyle = null; startStyle = null;
text = text.slice(part.to - start); text = text.slice(part.to - start);
start = part.to; start = part.to;
@ -6888,8 +6968,13 @@
var foundBookmarks = []; var foundBookmarks = [];
for (var j = 0; j < spans.length; ++j) { for (var j = 0; j < spans.length; ++j) {
var sp = spans[j], m = sp.marker; var sp = spans[j], m = sp.marker;
if (sp.from <= pos && (sp.to == null || sp.to > pos)) { if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; } foundBookmarks.push(m);
} else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
if (sp.to != null && sp.to != pos && nextChange > sp.to) {
nextChange = sp.to;
spanEndStyle = "";
}
if (m.className) spanStyle += " " + m.className; if (m.className) spanStyle += " " + m.className;
if (m.css) css = m.css; if (m.css) css = m.css;
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
@ -6900,12 +6985,12 @@
} else if (sp.from > pos && nextChange > sp.from) { } else if (sp.from > pos && nextChange > sp.from) {
nextChange = sp.from; nextChange = sp.from;
} }
if (m.type == "bookmark" && sp.from == pos && m.widgetNode) foundBookmarks.push(m);
} }
if (collapsed && (collapsed.from || 0) == pos) { if (collapsed && (collapsed.from || 0) == pos) {
buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
collapsed.marker, collapsed.from == null); collapsed.marker, collapsed.from == null);
if (collapsed.to == null) return; if (collapsed.to == null) return;
if (collapsed.to == pos) collapsed = false;
} }
if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j) if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
buildCollapsedSpan(builder, 0, foundBookmarks[j]); buildCollapsedSpan(builder, 0, foundBookmarks[j]);
@ -7368,13 +7453,19 @@
}); });
}), }),
addLineWidget: docMethodOp(function(handle, node, options) {
return addLineWidget(this, handle, node, options);
}),
removeLineWidget: function(widget) { widget.clear(); },
markText: function(from, to, options) { markText: function(from, to, options) {
return markText(this, clipPos(this, from), clipPos(this, to), options, "range"); return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
}, },
setBookmark: function(pos, options) { setBookmark: function(pos, options) {
var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
insertLeft: options && options.insertLeft, insertLeft: options && options.insertLeft,
clearWhenEmpty: false, shared: options && options.shared}; clearWhenEmpty: false, shared: options && options.shared,
handleMouseEvents: options && options.handleMouseEvents};
pos = clipPos(this, pos); pos = clipPos(this, pos);
return markText(this, pos, pos, realOpts, "bookmark"); return markText(this, pos, pos, realOpts, "bookmark");
}, },
@ -8108,7 +8199,7 @@
return function(){return f.apply(null, args);}; return function(){return f.apply(null, args);};
} }
var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
var isWordCharBasic = CodeMirror.isWordChar = function(ch) { var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
return /\w/.test(ch) || ch > "\x80" && return /\w/.test(ch) || ch > "\x80" &&
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
@ -8630,6 +8721,8 @@
lst(order).to -= m[0].length; lst(order).to -= m[0].length;
order.push(new BidiSpan(0, len - m[0].length, len)); order.push(new BidiSpan(0, len - m[0].length, len));
} }
if (order[0].level == 2)
order.unshift(new BidiSpan(1, order[0].to, order[0].to));
if (order[0].level != lst(order).level) if (order[0].level != lst(order).level)
order.push(new BidiSpan(order[0].level, len, len)); order.push(new BidiSpan(order[0].level, len, len));
@ -8639,7 +8732,7 @@
// THE END // THE END
CodeMirror.version = "5.0.0"; CodeMirror.version = "5.2.1";
return CodeMirror; return CodeMirror;
}); });

View File

@ -239,6 +239,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
if (type == "{" || type == "}") return popAndPass(type, stream, state); if (type == "{" || type == "}") return popAndPass(type, stream, state);
if (type == ")") return popContext(state); if (type == ")") return popContext(state);
if (type == "(") return pushContext(state, stream, "parens"); if (type == "(") return pushContext(state, stream, "parens");
if (type == "interpolation") return pushContext(state, stream, "interpolation");
if (type == "word") wordAsValue(stream); if (type == "word") wordAsValue(stream);
return "parens"; return "parens";
}; };
@ -327,7 +328,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
states.interpolation = function(type, stream, state) { states.interpolation = function(type, stream, state) {
if (type == "}") return popContext(state); if (type == "}") return popContext(state);
if (type == "{" || type == ";") return popAndPass(type, stream, state); if (type == "{" || type == ";") return popAndPass(type, stream, state);
if (type != "variable") override = "error"; if (type == "word") override = "variable";
else if (type != "variable") override = "error";
return "interpolation"; return "interpolation";
}; };
@ -749,6 +751,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
} }
}, },
"@": function(stream) { "@": function(stream) {
if (stream.eat("{")) return [null, "interpolation"];
if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false; if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
stream.eatWhile(/[\w\\\-]/); stream.eatWhile(/[\w\\\-]/);
if (stream.match(/^\s*:/, false)) if (stream.match(/^\s*:/, false))

View File

@ -146,7 +146,7 @@ fieldset span button, fieldset span input[type="file"] {
}); });
</script> </script>
<p>The LESS mode is a sub-mode of the <a href="index.html">CSS mode</a> (defined in <code>css.js</code>.</p> <p>The LESS mode is a sub-mode of the <a href="index.html">CSS mode</a> (defined in <code>css.js</code>).</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#less_*">normal</a>, <a href="../../test/index.html#verbose,less_*">verbose</a>.</p> <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#less_*">normal</a>, <a href="../../test/index.html#verbose,less_*">verbose</a>.</p>
</article> </article>

View File

@ -48,4 +48,7 @@
" }", " }",
" }", " }",
"}"); "}");
MT("interpolation", ".@{[variable foo]} { [property font-weight]: [atom bold]; }");
})(); })();

View File

@ -150,7 +150,7 @@ code {
}); });
</script> </script>
<p>The SCSS mode is a sub-mode of the <a href="index.html">CSS mode</a> (defined in <code>css.js</code>.</p> <p>The SCSS mode is a sub-mode of the <a href="index.html">CSS mode</a> (defined in <code>css.js</code>).</p>
<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#scss_*">normal</a>, <a href="../../test/index.html#verbose,scss_*">verbose</a>.</p> <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#scss_*">normal</a>, <a href="../../test/index.html#verbose,scss_*">verbose</a>.</p>

View File

@ -73,7 +73,7 @@
"[tag foo]#{[variable-2 $hello]} { [property color]:[atom #000]; }"); "[tag foo]#{[variable-2 $hello]} { [property color]:[atom #000]; }");
MT('interpolation_error', MT('interpolation_error',
"[tag foo]#{[error foo]} { [property color]:[atom #000]; }"); "[tag foo]#{[variable foo]} { [property color]:[atom #000]; }");
MT("divide_operator", MT("divide_operator",
"[tag foo] { [property width]:[number 4] [operator /] [number 2] }"); "[tag foo] { [property width]:[number 4] [operator /] [number 2] }");

View File

@ -1,6 +1,6 @@
{ {
"name": "codemirror", "name": "codemirror",
"version":"5.0.0", "version":"5.2.1",
"main": "lib/codemirror.js", "main": "lib/codemirror.js",
"description": "In-browser code editing made bearable", "description": "In-browser code editing made bearable",
"licenses": [{"type": "MIT", "licenses": [{"type": "MIT",

95
codemirror/theme/liquibyte.css vendored Normal file
View File

@ -0,0 +1,95 @@
.cm-s-liquibyte.CodeMirror {
background-color: #000;
color: #fff;
line-height: 1.2em;
font-size: 1em;
}
.CodeMirror-focused .cm-matchhighlight {
text-decoration: underline;
text-decoration-color: #0f0;
text-decoration-style: wavy;
}
.cm-trailingspace {
text-decoration: line-through;
text-decoration-color: #f00;
text-decoration-style: dotted;
}
.cm-tab {
text-decoration: line-through;
text-decoration-color: #404040;
text-decoration-style: dotted;
}
.cm-s-liquibyte .CodeMirror-gutters { background-color: #262626; border-right: 1px solid #505050; padding-right: 0.8em; }
.cm-s-liquibyte .CodeMirror-gutter-elt div{ font-size: 1.2em; }
.cm-s-liquibyte .CodeMirror-guttermarker { }
.cm-s-liquibyte .CodeMirror-guttermarker-subtle { }
.cm-s-liquibyte .CodeMirror-linenumber { color: #606060; padding-left: 0;}
.cm-s-liquibyte .CodeMirror-cursor { border-left: 1px solid #eee !important; }
.cm-s-liquibyte span.cm-comment { color: #008000; }
.cm-s-liquibyte span.cm-def { color: #ffaf40; font-weight: bold; }
.cm-s-liquibyte span.cm-keyword { color: #c080ff; font-weight: bold; }
.cm-s-liquibyte span.cm-builtin { color: #ffaf40; font-weight: bold; }
.cm-s-liquibyte span.cm-variable { color: #5967ff; font-weight: bold; }
.cm-s-liquibyte span.cm-string { color: #ff8000; }
.cm-s-liquibyte span.cm-number { color: #0f0; font-weight: bold; }
.cm-s-liquibyte span.cm-atom { color: #bf3030; font-weight: bold; }
.cm-s-liquibyte span.cm-variable-2 { color: #007f7f; font-weight: bold; }
.cm-s-liquibyte span.cm-variable-3 { color: #c080ff; font-weight: bold; }
.cm-s-liquibyte span.cm-property { color: #999; font-weight: bold; }
.cm-s-liquibyte span.cm-operator { color: #fff; }
.cm-s-liquibyte span.cm-meta { color: #0f0; }
.cm-s-liquibyte span.cm-qualifier { color: #fff700; font-weight: bold; }
.cm-s-liquibyte span.cm-bracket { color: #cc7; }
.cm-s-liquibyte span.cm-tag { color: #ff0; font-weight: bold; }
.cm-s-liquibyte span.cm-attribute { color: #c080ff; font-weight: bold; }
.cm-s-liquibyte span.cm-error { color: #f00; }
.cm-s-liquibyte .CodeMirror-selected { background-color: rgba(255, 0, 0, 0.25) !important; }
.cm-s-liquibyte span.cm-compilation { background-color: rgba(255, 255, 255, 0.12); }
.cm-s-liquibyte .CodeMirror-activeline-background {background-color: rgba(0, 255, 0, 0.15) !important;}
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket { color: #0f0; font-weight: bold; }
div.CodeMirror span.CodeMirror-nonmatchingbracket { color: #f00; font-weight: bold; }
.CodeMirror-matchingtag { background-color: rgba(150, 255, 0, .3); }
/* Scrollbars */
/* Simple */
div.CodeMirror-simplescroll-horizontal div:hover, div.CodeMirror-simplescroll-vertical div:hover {
background-color: rgba(80, 80, 80, .7);
}
div.CodeMirror-simplescroll-horizontal div, div.CodeMirror-simplescroll-vertical div {
background-color: rgba(80, 80, 80, .3);
border: 1px solid #404040;
border-radius: 5px;
}
div.CodeMirror-simplescroll-vertical div {
border-top: 1px solid #404040;
border-bottom: 1px solid #404040;
}
div.CodeMirror-simplescroll-horizontal div {
border-left: 1px solid #404040;
border-right: 1px solid #404040;
}
div.CodeMirror-simplescroll-vertical {
background-color: #262626;
}
div.CodeMirror-simplescroll-horizontal {
background-color: #262626;
border-top: 1px solid #404040;
}
/* Overlay */
div.CodeMirror-overlayscroll-horizontal div, div.CodeMirror-overlayscroll-vertical div {
background-color: #404040;
border-radius: 5px;
}
div.CodeMirror-overlayscroll-vertical div {
border: 1px solid #404040;
}
div.CodeMirror-overlayscroll-horizontal div {
border: 1px solid #404040;
}

View File

@ -13,7 +13,7 @@
.cm-s-mdn-like.CodeMirror ::-moz-selection { background: #cfc; } .cm-s-mdn-like.CodeMirror ::-moz-selection { background: #cfc; }
.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-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; } .cm-s-mdn-like .CodeMirror-linenumber { color: #aaa; padding-left: 8px; }
div.cm-s-mdn-like .CodeMirror-cursor { border-left: 2px solid #222; } 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-keyword { color: #6262FF; }

View File

@ -18,7 +18,7 @@
.cm-s-monokai span.cm-keyword {color: #f92672;} .cm-s-monokai span.cm-keyword {color: #f92672;}
.cm-s-monokai span.cm-string {color: #e6db74;} .cm-s-monokai span.cm-string {color: #e6db74;}
.cm-s-monokai span.cm-variable {color: #a6e22e;} .cm-s-monokai span.cm-variable {color: #f8f8f2;}
.cm-s-monokai span.cm-variable-2 {color: #9effff;} .cm-s-monokai span.cm-variable-2 {color: #9effff;}
.cm-s-monokai span.cm-def {color: #fd971f;} .cm-s-monokai span.cm-def {color: #fd971f;}
.cm-s-monokai span.cm-bracket {color: #f8f8f2;} .cm-s-monokai span.cm-bracket {color: #f8f8f2;}

View File

@ -53,7 +53,7 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png
.cm-s-solarized .cm-number { color: #d33682; } .cm-s-solarized .cm-number { color: #d33682; }
.cm-s-solarized .cm-def { color: #2aa198; } .cm-s-solarized .cm-def { color: #2aa198; }
.cm-s-solarized .cm-variable { color: #268bd2; } .cm-s-solarized .cm-variable { color: #839496; }
.cm-s-solarized .cm-variable-2 { color: #b58900; } .cm-s-solarized .cm-variable-2 { color: #b58900; }
.cm-s-solarized .cm-variable-3 { color: #6c71c4; } .cm-s-solarized .cm-variable-3 { color: #6c71c4; }