Skip to content

Commit

Permalink
Refactoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
hediet committed Sep 17, 2021
1 parent e5d2def commit 388735b
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 144 deletions.
50 changes: 23 additions & 27 deletions src/vs/editor/common/model/bracketPairColorizer/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type AstNode = PairAstNode | ListAstNode | BracketAstNode | InvalidBracke
abstract class BaseAstNode {
abstract readonly kind: AstNodeKind;
abstract readonly children: readonly AstNode[];
abstract readonly unopenedBrackets: SmallImmutableSet<number>;
abstract readonly missingBracketIds: SmallImmutableSet<number>;

/**
* In case of a list, determines the height of the (2,3) tree.
Expand Down Expand Up @@ -55,7 +55,6 @@ abstract class BaseAstNode {

export class PairAstNode extends BaseAstNode {
public static create(
category: SmallImmutableSet<number>,
openingBracket: BracketAstNode,
child: AstNode | null,
closingBracket: BracketAstNode | null
Expand All @@ -71,7 +70,7 @@ export class PairAstNode extends BaseAstNode {
children.push(closingBracket);
}

return new PairAstNode(length, category, children, child ? child.unopenedBrackets : SmallImmutableSet.getEmpty());
return new PairAstNode(length, children, child ? child.missingBracketIds : SmallImmutableSet.getEmpty());
}

get kind(): AstNodeKind.Pair {
Expand All @@ -82,7 +81,7 @@ export class PairAstNode extends BaseAstNode {
}

canBeReused(
expectedClosingCategories: SmallImmutableSet<number>,
openedBracketIds: SmallImmutableSet<number>,
endLineDidChange: boolean
) {
if (this.closingBracket === null) {
Expand All @@ -96,7 +95,7 @@ export class PairAstNode extends BaseAstNode {
return false;
}

if (expectedClosingCategories.intersects(this.unopenedBrackets)) {
if (openedBracketIds.intersects(this.missingBracketIds)) {
return false;
}

Expand All @@ -105,7 +104,6 @@ export class PairAstNode extends BaseAstNode {

flattenLists(): PairAstNode {
return PairAstNode.create(
this.category,
this.openingBracket.flattenLists(),
this.child && this.child.flattenLists(),
this.closingBracket && this.closingBracket.flattenLists()
Expand Down Expand Up @@ -138,19 +136,17 @@ export class PairAstNode extends BaseAstNode {

private constructor(
length: Length,
public readonly category: SmallImmutableSet<number>,
public readonly children: readonly AstNode[],
public readonly unopenedBrackets: SmallImmutableSet<number>
public readonly missingBracketIds: SmallImmutableSet<number>
) {
super(length);
}

clone(): PairAstNode {
return new PairAstNode(
this.length,
this.category,
clone(this.children),
this.unopenedBrackets
this.missingBracketIds
);
}
}
Expand All @@ -172,10 +168,10 @@ export class ListAstNode extends BaseAstNode {
return new ListAstNode(lengthZero, 0, items, SmallImmutableSet.getEmpty());
} else {
let length = items[0].length;
let unopenedBrackets = items[0].unopenedBrackets;
let unopenedBrackets = items[0].missingBracketIds;
for (let i = 1; i < items.length; i++) {
length = lengthAdd(length, items[i].length);
unopenedBrackets = unopenedBrackets.merge(items[i].unopenedBrackets);
unopenedBrackets = unopenedBrackets.merge(items[i].missingBracketIds);
}
return new ListAstNode(length, items[0].listHeight + 1, items, unopenedBrackets);
}
Expand All @@ -187,7 +183,7 @@ export class ListAstNode extends BaseAstNode {
get children(): readonly AstNode[] {
return this._items;
}
get unopenedBrackets(): SmallImmutableSet<number> {
get missingBracketIds(): SmallImmutableSet<number> {
return this._unopenedBrackets;
}

Expand All @@ -201,15 +197,15 @@ export class ListAstNode extends BaseAstNode {
}

canBeReused(
expectedClosingCategories: SmallImmutableSet<number>,
openedBracketIds: SmallImmutableSet<number>,
endLineDidChange: boolean
): boolean {
if (this._items.length === 0) {
// might not be very helpful
return true;
}

if (expectedClosingCategories.intersects(this.unopenedBrackets)) {
if (openedBracketIds.intersects(this.missingBracketIds)) {
return false;
}

Expand All @@ -219,7 +215,7 @@ export class ListAstNode extends BaseAstNode {
}

return lastChild.canBeReused(
expectedClosingCategories,
openedBracketIds,
endLineDidChange
);
}
Expand All @@ -238,7 +234,7 @@ export class ListAstNode extends BaseAstNode {
}

clone(): ListAstNode {
return new ListAstNode(this.length, this.listHeight, clone(this._items), this.unopenedBrackets);
return new ListAstNode(this.length, this.listHeight, clone(this._items), this.missingBracketIds);
}

private handleChildrenChanged(): void {
Expand All @@ -248,10 +244,10 @@ export class ListAstNode extends BaseAstNode {
}

let length = items[0].length;
let unopenedBrackets = items[0].unopenedBrackets;
let unopenedBrackets = items[0].missingBracketIds;
for (let i = 1; i < items.length; i++) {
length = lengthAdd(length, items[i].length);
unopenedBrackets = unopenedBrackets.merge(items[i].unopenedBrackets);
unopenedBrackets = unopenedBrackets.merge(items[i].missingBracketIds);
}
this._length = length;
this._unopenedBrackets = unopenedBrackets;
Expand Down Expand Up @@ -372,12 +368,12 @@ export class TextAstNode extends BaseAstNode {
get children(): readonly AstNode[] {
return emptyArray;
}
get unopenedBrackets(): SmallImmutableSet<number> {
get missingBracketIds(): SmallImmutableSet<number> {
return SmallImmutableSet.getEmpty();
}

canBeReused(
expectedClosingCategories: SmallImmutableSet<number>,
openedBracketIds: SmallImmutableSet<number>,
endLineDidChange: boolean
) {
// Don't reuse text from a line that got changed.
Expand Down Expand Up @@ -422,7 +418,7 @@ export class BracketAstNode extends BaseAstNode {
return emptyArray;
}

get unopenedBrackets(): SmallImmutableSet<number> {
get missingBracketIds(): SmallImmutableSet<number> {
return SmallImmutableSet.getEmpty();
}

Expand Down Expand Up @@ -456,18 +452,18 @@ export class InvalidBracketAstNode extends BaseAstNode {
return emptyArray;
}

public readonly unopenedBrackets: SmallImmutableSet<number>;
public readonly missingBracketIds: SmallImmutableSet<number>;

constructor(pairsWith: SmallImmutableSet<number>, length: Length) {
constructor(closingBrackets: SmallImmutableSet<number>, length: Length) {
super(length);
this.unopenedBrackets = pairsWith;
this.missingBracketIds = closingBrackets;
}

canBeReused(
expectedClosingCategories: SmallImmutableSet<number>,
openedBracketIds: SmallImmutableSet<number>,
endLineDidChange: boolean
) {
return !expectedClosingCategories.intersects(this.unopenedBrackets);
return !openedBracketIds.intersects(this.missingBracketIds);
}

flattenLists(): InvalidBracketAstNode {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ class BracketPairColorizerImpl extends Disposable implements DecorationProvider
private initialAstWithoutTokens: AstNode | undefined;
private astWithTokens: AstNode | undefined;

private readonly denseKeyProvider = new DenseKeyProvider<number>();
private readonly brackets = new LanguageAgnosticBracketTokens([], this.denseKeyProvider);
private readonly denseKeyProvider = new DenseKeyProvider<string>();
private readonly brackets = new LanguageAgnosticBracketTokens(this.denseKeyProvider);

public didLanguageChange(languageId: LanguageId): boolean {
return this.brackets.didLanguageChange(languageId);
Expand Down
118 changes: 47 additions & 71 deletions src/vs/editor/common/model/bracketPairColorizer/brackets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,70 @@
*--------------------------------------------------------------------------------------------*/

import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { SmallImmutableSet, DenseKeyProvider } from 'vs/editor/common/model/bracketPairColorizer/smallImmutableSet';
import { toLength } from 'vs/editor/common/model/bracketPairColorizer/length';
import { SmallImmutableSet, DenseKeyProvider, identityKeyProvider } from 'vs/editor/common/model/bracketPairColorizer/smallImmutableSet';
import { LanguageId } from 'vs/editor/common/modes';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { BracketAstNode } from './ast';
import { toLength } from './length';
import { Token, TokenKind } from './tokenizer';
import { OpeningBracketId, Token, TokenKind } from './tokenizer';

export class BracketTokens {
static createFromLanguage(languageId: LanguageId, customBracketPairs: readonly [string, string][], denseKeyProvider: DenseKeyProvider<number>): BracketTokens {
const brackets = [...(LanguageConfigurationRegistry.getColorizedBracketPairs(languageId))];
static createFromLanguage(languageId: LanguageId, denseKeyProvider: DenseKeyProvider<string>): BracketTokens {
function getId(languageId: LanguageId, openingText: string): OpeningBracketId {
return denseKeyProvider.getKey(`${languageId}:::${openingText}`);
}

const tokens = new BracketTokens();
const brackets = [...(LanguageConfigurationRegistry.getColorizedBracketPairs(languageId))];

// Used for detecting shared brackets.
const categoryCache = new Map<string, number>();
const closingBrackets = new Map</* closingText */ string, { openingBrackets: SmallImmutableSet<OpeningBracketId>, first: OpeningBracketId }>();
const openingBrackets = new Set</* openingText */ string>();

let newOpeningBracketCategory = 0;
let parseBracket = (openingBracket: string, closingBracket: string) => {
let currCategory = newOpeningBracketCategory;
for (const [openingText, closingText] of brackets) {
openingBrackets.add(openingText);

let existingOpeningBracketCategory = categoryCache.get(openingBracket);
if (existingOpeningBracketCategory === undefined) {
// Opening bracket is new, so create a new category for it and add it.
tokens.addBracket(languageId, openingBracket, TokenKind.OpeningBracket, newOpeningBracketCategory, denseKeyProvider);
categoryCache.set(openingBracket, newOpeningBracketCategory);
newOpeningBracketCategory++;
} else {
// Opening bracket exists already, remember its category.
currCategory = existingOpeningBracketCategory;
let info = closingBrackets.get(closingText);
const openingTextId = getId(languageId, openingText);
if (!info) {
info = { openingBrackets: SmallImmutableSet.getEmpty(), first: openingTextId };
closingBrackets.set(closingText, info);
}
// Add closing bracket with opening bracket's category as a new value to pair with.
tokens.addBracket(languageId, closingBracket, TokenKind.ClosingBracket, currCategory, denseKeyProvider);
};
info.openingBrackets = info.openingBrackets.add(openingTextId, identityKeyProvider);
}

for (const pair of brackets) {
parseBracket(pair[0], pair[1]);
const map = new Map<string, Token>();

for (const [closingText, info] of closingBrackets) {
const length = toLength(0, closingText.length);
map.set(closingText, new Token(
length,
TokenKind.ClosingBracket,
info.first,
info.openingBrackets,
BracketAstNode.create(length)
));
}

for (const pair of customBracketPairs) {
parseBracket(pair[0], pair[1]);
for (const openingText of openingBrackets) {
const length = toLength(0, openingText.length);
const openingTextId = getId(languageId, openingText);
map.set(openingText, new Token(
length,
TokenKind.OpeningBracket,
openingTextId,
SmallImmutableSet.getEmpty().add(openingTextId, identityKeyProvider),
BracketAstNode.create(length)
));
}

return tokens;
return new BracketTokens(map);
}

private hasRegExp = false;
private _regExpGlobal: RegExp | null = null;
private readonly map = new Map<string, Token>();

private addBracket(languageId: LanguageId, value: string, kind: TokenKind, category: number, denseKeyProvider: DenseKeyProvider<number>): void {
const length = toLength(0, value.length);

// A language can have at most 1000 bracket pairs.
const offsetCategory = languageId * 1000 + category;

let newCategory = SmallImmutableSet.getEmpty();
let mergedPairsWith = SmallImmutableSet.getEmpty();

const existingBracket = this.map.get(value);
switch (kind) {
case TokenKind.OpeningBracket:
if (existingBracket !== undefined) {
// Do not update existing opening brackets.
return;
}
// Add category to new set.
newCategory = newCategory.add(offsetCategory, denseKeyProvider);
break;
case TokenKind.ClosingBracket:
if (existingBracket !== undefined) {
// Closing bracket exists already, add to its pairsWith set.
mergedPairsWith = existingBracket.pairsWith || mergedPairsWith;
}
mergedPairsWith = mergedPairsWith.add(offsetCategory, denseKeyProvider);
break;
}

this.map.set(value,
new Token(
length,
kind,
newCategory,
mergedPairsWith,
languageId,
BracketAstNode.create(length)
)
);
}
constructor(
private readonly map: Map<string, Token>
) { }

getRegExpStr(): string | null {
if (this.isEmpty) {
Expand Down Expand Up @@ -128,22 +104,22 @@ export class BracketTokens {
export class LanguageAgnosticBracketTokens {
private readonly languageIdToBracketTokens: Map<LanguageId, BracketTokens> = new Map();

constructor(private readonly customBracketPairs: readonly [string, string][], private readonly denseKeyProvider: DenseKeyProvider<number>) {
constructor(private readonly denseKeyProvider: DenseKeyProvider<string>) {
}

public didLanguageChange(languageId: LanguageId): boolean {
const existing = this.languageIdToBracketTokens.get(languageId);
if (!existing) {
return false;
}
const newRegExpStr = BracketTokens.createFromLanguage(languageId, this.customBracketPairs, this.denseKeyProvider).getRegExpStr();
const newRegExpStr = BracketTokens.createFromLanguage(languageId, this.denseKeyProvider).getRegExpStr();
return existing.getRegExpStr() !== newRegExpStr;
}

getSingleLanguageBracketTokens(languageId: LanguageId): BracketTokens {
let singleLanguageBracketTokens = this.languageIdToBracketTokens.get(languageId);
if (!singleLanguageBracketTokens) {
singleLanguageBracketTokens = BracketTokens.createFromLanguage(languageId, this.customBracketPairs, this.denseKeyProvider);
singleLanguageBracketTokens = BracketTokens.createFromLanguage(languageId, this.denseKeyProvider);
this.languageIdToBracketTokens.set(languageId, singleLanguageBracketTokens);
}
return singleLanguageBracketTokens;
Expand Down
Loading

0 comments on commit 388735b

Please sign in to comment.