Skip to content

Commit 0204c00

Browse files
committed
Fixes microsoft#93370: Add ContinueBracketSearchPredicate and invoke it every 100th search
1 parent 18d381b commit 0204c00

1 file changed

Lines changed: 70 additions & 31 deletions

File tree

src/vs/editor/common/model/textModel.ts

Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import { withUndefinedAsNull } from 'vs/base/common/types';
3434
import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer';
3535
import { TokensStore, MultilineTokens, countEOL, MultilineTokens2, TokensStore2 } from 'vs/editor/common/model/tokensStore';
3636
import { Color } from 'vs/base/common/color';
37-
import { Constants } from 'vs/base/common/uint';
3837
import { EditorTheme } from 'vs/editor/common/view/viewContext';
3938
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
4039
import { TextChange } from 'vs/editor/common/model/textChange';
@@ -172,6 +171,21 @@ const enum StringOffsetValidationType {
172171
SurrogatePairs = 1,
173172
}
174173

174+
type ContinueBracketSearchPredicate = null | (() => boolean);
175+
176+
class BracketSearchCanceled {
177+
public static INSTANCE = new BracketSearchCanceled();
178+
_searchCanceledBrand = undefined;
179+
private constructor() { }
180+
}
181+
182+
function stripBracketSearchCanceled<T>(result: T | null | BracketSearchCanceled): T | null {
183+
if (result instanceof BracketSearchCanceled) {
184+
return null;
185+
}
186+
return result;
187+
}
188+
175189
export class TextModel extends Disposable implements model.ITextModel {
176190

177191
private static readonly MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB
@@ -2014,7 +2028,7 @@ export class TextModel extends Disposable implements model.ITextModel {
20142028
return null;
20152029
}
20162030

2017-
return this._findMatchingBracketUp(data, position);
2031+
return stripBracketSearchCanceled(this._findMatchingBracketUp(data, position, null));
20182032
}
20192033

20202034
public matchBracket(position: IPosition): [Range, Range] | null {
@@ -2062,8 +2076,11 @@ export class TextModel extends Disposable implements model.ITextModel {
20622076
// check that we didn't hit a bracket too far away from position
20632077
if (foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) {
20642078
const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase();
2065-
const r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText]);
2079+
const r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText], null);
20662080
if (r) {
2081+
if (r instanceof BracketSearchCanceled) {
2082+
return null;
2083+
}
20672084
bestResult = r;
20682085
}
20692086
}
@@ -2100,8 +2117,11 @@ export class TextModel extends Disposable implements model.ITextModel {
21002117
// check that we didn't hit a bracket too far away from position
21012118
if (foundBracket && foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) {
21022119
const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase();
2103-
const r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText]);
2120+
const r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText], null);
21042121
if (r) {
2122+
if (r instanceof BracketSearchCanceled) {
2123+
return null;
2124+
}
21052125
return r;
21062126
}
21072127
}
@@ -2111,35 +2131,41 @@ export class TextModel extends Disposable implements model.ITextModel {
21112131
return null;
21122132
}
21132133

2114-
private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean): [Range, Range] | null {
2134+
private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean, continueSearchPredicate: ContinueBracketSearchPredicate): [Range, Range] | null | BracketSearchCanceled {
21152135
if (!data) {
21162136
return null;
21172137
}
21182138

2119-
if (isOpen) {
2120-
let matched = this._findMatchingBracketDown(data, foundBracket.getEndPosition());
2121-
if (matched) {
2122-
return [foundBracket, matched];
2123-
}
2124-
} else {
2125-
let matched = this._findMatchingBracketUp(data, foundBracket.getStartPosition());
2126-
if (matched) {
2127-
return [foundBracket, matched];
2128-
}
2139+
const matched = (
2140+
isOpen
2141+
? this._findMatchingBracketDown(data, foundBracket.getEndPosition(), continueSearchPredicate)
2142+
: this._findMatchingBracketUp(data, foundBracket.getStartPosition(), continueSearchPredicate)
2143+
);
2144+
2145+
if (!matched) {
2146+
return null;
21292147
}
21302148

2131-
return null;
2149+
if (matched instanceof BracketSearchCanceled) {
2150+
return matched;
2151+
}
2152+
2153+
return [foundBracket, matched];
21322154
}
21332155

2134-
private _findMatchingBracketUp(bracket: RichEditBracket, position: Position): Range | null {
2156+
private _findMatchingBracketUp(bracket: RichEditBracket, position: Position, continueSearchPredicate: ContinueBracketSearchPredicate): Range | null | BracketSearchCanceled {
21352157
// console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
21362158

21372159
const languageId = bracket.languageIdentifier.id;
21382160
const reversedBracketRegex = bracket.reversedRegex;
21392161
let count = -1;
21402162

2141-
const searchPrevMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null => {
2163+
let totalCallCount = 0;
2164+
const searchPrevMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null | BracketSearchCanceled => {
21422165
while (true) {
2166+
if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) {
2167+
return BracketSearchCanceled.INSTANCE;
2168+
}
21432169
const r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
21442170
if (!r) {
21452171
break;
@@ -2214,15 +2240,19 @@ export class TextModel extends Disposable implements model.ITextModel {
22142240
return null;
22152241
}
22162242

2217-
private _findMatchingBracketDown(bracket: RichEditBracket, position: Position): Range | null {
2243+
private _findMatchingBracketDown(bracket: RichEditBracket, position: Position, continueSearchPredicate: ContinueBracketSearchPredicate): Range | null | BracketSearchCanceled {
22182244
// console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
22192245

22202246
const languageId = bracket.languageIdentifier.id;
22212247
const bracketRegex = bracket.forwardRegex;
22222248
let count = 1;
22232249

2224-
const searchNextMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null => {
2250+
let totalCallCount = 0;
2251+
const searchNextMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null | BracketSearchCanceled => {
22252252
while (true) {
2253+
if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) {
2254+
return BracketSearchCanceled.INSTANCE;
2255+
}
22262256
const r = BracketsUtils.findNextBracketInRange(bracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
22272257
if (!r) {
22282258
break;
@@ -2452,7 +2482,16 @@ export class TextModel extends Disposable implements model.ITextModel {
24522482
return null;
24532483
}
24542484

2455-
public findEnclosingBrackets(_position: IPosition, maxDuration = Constants.MAX_SAFE_SMALL_INTEGER): [Range, Range] | null {
2485+
public findEnclosingBrackets(_position: IPosition, maxDuration?: number): [Range, Range] | null {
2486+
let continueSearchPredicate: ContinueBracketSearchPredicate;
2487+
if (typeof maxDuration === 'undefined') {
2488+
continueSearchPredicate = null;
2489+
} else {
2490+
const startTime = Date.now();
2491+
continueSearchPredicate = () => {
2492+
return (Date.now() - startTime <= maxDuration);
2493+
};
2494+
}
24562495
const position = this.validatePosition(_position);
24572496
const lineCount = this.getLineCount();
24582497
const savedCounts = new Map<number, number[]>();
@@ -2468,8 +2507,13 @@ export class TextModel extends Disposable implements model.ITextModel {
24682507
}
24692508
counts = savedCounts.get(languageId)!;
24702509
};
2471-
const searchInRange = (modeBrackets: RichEditBrackets, lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): [Range, Range] | null => {
2510+
2511+
let totalCallCount = 0;
2512+
const searchInRange = (modeBrackets: RichEditBrackets, lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): [Range, Range] | null | BracketSearchCanceled => {
24722513
while (true) {
2514+
if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) {
2515+
return BracketSearchCanceled.INSTANCE;
2516+
}
24732517
const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
24742518
if (!r) {
24752519
break;
@@ -2485,7 +2529,7 @@ export class TextModel extends Disposable implements model.ITextModel {
24852529
}
24862530

24872531
if (counts[bracket.index] === -1) {
2488-
return this._matchFoundBracket(r, bracket, false);
2532+
return this._matchFoundBracket(r, bracket, false, continueSearchPredicate);
24892533
}
24902534
}
24912535

@@ -2496,12 +2540,7 @@ export class TextModel extends Disposable implements model.ITextModel {
24962540

24972541
let languageId: LanguageId = -1;
24982542
let modeBrackets: RichEditBrackets | null = null;
2499-
const startTime = Date.now();
25002543
for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) {
2501-
const elapsedTime = Date.now() - startTime;
2502-
if (elapsedTime > maxDuration) {
2503-
return null;
2504-
}
25052544
const lineTokens = this._getLineTokens(lineNumber);
25062545
const tokenCount = lineTokens.getCount();
25072546
const lineText = this._buffer.getLineContent(lineNumber);
@@ -2530,7 +2569,7 @@ export class TextModel extends Disposable implements model.ITextModel {
25302569
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
25312570
const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
25322571
if (r) {
2533-
return r;
2572+
return stripBracketSearchCanceled(r);
25342573
}
25352574
prevSearchInToken = false;
25362575
}
@@ -2555,7 +2594,7 @@ export class TextModel extends Disposable implements model.ITextModel {
25552594
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
25562595
const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
25572596
if (r) {
2558-
return r;
2597+
return stripBracketSearchCanceled(r);
25592598
}
25602599
}
25612600
}
@@ -2566,7 +2605,7 @@ export class TextModel extends Disposable implements model.ITextModel {
25662605
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
25672606
const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
25682607
if (r) {
2569-
return r;
2608+
return stripBracketSearchCanceled(r);
25702609
}
25712610
}
25722611
}

0 commit comments

Comments
 (0)