diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/src/semantic-highlighting.ts b/clang-tools-extra/clangd/clients/clangd-vscode/src/semantic-highlighting.ts --- a/clang-tools-extra/clangd/clients/clangd-vscode/src/semantic-highlighting.ts +++ b/clang-tools-extra/clangd/clients/clangd-vscode/src/semantic-highlighting.ts @@ -47,6 +47,8 @@ // The TextMate scope lookup table. A token with scope index i has the scopes // on index i in the lookup table. scopeLookupTable: string[][]; + // The rules for the current theme. + themeRules: ThemeRules; fillClientCapabilities(capabilities: vscodelc.ClientCapabilities) { // Extend the ClientCapabilities type and add semantic highlighting // capability to the object. @@ -58,6 +60,18 @@ }; } + async loadCurrentTheme() { + const name = + vscode.workspace.getConfiguration('workbench').get('colorTheme'); + if (typeof name != 'string') { + console.warn('The current theme name is not a string, is:' + + (typeof name) + ', value: ', + name); + return; + } + this.themeRules = await loadTheme(name); + } + initialize(capabilities: vscodelc.ServerCapabilities, documentSelector: vscodelc.DocumentSelector|undefined) { // The semantic highlighting capability information is in the capabilities @@ -68,6 +82,7 @@ if (!serverCapabilities.semanticHighlighting) return; this.scopeLookupTable = serverCapabilities.semanticHighlighting.scopes; + this.loadCurrentTheme(); } handleNotification(params: SemanticHighlightingParams) {} @@ -101,8 +116,31 @@ foreground: string; } +export class ThemeRules { + // The rules for the theme. + private rules: TokenColorRule[]; + // A cache for the getBestThemeRule function. + private bestRuleCache: Map = new Map(); + constructor(rules: TokenColorRule[]) { this.rules = rules; } + // Returns the best rule for a scope. + getBestThemeRule(scope: string): TokenColorRule { + if (this.bestRuleCache.has(scope)) + return this.bestRuleCache.get(scope); + let bestRule: TokenColorRule = {scope : '', foreground : ''}; + this.rules.forEach((rule) => { + if (rule.scope.length <= scope.length && + scope.substr(0, rule.scope.length) === rule.scope && + rule.scope.length > bestRule.scope.length) + // This rule matches and is more specific than the old rule. + bestRule = rule; + }); + this.bestRuleCache.set(scope, bestRule); + return bestRule; + } +} + // Get all token color rules provided by the theme. -function loadTheme(themeName: string): Promise { +async function loadTheme(themeName: string): Promise { const extension = vscode.extensions.all.find((extension: vscode.Extension) => { const contribs = extension.packageJSON.contributes; @@ -118,7 +156,8 @@ const themeInfo = extension.packageJSON.contributes.themes.find( (theme: any) => theme.id === themeName || theme.label === themeName); - return parseThemeFile(path.join(extension.extensionPath, themeInfo.path)); + return new ThemeRules( + await parseThemeFile(path.join(extension.extensionPath, themeInfo.path))); } /** diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts b/clang-tools-extra/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts --- a/clang-tools-extra/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts +++ b/clang-tools-extra/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts @@ -1,13 +1,13 @@ import * as assert from 'assert'; import * as path from 'path'; -import * as TM from '../src/semantic-highlighting'; +import * as SM from '../src/semantic-highlighting'; suite('SemanticHighlighting Tests', () => { test('Parses arrays of textmate themes.', async () => { const themePath = path.join(__dirname, '../../test/assets/includeTheme.jsonc'); - const scopeColorRules = await TM.parseThemeFile(themePath); + const scopeColorRules = await SM.parseThemeFile(themePath); const getScopeRule = (scope: string) => scopeColorRules.find((v) => v.scope === scope); assert.equal(scopeColorRules.length, 3); @@ -33,6 +33,21 @@ ] ]; testCases.forEach((testCase, i) => assert.deepEqual( - TM.decodeTokens(testCase), expected[i])); + SM.decodeTokens(testCase), expected[i])); + }); + test('ScopeRules overrides for more specific themes', () => { + const rules = [ + {scope : 'c.b', foreground : '1'}, + {scope : 'a', foreground : '2'}, + {scope : 'a.b', foreground : '3'}, + {scope : 'a', foreground : '4'}, + {scope : 'c.b.a', foreground : '5'}, + ]; + const tm = new SM.ThemeRules(rules); + assert.deepEqual(tm.getBestThemeRule('c.b'), rules[0]); + assert.deepEqual(tm.getBestThemeRule('a.b'), rules[2]); + assert.deepEqual(tm.getBestThemeRule('a'), rules[1]); + assert.deepEqual(tm.getBestThemeRule('c.b.a'), rules[4]); + assert.deepEqual(tm.getBestThemeRule('c.b.a.d.f'), rules[4]); }); });