More fine grained dependency resolution:
Only load the subsets of a grammar that are needed. For example, when having include patterns like text.html.markdown#fenced_code_block_js, only process and load needed scopes for the fenced_code_block_js rule in the markdown's grammar rule repository. Also improves the loading of grammars to load them in parallel. fixes microsoft/vscode#77990
This commit is contained in:
Родитель
1d44906143
Коммит
9aafda40f3
|
@ -1,181 +1,185 @@
|
|||
import { IRawGrammar, IOnigLib } from './types';
|
||||
export * from './types';
|
||||
/**
|
||||
* A single theme setting.
|
||||
*/
|
||||
export interface IRawThemeSetting {
|
||||
readonly name?: string;
|
||||
readonly scope?: string | string[];
|
||||
readonly settings: {
|
||||
readonly fontStyle?: string;
|
||||
readonly foreground?: string;
|
||||
readonly background?: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* A TextMate theme.
|
||||
*/
|
||||
export interface IRawTheme {
|
||||
readonly name?: string;
|
||||
readonly settings: IRawThemeSetting[];
|
||||
}
|
||||
export interface Thenable<T> extends PromiseLike<T> {
|
||||
}
|
||||
/**
|
||||
* A registry helper that can locate grammar file paths given scope names.
|
||||
*/
|
||||
export interface RegistryOptions {
|
||||
theme?: IRawTheme;
|
||||
loadGrammar(scopeName: string): Thenable<IRawGrammar | undefined | null>;
|
||||
getInjections?(scopeName: string): string[];
|
||||
getOnigLib?(): Thenable<IOnigLib>;
|
||||
}
|
||||
/**
|
||||
* A map from scope name to a language id. Please do not use language id 0.
|
||||
*/
|
||||
export interface IEmbeddedLanguagesMap {
|
||||
[scopeName: string]: number;
|
||||
}
|
||||
/**
|
||||
* A map from selectors to token types.
|
||||
*/
|
||||
export interface ITokenTypeMap {
|
||||
[selector: string]: StandardTokenType;
|
||||
}
|
||||
export declare const enum StandardTokenType {
|
||||
Other = 0,
|
||||
Comment = 1,
|
||||
String = 2,
|
||||
RegEx = 4
|
||||
}
|
||||
export interface IGrammarConfiguration {
|
||||
embeddedLanguages?: IEmbeddedLanguagesMap;
|
||||
tokenTypes?: ITokenTypeMap;
|
||||
}
|
||||
/**
|
||||
* The registry that will hold all grammars.
|
||||
*/
|
||||
export declare class Registry {
|
||||
private readonly _locator;
|
||||
private readonly _syncRegistry;
|
||||
constructor(locator?: RegistryOptions);
|
||||
/**
|
||||
* Change the theme. Once called, no previous `ruleStack` should be used anymore.
|
||||
*/
|
||||
setTheme(theme: IRawTheme): void;
|
||||
/**
|
||||
* Returns a lookup array for color ids.
|
||||
*/
|
||||
getColorMap(): string[];
|
||||
/**
|
||||
* Load the grammar for `scopeName` and all referenced included grammars asynchronously.
|
||||
* Please do not use language id 0.
|
||||
*/
|
||||
loadGrammarWithEmbeddedLanguages(initialScopeName: string, initialLanguage: number, embeddedLanguages: IEmbeddedLanguagesMap): Thenable<IGrammar>;
|
||||
/**
|
||||
* Load the grammar for `scopeName` and all referenced included grammars asynchronously.
|
||||
* Please do not use language id 0.
|
||||
*/
|
||||
loadGrammarWithConfiguration(initialScopeName: string, initialLanguage: number, configuration: IGrammarConfiguration): Thenable<IGrammar>;
|
||||
/**
|
||||
* Load the grammar for `scopeName` and all referenced included grammars asynchronously.
|
||||
*/
|
||||
loadGrammar(initialScopeName: string): Thenable<IGrammar>;
|
||||
private _loadGrammar;
|
||||
/**
|
||||
* Adds a rawGrammar.
|
||||
*/
|
||||
addGrammar(rawGrammar: IRawGrammar, injections?: string[], initialLanguage?: number, embeddedLanguages?: IEmbeddedLanguagesMap): Thenable<IGrammar>;
|
||||
/**
|
||||
* Get the grammar for `scopeName`. The grammar must first be created via `loadGrammar` or `addGrammar`.
|
||||
*/
|
||||
grammarForScopeName(scopeName: string, initialLanguage?: number, embeddedLanguages?: IEmbeddedLanguagesMap, tokenTypes?: ITokenTypeMap): Thenable<IGrammar>;
|
||||
}
|
||||
/**
|
||||
* A grammar
|
||||
*/
|
||||
export interface IGrammar {
|
||||
/**
|
||||
* Tokenize `lineText` using previous line state `prevState`.
|
||||
*/
|
||||
tokenizeLine(lineText: string, prevState: StackElement): ITokenizeLineResult;
|
||||
/**
|
||||
* Tokenize `lineText` using previous line state `prevState`.
|
||||
* The result contains the tokens in binary format, resolved with the following information:
|
||||
* - language
|
||||
* - token type (regex, string, comment, other)
|
||||
* - font style
|
||||
* - foreground color
|
||||
* - background color
|
||||
* e.g. for getting the languageId: `(metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET`
|
||||
*/
|
||||
tokenizeLine2(lineText: string, prevState: StackElement): ITokenizeLineResult2;
|
||||
}
|
||||
export interface ITokenizeLineResult {
|
||||
readonly tokens: IToken[];
|
||||
/**
|
||||
* The `prevState` to be passed on to the next line tokenization.
|
||||
*/
|
||||
readonly ruleStack: StackElement;
|
||||
}
|
||||
/**
|
||||
* Helpers to manage the "collapsed" metadata of an entire StackElement stack.
|
||||
* The following assumptions have been made:
|
||||
* - languageId < 256 => needs 8 bits
|
||||
* - unique color count < 512 => needs 9 bits
|
||||
*
|
||||
* The binary format is:
|
||||
* - -------------------------------------------
|
||||
* 3322 2222 2222 1111 1111 1100 0000 0000
|
||||
* 1098 7654 3210 9876 5432 1098 7654 3210
|
||||
* - -------------------------------------------
|
||||
* xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
* bbbb bbbb bfff ffff ffFF FTTT LLLL LLLL
|
||||
* - -------------------------------------------
|
||||
* - L = LanguageId (8 bits)
|
||||
* - T = StandardTokenType (3 bits)
|
||||
* - F = FontStyle (3 bits)
|
||||
* - f = foreground color (9 bits)
|
||||
* - b = background color (9 bits)
|
||||
*/
|
||||
export declare const enum MetadataConsts {
|
||||
LANGUAGEID_MASK = 255,
|
||||
TOKEN_TYPE_MASK = 1792,
|
||||
FONT_STYLE_MASK = 14336,
|
||||
FOREGROUND_MASK = 8372224,
|
||||
BACKGROUND_MASK = 4286578688,
|
||||
LANGUAGEID_OFFSET = 0,
|
||||
TOKEN_TYPE_OFFSET = 8,
|
||||
FONT_STYLE_OFFSET = 11,
|
||||
FOREGROUND_OFFSET = 14,
|
||||
BACKGROUND_OFFSET = 23
|
||||
}
|
||||
export interface ITokenizeLineResult2 {
|
||||
/**
|
||||
* The tokens in binary format. Each token occupies two array indices. For token i:
|
||||
* - at offset 2*i => startIndex
|
||||
* - at offset 2*i + 1 => metadata
|
||||
*
|
||||
*/
|
||||
readonly tokens: Uint32Array;
|
||||
/**
|
||||
* The `prevState` to be passed on to the next line tokenization.
|
||||
*/
|
||||
readonly ruleStack: StackElement;
|
||||
}
|
||||
export interface IToken {
|
||||
startIndex: number;
|
||||
readonly endIndex: number;
|
||||
readonly scopes: string[];
|
||||
}
|
||||
/**
|
||||
* **IMPORTANT** - Immutable!
|
||||
*/
|
||||
export interface StackElement {
|
||||
_stackElementBrand: void;
|
||||
readonly depth: number;
|
||||
clone(): StackElement;
|
||||
equals(other: StackElement): boolean;
|
||||
}
|
||||
export declare const INITIAL: StackElement;
|
||||
export declare const parseRawGrammar: (content: string, filePath?: string) => IRawGrammar;
|
||||
import { IRawGrammar, IOnigLib } from './types';
|
||||
export * from './types';
|
||||
/**
|
||||
* A single theme setting.
|
||||
*/
|
||||
export interface IRawThemeSetting {
|
||||
readonly name?: string;
|
||||
readonly scope?: string | string[];
|
||||
readonly settings: {
|
||||
readonly fontStyle?: string;
|
||||
readonly foreground?: string;
|
||||
readonly background?: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* A TextMate theme.
|
||||
*/
|
||||
export interface IRawTheme {
|
||||
readonly name?: string;
|
||||
readonly settings: IRawThemeSetting[];
|
||||
}
|
||||
export interface Thenable<T> extends PromiseLike<T> {
|
||||
}
|
||||
/**
|
||||
* A registry helper that can locate grammar file paths given scope names.
|
||||
*/
|
||||
export interface RegistryOptions {
|
||||
theme?: IRawTheme;
|
||||
loadGrammar(scopeName: string): Thenable<IRawGrammar | undefined | null>;
|
||||
getInjections?(scopeName: string): string[];
|
||||
getOnigLib?(): Thenable<IOnigLib>;
|
||||
}
|
||||
/**
|
||||
* A map from scope name to a language id. Please do not use language id 0.
|
||||
*/
|
||||
export interface IEmbeddedLanguagesMap {
|
||||
[scopeName: string]: number;
|
||||
}
|
||||
/**
|
||||
* A map from selectors to token types.
|
||||
*/
|
||||
export interface ITokenTypeMap {
|
||||
[selector: string]: StandardTokenType;
|
||||
}
|
||||
export declare const enum StandardTokenType {
|
||||
Other = 0,
|
||||
Comment = 1,
|
||||
String = 2,
|
||||
RegEx = 4
|
||||
}
|
||||
export interface IGrammarConfiguration {
|
||||
embeddedLanguages?: IEmbeddedLanguagesMap;
|
||||
tokenTypes?: ITokenTypeMap;
|
||||
}
|
||||
/**
|
||||
* The registry that will hold all grammars.
|
||||
*/
|
||||
export declare class Registry {
|
||||
private readonly _locator;
|
||||
private readonly _syncRegistry;
|
||||
private readonly _ensureGrammarCache;
|
||||
constructor(locator?: RegistryOptions);
|
||||
/**
|
||||
* Change the theme. Once called, no previous `ruleStack` should be used anymore.
|
||||
*/
|
||||
setTheme(theme: IRawTheme): void;
|
||||
/**
|
||||
* Returns a lookup array for color ids.
|
||||
*/
|
||||
getColorMap(): string[];
|
||||
/**
|
||||
* Load the grammar for `scopeName` and all referenced included grammars asynchronously.
|
||||
* Please do not use language id 0.
|
||||
*/
|
||||
loadGrammarWithEmbeddedLanguages(initialScopeName: string, initialLanguage: number, embeddedLanguages: IEmbeddedLanguagesMap): Thenable<IGrammar>;
|
||||
/**
|
||||
* Load the grammar for `scopeName` and all referenced included grammars asynchronously.
|
||||
* Please do not use language id 0.
|
||||
*/
|
||||
loadGrammarWithConfiguration(initialScopeName: string, initialLanguage: number, configuration: IGrammarConfiguration): Thenable<IGrammar>;
|
||||
/**
|
||||
* Load the grammar for `scopeName` and all referenced included grammars asynchronously.
|
||||
*/
|
||||
loadGrammar(initialScopeName: string): Thenable<IGrammar>;
|
||||
private _doLoadSingleGrammar;
|
||||
private _loadSingleGrammar;
|
||||
private _collectDependenciesForDep;
|
||||
private _loadGrammar;
|
||||
/**
|
||||
* Adds a rawGrammar.
|
||||
*/
|
||||
addGrammar(rawGrammar: IRawGrammar, injections?: string[], initialLanguage?: number, embeddedLanguages?: IEmbeddedLanguagesMap): Thenable<IGrammar>;
|
||||
/**
|
||||
* Get the grammar for `scopeName`. The grammar must first be created via `loadGrammar` or `addGrammar`.
|
||||
*/
|
||||
grammarForScopeName(scopeName: string, initialLanguage?: number, embeddedLanguages?: IEmbeddedLanguagesMap, tokenTypes?: ITokenTypeMap): Thenable<IGrammar>;
|
||||
}
|
||||
/**
|
||||
* A grammar
|
||||
*/
|
||||
export interface IGrammar {
|
||||
/**
|
||||
* Tokenize `lineText` using previous line state `prevState`.
|
||||
*/
|
||||
tokenizeLine(lineText: string, prevState: StackElement): ITokenizeLineResult;
|
||||
/**
|
||||
* Tokenize `lineText` using previous line state `prevState`.
|
||||
* The result contains the tokens in binary format, resolved with the following information:
|
||||
* - language
|
||||
* - token type (regex, string, comment, other)
|
||||
* - font style
|
||||
* - foreground color
|
||||
* - background color
|
||||
* e.g. for getting the languageId: `(metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET`
|
||||
*/
|
||||
tokenizeLine2(lineText: string, prevState: StackElement): ITokenizeLineResult2;
|
||||
}
|
||||
export interface ITokenizeLineResult {
|
||||
readonly tokens: IToken[];
|
||||
/**
|
||||
* The `prevState` to be passed on to the next line tokenization.
|
||||
*/
|
||||
readonly ruleStack: StackElement;
|
||||
}
|
||||
/**
|
||||
* Helpers to manage the "collapsed" metadata of an entire StackElement stack.
|
||||
* The following assumptions have been made:
|
||||
* - languageId < 256 => needs 8 bits
|
||||
* - unique color count < 512 => needs 9 bits
|
||||
*
|
||||
* The binary format is:
|
||||
* - -------------------------------------------
|
||||
* 3322 2222 2222 1111 1111 1100 0000 0000
|
||||
* 1098 7654 3210 9876 5432 1098 7654 3210
|
||||
* - -------------------------------------------
|
||||
* xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
* bbbb bbbb bfff ffff ffFF FTTT LLLL LLLL
|
||||
* - -------------------------------------------
|
||||
* - L = LanguageId (8 bits)
|
||||
* - T = StandardTokenType (3 bits)
|
||||
* - F = FontStyle (3 bits)
|
||||
* - f = foreground color (9 bits)
|
||||
* - b = background color (9 bits)
|
||||
*/
|
||||
export declare const enum MetadataConsts {
|
||||
LANGUAGEID_MASK = 255,
|
||||
TOKEN_TYPE_MASK = 1792,
|
||||
FONT_STYLE_MASK = 14336,
|
||||
FOREGROUND_MASK = 8372224,
|
||||
BACKGROUND_MASK = 4286578688,
|
||||
LANGUAGEID_OFFSET = 0,
|
||||
TOKEN_TYPE_OFFSET = 8,
|
||||
FONT_STYLE_OFFSET = 11,
|
||||
FOREGROUND_OFFSET = 14,
|
||||
BACKGROUND_OFFSET = 23
|
||||
}
|
||||
export interface ITokenizeLineResult2 {
|
||||
/**
|
||||
* The tokens in binary format. Each token occupies two array indices. For token i:
|
||||
* - at offset 2*i => startIndex
|
||||
* - at offset 2*i + 1 => metadata
|
||||
*
|
||||
*/
|
||||
readonly tokens: Uint32Array;
|
||||
/**
|
||||
* The `prevState` to be passed on to the next line tokenization.
|
||||
*/
|
||||
readonly ruleStack: StackElement;
|
||||
}
|
||||
export interface IToken {
|
||||
startIndex: number;
|
||||
readonly endIndex: number;
|
||||
readonly scopes: string[];
|
||||
}
|
||||
/**
|
||||
* **IMPORTANT** - Immutable!
|
||||
*/
|
||||
export interface StackElement {
|
||||
_stackElementBrand: void;
|
||||
readonly depth: number;
|
||||
clone(): StackElement;
|
||||
equals(other: StackElement): boolean;
|
||||
}
|
||||
export declare const INITIAL: StackElement;
|
||||
export declare const parseRawGrammar: (content: string, filePath?: string) => IRawGrammar;
|
||||
|
|
7344
release/main.js
7344
release/main.js
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,68 +1,68 @@
|
|||
export interface ILocation {
|
||||
readonly filename: string;
|
||||
readonly line: number;
|
||||
readonly char: number;
|
||||
}
|
||||
export interface ILocatable {
|
||||
readonly $vscodeTextmateLocation?: ILocation;
|
||||
}
|
||||
export interface IRawGrammar extends ILocatable {
|
||||
repository: IRawRepository;
|
||||
readonly scopeName: string;
|
||||
readonly patterns: IRawRule[];
|
||||
readonly injections?: {
|
||||
[expression: string]: IRawRule;
|
||||
};
|
||||
readonly injectionSelector?: string;
|
||||
readonly fileTypes?: string[];
|
||||
readonly name?: string;
|
||||
readonly firstLineMatch?: string;
|
||||
}
|
||||
export interface IRawRepositoryMap {
|
||||
[name: string]: IRawRule;
|
||||
$self: IRawRule;
|
||||
$base: IRawRule;
|
||||
}
|
||||
export declare type IRawRepository = IRawRepositoryMap & ILocatable;
|
||||
export interface IRawRule extends ILocatable {
|
||||
id?: number;
|
||||
readonly include?: string;
|
||||
readonly name?: string;
|
||||
readonly contentName?: string;
|
||||
readonly match?: string;
|
||||
readonly captures?: IRawCaptures;
|
||||
readonly begin?: string;
|
||||
readonly beginCaptures?: IRawCaptures;
|
||||
readonly end?: string;
|
||||
readonly endCaptures?: IRawCaptures;
|
||||
readonly while?: string;
|
||||
readonly whileCaptures?: IRawCaptures;
|
||||
readonly patterns?: IRawRule[];
|
||||
readonly repository?: IRawRepository;
|
||||
readonly applyEndPatternLast?: boolean;
|
||||
}
|
||||
export interface IRawCapturesMap {
|
||||
[captureId: string]: IRawRule;
|
||||
}
|
||||
export declare type IRawCaptures = IRawCapturesMap & ILocatable;
|
||||
export interface IOnigLib {
|
||||
createOnigScanner(sources: string[]): OnigScanner;
|
||||
createOnigString(sources: string): OnigString;
|
||||
}
|
||||
export interface IOnigCaptureIndex {
|
||||
start: number;
|
||||
end: number;
|
||||
length: number;
|
||||
}
|
||||
export interface IOnigMatch {
|
||||
index: number;
|
||||
captureIndices: IOnigCaptureIndex[];
|
||||
scanner: OnigScanner;
|
||||
}
|
||||
export interface OnigScanner {
|
||||
findNextMatchSync(string: string | OnigString, startPosition: number): IOnigMatch;
|
||||
}
|
||||
export interface OnigString {
|
||||
readonly content: string;
|
||||
readonly dispose?: () => void;
|
||||
}
|
||||
export interface ILocation {
|
||||
readonly filename: string;
|
||||
readonly line: number;
|
||||
readonly char: number;
|
||||
}
|
||||
export interface ILocatable {
|
||||
readonly $vscodeTextmateLocation?: ILocation;
|
||||
}
|
||||
export interface IRawGrammar extends ILocatable {
|
||||
repository: IRawRepository;
|
||||
readonly scopeName: string;
|
||||
readonly patterns: IRawRule[];
|
||||
readonly injections?: {
|
||||
[expression: string]: IRawRule;
|
||||
};
|
||||
readonly injectionSelector?: string;
|
||||
readonly fileTypes?: string[];
|
||||
readonly name?: string;
|
||||
readonly firstLineMatch?: string;
|
||||
}
|
||||
export interface IRawRepositoryMap {
|
||||
[name: string]: IRawRule;
|
||||
$self: IRawRule;
|
||||
$base: IRawRule;
|
||||
}
|
||||
export declare type IRawRepository = IRawRepositoryMap & ILocatable;
|
||||
export interface IRawRule extends ILocatable {
|
||||
id?: number;
|
||||
readonly include?: string;
|
||||
readonly name?: string;
|
||||
readonly contentName?: string;
|
||||
readonly match?: string;
|
||||
readonly captures?: IRawCaptures;
|
||||
readonly begin?: string;
|
||||
readonly beginCaptures?: IRawCaptures;
|
||||
readonly end?: string;
|
||||
readonly endCaptures?: IRawCaptures;
|
||||
readonly while?: string;
|
||||
readonly whileCaptures?: IRawCaptures;
|
||||
readonly patterns?: IRawRule[];
|
||||
readonly repository?: IRawRepository;
|
||||
readonly applyEndPatternLast?: boolean;
|
||||
}
|
||||
export interface IRawCapturesMap {
|
||||
[captureId: string]: IRawRule;
|
||||
}
|
||||
export declare type IRawCaptures = IRawCapturesMap & ILocatable;
|
||||
export interface IOnigLib {
|
||||
createOnigScanner(sources: string[]): OnigScanner;
|
||||
createOnigString(sources: string): OnigString;
|
||||
}
|
||||
export interface IOnigCaptureIndex {
|
||||
start: number;
|
||||
end: number;
|
||||
length: number;
|
||||
}
|
||||
export interface IOnigMatch {
|
||||
index: number;
|
||||
captureIndices: IOnigCaptureIndex[];
|
||||
scanner: OnigScanner;
|
||||
}
|
||||
export interface OnigScanner {
|
||||
findNextMatchSync(string: string | OnigString, startPosition: number): IOnigMatch;
|
||||
}
|
||||
export interface OnigString {
|
||||
readonly content: string;
|
||||
readonly dispose?: () => void;
|
||||
}
|
||||
|
|
|
@ -37,14 +37,63 @@ export interface IScopeNameSet {
|
|||
[scopeName: string]: boolean;
|
||||
}
|
||||
|
||||
export class FullScopeDependency {
|
||||
constructor(
|
||||
public readonly scopeName: string
|
||||
) { }
|
||||
}
|
||||
|
||||
export class PartialScopeDependency {
|
||||
constructor(
|
||||
public readonly scopeName: string,
|
||||
public readonly include: string
|
||||
) { }
|
||||
|
||||
public toKey(): string {
|
||||
return `${this.scopeName}#${this.include}`;
|
||||
}
|
||||
}
|
||||
|
||||
export type ScopeDependency = FullScopeDependency | PartialScopeDependency;
|
||||
|
||||
export class ScopeDependencyCollector {
|
||||
|
||||
public readonly full: FullScopeDependency[];
|
||||
public readonly partial: PartialScopeDependency[];
|
||||
|
||||
private readonly _seenFull: Set<string>;
|
||||
private readonly _seenPartial: Set<string>;
|
||||
|
||||
constructor() {
|
||||
this.full = [];
|
||||
this.partial = [];
|
||||
this._seenFull = new Set<string>();
|
||||
this._seenPartial = new Set<string>();
|
||||
}
|
||||
|
||||
public add(dep: ScopeDependency): void {
|
||||
if (dep instanceof FullScopeDependency) {
|
||||
if (!this._seenFull.has(dep.scopeName)) {
|
||||
this._seenFull.add(dep.scopeName);
|
||||
this.full.push(dep);
|
||||
}
|
||||
} else {
|
||||
if (!this._seenPartial.has(dep.toKey())) {
|
||||
this._seenPartial.add(dep.toKey());
|
||||
this.partial.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in `result` all external included scopes in `patterns`
|
||||
*/
|
||||
function _extractIncludedScopesInPatterns(result: IScopeNameSet, patterns: IRawRule[]): void {
|
||||
function _extractIncludedScopesInPatterns(result: ScopeDependencyCollector, grammarScopeName: string, patterns: IRawRule[]): void {
|
||||
for (const pattern of patterns) {
|
||||
|
||||
if (Array.isArray(pattern.patterns)) {
|
||||
_extractIncludedScopesInPatterns(result, pattern.patterns);
|
||||
_extractIncludedScopesInPatterns(result, grammarScopeName, pattern.patterns);
|
||||
}
|
||||
|
||||
const include = pattern.include;
|
||||
|
@ -65,9 +114,14 @@ function _extractIncludedScopesInPatterns(result: IScopeNameSet, patterns: IRawR
|
|||
|
||||
const sharpIndex = include.indexOf('#');
|
||||
if (sharpIndex >= 0) {
|
||||
result[include.substring(0, sharpIndex)] = true;
|
||||
const scopeName = include.substring(0, sharpIndex);
|
||||
if (scopeName !== grammarScopeName) {
|
||||
result.add(new PartialScopeDependency(scopeName, include.substring(sharpIndex + 1)));
|
||||
}
|
||||
} else {
|
||||
result[include] = true;
|
||||
if (include !== grammarScopeName) {
|
||||
result.add(new FullScopeDependency(include));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,16 +129,33 @@ function _extractIncludedScopesInPatterns(result: IScopeNameSet, patterns: IRawR
|
|||
/**
|
||||
* Fill in `result` all external included scopes in `repository`
|
||||
*/
|
||||
function _extractIncludedScopesInRepository(result: IScopeNameSet, repository: IRawRepository): void {
|
||||
function _extractIncludedScopesInRepository(result: ScopeDependencyCollector, grammarScopeName: string, repository: IRawRepository): void {
|
||||
for (let name in repository) {
|
||||
const rule = repository[name];
|
||||
|
||||
if (rule.patterns && Array.isArray(rule.patterns)) {
|
||||
_extractIncludedScopesInPatterns(result, rule.patterns);
|
||||
_extractIncludedScopesInPatterns(result, grammarScopeName, rule.patterns);
|
||||
}
|
||||
|
||||
if (rule.repository) {
|
||||
_extractIncludedScopesInRepository(result, rule.repository);
|
||||
_extractIncludedScopesInRepository(result, grammarScopeName, rule.repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect a specific dependency from the grammar's repository
|
||||
*/
|
||||
export function collectSpecificDependencies(result: ScopeDependencyCollector, grammar: IRawGrammar, include: string): void {
|
||||
if (grammar.repository && grammar.repository[include]) {
|
||||
const rule = grammar.repository[include];
|
||||
|
||||
if (rule.patterns && Array.isArray(rule.patterns)) {
|
||||
_extractIncludedScopesInPatterns(result, grammar.scopeName, rule.patterns);
|
||||
}
|
||||
|
||||
if (rule.repository) {
|
||||
_extractIncludedScopesInRepository(result, grammar.scopeName, rule.repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,17 +163,14 @@ function _extractIncludedScopesInRepository(result: IScopeNameSet, repository: I
|
|||
/**
|
||||
* Collects the list of all external included scopes in `grammar`.
|
||||
*/
|
||||
export function collectIncludedScopes(result: IScopeNameSet, grammar: IRawGrammar): void {
|
||||
export function collectDependencies(result: ScopeDependencyCollector, grammar: IRawGrammar): void {
|
||||
if (grammar.patterns && Array.isArray(grammar.patterns)) {
|
||||
_extractIncludedScopesInPatterns(result, grammar.patterns);
|
||||
_extractIncludedScopesInPatterns(result, grammar.scopeName, grammar.patterns);
|
||||
}
|
||||
|
||||
if (grammar.repository) {
|
||||
_extractIncludedScopesInRepository(result, grammar.repository);
|
||||
_extractIncludedScopesInRepository(result, grammar.scopeName, grammar.repository);
|
||||
}
|
||||
|
||||
// remove references to own scope (avoid recursion)
|
||||
delete result[grammar.scopeName];
|
||||
}
|
||||
|
||||
export interface Injection {
|
||||
|
|
93
src/main.ts
93
src/main.ts
|
@ -6,7 +6,7 @@
|
|||
import { SyncRegistry } from './registry';
|
||||
import * as grammarReader from './grammarReader';
|
||||
import { Theme } from './theme';
|
||||
import { StackElement as StackElementImpl } from './grammar';
|
||||
import { StackElement as StackElementImpl, collectDependencies, ScopeDependencyCollector, collectSpecificDependencies, FullScopeDependency, PartialScopeDependency, ScopeDependency } from './grammar';
|
||||
import { IRawGrammar, IOnigLib } from './types';
|
||||
|
||||
export * from './types';
|
||||
|
@ -78,10 +78,12 @@ export class Registry {
|
|||
|
||||
private readonly _locator: RegistryOptions;
|
||||
private readonly _syncRegistry: SyncRegistry;
|
||||
private readonly _ensureGrammarCache: Map<string, Promise<void>>;
|
||||
|
||||
constructor(locator: RegistryOptions = { loadGrammar: () => null }) {
|
||||
this._locator = locator;
|
||||
this._syncRegistry = new SyncRegistry(Theme.createFromRawTheme(locator.theme), locator.getOnigLib && locator.getOnigLib());
|
||||
this._ensureGrammarCache = new Map<string, Promise<void>>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,34 +123,83 @@ export class Registry {
|
|||
return this._loadGrammar(initialScopeName, 0, null, null);
|
||||
}
|
||||
|
||||
private async _doLoadSingleGrammar(scopeName: string): Promise<void> {
|
||||
const grammar = await this._locator.loadGrammar(scopeName);
|
||||
if (grammar) {
|
||||
const injections = (typeof this._locator.getInjections === 'function') && this._locator.getInjections(scopeName);
|
||||
this._syncRegistry.addGrammar(grammar, injections);
|
||||
}
|
||||
}
|
||||
|
||||
private async _loadSingleGrammar(scopeName: string): Promise<void> {
|
||||
if (!this._ensureGrammarCache.has(scopeName)) {
|
||||
this._ensureGrammarCache.set(scopeName, this._doLoadSingleGrammar(scopeName));
|
||||
}
|
||||
return this._ensureGrammarCache.get(scopeName);
|
||||
}
|
||||
|
||||
private _collectDependenciesForDep(initialScopeName: string, result: ScopeDependencyCollector, dep: FullScopeDependency | PartialScopeDependency) {
|
||||
const grammar = this._syncRegistry.lookup(dep.scopeName);
|
||||
if (!grammar) {
|
||||
if (dep.scopeName === initialScopeName) {
|
||||
throw new Error(`No grammar provided for <${initialScopeName}>`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dep instanceof FullScopeDependency) {
|
||||
collectDependencies(result, grammar);
|
||||
} else {
|
||||
collectSpecificDependencies(result, grammar, dep.include);
|
||||
}
|
||||
|
||||
const injections = this._syncRegistry.injections(dep.scopeName);
|
||||
if (injections) {
|
||||
for (const injection of injections) {
|
||||
result.add(new FullScopeDependency(injection));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _loadGrammar(initialScopeName: string, initialLanguage: number, embeddedLanguages: IEmbeddedLanguagesMap, tokenTypes: ITokenTypeMap): Promise<IGrammar> {
|
||||
|
||||
let remainingScopeNames = [initialScopeName];
|
||||
const seenFullScopeRequests = new Set<string>();
|
||||
const seenPartialScopeRequests = new Set<string>();
|
||||
|
||||
let seenScopeNames: { [name: string]: boolean; } = {};
|
||||
seenScopeNames[initialScopeName] = true;
|
||||
seenFullScopeRequests.add(initialScopeName);
|
||||
let Q: ScopeDependency[] = [new FullScopeDependency(initialScopeName)];
|
||||
|
||||
while (remainingScopeNames.length > 0) {
|
||||
let scopeName = remainingScopeNames.shift();
|
||||
while (Q.length > 0) {
|
||||
const q = Q;
|
||||
Q = [];
|
||||
|
||||
if (this._syncRegistry.lookup(scopeName)) {
|
||||
continue;
|
||||
await Promise.all(q.map(request => this._loadSingleGrammar(request.scopeName)));
|
||||
|
||||
const deps = new ScopeDependencyCollector();
|
||||
for (const dep of q) {
|
||||
this._collectDependenciesForDep(initialScopeName, deps, dep);
|
||||
}
|
||||
|
||||
let grammar = await this._locator.loadGrammar(scopeName);
|
||||
if (!grammar) {
|
||||
if (scopeName === initialScopeName) {
|
||||
throw new Error(`No grammar provided for <${initialScopeName}>`);
|
||||
for (const dep of deps.full) {
|
||||
if (seenFullScopeRequests.has(dep.scopeName)) {
|
||||
// already processed
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
let injections = (typeof this._locator.getInjections === 'function') && this._locator.getInjections(scopeName);
|
||||
let deps = this._syncRegistry.addGrammar(grammar, injections);
|
||||
deps.forEach((dep) => {
|
||||
if (!seenScopeNames[dep]) {
|
||||
seenScopeNames[dep] = true;
|
||||
remainingScopeNames.push(dep);
|
||||
}
|
||||
});
|
||||
seenFullScopeRequests.add(dep.scopeName);
|
||||
Q.push(dep);
|
||||
}
|
||||
|
||||
for (const dep of deps.partial) {
|
||||
if (seenFullScopeRequests.has(dep.scopeName)) {
|
||||
// already processed in full
|
||||
continue;
|
||||
}
|
||||
if (seenPartialScopeRequests.has(dep.toKey())) {
|
||||
// already processed
|
||||
continue;
|
||||
}
|
||||
seenPartialScopeRequests.add(dep.toKey());
|
||||
Q.push(dep);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*--------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { createGrammar, Grammar, collectIncludedScopes, IGrammarRepository, IScopeNameSet } from './grammar';
|
||||
import { createGrammar, Grammar, IGrammarRepository } from './grammar';
|
||||
import { IRawGrammar } from './types';
|
||||
import { Thenable } from './main';
|
||||
import { IGrammar, IEmbeddedLanguagesMap, ITokenTypeMap } from './main';
|
||||
|
@ -42,19 +42,12 @@ export class SyncRegistry implements IGrammarRepository {
|
|||
/**
|
||||
* Add `grammar` to registry and return a list of referenced scope names
|
||||
*/
|
||||
public addGrammar(grammar: IRawGrammar, injectionScopeNames?: string[]): string[] {
|
||||
public addGrammar(grammar: IRawGrammar, injectionScopeNames?: string[]): void {
|
||||
this._rawGrammars[grammar.scopeName] = grammar;
|
||||
|
||||
let includedScopes: IScopeNameSet = {};
|
||||
collectIncludedScopes(includedScopes, grammar);
|
||||
|
||||
if (injectionScopeNames) {
|
||||
this._injectionGrammars[grammar.scopeName] = injectionScopeNames;
|
||||
injectionScopeNames.forEach(scopeName => {
|
||||
includedScopes[scopeName] = true;
|
||||
});
|
||||
}
|
||||
return Object.keys(includedScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче