add first search support
This commit is contained in:
parent
61ac194472
commit
737d2ed9d0
7 changed files with 365 additions and 16 deletions
|
|
@ -54,6 +54,7 @@
|
||||||
<script src="../src/ace/mode/XmlHighlightRules.js" type="text/javascript" charset="utf-8"></script>
|
<script src="../src/ace/mode/XmlHighlightRules.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="../src/ace/mode/MatchingBraceOutdent.js" type="text/javascript" charset="utf-8"></script>
|
<script src="../src/ace/mode/MatchingBraceOutdent.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="../src/ace/MEventEmitter.js" type="text/javascript" charset="utf-8"></script>
|
<script src="../src/ace/MEventEmitter.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="../src/ace/Search.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="../src/ace/Selection.js" type="text/javascript" charset="utf-8"></script>
|
<script src="../src/ace/Selection.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="../src/ace/Document.js" type="text/javascript" charset="utf-8"></script>
|
<script src="../src/ace/Document.js" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="../src/ace/Tokenizer.js" type="text/javascript" charset="utf-8"></script>
|
<script src="../src/ace/Tokenizer.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ ace.Editor = function(renderer, doc) {
|
||||||
this.$highlightLineMarker = null;
|
this.$highlightLineMarker = null;
|
||||||
this.$blockScrolling = false;
|
this.$blockScrolling = false;
|
||||||
|
|
||||||
|
this.$search = new ace.Search().set({
|
||||||
|
wrap: true
|
||||||
|
});
|
||||||
|
|
||||||
this.setDocument(doc || new ace.Document(""));
|
this.setDocument(doc || new ace.Document(""));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -687,4 +691,33 @@ ace.Editor = function(renderer, doc) {
|
||||||
this.clearSelection();
|
this.clearSelection();
|
||||||
this.selection.moveCursorWordLeft();
|
this.selection.moveCursorWordLeft();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.find = function(needle) {
|
||||||
|
this.clearSelection();
|
||||||
|
this.$search.set({needle: needle});
|
||||||
|
this.findNext();
|
||||||
|
},
|
||||||
|
|
||||||
|
this.findNext = function() {
|
||||||
|
this.$find(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.findPrevious = function() {
|
||||||
|
this.$find(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$find = function(backwards) {
|
||||||
|
if (!this.selection.isEmpty()) {
|
||||||
|
this.$search.set({needle: this.doc.getTextRange(this.getSelectionRange())});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$search.set({
|
||||||
|
backwards: backwards
|
||||||
|
});
|
||||||
|
|
||||||
|
var range = this.$search.find(this.doc);
|
||||||
|
if (range)
|
||||||
|
this.selection.setSelectionRange(range);
|
||||||
|
};
|
||||||
|
|
||||||
}).call(ace.Editor.prototype);
|
}).call(ace.Editor.prototype);
|
||||||
|
|
@ -30,21 +30,21 @@ ace.KeyBinding = function(element, editor) {
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
this.keys = {
|
this.keys = {
|
||||||
8: "Backspace",
|
8 : "Backspace",
|
||||||
9: "Tab",
|
9 : "Tab",
|
||||||
16: "Shift",
|
16 : "Shift",
|
||||||
17: "Control",
|
17 : "Control",
|
||||||
18: "Alt",
|
18 : "Alt",
|
||||||
33: "PageUp",
|
33 : "PageUp",
|
||||||
34: "PageDown",
|
34 : "PageDown",
|
||||||
35: "End",
|
35 : "End",
|
||||||
36: "Home",
|
36 : "Home",
|
||||||
37: "Left",
|
37 : "Left",
|
||||||
38: "Up",
|
38 : "Up",
|
||||||
39: "Right",
|
39 : "Right",
|
||||||
40: "Down",
|
40 : "Down",
|
||||||
46: "Delete",
|
46 : "Delete",
|
||||||
91: "Meta"
|
91 : "Meta"
|
||||||
};
|
};
|
||||||
|
|
||||||
this["Control-A"] = function() {
|
this["Control-A"] = function() {
|
||||||
|
|
@ -66,6 +66,19 @@ ace.KeyBinding = function(element, editor) {
|
||||||
this.editor.toggleCommentLines();
|
this.editor.toggleCommentLines();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this["Control-K"] = function() {
|
||||||
|
this.editor.findNext();
|
||||||
|
};
|
||||||
|
|
||||||
|
this["Control-Shift-K"] = function() {
|
||||||
|
this.editor.findPrevious();
|
||||||
|
};
|
||||||
|
|
||||||
|
this["Control-F"] = function() {
|
||||||
|
var needle = prompt("Find:");
|
||||||
|
this.editor.find(needle);
|
||||||
|
};
|
||||||
|
|
||||||
this["Control-Alt-Up"] = function() {
|
this["Control-Alt-Up"] = function() {
|
||||||
this.editor.copyLinesUp();
|
this.editor.copyLinesUp();
|
||||||
};
|
};
|
||||||
|
|
@ -209,7 +222,8 @@ ace.KeyBinding = function(element, editor) {
|
||||||
this["Tab"] = function() {
|
this["Tab"] = function() {
|
||||||
if (this.selection.isMultiLine()) {
|
if (this.selection.isMultiLine()) {
|
||||||
this.editor.blockIndent();
|
this.editor.blockIndent();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
this.editor.onTextInput("\t");
|
this.editor.onTextInput("\t");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
156
src/ace/Search.js
Normal file
156
src/ace/Search.js
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
ace.provide("ace.Search");
|
||||||
|
|
||||||
|
ace.Search = function() {
|
||||||
|
this.$options = {
|
||||||
|
needle: "",
|
||||||
|
backwards: false,
|
||||||
|
wrap: false,
|
||||||
|
caseSensitive: false,
|
||||||
|
wholeWord: false
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ace.Search.ALL = 1;
|
||||||
|
ace.Search.SELECTION = 2;
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
this.set = function(options) {
|
||||||
|
ace.mixin(this.$options, options);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.find = function(doc) {
|
||||||
|
var needle = this.$options.needle;
|
||||||
|
if (!this.$options.needle)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (this.$options.backwards) {
|
||||||
|
return this.$findBackward(doc);
|
||||||
|
} else {
|
||||||
|
return this.$findForward(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$assembleRegExp = function() {
|
||||||
|
var needle = ace.escapeRegExp(this.$options.needle);
|
||||||
|
if (this.$options.wholeWord) {
|
||||||
|
needle = "\\b" + needle + "\\b";
|
||||||
|
}
|
||||||
|
|
||||||
|
var modifier = "g";
|
||||||
|
if (this.$options.caseSensitive) {
|
||||||
|
modifier += "i";
|
||||||
|
}
|
||||||
|
|
||||||
|
var re = new RegExp(needle, modifier);
|
||||||
|
return re;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$findForward = function(doc) {
|
||||||
|
var start = doc.getSelection().getCursor();
|
||||||
|
var row = start.row;
|
||||||
|
var column = start.column;
|
||||||
|
|
||||||
|
var startRow = row;
|
||||||
|
|
||||||
|
var line = doc.getLine(row);
|
||||||
|
var wrapped = false;
|
||||||
|
|
||||||
|
var re = this.$assembleRegExp();
|
||||||
|
re.lastIndex = column;
|
||||||
|
|
||||||
|
do {
|
||||||
|
var match = re.exec(line);
|
||||||
|
if (!match) {
|
||||||
|
if (row == startRow && wrapped) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
row++;
|
||||||
|
|
||||||
|
if (row >= doc.getLength()) {
|
||||||
|
if (this.$options.wrap) {
|
||||||
|
row = 0;
|
||||||
|
wrapped = true;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line = doc.getLine(row);
|
||||||
|
re.lastIndex = 0;
|
||||||
|
}
|
||||||
|
} while(!match);
|
||||||
|
|
||||||
|
var range = {
|
||||||
|
start: {
|
||||||
|
row: row,
|
||||||
|
column: match.index
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
row: row,
|
||||||
|
column: match.index + match[0].length
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$findBackward = function(doc) {
|
||||||
|
var start = doc.getSelection().getRange().start;
|
||||||
|
var row = start.row;
|
||||||
|
var column = start.column;
|
||||||
|
|
||||||
|
var startRow = row;
|
||||||
|
|
||||||
|
var line = doc.getLine(row).substring(0, column);
|
||||||
|
var wrapped = false;
|
||||||
|
|
||||||
|
var re = this.$assembleRegExp();
|
||||||
|
|
||||||
|
var found = false;
|
||||||
|
var lastOffset = 0;
|
||||||
|
var match = "";
|
||||||
|
|
||||||
|
do {
|
||||||
|
line.replace(re, function(str, offset) {
|
||||||
|
match = str;
|
||||||
|
found = true;
|
||||||
|
lastOffset = offset;
|
||||||
|
return str;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
if (row == startRow && wrapped) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
row--;
|
||||||
|
|
||||||
|
if (row < 0) {
|
||||||
|
if (this.$options.wrap) {
|
||||||
|
row = doc.getLength() - 1;
|
||||||
|
wrapped = true;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line = doc.getLine(row);
|
||||||
|
}
|
||||||
|
} while(!found);
|
||||||
|
|
||||||
|
var range = {
|
||||||
|
start: {
|
||||||
|
row: row,
|
||||||
|
column: lastOffset
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
row: row,
|
||||||
|
column: lastOffset + match.length
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
|
||||||
|
}).call(ace.Search.prototype);
|
||||||
|
|
@ -102,6 +102,11 @@ ace.Selection = function(doc) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.setSelectionRange = function(range) {
|
||||||
|
this.setSelectionAnchor(range.start.row, range.start.column);
|
||||||
|
this.selectTo(range.end.row, range.end.column);
|
||||||
|
};
|
||||||
|
|
||||||
this.$moveSelection = function(mover) {
|
this.$moveSelection = function(mover) {
|
||||||
var changed = false;
|
var changed = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,12 @@ ace.inherits = function(ctor, superCtor) {
|
||||||
ctor.prototype.constructor = ctor;
|
ctor.prototype.constructor = ctor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ace.mixin = function(obj, mixin) {
|
||||||
|
for (var key in mixin) {
|
||||||
|
obj[key] = mixin[key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ace.implement = function(proto, mixin) {
|
ace.implement = function(proto, mixin) {
|
||||||
mixin.call(proto);
|
mixin.call(proto);
|
||||||
};
|
};
|
||||||
|
|
@ -175,6 +181,10 @@ ace.isArray = function(value) {
|
||||||
return Object.prototype.toString.call(value) == "[object Array]";
|
return Object.prototype.toString.call(value) == "[object Array]";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ace.escapeRegExp = function(str) {
|
||||||
|
return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
|
||||||
|
};
|
||||||
|
|
||||||
ace.bind = function(fcn, context) {
|
ace.bind = function(fcn, context) {
|
||||||
return function() {
|
return function() {
|
||||||
return fcn.apply(context, arguments);
|
return fcn.apply(context, arguments);
|
||||||
|
|
|
||||||
130
src/test/SearchTest.js
Normal file
130
src/test/SearchTest.js
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
var SearchTest = new TestCase("SearchTest", {
|
||||||
|
|
||||||
|
"test: configure the search object" : function() {
|
||||||
|
var search = new ace.Search();
|
||||||
|
search.set({
|
||||||
|
needle: "juhu",
|
||||||
|
scope: ace.Search.ALL
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: find simple text in document" : function() {
|
||||||
|
var doc = new ace.Document(["juhu kinners 123", "456"]);
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "kinners"
|
||||||
|
});
|
||||||
|
|
||||||
|
var range = search.find(doc);
|
||||||
|
assertPosition(0, 5, range.start);
|
||||||
|
assertPosition(0, 12, range.end);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: find simple text in next line" : function() {
|
||||||
|
var doc = new ace.Document(["abc", "juhu kinners 123", "456"]);
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "kinners"
|
||||||
|
});
|
||||||
|
|
||||||
|
var range = search.find(doc);
|
||||||
|
assertPosition(1, 5, range.start);
|
||||||
|
assertPosition(1, 12, range.end);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: find text starting at cursor position" : function() {
|
||||||
|
var doc = new ace.Document(["juhu kinners", "juhu kinners 123"]);
|
||||||
|
doc.getSelection().moveCursorTo(0, 6);
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "kinners"
|
||||||
|
});
|
||||||
|
|
||||||
|
var range = search.find(doc);
|
||||||
|
assertPosition(1, 5, range.start);
|
||||||
|
assertPosition(1, 12, range.end);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: wrap search is off by default" : function() {
|
||||||
|
var doc = new ace.Document(["abc", "juhu kinners 123", "456"]);
|
||||||
|
doc.getSelection().moveCursorTo(2, 1);
|
||||||
|
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "kinners"
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEquals(null, search.find(doc));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: wrap search should wrap at file end" : function() {
|
||||||
|
var doc = new ace.Document(["abc", "juhu kinners 123", "456"]);
|
||||||
|
doc.getSelection().moveCursorTo(2, 1);
|
||||||
|
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "kinners",
|
||||||
|
wrap: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var range = search.find(doc);
|
||||||
|
assertPosition(1, 5, range.start);
|
||||||
|
assertPosition(1, 12, range.end);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: wrap search with no match should return 'null'": function() {
|
||||||
|
var doc = new ace.Document(["abc", "juhu kinners 123", "456"]);
|
||||||
|
doc.getSelection().moveCursorTo(2, 1);
|
||||||
|
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "xyz",
|
||||||
|
wrap: true
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEquals(null, search.find(doc));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: case sensitive is by default off": function() {
|
||||||
|
var doc = new ace.Document(["abc", "juhu kinners 123", "456"]);
|
||||||
|
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "JUHU"
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEquals(null, search.find(doc));
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: case sensitive search": function() {
|
||||||
|
var doc = new ace.Document(["abc", "juhu kinners 123", "456"]);
|
||||||
|
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "KINNERS",
|
||||||
|
caseSensitive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var range = search.find(doc);
|
||||||
|
assertPosition(1, 5, range.start);
|
||||||
|
assertPosition(1, 12, range.end);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: whole word search should not match inside of words": function() {
|
||||||
|
var doc = new ace.Document(["juhukinners", "juhu kinners 123", "456"]);
|
||||||
|
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "kinners",
|
||||||
|
wholeWord: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var range = search.find(doc);
|
||||||
|
assertPosition(1, 5, range.start);
|
||||||
|
assertPosition(1, 12, range.end);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: find backwards": function() {
|
||||||
|
var doc = new ace.Document(["juhu juhu juhu juhu"]);
|
||||||
|
doc.getSelection().moveCursorTo(0, 10);
|
||||||
|
var search = new ace.Search().set({
|
||||||
|
needle: "juhu",
|
||||||
|
backwards: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var range = search.find(doc);
|
||||||
|
assertPosition(0, 5, range.start);
|
||||||
|
assertPosition(0, 9, range.end);
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue