Skip to content

Commit 8db09a4

Browse files
committed
introducing tokenScopes
1 parent 9b0b596 commit 8db09a4

9 files changed

Lines changed: 640 additions & 6 deletions

File tree

src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
1414
import { Registry } from 'vs/platform/registry/common/platform';
1515
import { ColorIdentifier, Extensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry';
1616
import { Extensions as ThemingExtensions, ICssStyleCollector, IIconTheme, IThemingRegistry } from 'vs/platform/theme/common/themeService';
17+
import { TokenStyle, TokenStyleIdentifier } from 'vs/platform/theme/common/tokenStyleRegistry';
1718

1819
const VS_THEME_NAME = 'vs';
1920
const VS_DARK_THEME_NAME = 'vs-dark';
@@ -23,6 +24,7 @@ const colorRegistry = Registry.as<IColorRegistry>(Extensions.ColorContribution);
2324
const themingRegistry = Registry.as<IThemingRegistry>(ThemingExtensions.ThemingContribution);
2425

2526
class StandaloneTheme implements IStandaloneTheme {
27+
2628
public readonly id: string;
2729
public readonly themeName: string;
2830

@@ -128,6 +130,10 @@ class StandaloneTheme implements IStandaloneTheme {
128130
}
129131
return this._tokenTheme;
130132
}
133+
134+
getTokenStyle(tokenStyle: TokenStyleIdentifier, useDefault?: boolean | undefined): TokenStyle | undefined {
135+
return undefined;
136+
}
131137
}
132138

133139
function isBuiltinTheme(themeName: string): themeName is BuiltinTheme {

src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ILineTokens, IToken, TokenizationSupport2Adapter, TokensProvider } from
1313
import { IStandaloneTheme, IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService';
1414
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
1515
import { IIconTheme, ITheme, LIGHT } from 'vs/platform/theme/common/themeService';
16+
import { TokenStyleIdentifier } from 'vs/platform/theme/common/tokenStyleRegistry';
1617

1718
suite('TokenizationSupport2Adapter', () => {
1819

@@ -54,7 +55,9 @@ suite('TokenizationSupport2Adapter', () => {
5455

5556
defines: (color: ColorIdentifier): boolean => {
5657
throw new Error('Not implemented');
57-
}
58+
},
59+
60+
getTokenStyle: (tokenStyleId: TokenStyleIdentifier) => undefined
5861
};
5962
}
6063
public getIconTheme(): IIconTheme {

src/vs/platform/theme/common/themeService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as platform from 'vs/platform/registry/common/platform';
1010
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
1111
import { Event, Emitter } from 'vs/base/common/event';
1212
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
13+
import { TokenStyleIdentifier, TokenStyle } from 'vs/platform/theme/common/tokenStyleRegistry';
1314

1415
export const IThemeService = createDecorator<IThemeService>('themeService');
1516

@@ -59,6 +60,8 @@ export interface ITheme {
5960
* default color will be used.
6061
*/
6162
defines(color: ColorIdentifier): boolean;
63+
64+
getTokenStyle(color: TokenStyleIdentifier, useDefault?: boolean): TokenStyle | undefined;
6265
}
6366

6467
export interface IIconTheme {
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as platform from 'vs/platform/registry/common/platform';
7+
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
8+
import { Color } from 'vs/base/common/color';
9+
import { ITheme } from 'vs/platform/theme/common/themeService';
10+
import { Event, Emitter } from 'vs/base/common/event';
11+
12+
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
13+
import { RunOnceScheduler } from 'vs/base/common/async';
14+
15+
// ------ API types
16+
17+
export type TokenStyleIdentifier = string;
18+
19+
export interface TokenStyleContribution {
20+
readonly id: TokenStyleIdentifier;
21+
readonly description: string;
22+
readonly defaults: TokenStyleDefaults | null;
23+
readonly deprecationMessage: string | undefined;
24+
}
25+
26+
export const enum TokenStyleBits {
27+
BOLD = 0x01,
28+
UNDERLINE = 0x02,
29+
ITALIC = 0x04
30+
}
31+
32+
export class TokenStyle {
33+
constructor(
34+
public readonly foreground?: Color,
35+
public readonly background?: Color,
36+
public readonly styles?: number
37+
) {
38+
39+
}
40+
41+
hasStyle(style: number): boolean {
42+
return !!this.styles && ((this.styles & style) === style);
43+
}
44+
}
45+
46+
export namespace TokenStyle {
47+
export function fromString(s: string) {
48+
const parts = s.split('-');
49+
let part = parts.shift();
50+
if (part) {
51+
const foreground = Color.fromHex(part);
52+
let background = undefined;
53+
let style = undefined;
54+
part = parts.shift();
55+
if (part && part[0] === '#') {
56+
background = Color.fromHex(part);
57+
part = parts.shift();
58+
}
59+
if (part) {
60+
try {
61+
style = parseInt(part);
62+
} catch (e) {
63+
// ignore
64+
}
65+
}
66+
return new TokenStyle(foreground, background, style);
67+
}
68+
return new TokenStyle(Color.red);
69+
}
70+
}
71+
72+
73+
74+
export interface TokenStyleFunction {
75+
(theme: ITheme): TokenStyle | undefined;
76+
}
77+
78+
export interface TokenStyleDefaults {
79+
scopesToProbe?: string[];
80+
light: TokenStyle | null;
81+
dark: TokenStyle | null;
82+
hc: TokenStyle | null;
83+
}
84+
85+
/**
86+
* A TokenStyle Value is either a tokestyle literal, a reference to other color or a derived color
87+
*/
88+
export type TokenStyleValue = TokenStyle | string | TokenStyleIdentifier | TokenStyleFunction;
89+
90+
// TokenStyle registry
91+
export const Extensions = {
92+
TokenStyleContribution: 'base.contributions.tokenStyles'
93+
};
94+
95+
export interface ITokenStyleRegistry {
96+
97+
readonly onDidChangeSchema: Event<void>;
98+
99+
/**
100+
* Register a TokenStyle to the registry.
101+
* @param id The TokenStyle id as used in theme description files
102+
* @param defaults The default values
103+
* @description the description
104+
*/
105+
registerTokenStyle(id: string, defaults: TokenStyleDefaults, description: string): TokenStyleIdentifier;
106+
107+
/**
108+
* Register a TokenStyle to the registry.
109+
*/
110+
deregisterTokenStyle(id: string): void;
111+
112+
/**
113+
* Get all TokenStyle contributions
114+
*/
115+
getTokenStyles(): TokenStyleContribution[];
116+
117+
/**
118+
* Gets the default TokenStyle of the given id
119+
*/
120+
resolveDefaultTokenStyle(id: TokenStyleIdentifier, theme: ITheme, findTokenStyleForScope: (scope: string) => TokenStyle | undefined): TokenStyle | undefined;
121+
122+
/**
123+
* JSON schema for an object to assign TokenStyle values to one of the TokenStyle contributions.
124+
*/
125+
getTokenStyleSchema(): IJSONSchema;
126+
127+
/**
128+
* JSON schema to for a reference to a TokenStyle contribution.
129+
*/
130+
getTokenStyleReferenceSchema(): IJSONSchema;
131+
132+
}
133+
134+
135+
136+
class TokenStyleRegistry implements ITokenStyleRegistry {
137+
138+
private readonly _onDidChangeSchema = new Emitter<void>();
139+
readonly onDidChangeSchema: Event<void> = this._onDidChangeSchema.event;
140+
141+
private tokenStyleById: { [key: string]: TokenStyleContribution };
142+
private tokenStyleSchema: IJSONSchema & { properties: IJSONSchemaMap } = { type: 'object', properties: {} };
143+
private tokenStyleReferenceSchema: IJSONSchema & { enum: string[], enumDescriptions: string[] } = { type: 'string', enum: [], enumDescriptions: [] };
144+
145+
constructor() {
146+
this.tokenStyleById = {};
147+
}
148+
149+
public registerTokenStyle(id: string, defaults: TokenStyleDefaults | null, description: string, deprecationMessage?: string): TokenStyleIdentifier {
150+
let colorContribution: TokenStyleContribution = { id, description, defaults, deprecationMessage };
151+
this.tokenStyleById[id] = colorContribution;
152+
let propertySchema: IJSONSchema = { type: 'string', description, format: 'color-hex', default: '#ff0000' };
153+
if (deprecationMessage) {
154+
propertySchema.deprecationMessage = deprecationMessage;
155+
}
156+
this.tokenStyleSchema.properties[id] = propertySchema;
157+
this.tokenStyleReferenceSchema.enum.push(id);
158+
this.tokenStyleReferenceSchema.enumDescriptions.push(description);
159+
160+
this._onDidChangeSchema.fire();
161+
return id;
162+
}
163+
164+
165+
public deregisterTokenStyle(id: string): void {
166+
delete this.tokenStyleById[id];
167+
delete this.tokenStyleSchema.properties[id];
168+
const index = this.tokenStyleReferenceSchema.enum.indexOf(id);
169+
if (index !== -1) {
170+
this.tokenStyleReferenceSchema.enum.splice(index, 1);
171+
this.tokenStyleReferenceSchema.enumDescriptions.splice(index, 1);
172+
}
173+
this._onDidChangeSchema.fire();
174+
}
175+
176+
public getTokenStyles(): TokenStyleContribution[] {
177+
return Object.keys(this.tokenStyleById).map(id => this.tokenStyleById[id]);
178+
}
179+
180+
public resolveDefaultTokenStyle(id: TokenStyleIdentifier, theme: ITheme, findTokenStyleForScope: (scope: string) => TokenStyle | undefined): TokenStyle | undefined {
181+
const tokenStyleDesc = this.tokenStyleById[id];
182+
if (tokenStyleDesc && tokenStyleDesc.defaults) {
183+
const scopesToProbe = tokenStyleDesc.defaults.scopesToProbe;
184+
if (scopesToProbe) {
185+
for (let scope of scopesToProbe) {
186+
const style = findTokenStyleForScope(scope);
187+
if (style) {
188+
return style;
189+
}
190+
}
191+
}
192+
const tokenStyleValue = tokenStyleDesc.defaults[theme.type];
193+
return resolveTokenStyleValue(tokenStyleValue, theme);
194+
}
195+
return undefined;
196+
}
197+
198+
public getTokenStyleSchema(): IJSONSchema {
199+
return this.tokenStyleSchema;
200+
}
201+
202+
public getTokenStyleReferenceSchema(): IJSONSchema {
203+
return this.tokenStyleReferenceSchema;
204+
}
205+
206+
public toString() {
207+
let sorter = (a: string, b: string) => {
208+
let cat1 = a.indexOf('.') === -1 ? 0 : 1;
209+
let cat2 = b.indexOf('.') === -1 ? 0 : 1;
210+
if (cat1 !== cat2) {
211+
return cat1 - cat2;
212+
}
213+
return a.localeCompare(b);
214+
};
215+
216+
return Object.keys(this.tokenStyleById).sort(sorter).map(k => `- \`${k}\`: ${this.tokenStyleById[k].description}`).join('\n');
217+
}
218+
219+
}
220+
221+
const tokenStyleRegistry = new TokenStyleRegistry();
222+
platform.Registry.add(Extensions.TokenStyleContribution, tokenStyleRegistry);
223+
224+
export function registerTokenStyle(id: string, defaults: TokenStyleDefaults | null, description: string, deprecationMessage?: string): TokenStyleIdentifier {
225+
return tokenStyleRegistry.registerTokenStyle(id, defaults, description, deprecationMessage);
226+
}
227+
228+
export function getTokenStyleRegistry(): ITokenStyleRegistry {
229+
return tokenStyleRegistry;
230+
}
231+
232+
// ----- implementation
233+
234+
/**
235+
* @param colorValue Resolve a color value in the context of a theme
236+
*/
237+
function resolveTokenStyleValue(tokenStyleValue: TokenStyleValue | null, theme: ITheme): TokenStyle | undefined {
238+
if (tokenStyleValue === null) {
239+
return undefined;
240+
} else if (typeof tokenStyleValue === 'string') {
241+
if (tokenStyleValue[0] === '#') {
242+
return TokenStyle.fromString(tokenStyleValue);
243+
}
244+
return theme.getTokenStyle(tokenStyleValue);
245+
} else if (typeof tokenStyleValue === 'object') {
246+
return tokenStyleValue;
247+
} else if (typeof tokenStyleValue === 'function') {
248+
return tokenStyleValue(theme);
249+
}
250+
return undefined;
251+
}
252+
253+
export const tokenStyleColorsSchemaId = 'vscode://schemas/workbench-tokenstyles';
254+
255+
let schemaRegistry = platform.Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
256+
schemaRegistry.registerSchema(tokenStyleColorsSchemaId, tokenStyleRegistry.getTokenStyleSchema());
257+
258+
const delayer = new RunOnceScheduler(() => schemaRegistry.notifySchemaChanged(tokenStyleColorsSchemaId), 200);
259+
tokenStyleRegistry.onDidChangeSchema(() => {
260+
if (!delayer.isScheduled()) {
261+
delayer.schedule();
262+
}
263+
});
264+
265+
// setTimeout(_ => console.log(colorRegistry.toString()), 5000);
266+
267+
268+

src/vs/platform/theme/test/common/testThemeService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { Event, Emitter } from 'vs/base/common/event';
77
import { IThemeService, ITheme, DARK, IIconTheme } from 'vs/platform/theme/common/themeService';
88
import { Color } from 'vs/base/common/color';
9+
import { TokenStyle, TokenStyleIdentifier } from 'vs/platform/theme/common/tokenStyleRegistry';
910

1011
export class TestTheme implements ITheme {
1112

@@ -23,6 +24,10 @@ export class TestTheme implements ITheme {
2324
defines(color: string): boolean {
2425
throw new Error('Method not implemented.');
2526
}
27+
28+
getTokenStyle(tokenStyleIdentifier: TokenStyleIdentifier, useDefault?: boolean | undefined): TokenStyle | undefined {
29+
return undefined;
30+
}
2631
}
2732

2833
export class TestIconTheme implements IIconTheme {

src/vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
99
import { ansiColorIdentifiers, registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
1010
import { ITheme, ThemeType } from 'vs/platform/theme/common/themeService';
1111
import { Color } from 'vs/base/common/color';
12+
import { TokenStyleIdentifier } from 'vs/platform/theme/common/tokenStyleRegistry';
1213

1314
registerColors();
1415

@@ -19,7 +20,8 @@ function getMockTheme(type: ThemeType): ITheme {
1920
label: '',
2021
type: type,
2122
getColor: (colorId: ColorIdentifier): Color | undefined => themingRegistry.resolveDefaultColor(colorId, theme),
22-
defines: () => true
23+
defines: () => true,
24+
getTokenStyle: (tokenStyleId: TokenStyleIdentifier) => undefined
2325
};
2426
return theme;
2527
}
@@ -99,4 +101,4 @@ suite('Workbench - TerminalColorRegistry', () => {
99101
'#e5e5e5'
100102
], 'The dark terminal colors should be used when a dark theme is active');
101103
});
102-
});
104+
});

0 commit comments

Comments
 (0)