提交 b34da611 编写于 作者: M Martin Aeschlimann

Merge branch 'aeschli/tokenClassificationExtPoint'

......@@ -24,5 +24,34 @@
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7",
"vscode": "1.1.5"
},
"contributes": {
"tokenTypes": [
{
"id": "testToken",
"description": "A test token"
}
],
"tokenModifiers": [
{
"id": "testModifier",
"description": "A test modifier"
}
],
"tokenStyleDefaults": [
{
"selector": "testToken.testModifier",
"light": {
"fontStyle": "bold"
},
"dark": {
"fontStyle": "bold"
},
"highContrast": {
"fontStyle": "bold"
}
}
]
}
}
......@@ -8,11 +8,13 @@ import * as jsoncParser from 'jsonc-parser';
export function activate(context: vscode.ExtensionContext): any {
const tokenTypes = ['type', 'struct', 'class', 'interface', 'enum', 'parameterType', 'function', 'variable'];
const tokenModifiers = ['static', 'abstract', 'deprecated', 'declaration', 'documentation', 'member', 'async'];
const tokenTypes = ['type', 'struct', 'class', 'interface', 'enum', 'parameterType', 'function', 'variable', 'testToken'];
const tokenModifiers = ['static', 'abstract', 'deprecated', 'declaration', 'documentation', 'member', 'async', 'testModifier'];
const legend = new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers);
const outputChannel = vscode.window.createOutputChannel('Semantic Tokens Test');
const semanticHighlightProvider: vscode.SemanticTokensProvider = {
provideSemanticTokens(document: vscode.TextDocument): vscode.ProviderResult<vscode.SemanticTokens> {
const builder = new vscode.SemanticTokensBuilder();
......@@ -35,8 +37,13 @@ export function activate(context: vscode.ExtensionContext): any {
builder.push(startLine, startCharacter, length, tokenType, tokenModifiers);
const selectedModifiers = legend.tokenModifiers.filter((_val, bit) => tokenModifiers & (1 << bit)).join(' ');
outputChannel.appendLine(`line: ${startLine}, character: ${startCharacter}, length ${length}, ${legend.tokenTypes[tokenType]} (${tokenType}), ${selectedModifiers} ${tokenModifiers.toString(2)}`);
}
outputChannel.appendLine('---');
const visitor: jsoncParser.JSONVisitor = {
onObjectProperty: (property: string, _offset: number, _length: number, startLine: number, startCharacter: number) => {
addToken(property, startLine, startCharacter, property.length + 2);
......
{
"editor.tokenColorCustomizationsExperimental": {
"class": "#00b0b0",
"interface": "#845faf",
"function": "#ff00ff",
"*.declaration": {
"fontStyle": "underline"
},
"*.declaration.member": {
"fontStyle": "italic bold",
}
}
}
\ No newline at end of file
[
"class", "function.member.declaration",
"parameterType.declaration", "type", "parameterType.declaration", "type",
"variable.declaration", "parameterNames",
"function.member.declaration",
"interface.declaration",
"function.member.declaration", "testToken.testModifier"
]
......@@ -14,7 +14,7 @@ import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { LanguageIdentifier, SemanticTokensProviderRegistry, SemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits } from 'vs/editor/common/modes';
import { LanguageIdentifier, SemanticTokensProviderRegistry, SemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits, TokenMetadata } from 'vs/editor/common/modes';
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
......@@ -24,6 +24,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { SparseEncodedTokens, MultilineTokens2 } from 'vs/editor/common/model/tokensStore';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
function MODEL_ID(resource: URI): string {
return resource.toString();
......@@ -120,7 +121,8 @@ export class ModelServiceImpl extends Disposable implements IModelService {
constructor(
@IConfigurationService configurationService: IConfigurationService,
@ITextResourcePropertiesService resourcePropertiesService: ITextResourcePropertiesService,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@ILogService logService: ILogService
) {
super();
this._configurationService = configurationService;
......@@ -131,7 +133,7 @@ export class ModelServiceImpl extends Disposable implements IModelService {
this._configurationServiceSubscription = this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions());
this._updateModelOptions();
this._register(new SemanticColoringFeature(this, themeService));
this._register(new SemanticColoringFeature(this, themeService, logService));
}
private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions {
......@@ -443,10 +445,10 @@ class SemanticColoringFeature extends Disposable {
private _watchers: Record<string, ModelSemanticColoring>;
private _semanticStyling: SemanticStyling;
constructor(modelService: IModelService, themeService: IThemeService) {
constructor(modelService: IModelService, themeService: IThemeService, logService: ILogService) {
super();
this._watchers = Object.create(null);
this._semanticStyling = this._register(new SemanticStyling(themeService));
this._semanticStyling = this._register(new SemanticStyling(themeService, logService));
this._register(modelService.onModelAdded((model) => {
this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling);
}));
......@@ -462,7 +464,8 @@ class SemanticStyling extends Disposable {
private _caches: WeakMap<SemanticTokensProvider, SemanticColoringProviderStyling>;
constructor(
private readonly _themeService: IThemeService
private readonly _themeService: IThemeService,
private readonly _logService: ILogService
) {
super();
this._caches = new WeakMap<SemanticTokensProvider, SemanticColoringProviderStyling>();
......@@ -476,7 +479,7 @@ class SemanticStyling extends Disposable {
public get(provider: SemanticTokensProvider): SemanticColoringProviderStyling {
if (!this._caches.has(provider)) {
this._caches.set(provider, new SemanticColoringProviderStyling(provider.getLegend(), this._themeService));
this._caches.set(provider, new SemanticColoringProviderStyling(provider.getLegend(), this._themeService, this._logService));
}
return this._caches.get(provider)!;
}
......@@ -581,7 +584,8 @@ class SemanticColoringProviderStyling {
constructor(
private readonly _legend: SemanticTokensLegend,
private readonly _themeService: IThemeService
private readonly _themeService: IThemeService,
private readonly _logService: ILogService
) {
this._hashTable = new HashTable();
}
......@@ -605,6 +609,9 @@ class SemanticColoringProviderStyling {
if (typeof metadata === 'undefined') {
metadata = Constants.NO_STYLING;
}
if (this._logService.getLevel() === LogLevel.Trace) {
this._logService.trace(`getTokenStyleMetadata(${tokenType}${tokenModifiers.length ? ', ' + tokenModifiers.join(' ') : ''}): foreground: ${TokenMetadata.getForeground(metadata)}, fontStyle ${TokenMetadata.getFontStyle(metadata).toString(2)}`);
}
this._hashTable.add(tokenTypeIndex, tokenModifierSet, metadata);
return metadata;
......
......@@ -18,6 +18,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections';
import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/modelService.test';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
class MockJSMode extends MockMode {
......@@ -46,7 +47,7 @@ suite('SmartSelect', () => {
setup(() => {
const configurationService = new TestConfigurationService();
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService());
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService());
mode = new MockJSMode();
});
......
......@@ -146,7 +146,9 @@ export module StaticServices {
export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl());
export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o), standaloneThemeService.get(o)));
export const logService = define(ILogService, () => new NullLogService());
export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o), standaloneThemeService.get(o), logService.get(o)));
export const markerDecorationsService = define(IMarkerDecorationsService, (o) => new MarkerDecorationsService(modelService.get(o), markerService.get(o)));
......@@ -156,8 +158,6 @@ export module StaticServices {
export const storageService = define(IStorageService, () => new InMemoryStorageService());
export const logService = define(ILogService, () => new NullLogService());
export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o), logService.get(o)));
}
......
......@@ -17,6 +17,7 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/resour
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
const GENERATE_TESTS = false;
......@@ -28,7 +29,7 @@ suite('ModelService', () => {
configService.setUserConfiguration('files', { 'eol': '\n' });
configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot'));
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService());
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService());
});
teardown(() => {
......
......@@ -12,14 +12,15 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
// ------ API types
export const TOKEN_TYPE_WILDCARD = '*';
export const TOKEN_TYPE_WILDCARD_NUM = -1;
// qualified string [type|*](.modifier)*
export type TokenClassificationString = string;
export const typeAndModifierIdPattern = '^\\w+[-_\\w+]*$';
export const fontStylePattern = '^(\\s*(-?italic|-?bold|-?underline))*\\s*$';
export interface TokenClassification {
type: number;
modifiers: number;
......@@ -54,6 +55,34 @@ export namespace TokenStyle {
export function fromData(data: { foreground?: Color, bold?: boolean, underline?: boolean, italic?: boolean }) {
return new TokenStyle(data.foreground, data.bold, data.underline, data.italic);
}
export function fromSettings(foreground: string | undefined, fontStyle: string | undefined): TokenStyle {
let foregroundColor = undefined;
if (foreground !== undefined) {
foregroundColor = Color.fromHex(foreground);
}
let bold, underline, italic;
if (fontStyle !== undefined) {
fontStyle = fontStyle.trim();
if (fontStyle.length === 0) {
bold = italic = underline = false;
} else {
const expression = /-?italic|-?bold|-?underline/g;
let match;
while ((match = expression.exec(fontStyle))) {
switch (match[0]) {
case 'bold': bold = true; break;
case '-bold': bold = false; break;
case 'italic': italic = true; break;
case '-italic': italic = false; break;
case 'underline': underline = true; break;
case '-underline': underline = false; break;
}
}
}
}
return new TokenStyle(foregroundColor, bold, underline, italic);
}
}
export type ProbeScope = string[];
......@@ -63,10 +92,10 @@ export interface TokenStyleFunction {
}
export interface TokenStyleDefaults {
scopesToProbe: ProbeScope[];
light: TokenStyleValue | null;
dark: TokenStyleValue | null;
hc: TokenStyleValue | null;
scopesToProbe?: ProbeScope[];
light?: TokenStyleValue;
dark?: TokenStyleValue;
hc?: TokenStyleValue;
}
export interface TokenStylingDefaultRule {
......@@ -120,6 +149,12 @@ export interface ITokenClassificationRegistry {
*/
registerTokenStyleDefault(selector: TokenClassification, defaults: TokenStyleDefaults): void;
/**
* Deregister a TokenStyle default to the registry.
* @param selector The rule selector
*/
deregisterTokenStyleDefault(selector: TokenClassification): void;
/**
* Deregister a TokenType from the registry.
*/
......@@ -186,7 +221,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
fontStyle: {
type: 'string',
description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\', \'-italic\', \'-bold\' or \'-underline\'or a combination. The empty string unsets inherited settings.'),
pattern: '^(\\s*(-?italic|-?bold|-?underline))*\\s*$',
pattern: fontStylePattern,
patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' to set a style or \'-italic\', \'-bold\' or \'-underline\' to unset or a combination. The empty string unsets all styles.'),
defaultSnippets: [{ label: nls.localize('schema.token.fontStyle.none', 'None (clear inherited style)'), bodyText: '""' }, { body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: '-italic' }, { body: '-bold' }, { body: '-underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }]
}
......@@ -205,6 +240,10 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
}
public registerTokenType(id: string, description: string, deprecationMessage?: string): void {
if (!id.match(typeAndModifierIdPattern)) {
throw new Error('Invalid token type id.');
}
const num = this.currentTypeNumber++;
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
this.tokenTypeById[id] = tokenStyleContribution;
......@@ -213,6 +252,10 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
}
public registerTokenModifier(id: string, description: string, deprecationMessage?: string): void {
if (!id.match(typeAndModifierIdPattern)) {
throw new Error('Invalid token modifier id.');
}
const num = this.currentModifierBit;
this.currentModifierBit = this.currentModifierBit * 2;
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
......@@ -244,6 +287,10 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
this.tokenStylingDefaultRules.push({ classification, matchScore: getTokenStylingScore(classification), defaults });
}
public deregisterTokenStyleDefault(classification: TokenClassification): void {
this.tokenStylingDefaultRules = this.tokenStylingDefaultRules.filter(r => !(r.classification.type === classification.type && r.classification.modifiers === classification.modifiers));
}
public deregisterTokenType(id: string): void {
delete this.tokenTypeById[id];
delete this.tokenStylingSchema.properties[id];
......@@ -270,6 +317,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
return this.tokenStylingDefaultRules;
}
public toString() {
let sorter = (a: string, b: string) => {
let cat1 = a.indexOf('.') === -1 ? 0 : 1;
......@@ -301,64 +349,62 @@ export function matchTokenStylingRule(themeSelector: TokenStylingRule | TokenSty
const tokenClassificationRegistry = new TokenClassificationRegistry();
platform.Registry.add(Extensions.TokenClassificationContribution, tokenClassificationRegistry);
export function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], extendsTC: string | null = null, deprecationMessage?: string): string {
tokenClassificationRegistry.registerTokenType(id, description, deprecationMessage);
if (scopesToProbe || extendsTC) {
const classification = tokenClassificationRegistry.getTokenClassification(id, []);
tokenClassificationRegistry.registerTokenStyleDefault(classification!, { scopesToProbe, light: extendsTC, dark: extendsTC, hc: extendsTC });
}
return id;
}
registerDefaultClassifications();
export function registerTokenModifier(id: string, description: string, deprecationMessage?: string): string {
tokenClassificationRegistry.registerTokenModifier(id, description, deprecationMessage);
return id;
}
function registerDefaultClassifications(): void {
function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], extendsTC?: string, deprecationMessage?: string): string {
tokenClassificationRegistry.registerTokenType(id, description, deprecationMessage);
export function getTokenClassificationRegistry(): ITokenClassificationRegistry {
return tokenClassificationRegistry;
}
if (scopesToProbe || extendsTC) {
const classification = tokenClassificationRegistry.getTokenClassification(id, []);
tokenClassificationRegistry.registerTokenStyleDefault(classification!, { scopesToProbe, light: extendsTC, dark: extendsTC, hc: extendsTC });
}
return id;
}
// default token types
// default token types
registerTokenType('comment', nls.localize('comment', "Style for comments."), [['comment']]);
registerTokenType('string', nls.localize('string', "Style for strings."), [['string']]);
registerTokenType('keyword', nls.localize('keyword', "Style for keywords."), [['keyword.control']]);
registerTokenType('number', nls.localize('number', "Style for numbers."), [['constant.numeric']]);
registerTokenType('regexp', nls.localize('regexp', "Style for expressions."), [['constant.regexp']]);
registerTokenType('operator', nls.localize('operator', "Style for operators."), [['keyword.operator']]);
registerTokenType('comment', nls.localize('comment', "Style for comments."), [['comment']]);
registerTokenType('string', nls.localize('string', "Style for strings."), [['string']]);
registerTokenType('keyword', nls.localize('keyword', "Style for keywords."), [['keyword.control']]);
registerTokenType('number', nls.localize('number', "Style for numbers."), [['constant.numeric']]);
registerTokenType('regexp', nls.localize('regexp', "Style for expressions."), [['constant.regexp']]);
registerTokenType('operator', nls.localize('operator', "Style for operators."), [['keyword.operator']]);
registerTokenType('namespace', nls.localize('namespace', "Style for namespaces."), [['entity.name.namespace']]);
registerTokenType('namespace', nls.localize('namespace', "Style for namespaces."), [['entity.name.namespace']]);
registerTokenType('type', nls.localize('type', "Style for types."), [['entity.name.type'], ['entity.name.class'], ['support.type'], ['support.class']]);
registerTokenType('struct', nls.localize('struct', "Style for structs."), [['storage.type.struct']], 'type');
registerTokenType('class', nls.localize('class', "Style for classes."), [['entity.name.class']], 'type');
registerTokenType('interface', nls.localize('interface', "Style for interfaces."), undefined, 'type');
registerTokenType('enum', nls.localize('enum', "Style for enums."), undefined, 'type');
registerTokenType('parameterType', nls.localize('parameterType', "Style for parameter types."), undefined, 'type');
registerTokenType('type', nls.localize('type', "Style for types."), [['entity.name.type'], ['entity.name.class'], ['support.type'], ['support.class']]);
registerTokenType('struct', nls.localize('struct', "Style for structs."), [['storage.type.struct']], 'type');
registerTokenType('class', nls.localize('class', "Style for classes."), [['entity.name.class']], 'type');
registerTokenType('interface', nls.localize('interface', "Style for interfaces."), undefined, 'type');
registerTokenType('enum', nls.localize('enum', "Style for enums."), undefined, 'type');
registerTokenType('parameterType', nls.localize('parameterType', "Style for parameter types."), undefined, 'type');
registerTokenType('function', nls.localize('function', "Style for functions"), [['entity.name.function'], ['support.function']]);
registerTokenType('macro', nls.localize('macro', "Style for macros."), undefined, 'function');
registerTokenType('function', nls.localize('function', "Style for functions"), [['entity.name.function'], ['support.function']]);
registerTokenType('macro', nls.localize('macro', "Style for macros."), undefined, 'function');
registerTokenType('variable', nls.localize('variable', "Style for variables."), [['variable'], ['entity.name.variable']]);
registerTokenType('constant', nls.localize('constant', "Style for constants."), undefined, 'variable');
registerTokenType('parameter', nls.localize('parameter', "Style for parameters."), undefined, 'variable');
registerTokenType('property', nls.localize('propertie', "Style for properties."), undefined, 'variable');
registerTokenType('variable', nls.localize('variable', "Style for variables."), [['variable'], ['entity.name.variable']]);
registerTokenType('constant', nls.localize('constant', "Style for constants."), undefined, 'variable');
registerTokenType('parameter', nls.localize('parameter', "Style for parameters."), undefined, 'variable');
registerTokenType('property', nls.localize('propertie', "Style for properties."), undefined, 'variable');
registerTokenType('label', nls.localize('labels', "Style for labels. "), undefined);
registerTokenType('label', nls.localize('labels', "Style for labels. "), undefined);
// default token modifiers
// default token modifiers
registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined);
registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined);
registerTokenModifier('member', nls.localize('member', "Style to use for member functions, variables (fields) and types."), undefined);
registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined);
registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined);
registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined);
registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined);
registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined);
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('member', nls.localize('member', "Style to use for member functions, variables (fields) and types."), 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);
}
export function getTokenClassificationRegistry(): ITokenClassificationRegistry {
return tokenClassificationRegistry;
}
function bitCount(u: number) {
// https://blogs.msdn.microsoft.com/jeuge/2005/06/08/bit-fiddling-3/
......
......@@ -11,6 +11,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// --- other interested parties
import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint';
import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorExtensionPoint';
import { TokenClassificationExtensionPoints } from 'vs/workbench/services/themes/common/tokenClassificationExtensionPoint';
import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint';
// --- mainThread participants
......@@ -67,6 +68,7 @@ export class ExtensionPoints implements IWorkbenchContribution {
// Classes that handle extension points...
this.instantiationService.createInstance(JSONValidationExtensionPoint);
this.instantiationService.createInstance(ColorExtensionPoint);
this.instantiationService.createInstance(TokenClassificationExtensionPoints);
this.instantiationService.createInstance(LanguageConfigurationFileHandler);
}
}
......
......@@ -27,7 +27,7 @@ import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema';
import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorGroupSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema';
import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry';
import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
......@@ -684,16 +684,7 @@ configurationRegistry.registerConfiguration(themeSettingsConfiguration);
function tokenGroupSettings(description: string): IJSONSchema {
return {
description,
default: '#FF0000',
anyOf: [
{
type: 'string',
format: 'color-hex'
},
{
$ref: textmateColorSettingsSchemaId
}
]
$ref: textmateColorGroupSchemaId
};
}
......
......@@ -155,7 +155,10 @@ export class ColorThemeData implements IColorTheme {
for (const rule of tokenClassificationRegistry.getTokenStylingDefaultRules()) {
const matchScore = matchTokenStylingRule(rule, classification);
if (matchScore >= 0) {
let style = this.resolveScopes(rule.defaults.scopesToProbe);
let style: TokenStyle | undefined;
if (rule.defaults.scopesToProbe) {
style = this.resolveScopes(rule.defaults.scopesToProbe);
}
if (!style && useDefault !== false) {
style = this.resolveTokenStyleValue(rule.defaults[this.type]);
}
......@@ -185,8 +188,8 @@ export class ColorThemeData implements IColorTheme {
/**
* @param tokenStyleValue Resolve a tokenStyleValue in the context of a theme
*/
private resolveTokenStyleValue(tokenStyleValue: TokenStyleValue | null): TokenStyle | undefined {
if (tokenStyleValue === null) {
private resolveTokenStyleValue(tokenStyleValue: TokenStyleValue | undefined): TokenStyle | undefined {
if (tokenStyleValue === undefined) {
return undefined;
} else if (typeof tokenStyleValue === 'string') {
const [type, ...modifiers] = tokenStyleValue.split('.');
......@@ -289,7 +292,7 @@ export class ColorThemeData implements IColorTheme {
findTokenStyleForScopeInScopes(this.themeTokenScopeMatchers, this.themeTokenColors);
findTokenStyleForScopeInScopes(this.customTokenScopeMatchers, this.customTokenColors);
if (foreground !== undefined || fontStyle !== undefined) {
return getTokenStyle(foreground, fontStyle);
return TokenStyle.fromSettings(foreground, fontStyle);
}
}
return undefined;
......@@ -682,34 +685,7 @@ function getScopeMatcher(rule: ITextMateThemingRule): Matcher<ProbeScope> {
};
}
function getTokenStyle(foreground: string | undefined, fontStyle: string | undefined): TokenStyle {
let foregroundColor = undefined;
if (foreground !== undefined) {
foregroundColor = Color.fromHex(foreground);
}
let bold, underline, italic;
if (fontStyle !== undefined) {
fontStyle = fontStyle.trim();
if (fontStyle.length === 0) {
bold = italic = underline = false;
} else {
const expression = /-?italic|-?bold|-?underline/g;
let match;
while ((match = expression.exec(fontStyle))) {
switch (match[0]) {
case 'bold': bold = true; break;
case '-bold': bold = false; break;
case 'italic': italic = true; break;
case '-italic': italic = false; break;
case 'underline': underline = true; break;
case '-underline': underline = false; break;
}
}
}
}
return new TokenStyle(foregroundColor, bold, underline, italic);
}
function readCustomTokenStyleRules(tokenStylingRuleSection: IExperimentalTokenStyleCustomizations, result: TokenStylingRule[] = []) {
for (let key in tokenStylingRuleSection) {
......@@ -720,9 +696,9 @@ function readCustomTokenStyleRules(tokenStylingRuleSection: IExperimentalTokenSt
const settings = tokenStylingRuleSection[key];
let style: TokenStyle | undefined;
if (typeof settings === 'string') {
style = getTokenStyle(settings, undefined);
style = TokenStyle.fromSettings(settings, undefined);
} else if (isTokenColorizationSetting(settings)) {
style = getTokenStyle(settings.foreground, settings.fontStyle);
style = TokenStyle.fromSettings(settings.foreground, settings.fontStyle);
}
if (style) {
result.push(tokenClassificationRegistry.getTokenStylingRule(classification, style));
......
......@@ -115,10 +115,23 @@ let textMateScopes = [
export const textmateColorsSchemaId = 'vscode://schemas/textmate-colors';
export const textmateColorSettingsSchemaId = `${textmateColorsSchemaId}#definitions/settings`;
export const textmateColorGroupSchemaId = `${textmateColorsSchemaId}#definitions/colorGroup`;
const textmateColorSchema: IJSONSchema = {
type: 'array',
definitions: {
colorGroup: {
default: '#FF0000',
anyOf: [
{
type: 'string',
format: 'color-hex'
},
{
$ref: '#definitions/settings'
}
]
},
settings: {
type: 'object',
description: nls.localize('schema.token.settings', 'Colors and styles for the token.'),
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
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 { textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema';
interface ITokenTypeExtensionPoint {
id: string;
description: string;
}
interface ITokenModifierExtensionPoint {
id: string;
description: string;
}
interface ITokenStyleDefaultExtensionPoint {
selector: string;
scopes?: string[];
light?: {
foreground?: string;
fontStyle?: string;
};
dark?: {
foreground?: string;
fontStyle?: string;
};
highContrast?: {
foreground?: string;
fontStyle?: string;
};
}
const selectorPattern = '^([-_\\w]+|\\*)(\\.[-_\\w+]+)*$';
const colorPattern = '^#([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$';
const tokenClassificationRegistry: ITokenClassificationRegistry = getTokenClassificationRegistry();
const tokenTypeExtPoint = ExtensionsRegistry.registerExtensionPoint<ITokenTypeExtensionPoint[]>({
extensionPoint: 'tokenTypes',
jsonSchema: {
description: nls.localize('contributes.tokenTypes', 'Contributes semantic token types.'),
type: 'array',
items: {
type: 'object',
properties: {
id: {
type: 'string',
description: nls.localize('contributes.tokenTypes.id', 'The identifier of the token type'),
pattern: typeAndModifierIdPattern,
patternErrorMessage: nls.localize('contributes.tokenTypes.id.format', 'Identifiers should be in the form letterOrDigit[_-letterOrDigit]*'),
},
description: {
type: 'string',
description: nls.localize('contributes.color.description', 'The description of the token type'),
}
}
}
}
});
const tokenModifierExtPoint = ExtensionsRegistry.registerExtensionPoint<ITokenModifierExtensionPoint[]>({
extensionPoint: 'tokenModifiers',
jsonSchema: {
description: nls.localize('contributes.tokenModifiers', 'Contributes semantic token modifiers.'),
type: 'array',
items: {
type: 'object',
properties: {
id: {
type: 'string',
description: nls.localize('contributes.tokenModifiers.id', 'The identifier of the token modifier'),
pattern: typeAndModifierIdPattern,
patternErrorMessage: nls.localize('contributes.tokenModifiers.id.format', 'Identifiers should be in the form letterOrDigit[_-letterOrDigit]*')
},
description: {
description: nls.localize('contributes.tokenModifiers.description', 'The description of the token modifier')
}
}
}
}
});
const tokenStyleDefaultsExtPoint = ExtensionsRegistry.registerExtensionPoint<ITokenStyleDefaultExtensionPoint[]>({
extensionPoint: 'tokenStyleDefaults',
jsonSchema: {
description: nls.localize('contributes.tokenStyleDefaults', 'Contributes semantic token style default.'),
type: 'array',
items: {
type: 'object',
properties: {
selector: {
type: 'string',
description: nls.localize('contributes.tokenStyleDefaults.selector', 'The selector matching token types and modifiers.'),
pattern: selectorPattern,
patternErrorMessage: nls.localize('contributes.tokenStyleDefaults.selector.format', 'Selectors should be in the form (type|*)(.modifier)*'),
},
scopes: {
type: 'array',
description: nls.localize('contributes.scopes.light', 'A list of textmate scopes that are matched against the current color theme to find a default style'),
items: {
type: 'string'
}
},
light: {
description: nls.localize('contributes.tokenStyleDefaults.light', 'The default style used for light themes'),
$ref: textmateColorSettingsSchemaId
},
dark: {
description: nls.localize('contributes.tokenStyleDefaults.dark', 'The default style used for dark themes'),
$ref: textmateColorSettingsSchemaId
},
highContrast: {
description: nls.localize('contributes.tokenStyleDefaults.hc', 'The default style used for high contrast themes'),
$ref: textmateColorSettingsSchemaId
}
}
}
}
});
export class TokenClassificationExtensionPoints {
constructor() {
function validateTypeOrModifier(contribution: ITokenTypeExtensionPoint | ITokenModifierExtensionPoint, extensionPoint: string, collector: ExtensionMessageCollector): boolean {
if (typeof contribution.id !== 'string' || contribution.id.length === 0) {
collector.error(nls.localize('invalid.id', "'configuration.{0}.id' must be defined and can not be empty", extensionPoint));
return false;
}
if (!contribution.id.match(typeAndModifierIdPattern)) {
collector.error(nls.localize('invalid.id.format', "'configuration.{0}.id' must follow the pattern letterOrDigit[-_letterOrDigit]*", extensionPoint));
return false;
}
if (typeof contribution.description !== 'string' || contribution.id.length === 0) {
collector.error(nls.localize('invalid.description', "'configuration.{0}.description' must be defined and can not be empty", extensionPoint));
return false;
}
return true;
}
function validateStyle(style: { foreground?: string; fontStyle?: string; } | undefined, extensionPoint: string, collector: ExtensionMessageCollector): TokenStyle | undefined {
if (!style) {
return undefined;
}
if (style.foreground) {
if (typeof style.foreground !== 'string' || !style.foreground.match(colorPattern)) {
collector.error(nls.localize('invalid.color', "'configuration.{0}.foreground' must follow the pattern #RRGGBB[AA]", extensionPoint));
return undefined;
}
}
if (style.fontStyle) {
if (typeof style.fontStyle !== 'string' || !style.fontStyle.match(fontStylePattern)) {
collector.error(nls.localize('invalid.fontStyle', "'configuration.{0}.fontStyle' must be a one or a compination of \'italic\', \'bold\' or \'underline\' or the empty string", extensionPoint));
return undefined;
}
}
return TokenStyle.fromSettings(style.foreground, style.fontStyle);
}
tokenTypeExtPoint.setHandler((extensions, delta) => {
for (const extension of delta.added) {
const extensionValue = <ITokenTypeExtensionPoint[]>extension.value;
const collector = extension.collector;
if (!extensionValue || !Array.isArray(extensionValue)) {
collector.error(nls.localize('invalid.tokenTypeConfiguration', "'configuration.tokenType' must be a array"));
return;
}
for (const contribution of extensionValue) {
if (validateTypeOrModifier(contribution, 'tokenType', collector)) {
tokenClassificationRegistry.registerTokenType(contribution.id, contribution.description);
}
}
}
for (const extension of delta.removed) {
const extensionValue = <ITokenTypeExtensionPoint[]>extension.value;
for (const contribution of extensionValue) {
tokenClassificationRegistry.deregisterTokenType(contribution.id);
}
}
});
tokenModifierExtPoint.setHandler((extensions, delta) => {
for (const extension of delta.added) {
const extensionValue = <ITokenModifierExtensionPoint[]>extension.value;
const collector = extension.collector;
if (!extensionValue || !Array.isArray(extensionValue)) {
collector.error(nls.localize('invalid.tokenModifierConfiguration', "'configuration.tokenModifier' must be a array"));
return;
}
for (const contribution of extensionValue) {
if (validateTypeOrModifier(contribution, 'tokenModifier', collector)) {
tokenClassificationRegistry.registerTokenModifier(contribution.id, contribution.description);
}
}
}
for (const extension of delta.removed) {
const extensionValue = <ITokenModifierExtensionPoint[]>extension.value;
for (const contribution of extensionValue) {
tokenClassificationRegistry.deregisterTokenModifier(contribution.id);
}
}
});
tokenStyleDefaultsExtPoint.setHandler((extensions, delta) => {
for (const extension of delta.added) {
const extensionValue = <ITokenStyleDefaultExtensionPoint[]>extension.value;
const collector = extension.collector;
if (!extensionValue || !Array.isArray(extensionValue)) {
collector.error(nls.localize('invalid.tokenStyleDefaultConfiguration', "'configuration.tokenStyleDefaults' must be a array"));
return;
}
for (const contribution of extensionValue) {
if (typeof contribution.selector !== 'string' || contribution.selector.length === 0) {
collector.error(nls.localize('invalid.selector', "'configuration.tokenStyleDefaults.selector' must be defined and can not be empty"));
continue;
}
if (!contribution.selector.match(selectorPattern)) {
collector.error(nls.localize('invalid.selector.format', "'configuration.tokenStyleDefaults.selector' must be in the form (type|*)(.modifier)*"));
continue;
}
const tokenStyleDefault: TokenStyleDefaults = {};
if (contribution.scopes) {
if ((!Array.isArray(contribution.scopes) || contribution.scopes.some(s => typeof s !== 'string'))) {
collector.error(nls.localize('invalid.scopes', "If defined, 'configuration.tokenStyleDefaults.scopes' must must be an array or strings"));
continue;
}
tokenStyleDefault.scopesToProbe = [contribution.scopes];
}
tokenStyleDefault.light = validateStyle(contribution.light, 'tokenStyleDefaults.light', collector);
tokenStyleDefault.dark = validateStyle(contribution.dark, 'tokenStyleDefaults.dark', collector);
tokenStyleDefault.hc = validateStyle(contribution.highContrast, 'tokenStyleDefaults.highContrast', collector);
const [type, ...modifiers] = contribution.selector.split('.');
const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers);
if (classification) {
tokenClassificationRegistry.registerTokenStyleDefault(classification, tokenStyleDefault);
}
}
}
for (const extension of delta.removed) {
const extensionValue = <ITokenStyleDefaultExtensionPoint[]>extension.value;
for (const contribution of extensionValue) {
const [type, ...modifiers] = contribution.selector.split('.');
const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers);
if (classification) {
tokenClassificationRegistry.deregisterTokenStyleDefault(classification);
}
}
}
});
}
}
......@@ -21,6 +21,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
import { IFileService } from 'vs/platform/files/common/files';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
suite('MainThreadDocumentsAndEditors', () => {
......@@ -43,7 +44,7 @@ suite('MainThreadDocumentsAndEditors', () => {
deltas.length = 0;
const configService = new TestConfigurationService();
configService.setUserConfiguration('editor', { 'detectIndentation': false });
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService());
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService());
codeEditorService = new TestCodeEditorService();
textFileService = new class extends mock<ITextFileService>() {
isDirty() { return false; }
......
......@@ -43,7 +43,7 @@ suite('MainThreadEditors', () => {
setup(() => {
const configService = new TestConfigurationService();
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService());
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService());
const codeEditorService = new TestCodeEditorService();
movedResources.clear();
......
......@@ -31,6 +31,7 @@ import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbe
import { TestContextService, TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices';
import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
namespace Timer {
export interface ITimerEvent {
......@@ -74,7 +75,7 @@ suite.skip('QuickOpen performance (integration)', () => {
[ITelemetryService, telemetryService],
[IConfigurationService, configurationService],
[ITextResourcePropertiesService, textResourcePropertiesService],
[IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService())],
[IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService(), new NullLogService())],
[IWorkspaceContextService, new TestContextService(testWorkspace(URI.file(testWorkspacePath)))],
[IEditorService, new TestEditorService()],
[IEditorGroupsService, new TestEditorGroupsService()],
......
......@@ -60,18 +60,19 @@ suite.skip('TextSearch performance (integration)', () => {
const telemetryService = new TestTelemetryService();
const configurationService = new TestConfigurationService();
const textResourcePropertiesService = new TestTextResourcePropertiesService(configurationService);
const logService = new NullLogService();
const instantiationService = new InstantiationService(new ServiceCollection(
[ITelemetryService, telemetryService],
[IConfigurationService, configurationService],
[ITextResourcePropertiesService, textResourcePropertiesService],
[IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService())],
[IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService(), logService)],
[IWorkspaceContextService, new TestContextService(testWorkspace(URI.file(testWorkspacePath)))],
[IEditorService, new TestEditorService()],
[IEditorGroupsService, new TestEditorGroupsService()],
[IEnvironmentService, TestEnvironmentService],
[IUntitledTextEditorService, createSyncDescriptor(UntitledTextEditorService)],
[ISearchService, createSyncDescriptor(LocalSearchService)],
[ILogService, new NullLogService()]
[ILogService, logService]
));
const queryOptions: ITextQueryBuilderOptions = {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册