lazy highlighter for selected word
This commit is contained in:
parent
b1bb0b9ccc
commit
9d0f2e41e8
6 changed files with 105 additions and 108 deletions
|
|
@ -51,6 +51,7 @@ var TextMode = require("./mode/text").Mode;
|
|||
var Range = require("./range").Range;
|
||||
var Document = require("./document").Document;
|
||||
var BackgroundTokenizer = require("./background_tokenizer").BackgroundTokenizer;
|
||||
var SearchHighlight = require("./search_highlight").SearchHighlight;
|
||||
|
||||
/**
|
||||
* class EditSession
|
||||
|
|
@ -307,6 +308,13 @@ var EditSession = function(text, mode) {
|
|||
return token;
|
||||
};
|
||||
|
||||
this.highlight = function(re) {
|
||||
if (!this.$searchHighlight) {
|
||||
var highlight = new SearchHighlight(null, "ace_selected_word", "text");
|
||||
this.$searchHighlight = this.addDynamicMarker(highlight);
|
||||
}
|
||||
this.$searchHighlight.setRegexp(re);
|
||||
}
|
||||
/**
|
||||
* EditSession.setUndoManager(undoManager)
|
||||
* - undoManager (UndoManager): The new undo manager
|
||||
|
|
@ -556,7 +564,8 @@ var EditSession = function(text, mode) {
|
|||
type : type || "line",
|
||||
renderer: typeof type == "function" ? type : null,
|
||||
clazz : clazz,
|
||||
inFront: !!inFront
|
||||
inFront: !!inFront,
|
||||
id: id
|
||||
}
|
||||
|
||||
if (inFront) {
|
||||
|
|
@ -570,6 +579,30 @@ var EditSession = function(text, mode) {
|
|||
return id;
|
||||
};
|
||||
|
||||
/**
|
||||
* EditSession.addDynamicMarker(marker) -> {update}
|
||||
* - marker : object with update method
|
||||
* - inFront (Boolean): Set to `true` to establish a front marker
|
||||
*
|
||||
**/
|
||||
this.addDynamicMarker = function(marker, inFront) {
|
||||
if (!marker.update)
|
||||
return;
|
||||
var id = this.$markerId++;
|
||||
marker.id = id;
|
||||
marker.inFront = !!inFront;
|
||||
|
||||
if (inFront) {
|
||||
this.$frontMarkers[id] = marker;
|
||||
this._emit("changeFrontMarker")
|
||||
} else {
|
||||
this.$backMarkers[id] = marker;
|
||||
this._emit("changeBackMarker")
|
||||
}
|
||||
|
||||
return marker;
|
||||
};
|
||||
|
||||
/**
|
||||
* EditSession.removeMarker(markerId)
|
||||
* - markerId (Number): A number representing a marker
|
||||
|
|
@ -1080,7 +1113,7 @@ var EditSession = function(text, mode) {
|
|||
* {:Document.getTextRange.desc}
|
||||
**/
|
||||
this.getTextRange = function(range) {
|
||||
return this.doc.getTextRange(range);
|
||||
return this.doc.getTextRange(range || this.selection.getRange());
|
||||
};
|
||||
|
||||
/** related to: Document.insert
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ var Editor = function(renderer, session) {
|
|||
* Emitted when a selection has changed.
|
||||
**/
|
||||
this.onSelectionChange = function(e) {
|
||||
var session = this.getSession();
|
||||
var session = this.session;
|
||||
|
||||
if (session.$selectionMarker) {
|
||||
session.removeMarker(session.$selectionMarker);
|
||||
|
|
@ -538,12 +538,40 @@ var Editor = function(renderer, session) {
|
|||
this.$updateHighlightActiveLine();
|
||||
}
|
||||
|
||||
var self = this;
|
||||
if (this.$highlightSelectedWord && !this.$wordHighlightTimer)
|
||||
this.$wordHighlightTimer = setTimeout(function() {
|
||||
self.session.$mode.highlightSelection(self);
|
||||
self.$wordHighlightTimer = null;
|
||||
}, 30, this);
|
||||
var re = this.$highlightSelectedWord && this.$getSelectionHighLightRegexp()
|
||||
this.session.highlight(re);
|
||||
};
|
||||
|
||||
this.$getSelectionHighLightRegexp = function() {
|
||||
var session = this.session;
|
||||
|
||||
var selection = this.getSelectionRange();
|
||||
if (selection.isEmpty() || selection.isMultiLine())
|
||||
return;
|
||||
|
||||
var startOuter = selection.start.column - 1;
|
||||
var endOuter = selection.end.column + 1;
|
||||
var line = session.getLine(selection.start.row);
|
||||
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)) ||
|
||||
(endOuter <= lineCols && /[\w\d]$/.test(needle)))
|
||||
return;
|
||||
|
||||
needle = line.substring(selection.start.column, selection.end.column);
|
||||
if (!/^[\w\d]+$/.test(needle))
|
||||
return;
|
||||
|
||||
var re = this.$search.$assembleRegExp({
|
||||
wholeWord: true,
|
||||
caseSensitive: true,
|
||||
needle: needle
|
||||
});
|
||||
|
||||
return re;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -917,7 +945,7 @@ var Editor = function(renderer, session) {
|
|||
|
||||
this.$highlightSelectedWord = true;
|
||||
/**
|
||||
* Editor.setHighlightSelectedWord(shouldHighlight)
|
||||
* Editor.setHighlightSelectedWord(shouldHighlight)
|
||||
* - shouldHighlight (Boolean): Set to `true` to highlight the currently selected word
|
||||
*
|
||||
* Determines if the currently selected word should be highlighted.
|
||||
|
|
@ -927,20 +955,12 @@ var Editor = function(renderer, session) {
|
|||
return;
|
||||
|
||||
this.$highlightSelectedWord = shouldHighlight;
|
||||
if (shouldHighlight) {
|
||||
this.session.getMode().highlightSelection(this);
|
||||
} else {
|
||||
this.session.getMode().clearSelectionHighlight(this);
|
||||
if (this.$wordHighlightTimer) {
|
||||
clearTimeout(this.$wordHighlightTimer);
|
||||
this.$wordHighlightTimer = null;
|
||||
}
|
||||
}
|
||||
this.$onSelectionChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Editor.getHighlightSelectedWord() -> Boolean
|
||||
*
|
||||
*
|
||||
* Returns `true` if currently highlighted words are to be highlighted.
|
||||
**/
|
||||
this.getHighlightSelectedWord = function() {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,16 @@ var lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
|
|||
"libero vehicula odio, eget bibendum mauris velit eu lorem.\n" +
|
||||
"consectetur";
|
||||
|
||||
function callHighlighterUpdate(session, firstRow, lastRow) {
|
||||
var rangeCount = 0;
|
||||
var mockMarkerLayer = { drawSingleLineMarker: function() {rangeCount++;} }
|
||||
session.$searchHighlight.update([], mockMarkerLayer, session, {
|
||||
firstRow: firstRow,
|
||||
lastRow: lastRow
|
||||
});
|
||||
return rangeCount;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setUp: function(next) {
|
||||
this.session = new EditSession(lipsum);
|
||||
|
|
@ -87,55 +97,51 @@ module.exports = {
|
|||
this.editor.moveCursorTo(0, 9);
|
||||
this.selection.selectWord();
|
||||
|
||||
assert.ok(this.editor.$wordHighlightTimer != null);
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
var highlighter = this.editor.session.$searchHighlight;
|
||||
assert.ok(highlighter != null);
|
||||
|
||||
var range = this.selection.getRange();
|
||||
assert.equal(this.session.getTextRange(range), "ipsum");
|
||||
assert.equal(this.session.$selectionOccurrences.length, 1);
|
||||
assert.equal(highlighter.cache.length, 0);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 2);
|
||||
},
|
||||
|
||||
"test: highlight a word and clear highlight": function() {
|
||||
this.editor.moveCursorTo(0, 8);
|
||||
this.selection.selectWord();
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
|
||||
var range = this.selection.getRange();
|
||||
assert.equal(this.session.getTextRange(range), "ipsum");
|
||||
assert.equal(this.session.$selectionOccurrences.length, 1);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 2);
|
||||
|
||||
this.session.getMode().clearSelectionHighlight(this.editor);
|
||||
assert.equal(this.session.$selectionOccurrences.length, 0);
|
||||
this.session.highlight("");
|
||||
assert.equal(this.session.$searchHighlight.cache.length, 0);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
|
||||
},
|
||||
|
||||
"test: highlight another word": function() {
|
||||
this.selection.moveCursorTo(0, 14);
|
||||
this.selection.selectWord();
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
|
||||
var range = this.selection.getRange();
|
||||
assert.equal(this.session.getTextRange(range), "dolor");
|
||||
assert.equal(this.session.$selectionOccurrences.length, 3);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 4);
|
||||
},
|
||||
|
||||
"test: no selection, no highlight": function() {
|
||||
this.selection.clearSelection();
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
assert.equal(this.session.$selectionOccurrences.length, 0);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
|
||||
},
|
||||
|
||||
"test: select a word, no highlight": function() {
|
||||
this.selection.moveCursorTo(0, 14);
|
||||
this.selection.selectWord();
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
|
||||
this.editor.setHighlightSelectedWord(false);
|
||||
|
||||
assert.ok(this.editor.$wordHighlightTimer == null);
|
||||
|
||||
var range = this.selection.getRange();
|
||||
assert.equal(this.session.getTextRange(range), "dolor");
|
||||
assert.equal(this.session.$selectionOccurrences.length, 0);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
|
||||
},
|
||||
|
||||
"test: select a word with no matches": function() {
|
||||
|
|
@ -156,32 +162,29 @@ module.exports = {
|
|||
this.search.set(currentOptions);
|
||||
|
||||
this.selection.setSelectionRange(match);
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
|
||||
assert.equal(this.session.getTextRange(match), "Mauris");
|
||||
assert.equal(this.session.$selectionOccurrences.length, 0);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 1);
|
||||
},
|
||||
|
||||
"test: partial word selection 1": function() {
|
||||
this.selection.moveCursorTo(0, 14);
|
||||
this.selection.selectWord();
|
||||
this.selection.selectLeft();
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
|
||||
var range = this.selection.getRange();
|
||||
assert.equal(this.session.getTextRange(range), "dolo");
|
||||
assert.equal(this.session.$selectionOccurrences.length, 0);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
|
||||
},
|
||||
|
||||
"test: partial word selection 2": function() {
|
||||
this.selection.moveCursorTo(0, 13);
|
||||
this.selection.selectWord();
|
||||
this.selection.selectRight();
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
|
||||
var range = this.selection.getRange();
|
||||
assert.equal(this.session.getTextRange(range), "dolor ");
|
||||
assert.equal(this.session.$selectionOccurrences.length, 0);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
|
||||
},
|
||||
|
||||
"test: partial word selection 3": function() {
|
||||
|
|
@ -189,11 +192,10 @@ module.exports = {
|
|||
this.selection.selectWord();
|
||||
this.selection.selectLeft();
|
||||
this.selection.shiftSelection(1);
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
|
||||
var range = this.selection.getRange();
|
||||
assert.equal(this.session.getTextRange(range), "olor");
|
||||
assert.equal(this.session.$selectionOccurrences.length, 0);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
|
||||
},
|
||||
|
||||
"test: select last word": function() {
|
||||
|
|
@ -216,10 +218,9 @@ module.exports = {
|
|||
this.search.set(currentOptions);
|
||||
|
||||
this.selection.setSelectionRange(match);
|
||||
this.session.$mode.highlightSelection(this.editor);
|
||||
|
||||
assert.equal(this.session.getTextRange(match), "consectetur");
|
||||
assert.equal(this.session.$selectionOccurrences.length, 2);
|
||||
assert.equal(callHighlighterUpdate(this.session, 0, 1), 3);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,11 @@ var Marker = function(parentEl) {
|
|||
for ( var key in this.markers) {
|
||||
var marker = this.markers[key];
|
||||
|
||||
if (!marker.range) {
|
||||
marker.update(html, this, this.session, config);
|
||||
continue;
|
||||
}
|
||||
|
||||
var range = marker.range.clipRows(config.firstRow, config.lastRow);
|
||||
if (range.isEmpty()) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -98,68 +98,6 @@ var Mode = function() {
|
|||
return null;
|
||||
};
|
||||
|
||||
this.highlightSelection = function(editor) {
|
||||
var session = editor.session;
|
||||
if (!session.$selectionOccurrences)
|
||||
session.$selectionOccurrences = [];
|
||||
|
||||
if (session.$selectionOccurrences.length)
|
||||
this.clearSelectionHighlight(editor);
|
||||
|
||||
var selection = editor.getSelectionRange();
|
||||
if (selection.isEmpty() || selection.isMultiLine())
|
||||
return;
|
||||
|
||||
var startOuter = selection.start.column - 1;
|
||||
var endOuter = selection.end.column + 1;
|
||||
var line = session.getLine(selection.start.row);
|
||||
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)) ||
|
||||
(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
|
||||
};
|
||||
|
||||
var currentOptions = editor.$search.getOptions();
|
||||
editor.$search.set(newOptions);
|
||||
|
||||
var ranges = editor.$search.findAll(session);
|
||||
ranges.forEach(function(range) {
|
||||
if (!range.contains(cursor.row, cursor.column)) {
|
||||
var marker = session.addMarker(range, "ace_selected_word", "text");
|
||||
session.$selectionOccurrences.push(marker);
|
||||
}
|
||||
});
|
||||
|
||||
editor.$search.set(currentOptions);
|
||||
};
|
||||
|
||||
this.clearSelectionHighlight = function(editor) {
|
||||
if (!editor.session.$selectionOccurrences)
|
||||
return;
|
||||
|
||||
editor.session.$selectionOccurrences.forEach(function(marker) {
|
||||
editor.session.removeMarker(marker);
|
||||
});
|
||||
|
||||
editor.session.$selectionOccurrences = [];
|
||||
};
|
||||
|
||||
this.createModeDelegates = function (mapping) {
|
||||
if (!this.$embeds) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ var Range = require("./range").Range;
|
|||
/**
|
||||
* new Search()
|
||||
*
|
||||
* Creates a new `Search` object. The following search options ae avaliable:
|
||||
* Creates a new `Search` object. The following search options are avaliable:
|
||||
*
|
||||
* * needle: string or regular expression
|
||||
* * backwards: false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue