diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 0db7b1795d6c3..d44d76fb2ec3b 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -550,12 +550,14 @@ const enum Constants { class HashTableEntry { public readonly tokenTypeIndex: number; public readonly tokenModifierSet: number; + public readonly languageId: number; public readonly metadata: number; public next: HashTableEntry | null; - constructor(tokenTypeIndex: number, tokenModifierSet: number, metadata: number) { + constructor(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number) { this.tokenTypeIndex = tokenTypeIndex; this.tokenModifierSet = tokenModifierSet; + this.languageId = languageId; this.metadata = metadata; this.next = null; } @@ -586,16 +588,17 @@ class HashTable { } } - private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number): number { - return ((((tokenTypeIndex << 5) - tokenTypeIndex) + tokenModifierSet) | 0) % this._currentLength; // tokenTypeIndex * 31 + tokenModifierSet, keep as int32 + private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): number { + const hash = (n1: number, n2: number) => (((n1 << 5) - n1) + n2) | 0; // n1 * 31 + n2, keep as int32 + return hash(hash(tokenTypeIndex, tokenModifierSet), languageId) % this._currentLength; } - public get(tokenTypeIndex: number, tokenModifierSet: number): HashTableEntry | null { - const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet); + public get(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): HashTableEntry | null { + const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet, languageId); let p = this._elements[hash]; while (p) { - if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet) { + if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet && p.languageId === languageId) { return p; } p = p.next; @@ -604,7 +607,7 @@ class HashTable { return null; } - public add(tokenTypeIndex: number, tokenModifierSet: number, metadata: number): void { + public add(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number): void { this._elementsCount++; if (this._growCount !== 0 && this._elementsCount >= this._growCount) { // expand! @@ -626,11 +629,11 @@ class HashTable { } } } - this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, metadata)); + this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, languageId, metadata)); } private _add(element: HashTableEntry): void { - const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet); + const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet, element.languageId); element.next = this._elements[hash]; this._elements[hash] = element; } @@ -648,8 +651,8 @@ class SemanticColoringProviderStyling { this._hashTable = new HashTable(); } - public getMetadata(tokenTypeIndex: number, tokenModifierSet: number): number { - const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet); + public getMetadata(tokenTypeIndex: number, tokenModifierSet: number, languageId: LanguageIdentifier): number { + const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet, languageId.id); let metadata: number; if (entry) { metadata = entry.metadata; @@ -664,7 +667,7 @@ class SemanticColoringProviderStyling { modifierSet = modifierSet >> 1; } - const tokenStyle = this._themeService.getColorTheme().getTokenStyleMetadata(tokenType, tokenModifiers); + const tokenStyle = this._themeService.getColorTheme().getTokenStyleMetadata(tokenType, tokenModifiers, languageId.language); if (typeof tokenStyle === 'undefined') { metadata = Constants.NO_STYLING; } else { @@ -690,7 +693,7 @@ class SemanticColoringProviderStyling { metadata = Constants.NO_STYLING; } } - this._hashTable.add(tokenTypeIndex, tokenModifierSet, metadata); + this._hashTable.add(tokenTypeIndex, tokenModifierSet, languageId.id, metadata); } if (this._logService.getLevel() === LogLevel.Trace) { const type = this._legend.tokenTypes[tokenTypeIndex]; @@ -931,6 +934,8 @@ class ModelSemanticColoring extends Disposable { const result: MultilineTokens2[] = []; + const languageId = this._model.getLanguageIdentifier(); + let tokenIndex = 0; let lastLineNumber = 1; let lastStartCharacter = 0; @@ -970,7 +975,7 @@ class ModelSemanticColoring extends Disposable { const length = srcData[srcOffset + 2]; const tokenTypeIndex = srcData[srcOffset + 3]; const tokenModifierSet = srcData[srcOffset + 4]; - const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet); + const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet, languageId); if (metadata !== Constants.NO_STYLING) { if (areaLine === 0) { diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 2fb24d8a34519..3d4db53c94326 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -131,7 +131,7 @@ class StandaloneTheme implements IStandaloneTheme { return this._tokenTheme; } - public getTokenStyleMetadata(type: string, modifiers: string[]): ITokenStyle | undefined { + public getTokenStyleMetadata(type: string, modifiers: string[], modelLanguage: string): ITokenStyle | undefined { return undefined; } diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index fa53430b10ef7..07bfa4b14bb6f 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -56,7 +56,7 @@ suite('TokenizationSupport2Adapter', () => { throw new Error('Not implemented'); }, - getTokenStyleMetadata: (type: string, modifiers: string[]): ITokenStyle | undefined => { + getTokenStyleMetadata: (type: string, modifiers: string[], modelLanguage: string): ITokenStyle | undefined => { return undefined; }, diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index bca0e9bc03fb7..77f4795576fc3 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -106,7 +106,7 @@ export interface IColorTheme { /** * Returns the token style for a given classification. The result uses the MetadataConsts format */ - getTokenStyleMetadata(type: string, modifiers: string[]): ITokenStyle | undefined; + getTokenStyleMetadata(type: string, modifiers: string[], modelLanguage: string): ITokenStyle | undefined; /** * List of all colors used with tokens. getTokenStyleMetadata references the colors by index into this list. diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index 8911630ed91d2..027b03ef8f5e5 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -13,15 +13,21 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; export const TOKEN_TYPE_WILDCARD = '*'; +export const TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR = ':'; +export const CLASSIFIER_MODIFIER_SEPARATOR = '.'; -// qualified string [type|*](.modifier)* +// qualified string [type|*](.modifier)*(/language)! export type TokenClassificationString = string; -export const typeAndModifierIdPattern = '^\\w+[-_\\w+]*$'; +export const idPattern = '\\w+[-_\\w+]*'; +export const typeAndModifierIdPattern = `^${idPattern}$`; + +export const selectorPattern = `^(${idPattern}|\\*)(\\${CLASSIFIER_MODIFIER_SEPARATOR}${idPattern})*(\\${TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR}${idPattern})?$`; + export const fontStylePattern = '^(\\s*(-?italic|-?bold|-?underline))*\\s*$'; export interface TokenSelector { - match(type: string, modifiers: string[]): number; + match(type: string, modifiers: string[], language: string): number; readonly selectorString: string; } @@ -269,9 +275,9 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { } public parseTokenSelector(selectorString: string): TokenSelector { - const [selectorType, ...selectorModifiers] = selectorString.split('.'); + const selector = parseClassifierString(selectorString); - if (!selectorType) { + if (!selector.type) { return { match: () => -1, selectorString @@ -279,23 +285,29 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { } return { - match: (type: string, modifiers: string[]) => { + match: (type: string, modifiers: string[], language: string) => { let score = 0; - if (selectorType !== TOKEN_TYPE_WILDCARD) { + if (selector.language !== undefined) { + if (selector.language !== language) { + return -1; + } + score += 100; + } + if (selector.type !== TOKEN_TYPE_WILDCARD) { const hierarchy = this.getTypeHierarchy(type); - const level = hierarchy.indexOf(selectorType); + const level = hierarchy.indexOf(selector.type); if (level === -1) { return -1; } - score = 100 - level; + score += (100 - level); } // all selector modifiers must be present - for (const selectorModifier of selectorModifiers) { + for (const selectorModifier of selector.modifiers) { if (modifiers.indexOf(selectorModifier) === -1) { return -1; } } - return score + selectorModifiers.length * 100; + return score + selector.modifiers.length * 100; }, selectorString }; @@ -366,15 +378,41 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry { } +const CHAR_LANGUAGE = TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR.charCodeAt(0); +const CHAR_MODIFIER = CLASSIFIER_MODIFIER_SEPARATOR.charCodeAt(0); + +export function parseClassifierString(s: string): { type: string, modifiers: string[], language: string | undefined; } { + let k = s.length; + let language: string | undefined = undefined; + const modifiers = []; + + for (let i = k - 1; i >= 0; i--) { + const ch = s.charCodeAt(i); + if (ch === CHAR_LANGUAGE || ch === CHAR_MODIFIER) { + const segment = s.substring(i + 1, k); + k = i; + if (ch === CHAR_LANGUAGE) { + language = segment; + } else { + modifiers.push(segment); + } + } + } + const type = s.substring(0, k); + return { type, modifiers, language }; +} + -const tokenClassificationRegistry = new TokenClassificationRegistry(); +let tokenClassificationRegistry = createDefaultTokenClassificationRegistry(); platform.Registry.add(Extensions.TokenClassificationContribution, tokenClassificationRegistry); -registerDefaultClassifications(); -function registerDefaultClassifications(): void { +function createDefaultTokenClassificationRegistry(): TokenClassificationRegistry { + + const registry = new TokenClassificationRegistry(); + function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], superType?: string, deprecationMessage?: string): string { - tokenClassificationRegistry.registerTokenType(id, description, superType, deprecationMessage); + registry.registerTokenType(id, description, superType, deprecationMessage); if (scopesToProbe) { registerTokenStyleDefault(id, scopesToProbe); } @@ -383,8 +421,8 @@ function registerDefaultClassifications(): void { function registerTokenStyleDefault(selectorString: string, scopesToProbe: ProbeScope[]) { try { - const selector = tokenClassificationRegistry.parseTokenSelector(selectorString); - tokenClassificationRegistry.registerTokenStyleDefault(selector, { scopesToProbe }); + const selector = registry.parseTokenSelector(selectorString); + registry.registerTokenStyleDefault(selector, { scopesToProbe }); } catch (e) { console.log(e); } @@ -422,18 +460,20 @@ function registerDefaultClassifications(): void { // default token modifiers - tokenClassificationRegistry.registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined); - tokenClassificationRegistry.registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined); - tokenClassificationRegistry.registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined); - tokenClassificationRegistry.registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined); - tokenClassificationRegistry.registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined); - tokenClassificationRegistry.registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined); - tokenClassificationRegistry.registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined); - tokenClassificationRegistry.registerTokenModifier('readonly', nls.localize('readonly', "Style to use for symbols that are readonly."), undefined); + registry.registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined); + registry.registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined); + registry.registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined); + registry.registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined); + registry.registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined); + registry.registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined); + registry.registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined); + registry.registerTokenModifier('readonly', nls.localize('readonly', "Style to use for symbols that are readonly."), undefined); registerTokenStyleDefault('variable.readonly', [['variable.other.constant']]); registerTokenStyleDefault('property.readonly', [['variable.other.constant.property']]); + + return registry; } export function getTokenClassificationRegistry(): ITokenClassificationRegistry { diff --git a/src/vs/platform/theme/test/common/testThemeService.ts b/src/vs/platform/theme/test/common/testThemeService.ts index acada67c30293..82dbe518768ac 100644 --- a/src/vs/platform/theme/test/common/testThemeService.ts +++ b/src/vs/platform/theme/test/common/testThemeService.ts @@ -24,7 +24,7 @@ export class TestColorTheme implements IColorTheme { throw new Error('Method not implemented.'); } - getTokenStyleMetadata(type: string, modifiers: string[]): ITokenStyle | undefined { + getTokenStyleMetadata(type: string, modifiers: string[], modelLanguage: string): ITokenStyle | undefined { return undefined; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index 6bb934a241d7d..429d79afcc145 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -491,6 +491,7 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { private _getSemanticTokenAtPosition(semanticTokens: SemanticTokensResult, pos: Position): ISemanticTokenInfo | null { const tokenData = semanticTokens.tokens.data; + const defaultLanguage = this._model.getLanguageIdentifier().language; let lastLine = 0; let lastCharacter = 0; const posLine = pos.lineNumber - 1, posCharacter = pos.column - 1; // to 0-based position @@ -505,7 +506,7 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { const definitions = {}; const colorMap = this._themeService.getColorTheme().tokenColorMap; const theme = this._themeService.getColorTheme() as ColorThemeData; - const tokenStyle = theme.getTokenStyleMetadata(type, modifiers, true, definitions); + const tokenStyle = theme.getTokenStyleMetadata(type, modifiers, defaultLanguage, true, definitions); let metadata: IDecodedMetadata | undefined = undefined; if (tokenStyle) { @@ -550,16 +551,9 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { return `Color theme: ${definition.selector.selectorString} - ${this._renderStyleProperty(definition.style, property)}`; } return ''; - } else if (typeof definition === 'string') { - const [type, ...modifiers] = definition.split('.'); - const definitions: TokenStyleDefinitions = {}; - const m = theme.getTokenStyleMetadata(type, modifiers, true, definitions); - if (m && definitions.foreground) { - return this._renderTokenStyleDefinition(definitions[property], property); - } - return ''; } else { - return this._renderStyleProperty(definition, property); + const style = theme.resolveTokenStyleValue(definition); + return `Default: ${style ? this._renderStyleProperty(style, property) : ''}`; } } diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 05992b7104476..d7fc70d3e41aa 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -19,7 +19,7 @@ import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; import { URI } from 'vs/base/common/uri'; import { parse as parsePList } from 'vs/workbench/services/themes/common/plistParser'; import { startsWith } from 'vs/base/common/strings'; -import { TokenStyle, ProbeScope, TokenStylingRule, getTokenClassificationRegistry, TokenStyleValue, TokenStyleData } from 'vs/platform/theme/common/tokenClassificationRegistry'; +import { TokenStyle, ProbeScope, TokenStylingRule, getTokenClassificationRegistry, TokenStyleValue, TokenStyleData, parseClassifierString } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { MatcherWithPriority, Matcher, createMatchers } from 'vs/workbench/services/themes/common/textMateScopeMatcher'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; import { CharCode } from 'vs/base/common/charCode'; @@ -137,7 +137,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { return color; } - public getTokenStyle(type: string, modifiers: string[], useDefault = true, definitions: TokenStyleDefinitions = {}): TokenStyle | undefined { + private getTokenStyle(type: string, modifiers: string[], language: string, useDefault = true, definitions: TokenStyleDefinitions = {}): TokenStyle | undefined { let result: any = { foreground: undefined, bold: undefined, @@ -171,7 +171,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { } if (this.tokenStylingRules === undefined) { for (const rule of tokenClassificationRegistry.getTokenStylingDefaultRules()) { - const matchScore = rule.selector.match(type, modifiers); + const matchScore = rule.selector.match(type, modifiers, language); if (matchScore >= 0) { let style: TokenStyle | undefined; if (rule.defaults.scopesToProbe) { @@ -191,14 +191,14 @@ export class ColorThemeData implements IWorkbenchColorTheme { } } else { for (const rule of this.tokenStylingRules) { - const matchScore = rule.selector.match(type, modifiers); + const matchScore = rule.selector.match(type, modifiers, language); if (matchScore >= 0) { _processStyle(matchScore, rule.style, rule); } } } for (const rule of this.customTokenStylingRules) { - const matchScore = rule.selector.match(type, modifiers); + const matchScore = rule.selector.match(type, modifiers, language); if (matchScore >= 0) { _processStyle(matchScore, rule.style, rule); } @@ -210,12 +210,12 @@ export class ColorThemeData implements IWorkbenchColorTheme { /** * @param tokenStyleValue Resolve a tokenStyleValue in the context of a theme */ - private resolveTokenStyleValue(tokenStyleValue: TokenStyleValue | undefined): TokenStyle | undefined { + public resolveTokenStyleValue(tokenStyleValue: TokenStyleValue | undefined): TokenStyle | undefined { if (tokenStyleValue === undefined) { return undefined; } else if (typeof tokenStyleValue === 'string') { - const [type, ...modifiers] = tokenStyleValue.split('.'); - return this.getTokenStyle(type, modifiers); + const { type, modifiers, language } = parseClassifierString(tokenStyleValue); + return this.getTokenStyle(type, modifiers, language || ''); } else if (typeof tokenStyleValue === 'object') { return tokenStyleValue; } @@ -252,8 +252,9 @@ export class ColorThemeData implements IWorkbenchColorTheme { return this.getTokenColorIndex().asArray(); } - public getTokenStyleMetadata(type: string, modifiers: string[], useDefault = true, definitions: TokenStyleDefinitions = {}): ITokenStyle | undefined { - const style = this.getTokenStyle(type, modifiers, useDefault, definitions); + public getTokenStyleMetadata(typeWithLanguage: string, modifiers: string[], defaultLanguage: string, useDefault = true, definitions: TokenStyleDefinitions = {}): ITokenStyle | undefined { + const { type, language } = parseClassifierString(typeWithLanguage); + let style = this.getTokenStyle(type, modifiers, language || defaultLanguage, useDefault, definitions); if (!style) { return undefined; } diff --git a/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts b/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts index 55e11a97374f8..ccc374f5ca537 100644 --- a/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/tokenClassificationExtensionPoint.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { getTokenClassificationRegistry, ITokenClassificationRegistry, typeAndModifierIdPattern, TokenStyleDefaults, TokenStyle, fontStylePattern } from 'vs/platform/theme/common/tokenClassificationRegistry'; +import { getTokenClassificationRegistry, ITokenClassificationRegistry, typeAndModifierIdPattern, TokenStyleDefaults, TokenStyle, fontStylePattern, selectorPattern } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; interface ITokenTypeExtensionPoint { @@ -36,7 +36,6 @@ interface ITokenStyleDefaultExtensionPoint { }; } -const selectorPattern = '^([-_\\w]+|\\*)(\\.[-_\\w+]+)*$'; const colorPattern = '^#([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$'; const tokenClassificationRegistry: ITokenClassificationRegistry = getTokenClassificationRegistry(); @@ -98,7 +97,7 @@ const tokenStyleDefaultsExtPoint = ExtensionsRegistry.registerExtensionPoint { }); + test('rule matching', async () => { const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test'); themeData.setCustomColors({ 'editor.foreground': '#000000' }); @@ -350,8 +349,10 @@ suite('Themes - TokenStyleResolving', () => { }); test('super type', async () => { - getTokenClassificationRegistry().registerTokenType('myTestInterface', 'A type just for testing', 'interface'); - getTokenClassificationRegistry().registerTokenType('myTestSubInterface', 'A type just for testing', 'myTestInterface'); + const registry = getTokenClassificationRegistry(); + + registry.registerTokenType('myTestInterface', 'A type just for testing', 'interface'); + registry.registerTokenType('myTestSubInterface', 'A type just for testing', 'myTestInterface'); try { const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test'); @@ -371,7 +372,56 @@ suite('Themes - TokenStyleResolving', () => { }); assertTokenStyles(themeData, { 'myTestSubInterface': ts('#ff00ff', { italic: true }) }); } finally { - getTokenClassificationRegistry().deregisterTokenType('myTestInterface'); + registry.deregisterTokenType('myTestInterface'); + registry.deregisterTokenType('myTestSubInterface'); + } + }); + + test('language', async () => { + try { + const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test'); + themeData.setCustomColors({ 'editor.foreground': '#000000' }); + themeData.setCustomTokenStyleRules({ + 'interface': '#fff000', + 'interface:java': '#ff0000', + 'interface.static': { fontStyle: 'bold' }, + 'interface.static:typescript': { fontStyle: 'italic' } + }); + + assertTokenStyles(themeData, { 'interface': ts('#ff0000', undefined) }, 'java'); + assertTokenStyles(themeData, { 'interface': ts('#fff000', undefined) }, 'typescript'); + assertTokenStyles(themeData, { 'interface.static': ts('#ff0000', { bold: true }) }, 'java'); + assertTokenStyles(themeData, { 'interface.static': ts('#fff000', { bold: true, italic: true }) }, 'typescript'); + } finally { + } + }); + + test('language - scope resolving', async () => { + const registry = getTokenClassificationRegistry(); + registry.registerTokenStyleDefault(registry.parseTokenSelector('type:typescript'), { scopesToProbe: [['entity.name.type.ts']] }); + + + try { + const themeData = ColorThemeData.createLoadedEmptyTheme('test', 'test'); + themeData.setCustomColors({ 'editor.foreground': '#000000' }); + themeData.setCustomTokenColors({ + textMateRules: [ + { + scope: 'entity.name.type', + settings: { foreground: '#aa0000' } + }, + { + scope: 'entity.name.type.ts', + settings: { foreground: '#bb0000' } + } + ] + }); + + assertTokenStyles(themeData, { 'type': ts('#aa0000', undefined) }, 'javascript'); + assertTokenStyles(themeData, { 'type': ts('#bb0000', undefined) }, 'typescript'); + + } finally { + registry.deregisterTokenType('type/typescript'); } }); });