diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000000..54322f9e2a
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,9 @@
+/node_modules
+/demo
+/doc
+/test
+/index.html
+/mode/*/*test.js
+/mode/*/*.html
+/mode/index.html
+.*
diff --git a/.travis.yml b/.travis.yml
index 20fd86b6a5..52b8b81591 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,4 @@
language: node_js
node_js:
- - 0.10
+ - stable
+sudo: false
diff --git a/AUTHORS b/AUTHORS
index 3299d4fd0c..c8e17d0778 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -59,6 +59,7 @@ AtomicPages LLC
Atul Bhouraskar
Aurelian Oancea
Bastian Müller
+belhaj
Bem Jones-Bey
benbro
Beni Cherniavsky-Paskin
@@ -215,6 +216,7 @@ Jason San Jose
Jason Siefken
Jaydeep Solanki
Jean Boussier
+Jeff Blaisdell
jeffkenton
Jeff Pickhardt
jem (graphite)
@@ -272,6 +274,7 @@ Lorenzo Stoakes
Luciano Longo
Luke Stagner
lynschinzer
+M1cha
Madhura Jayaratne
Maksim Lin
Maksym Taran
@@ -307,6 +310,7 @@ mauricio
Maximilian Hils
Maxim Kraev
Max Kirsch
+Max Schaefer
Max Xiantu
mbarkhau
Metatheos
@@ -327,6 +331,7 @@ Mike Kadin
MinRK
Miraculix87
misfo
+mkaminsky11
mloginov
Moritz Schwörer
mps
@@ -354,6 +359,7 @@ Nisarg Jhaveri
nlwillia
noragrossman
Norman Rzepka
+Oreoluwa Onatemowo
pablo
Page
Panupong Pasupat
@@ -387,6 +393,7 @@ Robert Crossfield
Roberto Abdelkader Martínez Pérez
robertop23
Robert Plummer
+Rrandom
Ruslan Osmanov
Ryan Prior
sabaca
@@ -421,6 +428,7 @@ Stefan Borsje
Steffen Beyer
Steve O'Hara
stoskov
+Sungho Kim
Taha Jahangir
Takuji Shimokawa
Tarmil
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1645548239..6d65e41129 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -61,7 +61,8 @@ should be asked on the
- Make sure all tests pass. Visit `test/index.html` in your browser to
run them.
- Submit a pull request
-([how to create a pull request](https://help.github.com/articles/fork-a-repo))
+([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
+ Don't put more than one feature/fix in a single pull request.
By contributing code to CodeMirror you
diff --git a/README.md b/README.md
index 38156a7427..77824ebdbf 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,25 @@
[](https://www.npmjs.org/package/codemirror)
[Funding status: ](https://marijnhaverbeke.nl/fund/)
-CodeMirror is a JavaScript component that provides a code editor in
-the browser. When a mode is available for the language you are coding
-in, it will color your code, and optionally help with indentation.
+CodeMirror is a versatile text editor implemented in JavaScript for
+the browser. It is specialized for editing code, and comes with over
+100 language modes and various addons that implement more advanced
+editing functionality.
-The project page is http://codemirror.net
-The manual is at http://codemirror.net/doc/manual.html
-The contributing guidelines are in [CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md)
+A rich programming API and a CSS theming system are available for
+customizing CodeMirror to fit your application, and extending it with
+new functionality.
+
+You can find more information (and the
+[manual](http://codemirror.net/doc/manual.html)) on the [project
+page](http://codemirror.net). For questions and discussion, use the
+[discussion forum](http://discuss.codemirror.net/).
+
+See
+[CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md)
+for contributing guidelines.
+
+The CodeMirror community aims to be welcoming to everybody. We use the
+[Contributor Covenant
+(1.1)](http://contributor-covenant.org/version/1/1/0/) as our code of
+conduct.
diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js
index d228fc8890..980da5235d 100644
--- a/addon/hint/show-hint.js
+++ b/addon/hint/show-hint.js
@@ -99,7 +99,6 @@
update: function(first) {
if (this.tick == null) return;
- if (this.data) CodeMirror.signal(this.data, "update");
if (!this.options.hint.async) {
this.finishUpdate(this.options.hint(this.cm, this.options), first);
} else {
@@ -111,6 +110,8 @@
},
finishUpdate: function(data, first) {
+ if (this.data) CodeMirror.signal(this.data, "update");
+ if (data && this.data && CodeMirror.cmpPos(data.from, this.data.from)) data = null;
this.data = data;
var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
@@ -351,18 +352,20 @@
CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
+ var to = CodeMirror.Pos(cur.line, token.end);
+ if (token.string && /\w/.test(token.string[token.string.length - 1])) {
+ var term = token.string, from = CodeMirror.Pos(cur.line, token.start);
+ } else {
+ var term = "", from = to;
+ }
var found = [];
for (var i = 0; i < options.words.length; i++) {
var word = options.words[i];
- if (word.slice(0, token.string.length) == token.string)
+ if (word.slice(0, term.length) == term)
found.push(word);
}
- if (found.length) return {
- list: found,
- from: CodeMirror.Pos(cur.line, token.start),
- to: CodeMirror.Pos(cur.line, token.end)
- };
+ if (found.length) return {list: found, from: from, to: to};
});
CodeMirror.commands.autocomplete = CodeMirror.showHint;
diff --git a/addon/search/search.js b/addon/search/search.js
index 761cb9492e..ed3c477550 100644
--- a/addon/search/search.js
+++ b/addon/search/search.js
@@ -18,6 +18,7 @@
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
+
function searchOverlay(query, caseInsensitive) {
if (typeof query == "string")
query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
@@ -42,24 +43,39 @@
this.posFrom = this.posTo = this.lastQuery = this.query = null;
this.overlay = null;
}
+
function getSearchState(cm) {
return cm.state.search || (cm.state.search = new SearchState());
}
+
function queryCaseInsensitive(query) {
return typeof query == "string" && query == query.toLowerCase();
}
+
function getSearchCursor(cm, query, pos) {
// Heuristic: if the query string is all lowercase, do a case insensitive search.
return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
}
+
+ function persistentDialog(cm, text, deflt, f) {
+ cm.openDialog(text, f, {
+ value: deflt,
+ selectValueOnOpen: true,
+ closeOnEnter: false,
+ onClose: function() { clearSearch(cm); }
+ });
+ }
+
function dialog(cm, text, shortText, deflt, f) {
if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
else f(prompt(shortText, deflt));
}
+
function confirmDialog(cm, text, shortText, fs) {
if (cm.openConfirm) cm.openConfirm(text, fs);
else if (confirm(shortText)) fs[0]();
}
+
function parseQuery(query) {
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
if (isRE) {
@@ -70,28 +86,44 @@
query = /x^/;
return query;
}
+
var queryDialog =
'Search: (Use /re/ syntax for regexp search)';
- function doSearch(cm, rev) {
+
+ function startSearch(cm, state, query) {
+ state.queryText = query;
+ state.query = parseQuery(query);
+ cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
+ state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
+ cm.addOverlay(state.overlay);
+ if (cm.showMatchesOnScrollbar) {
+ if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+ state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
+ }
+ }
+
+ function doSearch(cm, rev, persistent) {
var state = getSearchState(cm);
if (state.query) return findNext(cm, rev);
var q = cm.getSelection() || state.lastQuery;
- dialog(cm, queryDialog, "Search for:", q, function(query) {
- cm.operation(function() {
- if (!query || state.query) return;
- state.query = parseQuery(query);
- cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
- state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
- cm.addOverlay(state.overlay);
- if (cm.showMatchesOnScrollbar) {
- if (state.annotate) { state.annotate.clear(); state.annotate = null; }
- state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
- }
- state.posFrom = state.posTo = cm.getCursor();
- findNext(cm, rev);
+ if (persistent && cm.openDialog) {
+ persistentDialog(cm, queryDialog, q, function(query, event) {
+ CodeMirror.e_stop(event);
+ if (!query) return;
+ if (query != state.queryText) startSearch(cm, state, query);
+ findNext(cm, event.shiftKey);
});
- });
+ } else {
+ dialog(cm, queryDialog, "Search for:", q, function(query) {
+ if (query && !state.query) cm.operation(function() {
+ startSearch(cm, state, query);
+ state.posFrom = state.posTo = cm.getCursor();
+ findNext(cm, rev);
+ });
+ });
+ }
}
+
function findNext(cm, rev) {cm.operation(function() {
var state = getSearchState(cm);
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
@@ -100,14 +132,15 @@
if (!cursor.find(rev)) return;
}
cm.setSelection(cursor.from(), cursor.to());
- cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
+ cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
state.posFrom = cursor.from(); state.posTo = cursor.to();
});}
+
function clearSearch(cm) {cm.operation(function() {
var state = getSearchState(cm);
state.lastQuery = state.query;
if (!state.query) return;
- state.query = null;
+ state.query = state.queryText = null;
cm.removeOverlay(state.overlay);
if (state.annotate) { state.annotate.clear(); state.annotate = null; }
});}
@@ -116,6 +149,7 @@
'Replace: (Use /re/ syntax for regexp search)';
var replacementQueryDialog = 'With: ';
var doReplaceConfirm = "Replace? ";
+
function replace(cm, all) {
if (cm.getOption("readOnly")) return;
var query = cm.getSelection() || getSearchState(cm).lastQuery;
@@ -159,6 +193,7 @@
}
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
+ CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
CodeMirror.commands.findNext = doSearch;
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
CodeMirror.commands.clearSearch = clearSearch;
diff --git a/addon/tern/tern.css b/addon/tern/tern.css
index 76fba33d4a..c4b8a2f77e 100644
--- a/addon/tern/tern.css
+++ b/addon/tern/tern.css
@@ -1,6 +1,7 @@
.CodeMirror-Tern-completion {
padding-left: 22px;
position: relative;
+ line-height: 1.5;
}
.CodeMirror-Tern-completion:before {
position: absolute;
diff --git a/addon/tern/tern.js b/addon/tern/tern.js
index dfb19b8451..42c3cdee24 100644
--- a/addon/tern/tern.js
+++ b/addon/tern/tern.js
@@ -216,7 +216,7 @@
var completion = data.completions[i], className = typeToIcon(completion.type);
if (data.guess) className += " " + cls + "guess";
completions.push({text: completion.name + after,
- displayText: completion.name,
+ displayText: completion.displayName || completion.name,
className: className,
data: completion});
}
diff --git a/demo/search.html b/demo/search.html
index 04ba7ac09a..21c34251e2 100644
--- a/demo/search.html
+++ b/demo/search.html
@@ -71,18 +71,20 @@
Search/Replace Demo
Demonstration of primitive search/replace functionality. The
- keybindings (which can be overridden by custom keymaps) are:
+ keybindings (which can be configured with custom keymaps) are:
- Ctrl-F / Cmd-F
- Start searching
- Ctrl-G / Cmd-G
- Find next
- Shift-Ctrl-G / Shift-Cmd-G
- Find previous
- Shift-Ctrl-F / Cmd-Option-F
- Replace
- Shift-Ctrl-R / Shift-Cmd-Option-F
- Replace all
+ - Alt-F
- Persistent search (dialog doesn't autoclose, enter to find next, shift-enter to find previous)
Searching is enabled by
including addon/search/search.js
diff --git a/demo/theme.html b/demo/theme.html
index a5599859ad..93d52510fb 100644
--- a/demo/theme.html
+++ b/demo/theme.html
@@ -13,11 +13,14 @@
+
+
+
@@ -29,6 +32,7 @@
+
@@ -38,6 +42,7 @@
+
@@ -85,11 +90,14 @@
Theme Demo
+
+
+
@@ -101,6 +109,7 @@ Theme Demo
+
@@ -111,6 +120,7 @@ Theme Demo
+
@@ -123,14 +133,20 @@ Theme Demo
});
var input = document.getElementById("select");
function selectTheme() {
- var theme = input.options[input.selectedIndex].innerHTML;
+ var theme = input.options[input.selectedIndex].textContent;
editor.setOption("theme", theme);
+ location.hash = "#" + theme;
}
- var choice = document.location.search &&
- decodeURIComponent(document.location.search.slice(1));
+ var choice = (location.hash && location.hash.slice(1)) ||
+ (document.location.search &&
+ decodeURIComponent(document.location.search.slice(1)));
if (choice) {
input.value = choice;
editor.setOption("theme", choice);
}
+ CodeMirror.on(window, "hashchange", function() {
+ var theme = location.hash.slice(1);
+ if (theme) { input.value = theme; selectTheme(); }
+ });
diff --git a/doc/compress.html b/doc/compress.html
index ecbc406591..f64f4bbfaf 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@ Script compression helper
Version: