提交 6b1a8e89 编写于 作者: A Alex Dima

Fixes #14437: Update state in selection highlighter only if needed

上级 cef4a072
......@@ -883,6 +883,36 @@ export class CompatChangeAll extends AbstractSelectHighlightsAction {
}
}
class SelectionHighlighterState {
public readonly lastWordUnderCursor: Selection;
public readonly searchText: string;
public readonly matchCase: boolean;
public readonly wordSeparators: string;
constructor(lastWordUnderCursor: Selection, searchText: string, matchCase: boolean, wordSeparators: string) {
this.searchText = searchText;
this.matchCase = matchCase;
this.wordSeparators = wordSeparators;
}
/**
* Everything equals except for `lastWordUnderCursor`
*/
public static softEquals(a: SelectionHighlighterState, b: SelectionHighlighterState): boolean {
if (!a && !b) {
return true;
}
if (!a || !b) {
return false;
}
return (
a.searchText === b.searchText
&& a.matchCase === b.matchCase
&& a.wordSeparators === b.wordSeparators
);
}
}
@commonEditorContribution
export class SelectionHighlighter extends Disposable implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.selectionHighlighter';
......@@ -890,25 +920,25 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
private editor: editorCommon.ICommonCodeEditor;
private decorations: string[];
private updateSoon: RunOnceScheduler;
private lastWordUnderCursor: Range;
private state: SelectionHighlighterState;
constructor(editor: editorCommon.ICommonCodeEditor) {
super();
this.editor = editor;
this.decorations = [];
this.updateSoon = this._register(new RunOnceScheduler(() => this._update(), 300));
this.lastWordUnderCursor = null;
this.state = null;
this._register(editor.onDidChangeCursorSelection((e: ICursorSelectionChangedEvent) => {
if (e.selection.isEmpty()) {
if (e.reason === CursorChangeReason.Explicit) {
if (!this.lastWordUnderCursor || !this.lastWordUnderCursor.containsPosition(e.selection.getStartPosition())) {
if (this.state && (!this.state.lastWordUnderCursor || !this.state.lastWordUnderCursor.containsPosition(e.selection.getStartPosition()))) {
// no longer valid
this.removeDecorations();
this._setState(null);
}
this.updateSoon.schedule();
} else {
this.removeDecorations();
this._setState(null);
}
} else {
......@@ -916,7 +946,7 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
}
}));
this._register(editor.onDidChangeModel((e) => {
this.removeDecorations();
this._setState(null);
}));
this._register(CommonFindController.get(editor).getState().addChangeListener((e) => {
this._update();
......@@ -927,73 +957,63 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
return SelectionHighlighter.ID;
}
private removeDecorations(): void {
this.lastWordUnderCursor = null;
if (this.decorations.length > 0) {
this.decorations = this.editor.deltaDecorations(this.decorations, []);
}
private _update(): void {
this._setState(SelectionHighlighter._createState(this.editor));
}
private _update(): void {
const model = this.editor.getModel();
private static _createState(editor: editorCommon.ICommonCodeEditor): SelectionHighlighterState {
const model = editor.getModel();
if (!model) {
return;
return null;
}
const config = this.editor.getConfiguration();
const config = editor.getConfiguration();
this.lastWordUnderCursor = null;
let lastWordUnderCursor: Selection = null;
if (!config.contribInfo.selectionHighlight) {
this.removeDecorations();
return;
return null;
}
let r = multiCursorFind(this.editor, {
const r = multiCursorFind(editor, {
changeFindSearchString: false,
allowMultiline: false,
highlightFindOptions: false
});
if (!r) {
this.removeDecorations();
return;
return null;
}
let hasFindOccurences = DocumentHighlightProviderRegistry.has(model);
const hasFindOccurences = DocumentHighlightProviderRegistry.has(model);
if (r.currentMatch) {
// This is an empty selection
if (hasFindOccurences) {
// Do not interfere with semantic word highlighting in the no selection case
this.removeDecorations();
return;
return null;
}
if (!config.contribInfo.occurrencesHighlight) {
this.removeDecorations();
return;
return null;
}
this.lastWordUnderCursor = r.currentMatch;
lastWordUnderCursor = r.currentMatch;
}
if (/^[ \t]+$/.test(r.searchText)) {
// whitespace only selection
this.removeDecorations();
return;
return null;
}
if (r.searchText.length > 200) {
// very long selection
this.removeDecorations();
return;
return null;
}
const controller = CommonFindController.get(this.editor);
const controller = CommonFindController.get(editor);
if (!controller) {
this.removeDecorations();
return;
return null;
}
const findState = controller.getState();
const caseSensitive = findState.matchCase;
let selections = this.editor.getSelections();
const selections = editor.getSelections();
let firstSelectedText = model.getValueInRange(selections[0]);
if (!caseSensitive) {
firstSelectedText = firstSelectedText.toLowerCase();
......@@ -1005,28 +1025,48 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
}
if (firstSelectedText !== selectedText) {
// not all selections have the same text
this.removeDecorations();
return;
return null;
}
}
return new SelectionHighlighterState(lastWordUnderCursor, r.searchText, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null);
}
private _setState(state: SelectionHighlighterState): void {
if (SelectionHighlighterState.softEquals(this.state, state)) {
this.state = state;
return;
}
this.state = state;
if (!this.state) {
if (this.decorations.length > 0) {
this.decorations = this.editor.deltaDecorations(this.decorations, []);
}
return;
}
const model = this.editor.getModel();
const hasFindOccurences = DocumentHighlightProviderRegistry.has(model);
let allMatches = model.findMatches(r.searchText, true, false, r.matchCase, r.wholeWord ? this.editor.getConfiguration().wordSeparators : null, false).map(m => m.range);
let allMatches = model.findMatches(this.state.searchText, true, false, this.state.matchCase, this.state.wordSeparators, false).map(m => m.range);
allMatches.sort(Range.compareRangesUsingStarts);
let selections = this.editor.getSelections();
selections.sort(Range.compareRangesUsingStarts);
// do not overlap with selection (issue #64 and #512)
let matches: Range[] = [];
for (let i = 0, j = 0, len = allMatches.length, lenJ = selections.length; i < len;) {
let match = allMatches[i];
const match = allMatches[i];
if (j >= lenJ) {
// finished all editor selections
matches.push(match);
i++;
} else {
let cmp = Range.compareRangesUsingStarts(match, selections[j]);
const cmp = Range.compareRangesUsingStarts(match, selections[j]);
if (cmp < 0) {
// match is before sel
matches.push(match);
......@@ -1042,7 +1082,7 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
}
}
let decorations = matches.map(r => {
const decorations = matches.map(r => {
return {
range: r,
// Show in overviewRuler only if model has no semantic highlighting
......@@ -1069,7 +1109,7 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
});
public dispose(): void {
this.removeDecorations();
this._setState(null);
super.dispose();
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册