diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/package.json b/clang-tools-extra/clangd/clients/clangd-vscode/package.json
--- a/clang-tools-extra/clangd/clients/clangd-vscode/package.json
+++ b/clang-tools-extra/clangd/clients/clangd-vscode/package.json
@@ -36,14 +36,15 @@
         "test": "node ./node_modules/vscode/bin/test"
     },
     "dependencies": {
+        "jsonc-parser": "^2.1.0",
         "vscode-languageclient": "^5.3.0-next.6",
         "vscode-languageserver": "^5.3.0-next.6"
     },
     "devDependencies": {
         "@types/mocha": "^2.2.32",
         "@types/node": "^6.0.40",
-        "mocha": "^5.2.0",
         "clang-format": "1.2.4",
+        "mocha": "^5.2.0",
         "typescript": "^2.0.3",
         "vscode": "^1.1.0"
     },
diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/src/TextMate.ts b/clang-tools-extra/clangd/clients/clangd-vscode/src/TextMate.ts
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/clangd/clients/clangd-vscode/src/TextMate.ts
@@ -0,0 +1,174 @@
+import * as fs from 'fs';
+import * as jsonc from "jsonc-parser";
+import * as path from 'path';
+import * as vscode from 'vscode';
+
+export namespace SemanticHighlighting {
+export class TMColors {
+  // The clangd tm scopes.
+  private scopes: string[];
+  // Mapping from a clangd scope index to a color hex string.
+  private colors: string[];
+  // The current best matching scope for every index.
+  private colorScopes: string[];
+  constructor(scopes: string[]) {
+    this.scopes = scopes;
+    this.colors = this.scopes.map(() => '#000');
+    this.colorScopes = this.scopes.map(() => '');
+  }
+
+  setColor(scope: string|Array<string>, color: string) {
+    if (scope instanceof Array) {
+      scope.forEach((s: string) => this.setColor(s, color));
+      return;
+    }
+
+    // Find the associated clangd scope(s) index for this scope.
+    // If "scope" is a candiate for a clangd scope the clangd scope must have
+    // "scope" as a prefix.
+    const allCandidates =
+        this.scopes.map((s, i) => ({s : s, i : i}))
+            .filter(({s}) => s.substr(0, scope.length) === scope);
+    // If this scope is more specific than any of current scopes for the clangd
+    // scopes it should be replaced. As both options are prefixes of the clangd
+    // scope it's enough to compare lengths.
+    allCandidates.forEach(({i}) => {
+      if (scope.length > this.colorScopes[i].length) {
+        this.colorScopes[i] = scope;
+        this.colors[i] = color;
+      }
+    });
+  }
+
+  getColor(idx: number) { return this.colors[idx]; }
+}
+
+// Singleton for reading/writing TM scopes/colors.
+export class TMColorProvider {
+  private static instance: TMColorProvider = new TMColorProvider();
+  private colors: TMColors = undefined;
+  static get() { return TMColorProvider.instance; }
+
+  setColors(colors: TMColors) { this.colors = colors; }
+  getColors(): TMColors { return this.colors; }
+}
+
+/**
+ * @param scopes The TextMate scopes clangd sends on initialize.
+ * @param cb A callback that is called every time the theme changes and the new
+ *     theme has been loaded (not called on the first load).
+ */
+export async function setupTMScopes(scopes: string[],
+                                    cb: Function): Promise<vscode.Disposable> {
+  let oldThemeName = '';
+  async function setTMColors() {
+    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;
+    }
+
+    if (oldThemeName == name) {
+      return;
+    }
+
+    oldThemeName = name;
+    TMColorProvider.get().setColors(
+        await getTextMateColors(scopes, name as string));
+  }
+
+  // Initialize the TM scopes and colors.
+  await setTMColors();
+
+  // Need to change the color configuration if a theme changes otherwise the
+  // highlightings will have the wrong colors.
+  return vscode.workspace.onDidChangeConfiguration(
+      async (conf: vscode.ConfigurationChangeEvent) => {
+        if (conf.affectsConfiguration('workbench'))
+          // Configuration affected the workbench meaning the current theme
+          // might have changed.
+          await setTMColors();
+        cb();
+      });
+}
+
+// Gets a TM theme with themeName and returns class with the mapping from the
+// clangd scope index to a color.
+async function getTextMateColors(scopes: string[],
+                                 themeName: string): Promise<TMColors> {
+  const fileContents = await getFullNamedTheme(themeName);
+  const tmColors = new TMColors(scopes);
+  fileContents.forEach((content) => {
+    if (!content.tokenColors)
+      return;
+    content.tokenColors.forEach((rule: any) => {
+      if (!rule.scope || !rule.settings || !rule.settings.foreground)
+        return;
+
+      tmColors.setColor(rule.scope, rule.settings.foreground);
+    });
+  });
+
+  return tmColors;
+}
+
+// Gets a TextMate theme by its name and all its included themes.
+async function getFullNamedTheme(themeName: string): Promise<any[]> {
+  const extension =
+      vscode.extensions.all.find((extension: vscode.Extension<any>) => {
+        const contribs = extension.packageJSON.contributes;
+        if (!contribs || !contribs.themes)
+          return false;
+        return contribs.themes.some((theme: any) => theme.id === themeName ||
+                                                    theme.label === themeName);
+      });
+
+  if (!extension) {
+    return Promise.reject('Could not find a theme with name: ' + themeName);
+  }
+
+  const extensionInfo = extension.packageJSON.contributes.themes.find(
+      (theme: any) => theme.id === themeName || theme.label === themeName);
+  return recursiveGetTextMateGrammarPath(
+      path.join(extension.extensionPath, extensionInfo.path));
+}
+
+// TM grammars can include other TM grammars, this function recursively gets all
+// of them.
+async function recursiveGetTextMateGrammarPath(fullPath: string):
+    Promise<any[]> {
+  // If there is an error opening a file, the TM files that were correctly found
+  // and parsed further up the chain should be returned. Otherwise there will be
+  // no highlightings at all.
+  try {
+    const contents = await readFileText(fullPath);
+    const parsed = jsonc.parse(contents);
+    if (parsed.include)
+      // Get all includes and merge into a flat list of parsed json.
+      return [
+        ...(await recursiveGetTextMateGrammarPath(
+            path.join(path.dirname(fullPath), parsed.include))),
+        parsed
+      ];
+    return [ parsed ];
+  } catch (err) {
+    console.warn('Could not open file: ' + fullPath + ', error: ', err);
+  }
+
+  return [];
+}
+
+function readFileText(path: string): Promise<string> {
+  return new Promise((res, rej) => {
+    fs.readFile(path, 'utf8', (err, data) => {
+      if (err) {
+        rej(err);
+      }
+      res(data);
+    });
+  });
+}
+}
diff --git a/clang-tools-extra/clangd/clients/clangd-vscode/test/TextMate.test.ts b/clang-tools-extra/clangd/clients/clangd-vscode/test/TextMate.test.ts
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/clangd/clients/clangd-vscode/test/TextMate.test.ts
@@ -0,0 +1,32 @@
+/** The module 'assert' provides assertion methods from node */
+import * as assert from 'assert';
+
+import * as vscode from 'vscode';
+import {SemanticHighlighting} from '../src/TextMate';
+
+// TODO: add tests
+suite("Extension Tests", () => {
+  test('overrides for more specific themes', () => {
+    const scopes = [ 'a.b.c.d', 'a.b.f', 'a' ];
+    const colorPairs = [
+      [ [ 'a.b.c', 'a.b.d' ], '1' ],
+      [ 'a.b', '2' ],
+      [ 'a.b.c.d', '3' ],
+      [ 'a', '4' ],
+    ];
+    const tm = new SemanticHighlighting.TMColors(scopes);
+    colorPairs.forEach((p) => tm.setColor(p[0], p[1] as string));
+    assert.deepEqual(tm.getColor(0), '3');
+    assert.deepEqual(tm.getColor(1), '2');
+    assert.deepEqual(tm.getColor(2), '4');
+  });
+  test('Sets an instance of TMColors on setup.', async () => {
+    const scopes = [
+      'variable',
+    ];
+    const disp = await SemanticHighlighting.setupTMScopes(scopes, () => {});
+    assert.notEqual(SemanticHighlighting.TMColorProvider.get().getColors(),
+                    undefined);
+    disp.dispose();
+  });
+});
\ No newline at end of file