summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang-tools-extra/clangd/clients/clangd-vscode/src/semantic-highlighting.ts42
-rw-r--r--clang-tools-extra/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts28
2 files changed, 67 insertions, 3 deletions
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
index 46ec4047ca6..a403db2c75b 100644
--- 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 @@ export class SemanticHighlightingFeature implements vscodelc.StaticFeature {
// 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.
+ themeRuleMatcher: ThemeRuleMatcher;
fillClientCapabilities(capabilities: vscodelc.ClientCapabilities) {
// Extend the ClientCapabilities type and add semantic highlighting
// capability to the object.
@@ -58,6 +60,12 @@ export class SemanticHighlightingFeature implements vscodelc.StaticFeature {
};
}
+ async loadCurrentTheme() {
+ this.themeRuleMatcher = new ThemeRuleMatcher(
+ await loadTheme(vscode.workspace.getConfiguration('workbench')
+ .get<string>('colorTheme')));
+ }
+
initialize(capabilities: vscodelc.ServerCapabilities,
documentSelector: vscodelc.DocumentSelector|undefined) {
// The semantic highlighting capability information is in the capabilities
@@ -68,6 +76,7 @@ export class SemanticHighlightingFeature implements vscodelc.StaticFeature {
if (!serverCapabilities.semanticHighlighting)
return;
this.scopeLookupTable = serverCapabilities.semanticHighlighting.scopes;
+ this.loadCurrentTheme();
}
handleNotification(params: SemanticHighlightingParams) {}
@@ -101,6 +110,39 @@ interface TokenColorRule {
foreground: string;
}
+export class ThemeRuleMatcher {
+ // The rules for the theme.
+ private themeRules: TokenColorRule[];
+ // A cache for the getBestThemeRule function.
+ private bestRuleCache: Map<string, TokenColorRule> = new Map();
+ constructor(rules: TokenColorRule[]) { this.themeRules = 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.themeRules.forEach((rule) => {
+ // The best rule for a scope is the rule that is the longest prefix of the
+ // scope (unless a perfect match exists in which case the perfect match is
+ // the best). If a rule is not a prefix and we tried to match with longest
+ // common prefix instead variables would be highlighted as `less`
+ // variables when using Light+ (as variable.other would be matched against
+ // variable.other.less in this case). Doing common prefix matching also
+ // means we could match variable.cpp to variable.css if variable.css
+ // occurs before variable in themeRules.
+ // FIXME: This is not defined in the TextMate standard (it is explicitly
+ // undefined, https://macromates.com/manual/en/scope_selectors). Might
+ // want to rank some other way.
+ if (scope.startsWith(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<TokenColorRule[]> {
const extension =
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
index f7307633bd6..a8b39306eb4 100644
--- 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,28 @@ suite('SemanticHighlighting Tests', () => {
]
];
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 : 'variable.other.css', foreground : '1'},
+ {scope : 'variable.other', foreground : '2'},
+ {scope : 'storage', foreground : '3'},
+ {scope : 'storage.static', foreground : '4'},
+ {scope : 'storage', foreground : '5'},
+ {scope : 'variable.other.parameter', foreground : '6'},
+ ];
+ const tm = new SM.ThemeRuleMatcher(rules);
+ assert.deepEqual(tm.getBestThemeRule('variable.other.cpp').scope,
+ 'variable.other');
+ assert.deepEqual(tm.getBestThemeRule('storage.static').scope,
+ 'storage.static');
+ assert.deepEqual(
+ tm.getBestThemeRule('storage'),
+ rules[2]); // Match the first element if there are duplicates.
+ assert.deepEqual(tm.getBestThemeRule('variable.other.parameter').scope,
+ 'variable.other.parameter');
+ assert.deepEqual(tm.getBestThemeRule('variable.other.parameter.cpp').scope,
+ 'variable.other.parameter');
});
});
OpenPOWER on IntegriCloud