codemirror-rails 2.21.1 → 2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +8 -0
- data/lib/codemirror/rails/version.rb +2 -2
- data/vendor/assets/javascripts/codemirror.js +160 -86
- data/vendor/assets/javascripts/codemirror/modes/clike.js +2 -2
- data/vendor/assets/javascripts/codemirror/modes/clojure.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/ecl.js +203 -0
- data/vendor/assets/javascripts/codemirror/modes/gfm.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/go.js +20 -22
- data/vendor/assets/javascripts/codemirror/modes/less.js +5 -2
- data/vendor/assets/javascripts/codemirror/modes/markdown.js +58 -24
- data/vendor/assets/javascripts/codemirror/modes/pascal.js +2 -46
- data/vendor/assets/javascripts/codemirror/modes/perl.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/php.js +53 -24
- data/vendor/assets/javascripts/codemirror/modes/properties.js +57 -0
- data/vendor/assets/javascripts/codemirror/modes/rpm-spec.css +5 -0
- data/vendor/assets/javascripts/codemirror/modes/rpm-spec.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/ruby.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/smalltalk.js +16 -16
- data/vendor/assets/javascripts/codemirror/modes/verilog.js +194 -194
- data/vendor/assets/javascripts/codemirror/modes/xml.js +15 -8
- data/vendor/assets/javascripts/codemirror/utils/dialog.js +63 -0
- data/vendor/assets/javascripts/codemirror/utils/foldcode.js +186 -0
- data/vendor/assets/javascripts/codemirror/utils/formatting.js +294 -0
- data/vendor/assets/javascripts/codemirror/utils/javascript-hint.js +132 -0
- data/vendor/assets/javascripts/codemirror/utils/match-highlighter.js +44 -0
- data/vendor/assets/javascripts/codemirror/{overlay.js → utils/overlay.js} +0 -0
- data/vendor/assets/javascripts/codemirror/utils/runmode.js +49 -0
- data/vendor/assets/javascripts/codemirror/utils/search.js +114 -0
- data/vendor/assets/javascripts/codemirror/utils/searchcursor.js +117 -0
- data/vendor/assets/javascripts/codemirror/utils/simple-hint.js +66 -0
- data/vendor/assets/stylesheets/codemirror.css +3 -0
- data/vendor/assets/stylesheets/codemirror/modes/properties.css +3 -0
- data/vendor/assets/stylesheets/codemirror/themes/rubyblue.css +1 -1
- data/vendor/assets/stylesheets/codemirror/utils/dialog.css +23 -0
- data/vendor/assets/stylesheets/codemirror/utils/simple-hint.css +16 -0
- metadata +20 -6
- data/vendor/assets/javascripts/codemirror/runmode.js +0 -27
@@ -0,0 +1,132 @@
|
|
1
|
+
(function () {
|
2
|
+
function forEach(arr, f) {
|
3
|
+
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
|
4
|
+
}
|
5
|
+
|
6
|
+
function arrayContains(arr, item) {
|
7
|
+
if (!Array.prototype.indexOf) {
|
8
|
+
var i = arr.length;
|
9
|
+
while (i--) {
|
10
|
+
if (arr[i] === item) {
|
11
|
+
return true;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
return false;
|
15
|
+
}
|
16
|
+
return arr.indexOf(item) != -1;
|
17
|
+
}
|
18
|
+
|
19
|
+
function scriptHint(editor, keywords, getToken) {
|
20
|
+
// Find the token at the cursor
|
21
|
+
var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
|
22
|
+
// If it's not a 'word-style' token, ignore the token.
|
23
|
+
if (!/^[\w$_]*$/.test(token.string)) {
|
24
|
+
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
|
25
|
+
className: token.string == "." ? "property" : null};
|
26
|
+
}
|
27
|
+
// If it is a property, find out what it is a property of.
|
28
|
+
while (tprop.className == "property") {
|
29
|
+
tprop = getToken(editor, {line: cur.line, ch: tprop.start});
|
30
|
+
if (tprop.string != ".") return;
|
31
|
+
tprop = getToken(editor, {line: cur.line, ch: tprop.start});
|
32
|
+
if (tprop.string == ')') {
|
33
|
+
var level = 1;
|
34
|
+
do {
|
35
|
+
tprop = getToken(editor, {line: cur.line, ch: tprop.start});
|
36
|
+
switch (tprop.string) {
|
37
|
+
case ')': level++; break;
|
38
|
+
case '(': level--; break;
|
39
|
+
default: break;
|
40
|
+
}
|
41
|
+
} while (level > 0)
|
42
|
+
tprop = getToken(editor, {line: cur.line, ch: tprop.start});
|
43
|
+
if (tprop.className == 'variable')
|
44
|
+
tprop.className = 'function';
|
45
|
+
else return; // no clue
|
46
|
+
}
|
47
|
+
if (!context) var context = [];
|
48
|
+
context.push(tprop);
|
49
|
+
}
|
50
|
+
return {list: getCompletions(token, context, keywords),
|
51
|
+
from: {line: cur.line, ch: token.start},
|
52
|
+
to: {line: cur.line, ch: token.end}};
|
53
|
+
}
|
54
|
+
|
55
|
+
CodeMirror.javascriptHint = function(editor) {
|
56
|
+
return scriptHint(editor, javascriptKeywords,
|
57
|
+
function (e, cur) {return e.getTokenAt(cur);});
|
58
|
+
}
|
59
|
+
|
60
|
+
function getCoffeeScriptToken(editor, cur) {
|
61
|
+
// This getToken, it is for coffeescript, imitates the behavior of
|
62
|
+
// getTokenAt method in javascript.js, that is, returning "property"
|
63
|
+
// type and treat "." as indepenent token.
|
64
|
+
var token = editor.getTokenAt(cur);
|
65
|
+
if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
|
66
|
+
token.end = token.start;
|
67
|
+
token.string = '.';
|
68
|
+
token.className = "property";
|
69
|
+
}
|
70
|
+
else if (/^\.[\w$_]*$/.test(token.string)) {
|
71
|
+
token.className = "property";
|
72
|
+
token.start++;
|
73
|
+
token.string = token.string.replace(/\./, '');
|
74
|
+
}
|
75
|
+
return token;
|
76
|
+
}
|
77
|
+
|
78
|
+
CodeMirror.coffeescriptHint = function(editor) {
|
79
|
+
return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken);
|
80
|
+
}
|
81
|
+
|
82
|
+
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
|
83
|
+
"toUpperCase toLowerCase split concat match replace search").split(" ");
|
84
|
+
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
|
85
|
+
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
|
86
|
+
var funcProps = "prototype apply call bind".split(" ");
|
87
|
+
var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
|
88
|
+
"if in instanceof new null return switch throw true try typeof var void while with").split(" ");
|
89
|
+
var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
|
90
|
+
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
|
91
|
+
|
92
|
+
function getCompletions(token, context, keywords) {
|
93
|
+
var found = [], start = token.string;
|
94
|
+
function maybeAdd(str) {
|
95
|
+
if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
|
96
|
+
}
|
97
|
+
function gatherCompletions(obj) {
|
98
|
+
if (typeof obj == "string") forEach(stringProps, maybeAdd);
|
99
|
+
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
|
100
|
+
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
|
101
|
+
for (var name in obj) maybeAdd(name);
|
102
|
+
}
|
103
|
+
|
104
|
+
if (context) {
|
105
|
+
// If this is a property, see if it belongs to some object we can
|
106
|
+
// find in the current environment.
|
107
|
+
var obj = context.pop(), base;
|
108
|
+
if (obj.className == "variable")
|
109
|
+
base = window[obj.string];
|
110
|
+
else if (obj.className == "string")
|
111
|
+
base = "";
|
112
|
+
else if (obj.className == "atom")
|
113
|
+
base = 1;
|
114
|
+
else if (obj.className == "function") {
|
115
|
+
if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
|
116
|
+
(typeof jQuery == 'function')) base = jQuery();
|
117
|
+
else if (window._ != null && (obj.string == '_') && (typeof _ == 'function')) base = _();
|
118
|
+
}
|
119
|
+
while (base != null && context.length)
|
120
|
+
base = base[context.pop().string];
|
121
|
+
if (base != null) gatherCompletions(base);
|
122
|
+
}
|
123
|
+
else {
|
124
|
+
// If not, just look in the window object and any local scope
|
125
|
+
// (reading into JS mode internals to get at the local variables)
|
126
|
+
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
|
127
|
+
gatherCompletions(window);
|
128
|
+
forEach(keywords, maybeAdd);
|
129
|
+
}
|
130
|
+
return found;
|
131
|
+
}
|
132
|
+
})();
|
@@ -0,0 +1,44 @@
|
|
1
|
+
// Define match-highlighter commands. Depends on searchcursor.js
|
2
|
+
// Use by attaching the following function call to the onCursorActivity event:
|
3
|
+
//myCodeMirror.matchHighlight(minChars);
|
4
|
+
// And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html)
|
5
|
+
|
6
|
+
(function() {
|
7
|
+
var DEFAULT_MIN_CHARS = 2;
|
8
|
+
|
9
|
+
function MatchHighlightState() {
|
10
|
+
this.marked = [];
|
11
|
+
}
|
12
|
+
function getMatchHighlightState(cm) {
|
13
|
+
return cm._matchHighlightState || (cm._matchHighlightState = new MatchHighlightState());
|
14
|
+
}
|
15
|
+
|
16
|
+
function clearMarks(cm) {
|
17
|
+
var state = getMatchHighlightState(cm);
|
18
|
+
for (var i = 0; i < state.marked.length; ++i)
|
19
|
+
state.marked[i].clear();
|
20
|
+
state.marked = [];
|
21
|
+
}
|
22
|
+
|
23
|
+
function markDocument(cm, className, minChars) {
|
24
|
+
clearMarks(cm);
|
25
|
+
minChars = (typeof minChars !== 'undefined' ? minChars : DEFAULT_MIN_CHARS);
|
26
|
+
if (cm.somethingSelected() && cm.getSelection().length >= minChars) {
|
27
|
+
var state = getMatchHighlightState(cm);
|
28
|
+
var query = cm.getSelection();
|
29
|
+
cm.operation(function() {
|
30
|
+
if (cm.lineCount() < 2000) { // This is too expensive on big documents.
|
31
|
+
for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
|
32
|
+
//Only apply matchhighlight to the matches other than the one actually selected
|
33
|
+
if (!(cursor.from().line === cm.getCursor(true).line && cursor.from().ch === cm.getCursor(true).ch))
|
34
|
+
state.marked.push(cm.markText(cursor.from(), cursor.to(), className));
|
35
|
+
}
|
36
|
+
}
|
37
|
+
});
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
CodeMirror.defineExtension("matchHighlight", function(className, minChars) {
|
42
|
+
markDocument(this, className, minChars);
|
43
|
+
});
|
44
|
+
})();
|
File without changes
|
@@ -0,0 +1,49 @@
|
|
1
|
+
CodeMirror.runMode = function(string, modespec, callback, options) {
|
2
|
+
var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
|
3
|
+
var isNode = callback.nodeType == 1;
|
4
|
+
var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
|
5
|
+
if (isNode) {
|
6
|
+
var node = callback, accum = [], col = 0;
|
7
|
+
callback = function(text, style) {
|
8
|
+
if (text == "\n") {
|
9
|
+
accum.push("<br>");
|
10
|
+
col = 0;
|
11
|
+
return;
|
12
|
+
}
|
13
|
+
var escaped = "";
|
14
|
+
// HTML-escape and replace tabs
|
15
|
+
for (var pos = 0;;) {
|
16
|
+
var idx = text.indexOf("\t", pos);
|
17
|
+
if (idx == -1) {
|
18
|
+
escaped += CodeMirror.htmlEscape(text.slice(pos));
|
19
|
+
col += text.length - pos;
|
20
|
+
break;
|
21
|
+
} else {
|
22
|
+
col += idx - pos;
|
23
|
+
escaped += CodeMirror.htmlEscape(text.slice(pos, idx));
|
24
|
+
var size = tabSize - col % tabSize;
|
25
|
+
col += size;
|
26
|
+
for (var i = 0; i < size; ++i) escaped += " ";
|
27
|
+
pos = idx + 1;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
if (style)
|
32
|
+
accum.push("<span class=\"cm-" + CodeMirror.htmlEscape(style) + "\">" + escaped + "</span>");
|
33
|
+
else
|
34
|
+
accum.push(escaped);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode);
|
38
|
+
for (var i = 0, e = lines.length; i < e; ++i) {
|
39
|
+
if (i) callback("\n");
|
40
|
+
var stream = new CodeMirror.StringStream(lines[i]);
|
41
|
+
while (!stream.eol()) {
|
42
|
+
var style = mode.token(stream, state);
|
43
|
+
callback(stream.current(), style, i, stream.start);
|
44
|
+
stream.start = stream.pos;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
if (isNode)
|
48
|
+
node.innerHTML = accum.join("");
|
49
|
+
};
|
@@ -0,0 +1,114 @@
|
|
1
|
+
// Define search commands. Depends on dialog.js or another
|
2
|
+
// implementation of the openDialog method.
|
3
|
+
|
4
|
+
// Replace works a little oddly -- it will do the replace on the next
|
5
|
+
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
|
6
|
+
// replace by making sure the match is no longer selected when hitting
|
7
|
+
// Ctrl-G.
|
8
|
+
|
9
|
+
(function() {
|
10
|
+
function SearchState() {
|
11
|
+
this.posFrom = this.posTo = this.query = null;
|
12
|
+
this.marked = [];
|
13
|
+
}
|
14
|
+
function getSearchState(cm) {
|
15
|
+
return cm._searchState || (cm._searchState = new SearchState());
|
16
|
+
}
|
17
|
+
function dialog(cm, text, shortText, f) {
|
18
|
+
if (cm.openDialog) cm.openDialog(text, f);
|
19
|
+
else f(prompt(shortText, ""));
|
20
|
+
}
|
21
|
+
function confirmDialog(cm, text, shortText, fs) {
|
22
|
+
if (cm.openConfirm) cm.openConfirm(text, fs);
|
23
|
+
else if (confirm(shortText)) fs[0]();
|
24
|
+
}
|
25
|
+
function parseQuery(query) {
|
26
|
+
var isRE = query.match(/^\/(.*)\/$/);
|
27
|
+
return isRE ? new RegExp(isRE[1]) : query;
|
28
|
+
}
|
29
|
+
var queryDialog =
|
30
|
+
'Search: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
|
31
|
+
function doSearch(cm, rev) {
|
32
|
+
var state = getSearchState(cm);
|
33
|
+
if (state.query) return findNext(cm, rev);
|
34
|
+
dialog(cm, queryDialog, "Search for:", function(query) {
|
35
|
+
cm.operation(function() {
|
36
|
+
if (!query || state.query) return;
|
37
|
+
state.query = parseQuery(query);
|
38
|
+
if (cm.lineCount() < 2000) { // This is too expensive on big documents.
|
39
|
+
for (var cursor = cm.getSearchCursor(query); cursor.findNext();)
|
40
|
+
state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching"));
|
41
|
+
}
|
42
|
+
state.posFrom = state.posTo = cm.getCursor();
|
43
|
+
findNext(cm, rev);
|
44
|
+
});
|
45
|
+
});
|
46
|
+
}
|
47
|
+
function findNext(cm, rev) {cm.operation(function() {
|
48
|
+
var state = getSearchState(cm);
|
49
|
+
var cursor = cm.getSearchCursor(state.query, rev ? state.posFrom : state.posTo);
|
50
|
+
if (!cursor.find(rev)) {
|
51
|
+
cursor = cm.getSearchCursor(state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
|
52
|
+
if (!cursor.find(rev)) return;
|
53
|
+
}
|
54
|
+
cm.setSelection(cursor.from(), cursor.to());
|
55
|
+
state.posFrom = cursor.from(); state.posTo = cursor.to();
|
56
|
+
})}
|
57
|
+
function clearSearch(cm) {cm.operation(function() {
|
58
|
+
var state = getSearchState(cm);
|
59
|
+
if (!state.query) return;
|
60
|
+
state.query = null;
|
61
|
+
for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear();
|
62
|
+
state.marked.length = 0;
|
63
|
+
})}
|
64
|
+
|
65
|
+
var replaceQueryDialog =
|
66
|
+
'Replace: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
|
67
|
+
var replacementQueryDialog = 'With: <input type="text" style="width: 10em">';
|
68
|
+
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
|
69
|
+
function replace(cm, all) {
|
70
|
+
dialog(cm, replaceQueryDialog, "Replace:", function(query) {
|
71
|
+
if (!query) return;
|
72
|
+
query = parseQuery(query);
|
73
|
+
dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
|
74
|
+
if (all) {
|
75
|
+
cm.operation(function() {
|
76
|
+
for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
|
77
|
+
if (typeof query != "string") {
|
78
|
+
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
|
79
|
+
cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];}));
|
80
|
+
} else cursor.replace(text);
|
81
|
+
}
|
82
|
+
});
|
83
|
+
} else {
|
84
|
+
clearSearch(cm);
|
85
|
+
var cursor = cm.getSearchCursor(query, cm.getCursor());
|
86
|
+
function advance() {
|
87
|
+
var start = cursor.from(), match;
|
88
|
+
if (!(match = cursor.findNext())) {
|
89
|
+
cursor = cm.getSearchCursor(query);
|
90
|
+
if (!(match = cursor.findNext()) ||
|
91
|
+
(cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
|
92
|
+
}
|
93
|
+
cm.setSelection(cursor.from(), cursor.to());
|
94
|
+
confirmDialog(cm, doReplaceConfirm, "Replace?",
|
95
|
+
[function() {doReplace(match);}, advance]);
|
96
|
+
}
|
97
|
+
function doReplace(match) {
|
98
|
+
cursor.replace(typeof query == "string" ? text :
|
99
|
+
text.replace(/\$(\d)/, function(w, i) {return match[i];}));
|
100
|
+
advance();
|
101
|
+
}
|
102
|
+
advance();
|
103
|
+
}
|
104
|
+
});
|
105
|
+
});
|
106
|
+
}
|
107
|
+
|
108
|
+
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
|
109
|
+
CodeMirror.commands.findNext = doSearch;
|
110
|
+
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
|
111
|
+
CodeMirror.commands.clearSearch = clearSearch;
|
112
|
+
CodeMirror.commands.replace = replace;
|
113
|
+
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
|
114
|
+
})();
|
@@ -0,0 +1,117 @@
|
|
1
|
+
(function(){
|
2
|
+
function SearchCursor(cm, query, pos, caseFold) {
|
3
|
+
this.atOccurrence = false; this.cm = cm;
|
4
|
+
if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
|
5
|
+
|
6
|
+
pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
|
7
|
+
this.pos = {from: pos, to: pos};
|
8
|
+
|
9
|
+
// The matches method is filled in based on the type of query.
|
10
|
+
// It takes a position and a direction, and returns an object
|
11
|
+
// describing the next occurrence of the query, or null if no
|
12
|
+
// more matches were found.
|
13
|
+
if (typeof query != "string") // Regexp match
|
14
|
+
this.matches = function(reverse, pos) {
|
15
|
+
if (reverse) {
|
16
|
+
var line = cm.getLine(pos.line).slice(0, pos.ch), match = line.match(query), start = 0;
|
17
|
+
while (match) {
|
18
|
+
var ind = line.indexOf(match[0]);
|
19
|
+
start += ind;
|
20
|
+
line = line.slice(ind + 1);
|
21
|
+
var newmatch = line.match(query);
|
22
|
+
if (newmatch) match = newmatch;
|
23
|
+
else break;
|
24
|
+
start++;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
else {
|
28
|
+
var line = cm.getLine(pos.line).slice(pos.ch), match = line.match(query),
|
29
|
+
start = match && pos.ch + line.indexOf(match[0]);
|
30
|
+
}
|
31
|
+
if (match)
|
32
|
+
return {from: {line: pos.line, ch: start},
|
33
|
+
to: {line: pos.line, ch: start + match[0].length},
|
34
|
+
match: match};
|
35
|
+
};
|
36
|
+
else { // String query
|
37
|
+
if (caseFold) query = query.toLowerCase();
|
38
|
+
var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
|
39
|
+
var target = query.split("\n");
|
40
|
+
// Different methods for single-line and multi-line queries
|
41
|
+
if (target.length == 1)
|
42
|
+
this.matches = function(reverse, pos) {
|
43
|
+
var line = fold(cm.getLine(pos.line)), len = query.length, match;
|
44
|
+
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
|
45
|
+
: (match = line.indexOf(query, pos.ch)) != -1)
|
46
|
+
return {from: {line: pos.line, ch: match},
|
47
|
+
to: {line: pos.line, ch: match + len}};
|
48
|
+
};
|
49
|
+
else
|
50
|
+
this.matches = function(reverse, pos) {
|
51
|
+
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
|
52
|
+
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
|
53
|
+
if (reverse ? offsetA >= pos.ch || offsetA != match.length
|
54
|
+
: offsetA <= pos.ch || offsetA != line.length - match.length)
|
55
|
+
return;
|
56
|
+
for (;;) {
|
57
|
+
if (reverse ? !ln : ln == cm.lineCount() - 1) return;
|
58
|
+
line = fold(cm.getLine(ln += reverse ? -1 : 1));
|
59
|
+
match = target[reverse ? --idx : ++idx];
|
60
|
+
if (idx > 0 && idx < target.length - 1) {
|
61
|
+
if (line != match) return;
|
62
|
+
else continue;
|
63
|
+
}
|
64
|
+
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
|
65
|
+
if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
|
66
|
+
return;
|
67
|
+
var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
|
68
|
+
return {from: reverse ? end : start, to: reverse ? start : end};
|
69
|
+
}
|
70
|
+
};
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
SearchCursor.prototype = {
|
75
|
+
findNext: function() {return this.find(false);},
|
76
|
+
findPrevious: function() {return this.find(true);},
|
77
|
+
|
78
|
+
find: function(reverse) {
|
79
|
+
var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
|
80
|
+
function savePosAndFail(line) {
|
81
|
+
var pos = {line: line, ch: 0};
|
82
|
+
self.pos = {from: pos, to: pos};
|
83
|
+
self.atOccurrence = false;
|
84
|
+
return false;
|
85
|
+
}
|
86
|
+
|
87
|
+
for (;;) {
|
88
|
+
if (this.pos = this.matches(reverse, pos)) {
|
89
|
+
this.atOccurrence = true;
|
90
|
+
return this.pos.match || true;
|
91
|
+
}
|
92
|
+
if (reverse) {
|
93
|
+
if (!pos.line) return savePosAndFail(0);
|
94
|
+
pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
|
95
|
+
}
|
96
|
+
else {
|
97
|
+
var maxLine = this.cm.lineCount();
|
98
|
+
if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
|
99
|
+
pos = {line: pos.line+1, ch: 0};
|
100
|
+
}
|
101
|
+
}
|
102
|
+
},
|
103
|
+
|
104
|
+
from: function() {if (this.atOccurrence) return this.pos.from;},
|
105
|
+
to: function() {if (this.atOccurrence) return this.pos.to;},
|
106
|
+
|
107
|
+
replace: function(newText) {
|
108
|
+
var self = this;
|
109
|
+
if (this.atOccurrence)
|
110
|
+
self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to);
|
111
|
+
}
|
112
|
+
};
|
113
|
+
|
114
|
+
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
|
115
|
+
return new SearchCursor(this, query, pos, caseFold);
|
116
|
+
});
|
117
|
+
})();
|