Skip to content

Commit

Permalink
Improves performance of bracket pair colorization
Browse files Browse the repository at this point in the history
  • Loading branch information
hediet committed Jul 25, 2022
1 parent 55a267a commit d53f4c5
Show file tree
Hide file tree
Showing 16 changed files with 263 additions and 178 deletions.
70 changes: 70 additions & 0 deletions src/vs/base/common/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -829,3 +829,73 @@ export class ArrayQueue<T> {
return result;
}
}

/**
* This class is faster than an iterator and array for lazy computed data.
*/
export class CallbackIterable<T> {
public static readonly empty = new CallbackIterable<never>(_callback => { });

constructor(
/**
* Calls the callback for every item.
* Stops when the callback returns false.
*/
public readonly iterate: (callback: (item: T) => boolean) => void
) {
}

forEach(handler: (item: T) => void) {
this.iterate(item => { handler(item); return true; });
}

toArray(): T[] {
const result: T[] = [];
this.iterate(item => { result.push(item); return true; });
return result;
}

filter(predicate: (item: T) => boolean): CallbackIterable<T> {
return new CallbackIterable(cb => this.iterate(item => predicate(item) ? cb(item) : true));
}

map<TResult>(mapFn: (item: T) => TResult): CallbackIterable<TResult> {
return new CallbackIterable<TResult>(cb => this.iterate(item => cb(mapFn(item))));
}

findFirst(predicate: (item: T) => boolean): T | undefined {
let result: T | undefined;
this.iterate(item => {
if (predicate(item)) {
result = item;
return false;
}
return true;
});
return result;
}

findLast(predicate: (item: T) => boolean): T | undefined {
let result: T | undefined;
this.iterate(item => {
if (predicate(item)) {
result = item;
}
return true;
});
return result;
}

findLastMaxBy(comparator: Comparator<T>): T | undefined {
let result: T | undefined;
let first = true;
this.iterate(item => {
if (first || CompareResult.isGreaterThan(comparator(item, result!))) {
first = false;
result = item;
}
return true;
});
return result;
}
}
2 changes: 1 addition & 1 deletion src/vs/editor/browser/viewParts/minimap/minimap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,7 @@ export class Minimap extends ViewPart implements IMinimapModel {
} else {
visibleRange = new Range(startLineNumber, 1, endLineNumber, this._context.viewModel.getLineMaxColumn(endLineNumber));
}
const decorations = this._context.viewModel.getDecorationsInViewport(visibleRange);
const decorations = this._context.viewModel.getDecorationsInViewport(visibleRange, true);

if (this._samplingState) {
const result: ViewModelDecoration[] = [];
Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/common/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,7 @@ export interface ITextModel {
* @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors).
* @return An array with the decorations
*/
getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[];
getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean, onlyMinimapDecorations?: boolean): IModelDecoration[];

/**
* Gets all the decorations as an array.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { CallbackIterable, compareBy } from 'vs/base/common/arrays';
import { Emitter } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable } from 'vs/base/common/lifecycle';
import { LineTokens } from 'vs/editor/common/tokens/lineTokens';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { BracketPairsTree } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree';
import { BracketInfo, BracketPairInfo, BracketPairWithMinIndentationInfo, IBracketPairsTextModelPart, IFoundBracket } from 'vs/editor/common/textModelBracketPairs';
import { TextModel } from 'vs/editor/common/model/textModel';
import { IModelContentChangedEvent, IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from 'vs/editor/common/textModelEvents';
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
import { ignoreBracketsInToken } from 'vs/editor/common/languages/supports';
import { RichEditBrackets, BracketsUtils, RichEditBracket } from 'vs/editor/common/languages/supports/richEditBrackets';
import { compareBy, findLast, findLastMaxBy } from 'vs/base/common/arrays';
import { LanguageBracketsConfiguration } from 'vs/editor/common/languages/supports/languageBracketsConfiguration';
import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/common/languages/supports/richEditBrackets';
import { BracketPairsTree } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree';
import { TextModel } from 'vs/editor/common/model/textModel';
import { BracketInfo, BracketPairInfo, BracketPairWithMinIndentationInfo, IBracketPairsTextModelPart, IFoundBracket } from 'vs/editor/common/textModelBracketPairs';
import { IModelContentChangedEvent, IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from 'vs/editor/common/textModelEvents';
import { LineTokens } from 'vs/editor/common/tokens/lineTokens';

export class BracketPairsTextModelPart extends Disposable implements IBracketPairsTextModelPart {
private readonly bracketPairsTree = this._register(new MutableDisposable<IReference<BracketPairsTree>>());
Expand Down Expand Up @@ -102,22 +102,22 @@ export class BracketPairsTextModelPart extends Disposable implements IBracketPai
* Returns all bracket pairs that intersect the given range.
* The result is sorted by the start position.
*/
public getBracketPairsInRange(range: Range): BracketPairInfo[] {
public getBracketPairsInRange(range: Range): CallbackIterable<BracketPairInfo> {
this.bracketsRequested = true;
this.updateBracketPairsTree();
return this.bracketPairsTree.value?.object.getBracketPairsInRange(range, false) || [];
return this.bracketPairsTree.value?.object.getBracketPairsInRange(range, false) || CallbackIterable.empty;
}

public getBracketPairsInRangeWithMinIndentation(range: Range): BracketPairWithMinIndentationInfo[] {
public getBracketPairsInRangeWithMinIndentation(range: Range): CallbackIterable<BracketPairWithMinIndentationInfo> {
this.bracketsRequested = true;
this.updateBracketPairsTree();
return this.bracketPairsTree.value?.object.getBracketPairsInRange(range, true) || [];
return this.bracketPairsTree.value?.object.getBracketPairsInRange(range, true) || CallbackIterable.empty;
}

public getBracketsInRange(range: Range): BracketInfo[] {
public getBracketsInRange(range: Range): CallbackIterable<BracketInfo> {
this.bracketsRequested = true;
this.updateBracketPairsTree();
return this.bracketPairsTree.value?.object.getBracketsInRange(range) || [];
return this.bracketPairsTree.value?.object.getBracketsInRange(range) || CallbackIterable.empty;
}

public findMatchingBracketUp(_bracket: string, _position: IPosition, maxDuration?: number): Range | null {
Expand All @@ -133,7 +133,7 @@ export class BracketPairsTextModelPart extends Disposable implements IBracketPai
return null;
}

const bracketPair = findLast(this.getBracketPairsInRange(Range.fromPositions(_position, _position)) || [], (b) =>
const bracketPair = this.getBracketPairsInRange(Range.fromPositions(_position, _position)).findLast((b) =>
closingBracketInfo.closes(b.openingBracketInfo)
);

Expand Down Expand Up @@ -163,23 +163,23 @@ export class BracketPairsTextModelPart extends Disposable implements IBracketPai

public matchBracket(position: IPosition, maxDuration?: number): [Range, Range] | null {
if (this.canBuildAST) {
const bracketPair = findLastMaxBy(
const bracketPair =
this.getBracketPairsInRange(
Range.fromPositions(position, position)
).filter(
(item) =>
item.closingBracketRange !== undefined &&
(item.openingBracketRange.containsPosition(position) ||
item.closingBracketRange.containsPosition(position))
),
compareBy(
(item) =>
item.openingBracketRange.containsPosition(position)
? item.openingBracketRange
: item.closingBracketRange,
Range.compareRangesUsingStarts
)
);
).findLastMaxBy(
compareBy(
(item) =>
item.openingBracketRange.containsPosition(position)
? item.openingBracketRange
: item.closingBracketRange,
Range.compareRangesUsingStarts
)
);
if (bracketPair) {
return [bracketPair.openingBracketRange, bracketPair.closingBracketRange!];
}
Expand Down Expand Up @@ -674,10 +674,10 @@ export class BracketPairsTextModelPart extends Disposable implements IBracketPai

if (this.canBuildAST) {
const range = Range.fromPositions(position);
const bracketPair = findLast(
this.getBracketPairsInRange(Range.fromPositions(position, position)),
(item) => item.closingBracketRange !== undefined && item.range.strictContainsRange(range)
);
const bracketPair =
this.getBracketPairsInRange(Range.fromPositions(position, position)).findLast(
(item) => item.closingBracketRange !== undefined && item.range.strictContainsRange(range)
);
if (bracketPair) {
return [bracketPair.openingBracketRange, bracketPair.closingBracketRange!];
}
Expand Down
Loading

0 comments on commit d53f4c5

Please sign in to comment.