Skip to content

Commit 3c6f6af

Browse files
committed
improve inspect tokens hover
1 parent e715f39 commit 3c6f6af

3 files changed

Lines changed: 125 additions & 41 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export interface TokenStylingDefaultRule {
104104
export interface TokenStylingRule {
105105
match(classification: TokenClassification): number;
106106
value: TokenStyle;
107+
selector: TokenClassification;
107108
}
108109

109110
/**
@@ -294,7 +295,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
294295
public getTokenStylingRule(selector: TokenClassification, value: TokenStyle): TokenStylingRule {
295296
return {
296297
match: this.newMatcher(selector),
297-
value
298+
value,
299+
selector
298300
};
299301
}
300302

src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import { findMatchingThemeRule } from 'vs/workbench/services/textMate/common/TMH
2626
import { ITextMateService, IGrammar, IToken, StackElement } from 'vs/workbench/services/textMate/common/textMateService';
2727
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
2828
import { CancellationTokenSource } from 'vs/base/common/cancellation';
29+
import { ColorThemeData, TokenStyleDefinitions, TokenStyleDefinition } from 'vs/workbench/services/themes/common/colorThemeData';
30+
import { TokenStylingRule } from 'vs/platform/theme/common/tokenClassificationRegistry';
2931

3032
class InspectEditorTokensController extends Disposable implements IEditorContribution {
3133

@@ -121,7 +123,8 @@ interface ISemanticTokenInfo {
121123
type: string;
122124
modifiers: string[];
123125
range: Range;
124-
metadata: IDecodedMetadata
126+
metadata: IDecodedMetadata,
127+
definitions: TokenStyleDefinitions
125128
}
126129

127130
interface IDecodedMetadata {
@@ -248,17 +251,21 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget {
248251
const semanticTokenInfo = semanticTokens && this._getSemanticTokenAtPosition(semanticTokens, position);
249252

250253
let tokenText;
254+
251255
let metadata: IDecodedMetadata | undefined;
252-
let primary: IDecodedMetadata | undefined;
253-
if (textMateTokenInfo) {
256+
let tmFallback: IDecodedMetadata | undefined;
257+
258+
if (semanticTokenInfo) {
259+
tokenText = this._model.getValueInRange(semanticTokenInfo.range);
260+
metadata = semanticTokenInfo.metadata;
261+
if (textMateTokenInfo) {
262+
tmFallback = textMateTokenInfo.metadata;
263+
}
264+
} else if (textMateTokenInfo) {
254265
let tokenStartIndex = textMateTokenInfo.token.startIndex;
255266
let tokenEndIndex = textMateTokenInfo.token.endIndex;
256267
tokenText = this._model.getLineContent(position.lineNumber).substring(tokenStartIndex, tokenEndIndex);
257268
metadata = textMateTokenInfo.metadata;
258-
primary = semanticTokenInfo?.metadata;
259-
} else if (semanticTokenInfo) {
260-
tokenText = this._model.getValueInRange(semanticTokenInfo.range);
261-
metadata = semanticTokenInfo.metadata;
262269
} else {
263270
return 'No grammar or semantic tokens available.';
264271
}
@@ -268,28 +275,36 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget {
268275
result += `<hr class="tiw-metadata-separator" style="clear:both"/>`;
269276

270277
result += `<table class="tiw-metadata-table"><tbody>`;
271-
result += `<tr><td class="tiw-metadata-key">language</td><td class="tiw-metadata-value">${escape(metadata.languageIdentifier.language)}</td></tr>`;
272-
result += `<tr><td class="tiw-metadata-key">standard token type</td><td class="tiw-metadata-value">${this._tokenTypeToString(metadata.tokenType)}</td></tr>`;
273-
if (semanticTokenInfo) {
274-
result += `<tr><td class="tiw-metadata-key">semantic token type</td><td class="tiw-metadata-value">${semanticTokenInfo.type}</td></tr>`;
275-
const modifiers = semanticTokenInfo.modifiers.join(' ') || '-';
276-
result += `<tr><td class="tiw-metadata-key">semantic token modifiers</td><td class="tiw-metadata-value">${modifiers}</td></tr>`;
277-
}
278+
result += `<tr><td class="tiw-metadata-key">language</td><td class="tiw-metadata-value">${escape(textMateTokenInfo?.metadata.languageIdentifier.language || '')}</td></tr>`;
279+
result += `<tr><td class="tiw-metadata-key">standard token type</td><td class="tiw-metadata-value">${this._tokenTypeToString(textMateTokenInfo?.metadata.tokenType || StandardTokenType.Other)}</td></tr>`;
278280
result += `</tbody></table>`;
279281

280282
result += `<hr class="tiw-metadata-separator"/>`;
281283
result += `<table class="tiw-metadata-table"><tbody>`;
282-
result += this._formatMetadata(metadata, primary);
284+
result += this._formatMetadata(metadata, tmFallback);
283285
result += `</tbody></table>`;
284286

287+
if (semanticTokenInfo) {
288+
result += `<hr class="tiw-metadata-separator"/>`;
289+
result += `<table class="tiw-metadata-table"><tbody>`;
290+
result += `<tr><td class="tiw-metadata-key">semantic token type</td><td class="tiw-metadata-value">${semanticTokenInfo.type}</td></tr>`;
291+
const modifiers = semanticTokenInfo.modifiers.join(' ') || '-';
292+
result += `<tr><td class="tiw-metadata-key">semantic token modifiers</td><td class="tiw-metadata-value">${modifiers}</td></tr>`;
293+
result += `</tbody></table>`;
294+
295+
result += `<div>${this._renderTokenStyleDefinition(semanticTokenInfo.definitions.foreground)}</div>`;
296+
}
297+
285298
if (textMateTokenInfo) {
286299
let theme = this._themeService.getColorTheme();
287300
result += `<hr class="tiw-metadata-separator"/>`;
288-
let matchingRule = findMatchingThemeRule(theme, textMateTokenInfo.token.scopes, false);
289-
if (matchingRule) {
290-
result += `<code class="tiw-theme-selector">${matchingRule.rawSelector}\n${JSON.stringify(matchingRule.settings, null, '\t')}</code>`;
291-
} else {
292-
result += `<span class="tiw-theme-selector">No theme selector.</span>`;
301+
if (!semanticTokenInfo) {
302+
let matchingRule = findMatchingThemeRule(theme, textMateTokenInfo.token.scopes, false);
303+
if (matchingRule) {
304+
result += `<code class="tiw-theme-selector">${matchingRule.rawSelector}\n${JSON.stringify(matchingRule.settings, null, '\t')}</code>`;
305+
} else {
306+
result += `<span class="tiw-theme-selector">No theme selector.</span>`;
307+
}
293308
}
294309

295310
result += `<ul>`;
@@ -301,15 +316,20 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget {
301316
return result;
302317
}
303318

304-
private _formatMetadata(metadata: IDecodedMetadata, master?: IDecodedMetadata) {
319+
private _formatMetadata(metadata?: IDecodedMetadata, fallback?: IDecodedMetadata) {
305320
let result = '';
306321

307-
const fontStyle = master ? master.fontStyle : metadata.fontStyle;
308-
result += `<tr><td class="tiw-metadata-key">font style</td><td class="tiw-metadata-value">${fontStyle}</td></tr>`;
309-
const foreground = master && master.foreground || metadata.foreground;
310-
result += `<tr><td class="tiw-metadata-key">foreground</td><td class="tiw-metadata-value">${foreground}</td></tr>`;
311-
const background = master && master.background || metadata.background;
312-
result += `<tr><td class="tiw-metadata-key">background</td><td class="tiw-metadata-value">${background}</td></tr>`;
322+
function render(label: string, value: string | undefined, property: keyof IDecodedMetadata) {
323+
const info = metadata?.[property] !== value ? ` (tm)` : '';
324+
return `<tr><td class="tiw-metadata-key">${label}</td><td class="tiw-metadata-value">${value + info}</td></tr>`;
325+
}
326+
327+
const fontStyle = metadata?.fontStyle || fallback?.fontStyle;
328+
result += render('font style', fontStyle, 'fontStyle');
329+
const foreground = metadata?.foreground || fallback?.foreground;
330+
result += render('foreground', foreground, 'foreground');
331+
const background = metadata?.background || fallback?.background;
332+
result += render('background', background, 'background');
313333

314334
if (foreground && background) {
315335
const backgroundColor = Color.fromHex(background), foregroundColor = Color.fromHex(foreground);
@@ -436,15 +456,58 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget {
436456
const type = semanticTokens.legend.tokenTypes[typeIdx];
437457
const modifiers = semanticTokens.legend.tokenModifiers.filter((_, k) => modSet & 1 << k);
438458
const range = new Range(line + 1, character + 1, line + 1, character + 1 + len);
439-
const metadata = this._decodeMetadata(this._themeService.getTheme().getTokenStyleMetadata(type, modifiers) || 0);
440-
return { type, modifiers, range, metadata };
459+
const definitions = {};
460+
const theme = this._themeService.getTheme() as ColorThemeData;
461+
const m = theme.getTokenStyleMetadata(type, modifiers, true, definitions);
462+
const metadata = this._decodeMetadata(m || 0);
463+
return { type, modifiers, range, metadata, definitions };
441464
}
442465
lastLine = line;
443466
lastCharacter = character;
444467
}
445468
return null;
446469
}
447470

471+
private _renderTokenStyleDefinition(definition: TokenStyleDefinition | undefined): string {
472+
if (definition === undefined) {
473+
return '';
474+
}
475+
const theme = this._themeService.getTheme() as ColorThemeData;
476+
477+
const isTokenStylingRule = (d: any): d is TokenStylingRule => !!d.value;
478+
if (Array.isArray(definition)) {
479+
let result = '';
480+
result += `<ul>`;
481+
for (const d of definition) {
482+
result += `<li>${escape(d.join(' '))}</li>`;
483+
}
484+
result += `</ul>`;
485+
486+
for (const d of definition) {
487+
let matchingRule = findMatchingThemeRule(theme, d, false);
488+
if (matchingRule) {
489+
result += `<code class="tiw-theme-selector">${matchingRule.rawSelector}\n${JSON.stringify(matchingRule.settings, null, '\t')}</code>`;
490+
break;
491+
}
492+
}
493+
return result;
494+
} else if (isTokenStylingRule(definition)) {
495+
496+
const scope = theme.getTokenStylingRuleScope(definition);
497+
498+
if (scope === 'setting') {
499+
return `User settings: ${definition.selector}`;
500+
} else if (scope === 'theme') {
501+
return `Color theme: ${definition.selector}`;
502+
}
503+
return '';
504+
} else if (typeof definition === 'string') {
505+
return `Selector: ${definition}`;
506+
} else {
507+
return `Token style: Foreground: ${definition.foreground}, bold: ${definition.bold}, italic: ${definition.italic}, underline: ${definition.underline},`;
508+
}
509+
}
510+
448511
public getDomNode(): HTMLElement {
449512
return this._domNode;
450513
}

src/vs/workbench/services/themes/common/colorThemeData.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages';
1919
import { URI } from 'vs/base/common/uri';
2020
import { parse as parsePList } from 'vs/workbench/services/themes/common/plistParser';
2121
import { startsWith } from 'vs/base/common/strings';
22-
import { TokenStyle, TokenClassification, ProbeScope, TokenStylingRule, getTokenClassificationRegistry, TokenStyleValue } from 'vs/platform/theme/common/tokenClassificationRegistry';
22+
import { TokenStyle, TokenClassification, ProbeScope, TokenStylingRule, getTokenClassificationRegistry, TokenStyleValue, TokenStyleData } from 'vs/platform/theme/common/tokenClassificationRegistry';
2323
import { MatcherWithPriority, Matcher, createMatchers } from 'vs/workbench/services/themes/common/textMateScopeMatcher';
2424
import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader';
2525
import { FontStyle, ColorId, MetadataConsts } from 'vs/editor/common/modes';
@@ -40,6 +40,9 @@ const tokenGroupToScopesMap = {
4040
};
4141

4242

43+
export type TokenStyleDefinition = TokenStylingRule | ProbeScope[] | TokenStyleValue;
44+
export type TokenStyleDefinitions = { [P in keyof TokenStyleData]?: TokenStyleDefinition | undefined };
45+
4346
export class ColorThemeData implements IColorTheme {
4447

4548
id: string;
@@ -122,7 +125,7 @@ export class ColorThemeData implements IColorTheme {
122125
return color;
123126
}
124127

125-
public getTokenStyle(classification: TokenClassification, useDefault?: boolean): TokenStyle | undefined {
128+
public getTokenStyle(classification: TokenClassification, useDefault = true, definitions: TokenStyleDefinitions = {}): TokenStyle | undefined {
126129
let result: any = {
127130
foreground: undefined,
128131
bold: undefined,
@@ -136,10 +139,11 @@ export class ColorThemeData implements IColorTheme {
136139
italic: -1
137140
};
138141

139-
function _processStyle(matchScore: number, style: TokenStyle) {
142+
function _processStyle(matchScore: number, style: TokenStyle, definition: TokenStyleDefinition) {
140143
if (style.foreground && score.foreground <= matchScore) {
141144
score.foreground = matchScore;
142145
result.foreground = style.foreground;
146+
definitions.foreground = definition;
143147
}
144148
for (let p of ['bold', 'underline', 'italic']) {
145149
const property = p as keyof TokenStyle;
@@ -148,6 +152,7 @@ export class ColorThemeData implements IColorTheme {
148152
if (score[property] <= matchScore) {
149153
score[property] = matchScore;
150154
result[property] = info;
155+
definitions[property] = definition;
151156
}
152157
}
153158
}
@@ -159,27 +164,31 @@ export class ColorThemeData implements IColorTheme {
159164
let style: TokenStyle | undefined;
160165
if (rule.defaults.scopesToProbe) {
161166
style = this.resolveScopes(rule.defaults.scopesToProbe);
167+
if (style) {
168+
_processStyle(matchScore, style, rule.defaults.scopesToProbe);
169+
}
162170
}
163171
if (!style && useDefault !== false) {
164-
style = this.resolveTokenStyleValue(rule.defaults[this.type]);
165-
}
166-
if (style) {
167-
_processStyle(matchScore, style);
172+
const tokenStyleValue = rule.defaults[this.type];
173+
style = this.resolveTokenStyleValue(tokenStyleValue);
174+
if (style) {
175+
_processStyle(matchScore, style, tokenStyleValue!);
176+
}
168177
}
169178
}
170179
}
171180
} else {
172181
for (const rule of this.tokenStylingRules) {
173182
const matchScore = rule.match(classification);
174183
if (matchScore >= 0) {
175-
_processStyle(matchScore, rule.value);
184+
_processStyle(matchScore, rule.value, rule);
176185
}
177186
}
178187
}
179188
for (const rule of this.customTokenStylingRules) {
180189
const matchScore = rule.match(classification);
181190
if (matchScore >= 0) {
182-
_processStyle(matchScore, rule.value);
191+
_processStyle(matchScore, rule.value, rule);
183192
}
184193
}
185194
return TokenStyle.fromData(result);
@@ -234,12 +243,12 @@ export class ColorThemeData implements IColorTheme {
234243
return this.getTokenColorIndex().asArray();
235244
}
236245

237-
public getTokenStyleMetadata(type: string, modifiers: string[], useDefault?: boolean): number | undefined {
246+
public getTokenStyleMetadata(type: string, modifiers: string[], useDefault = true, definitions: TokenStyleDefinitions = {}): number | undefined {
238247
const classification = tokenClassificationRegistry.getTokenClassification(type, modifiers);
239248
if (!classification) {
240249
return undefined;
241250
}
242-
const style = this.getTokenStyle(classification, useDefault);
251+
const style = this.getTokenStyle(classification, useDefault, definitions);
243252
let fontStyle = FontStyle.None;
244253
let foreground = 0;
245254
if (style) {
@@ -257,6 +266,16 @@ export class ColorThemeData implements IColorTheme {
257266
return toMetadata(fontStyle, foreground, 0);
258267
}
259268

269+
public getTokenStylingRuleScope(rule: TokenStylingRule): 'setting' | 'theme' | undefined {
270+
if (this.customTokenStylingRules.indexOf(rule) !== -1) {
271+
return 'setting';
272+
}
273+
if (this.tokenStylingRules && this.tokenStylingRules.indexOf(rule) !== -1) {
274+
return 'theme';
275+
}
276+
return undefined;
277+
}
278+
260279
public getDefault(colorId: ColorIdentifier): Color | undefined {
261280
return colorRegistry.resolveDefaultColor(colorId, this);
262281
}

0 commit comments

Comments
 (0)