add multiline string support to editor find

(works for plain text searches not regexps)
This commit is contained in:
nightwing 2012-05-08 14:11:00 +04:00
commit 707adc2ff5

View file

@ -63,7 +63,7 @@ var Range = require("./range").Range;
* * `wholeWord`: `false` * * `wholeWord`: `false`
* * `scope`: `ALL` * * `scope`: `ALL`
* * `regExp`: `false` * * `regExp`: `false`
* *
**/ **/
var Search = function() { var Search = function() {
@ -88,7 +88,7 @@ Search.SELECTION = 2;
* - options (Object): An object containing all the new search properties * - options (Object): An object containing all the new search properties
* *
* Sets the search options via the `options` parameter. * Sets the search options via the `options` parameter.
* *
**/ **/
this.set = function(options) { this.set = function(options) {
oop.mixin(this.$options, options); oop.mixin(this.$options, options);
@ -99,8 +99,8 @@ Search.SELECTION = 2;
* Search.getOptions() -> Object * Search.getOptions() -> Object
* *
* [Returns an object containing all the search options.]{: #Search.getOptions} * [Returns an object containing all the search options.]{: #Search.getOptions}
* *
**/ **/
this.getOptions = function() { this.getOptions = function() {
return lang.copyObject(this.$options); return lang.copyObject(this.$options);
}; };
@ -110,16 +110,14 @@ Search.SELECTION = 2;
* - session (EditSession): The session to search with * - session (EditSession): The session to search with
* *
* Searches for `options.needle`. If found, this method returns the [[Range `Range`]] where the text first occurs. If `options.backwards` is `true`, the search goes backwards in the session. * Searches for `options.needle`. If found, this method returns the [[Range `Range`]] where the text first occurs. If `options.backwards` is `true`, the search goes backwards in the session.
* *
**/ **/
this.find = function(session) { this.find = function(session) {
if (!this.$options.needle) if (!this.$options.needle)
return null; return null;
var iterator = this.$options.backwards var iterator = this.$matchIterator(session);
? this.$backwardMatchIterator(session)
: this.$forwardMatchIterator(session);
if (!iterator) if (!iterator)
return false; return false;
@ -137,17 +135,15 @@ Search.SELECTION = 2;
* - session (EditSession): The session to search with * - session (EditSession): The session to search with
* *
* Searches for all occurances `options.needle`. If found, this method returns an array of [[Range `Range`s]] where the text first occurs. If `options.backwards` is `true`, the search goes backwards in the session. * Searches for all occurances `options.needle`. If found, this method returns an array of [[Range `Range`s]] where the text first occurs. If `options.backwards` is `true`, the search goes backwards in the session.
* *
**/ **/
this.findAll = function(session) { this.findAll = function(session) {
var options = this.$options; var options = this.$options;
if (!options.needle) if (!options.needle)
return []; return [];
var iterator = options.backwards var iterator = this.$matchIterator(session);
? this.$backwardMatchIterator(session)
: this.$forwardMatchIterator(session);
if (!iterator) if (!iterator)
return false; return false;
@ -173,16 +169,14 @@ Search.SELECTION = 2;
* + (String): If `options.regExp` is `true`, this function returns `input` with the replacement already made. Otherwise, this function just returns `replacement`.<br/> * + (String): If `options.regExp` is `true`, this function returns `input` with the replacement already made. Otherwise, this function just returns `replacement`.<br/>
* If `options.needle` was not found, this function returns `null`. * If `options.needle` was not found, this function returns `null`.
* *
* Searches for `options.needle` in `input`, and, if found, replaces it with `replacement`. * Searches for `options.needle` in `input`, and, if found, replaces it with `replacement`.
* *
* **/
*
**/
this.replace = function(input, replacement) { this.replace = function(input, replacement) {
var re = this.$assembleRegExp(); var re = this.$assembleRegExp();
if (!re) if (!re)
return; return;
var match = re.exec(input); var match = re.exec(input);
if (match && match[0].length == input.length) { if (match && match[0].length == input.length) {
if (this.$options.regExp) { if (this.$options.regExp) {
@ -196,89 +190,97 @@ Search.SELECTION = 2;
}; };
/** internal, hide /** internal, hide
* Search.$forwardMatchIterator(session) -> String | Boolean * Search.$matchIterator(session) -> String | Boolean
* - session (EditSession): The session to search with * - session (EditSession): The session to search with
* *
* *
* *
**/ **/
this.$forwardMatchIterator = function(session) { this.$matchIterator = function(session) {
var re = this.$assembleRegExp(); var re = this.$assembleRegExp();
if (!re) if (!re)
return false; return false;
var self = this;
return { var self = this, callback, backwards = this.$options.backwards;
forEach: function(callback) {
self.$forwardLineIterator(session).forEach(function(line, startIndex, row) {
if (startIndex) {
line = line.substring(startIndex);
}
var matches = []; if (this.$options.$isMultiLine) {
var matchIterator = function(line, startIndex, row) {
var startLine = line;
if (startIndex)
line = line.substring(startIndex);
line.replace(re, function(str) { var len = re.length;
var offset = arguments[arguments.length-2]; var part = re[0];
matches.push({ if (line.slice(-part.length) != part)
str: str, return;
offset: startIndex + offset
});
return str;
});
for (var i=0; i<matches.length; i++) { for (var i = 1; i < len - 1; i++)
var match = matches[i]; if (re[i] != session.getLine(row + i))
var range = self.$rangeFromMatch(row, match.offset, match.str.length); return;
if (callback(range))
return true;
}
}); part = re[len - 1];
if (session.getLine(row + len - 1).slice(0, part.length) != part)
return;
var range = new Range(
row, startLine.length - re[0].length,
row + len - 1, re[len - 1].length
);
if (callback(range))
return true;
} }
}; } else if (backwards) {
}; var matchIterator = function(line, startIndex, row) {
if (startIndex)
line = line.substring(startIndex);
/** internal, hide var matches = [];
* Search.$backwardMatchIterator(session) -> String
* - session (EditSession): The session to search with
*
*
*
**/
this.$backwardMatchIterator = function(session) {
var re = this.$assembleRegExp();
if (!re)
return false;
var self = this;
return { line.replace(re, function(str) {
forEach: function(callback) { var offset = arguments[arguments.length-2];
self.$backwardLineIterator(session).forEach(function(line, startIndex, row) { matches.push({
if (startIndex) { str: str,
line = line.substring(startIndex); offset: startIndex + offset
}
var matches = [];
line.replace(re, function(str, offset) {
matches.push({
str: str,
offset: startIndex + offset
});
return str;
}); });
return str;
for (var i=matches.length-1; i>= 0; i--) {
var match = matches[i];
var range = self.$rangeFromMatch(row, match.offset, match.str.length);
if (callback(range))
return true;
}
}); });
for (var i=matches.length-1; i>= 0; i--) {
var match = matches[i];
var range = self.$rangeFromMatch(row, match.offset, match.str.length);
if (callback(range))
return true;
}
} }
}; } else {
var matchIterator = function(line, startIndex, row) {
if (startIndex)
line = line.substring(startIndex);
var matches = [];
line.replace(re, function(str) {
var offset = arguments[arguments.length-2];
matches.push({
str: str,
offset: startIndex + offset
});
return str;
});
for (var i=0; i<matches.length; i++) {
var match = matches[i];
var range = self.$rangeFromMatch(row, match.offset, match.str.length);
if (callback(range))
return true;
}
}
}
return {forEach: function(_callback) {
callback = _callback;
self.$lineIterator(session).forEach(matchIterator);
}};
}; };
this.$rangeFromMatch = function(row, column, length) { this.$rangeFromMatch = function(row, column, length) {
@ -289,12 +291,18 @@ Search.SELECTION = 2;
if (typeof this.$options.needle != 'string') if (typeof this.$options.needle != 'string')
return this.$options.needle; return this.$options.needle;
if (this.$options.regExp) { var needle = this.$options.needle;
var needle = this.$options.needle;
} else { if (!this.$options.regExp) {
needle = lang.escapeRegExp(this.$options.needle); if (/[\n\r]/.test(needle)){
this.$options.$isMultiLine = true;
return needle.split(/\r\n|\r|\n/)
}
needle = lang.escapeRegExp(needle);
} }
this.$options.$isMultiLine = false;
if (this.$options.wholeWord) { if (this.$options.wholeWord) {
needle = "\\b" + needle + "\\b"; needle = "\\b" + needle + "\\b";
} }
@ -310,15 +318,16 @@ Search.SELECTION = 2;
catch(e) { catch(e) {
return false; return false;
} }
return re; return re;
}; };
this.$forwardLineIterator = function(session) { this.$lineIterator = function(session) {
var searchSelection = this.$options.scope == Search.SELECTION; var searchSelection = this.$options.scope == Search.SELECTION;
var backwards = this.$options.backwards;
var range = this.$options.range || session.getSelection().getRange(); var range = this.$options.range || session.getSelection().getRange();
var start = this.$options.start || range[searchSelection ? "start" : "end"]; var start = this.$options.start || range[searchSelection != backwards ? "start" : "end"];
var firstRow = searchSelection ? range.start.row : 0; var firstRow = searchSelection ? range.start.row : 0;
var firstColumn = searchSelection ? range.start.column : 0; var firstColumn = searchSelection ? range.start.column : 0;
@ -338,8 +347,8 @@ Search.SELECTION = 2;
return line; return line;
} }
return { if (!backwards) {
forEach: function(callback) { var forEach = function(callback) {
var row = start.row; var row = start.row;
var line = getLine(row); var line = getLine(row);
@ -349,10 +358,8 @@ Search.SELECTION = 2;
inWrap = false; inWrap = false;
while (!callback(line, startIndex, row)) { while (!callback(line, startIndex, row)) {
if (stop)
if (stop) {
return; return;
}
row++; row++;
startIndex = 0; startIndex = 0;
@ -373,23 +380,8 @@ Search.SELECTION = 2;
line = getLine(row); line = getLine(row);
} }
} }
}; } else {
}; var forEach = function(callback) {
this.$backwardLineIterator = function(session) {
var searchSelection = this.$options.scope == Search.SELECTION;
var range = this.$options.range || session.getSelection().getRange();
var start = this.$options.start || range[searchSelection ? "end" : "start"];
var firstRow = searchSelection ? range.start.row : 0;
var firstColumn = searchSelection ? range.start.column : 0;
var lastRow = searchSelection ? range.end.row : session.getLength() - 1;
var wrap = this.$options.wrap;
return {
forEach : function(callback) {
var row = start.row; var row = start.row;
var line = session.getLine(row).substring(0, start.column); var line = session.getLine(row).substring(0, start.column);
@ -398,7 +390,6 @@ Search.SELECTION = 2;
var inWrap = false; var inWrap = false;
while (!callback(line, startIndex, row)) { while (!callback(line, startIndex, row)) {
if (stop) if (stop)
return; return;
@ -429,7 +420,9 @@ Search.SELECTION = 2;
startIndex = start.column; startIndex = start.column;
} }
} }
}; }
return {forEach: forEach};
}; };
}).call(Search.prototype); }).call(Search.prototype);