diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index d052fda8..4b4a6b4f 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -94,22 +94,25 @@ var Mode = function() { var startOuter = selection.start.column - 1; var endOuter = selection.end.column + 1; var line = session.getLine(selection.start.row); - var lineCols = line.length - 1; + var lineCols = line.length; var needle = line.substring(Math.max(startOuter, 0), Math.min(endOuter, lineCols)); // Make sure the outer characters are not part of the word. - if ((startOuter >= 0 && !/[^\w\d]/.test(needle.charAt(0))) || - (endOuter <= lineCols && !/[^\w\d]/.test(needle.charAt(needle.length - 1)))) + if ((startOuter >= 0 && /^[\w\d]/.test(needle)) || + (endOuter <= lineCols && /[\w\d]$/.test(needle))) return; needle = line.substring(selection.start.column, selection.end.column); if (!/^[\w\d]+$/.test(needle)) return; + var cursor = editor.getCursorPosition(); + var newOptions = { wrap: true, wholeWord: true, + caseSensitive: true, needle: needle }; @@ -117,9 +120,8 @@ var Mode = function() { editor.$search.set(newOptions); var ranges = editor.$search.findAll(session); - session.$selectionOccurrences = []; ranges.forEach(function(range) { - if (!range.contains(selection.start.row, selection.start.column)) { + if (!range.contains(cursor.row, cursor.column)) { var marker = session.addMarker(range, "ace_selected_word"); session.$selectionOccurrences.push(marker); } @@ -135,6 +137,8 @@ var Mode = function() { editor.session.$selectionOccurrences.forEach(function(marker) { editor.session.removeMarker(marker); }); + + editor.session.$selectionOccurrences = []; }; }).call(Mode.prototype); diff --git a/lib/ace/search.js b/lib/ace/search.js index aed0a8c5..9df4f6f8 100644 --- a/lib/ace/search.js +++ b/lib/ace/search.js @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -20,6 +21,7 @@ * * Contributor(s): * Fabian Jakobs + * Mihai Sucan * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -219,12 +221,16 @@ Search.SELECTION = 2; var lastRow = searchSelection ? range.end.row : session.getLength() - 1; var wrap = this.$options.wrap; + var inWrap = false; function getLine(row) { var line = session.getLine(row); if (searchSelection && row == range.end.row) { line = line.substring(0, range.end.column); } + if (inWrap && row == start.row) { + line = line.substring(0, start.column); + } return line; } @@ -236,6 +242,7 @@ Search.SELECTION = 2; var startIndex = start.column; var stop = false; + inWrap = false; while (!callback(line, startIndex, row)) { @@ -250,6 +257,7 @@ Search.SELECTION = 2; if (wrap) { row = firstRow; startIndex = firstColumn; + inWrap = true; } else { return; } @@ -283,6 +291,7 @@ Search.SELECTION = 2; var line = session.getLine(row).substring(0, start.column); var startIndex = 0; var stop = false; + var inWrap = false; while (!callback(line, startIndex, row)) { @@ -295,6 +304,7 @@ Search.SELECTION = 2; if (row < firstRow) { if (wrap) { row = lastRow; + inWrap = true; } else { return; } @@ -310,6 +320,9 @@ Search.SELECTION = 2; else if (row == lastRow) line = line.substring(0, range.end.column); } + + if (inWrap && row == start.row) + startIndex = start.column; } } }; diff --git a/lib/ace/test/all.js b/lib/ace/test/all.js index 4582ff11..19101373 100644 --- a/lib/ace/test/all.js +++ b/lib/ace/test/all.js @@ -51,6 +51,7 @@ async.concat( require("./search_test"), require("./selection_test"), require("./text_edit_test"), + require("./highlight_selected_word_test"), require("./mode/css_test"), require("./mode/css_tokenizer_test"), require("./mode/html_test"), @@ -59,6 +60,5 @@ async.concat( require("./mode/javascript_tokenizer_test"), require("./mode/text_test"), require("./mode/xml_test"), - require("./mode/xml_tokenizer_test"), - require("./issue57_test") + require("./mode/xml_tokenizer_test") ).exec(); diff --git a/lib/ace/test/all_browser.js b/lib/ace/test/all_browser.js index c11076ca..f3d5638e 100644 --- a/lib/ace/test/all_browser.js +++ b/lib/ace/test/all_browser.js @@ -20,7 +20,7 @@ async.concat( require("./selection_test"), require("./text_edit_test"), require("./virtual_renderer_test"), - require("./issue57_test"), + require("./highlight_selected_word_test"), require("./mode/css_test"), require("./mode/css_tokenizer_test"), require("./mode/html_test"), diff --git a/lib/ace/test/issue57_test.js b/lib/ace/test/highlight_selected_word_test.js similarity index 58% rename from lib/ace/test/issue57_test.js rename to lib/ace/test/highlight_selected_word_test.js index 1f58282e..34526333 100644 --- a/lib/ace/test/issue57_test.js +++ b/lib/ace/test/highlight_selected_word_test.js @@ -63,21 +63,23 @@ var lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "felis sodales. In dignissim magna eget nunc lobortis non " + "fringilla nibh ullamcorper. Donec facilisis malesuada elit " + "at egestas. Etiam bibendum, diam vitae tempor aliquet, dui " + - "libero vehicula odio, eget bibendum mauris velit eu lorem."; + "libero vehicula odio, eget bibendum mauris velit eu lorem.\n" + + "consectetur"; var Test = { setUp: function() { this.session = new EditSession(lipsum); this.editor = new Editor(new MockRenderer(), this.session); this.selection = this.session.getSelection(); + this.search = this.editor.$search; }, - "issue 57: highlight selected words by default": function() { + "test: highlight selected words by default": function() { assert.equal(this.editor.getHighlightSelectedWord(), true); }, - "issue 57: higlight a word": function() { - this.selection.moveCursorTo(0, 9); + "test: highlight a word": function() { + this.editor.moveCursorTo(0, 9); this.selection.selectWord(); var range = this.selection.getRange(); @@ -85,7 +87,19 @@ var Test = { assert.equal(this.session.$selectionOccurrences.length, 1); }, - "issue 57: higlight another word": function() { + "test: highlight a word and clear highlight": function() { + this.editor.moveCursorTo(0, 8); + this.selection.selectWord(); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "ipsum"); + assert.equal(this.session.$selectionOccurrences.length, 1); + + this.session.getMode().clearSelectionHighlight(this.editor); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: highlight another word": function() { this.selection.moveCursorTo(0, 14); this.selection.selectWord(); @@ -94,12 +108,12 @@ var Test = { assert.equal(this.session.$selectionOccurrences.length, 3); }, - "issue 57: no selection, no highlight": function() { + "test: no selection, no highlight": function() { this.selection.clearSelection(); assert.equal(this.session.$selectionOccurrences.length, 0); }, - "issue 57: select a word, no highlight": function() { + "test: select a word, no highlight": function() { this.editor.setHighlightSelectedWord(false); this.selection.moveCursorTo(0, 14); this.selection.selectWord(); @@ -107,7 +121,86 @@ var Test = { var range = this.selection.getRange(); assert.equal(this.session.getTextRange(range), "dolor"); assert.equal(this.session.$selectionOccurrences.length, 0); - } + }, + + "test: select a word with no matches": function() { + this.editor.setHighlightSelectedWord(true); + + var currentOptions = this.search.getOptions(); + var newOptions = { + wrap: true, + wholeWord: true, + caseSensitive: true, + needle: "Mauris" + }; + this.search.set(newOptions); + + var match = this.search.find(this.session); + assert.notEqual(match, null, "found a match for 'Mauris'"); + + this.search.set(currentOptions); + + this.selection.setSelectionRange(match); + + assert.equal(this.session.getTextRange(match), "Mauris"); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: partial word selection 1": function() { + this.selection.moveCursorTo(0, 14); + this.selection.selectWord(); + this.selection.selectLeft(); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "dolo"); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: partial word selection 2": function() { + this.selection.moveCursorTo(0, 13); + this.selection.selectWord(); + this.selection.selectRight(); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "dolor "); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: partial word selection 3": function() { + this.selection.moveCursorTo(0, 14); + this.selection.selectWord(); + this.selection.selectLeft(); + this.selection.shiftSelection(1); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "olor"); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: select last word": function() { + this.selection.moveCursorTo(0, 1); + + var currentOptions = this.search.getOptions(); + var newOptions = { + wrap: true, + wholeWord: true, + caseSensitive: true, + backwards: true, + needle: "consectetur" + }; + this.search.set(newOptions); + + var match = this.search.find(this.session); + assert.notEqual(match, null, "found a match for 'consectetur'"); + assert.position(match.start, 1, 0); + + this.search.set(currentOptions); + + this.selection.setSelectionRange(match); + + assert.equal(this.session.getTextRange(match), "consectetur"); + assert.equal(this.session.$selectionOccurrences.length, 2); + }, }; module.exports = require("asyncjs/test").testcase(Test); diff --git a/lib/ace/test/search_test.js b/lib/ace/test/search_test.js index 3bd0a05f..0f9c89e9 100644 --- a/lib/ace/test/search_test.js +++ b/lib/ace/test/search_test.js @@ -20,6 +20,7 @@ * * Contributor(s): * Fabian Jakobs + * Mihai Sucan * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -341,7 +342,52 @@ var Test = { assert.equal(search.replace("ab12", "cd$1"), "cd12"); assert.equal(search.replace("ab12", "-$&-"), "-ab12-"); assert.equal(search.replace("ab12", "$$"), "$"); - } + }, + + "test: find all matches in a line" : function() { + var session = new EditSession("foo bar foo baz foobar foo"); + + var search = new Search().set({ + needle: "foo", + wrap: true, + wholeWord: true, + }); + + session.getSelection().moveCursorTo(0, 4); + + var ranges = search.findAll(session); + + assert.equal(ranges.length, 3); + assert.position(ranges[0].start, 0, 8); + assert.position(ranges[0].end, 0, 11); + assert.position(ranges[1].start, 0, 23); + assert.position(ranges[1].end, 0, 26); + assert.position(ranges[2].start, 0, 0); + assert.position(ranges[2].end, 0, 3); + }, + + "test: find all matches in a line backwards" : function() { + var session = new EditSession("foo bar foo baz foobar foo"); + + var search = new Search().set({ + needle: "foo", + wrap: true, + wholeWord: true, + backwards: true, + }); + + session.getSelection().moveCursorTo(0, 13); + + var ranges = search.findAll(session); + + assert.equal(ranges.length, 3); + assert.position(ranges[0].start, 0, 8); + assert.position(ranges[0].end, 0, 11); + assert.position(ranges[1].start, 0, 0); + assert.position(ranges[1].end, 0, 3); + assert.position(ranges[2].start, 0, 23); + assert.position(ranges[2].end, 0, 26); + }, }; module.exports = require("asyncjs/test").testcase(Test)