[clang-tools-extra] r368834 - [clangd] Loading TokenColorRules as a class mapping the rules to their associated clangd TextMate scope index.

Johan Vikstrom via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 14 05:11:59 PDT 2019


Author: jvikstrom
Date: Wed Aug 14 05:11:58 2019
New Revision: 368834

URL: http://llvm.org/viewvc/llvm-project?rev=368834&view=rev
Log:
[clangd] Loading TokenColorRules as a class mapping the rules to their associated clangd TextMate scope index.

Summary: Loads a mapping of the clangd scope lookup table scopes to the most specific rule with the highest "precedence" on initialize. Preprocesses into a class so it's simple/fast to access when doing the actual coloring later.

Reviewers: hokein, ilya-biryukov

Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D65856

Modified:
    clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/semantic-highlighting.ts
    clang-tools-extra/trunk/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts

Modified: clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/semantic-highlighting.ts
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/semantic-highlighting.ts?rev=368834&r1=368833&r2=368834&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/semantic-highlighting.ts (original)
+++ clang-tools-extra/trunk/clangd/clients/clangd-vscode/src/semantic-highlighting.ts Wed Aug 14 05:11:58 2019
@@ -47,6 +47,8 @@ export class SemanticHighlightingFeature
   // 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
     };
   }
 
+  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
     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 =

Modified: clang-tools-extra/trunk/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts?rev=368834&r1=368833&r2=368834&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts (original)
+++ clang-tools-extra/trunk/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts Wed Aug 14 05:11:58 2019
@@ -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');
   });
 });




More information about the cfe-commits mailing list