Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[theming] Access theme's colors programmatically #32813

Open
fabiospampinato opened this issue Aug 19, 2017 · 27 comments

Comments

@fabiospampinato
Copy link
Contributor

commented Aug 19, 2017

I think we should add support for accessing theme's colors programmatically.

For instance let's take as an example the popular OneDark Pro theme, I'd like to access colors defined under tokenColors.

My use case: I'm making an extension that decorates some tokens, and I'd like them to have the same color that comments have in my theme, the problem is that the regex I use to find those tokens depends on some configurable value, so I cannot pre-compute it in advance and just put it in a .tmLanguage file.

It's already possible to somehow access colors defined under the colors key, via something like new vscode.ThemeColor ( 'activityBar.background' ), adding support for this sounds like a useful generalization to me.

What do you think?

@vscodebot vscodebot bot added the workbench label Aug 19, 2017

@aeschli

This comment has been minimized.

Copy link
Contributor

commented Aug 21, 2017

I'd rather avoid API that gives out the actual color, but hope that all can be done with color references. That way extensions don't need to worry about listening to theme changes. Yes, sounds like a useful idea but is not an easy addition as token colors are based on text mate theme rules and the text mate scope hierarchy.
If there are more requests, we can certainly look at it. Until then I recommend you to use one of the regular workbench colors, or - added to 1.16 - define a new color in your extension.

@aeschli aeschli self-assigned this Aug 21, 2017

@aeschli aeschli added this to the Backlog milestone Aug 21, 2017

@aeschli aeschli changed the title Access theme's colors programmatically [themeing] Access theme's colors programmatically Aug 21, 2017

@aeschli aeschli changed the title [themeing] Access theme's colors programmatically [theming] Access theme's colors programmatically Sep 15, 2017

@bpasero bpasero removed the workbench label Nov 16, 2017

@eamodio

This comment has been minimized.

Copy link
Contributor

commented Mar 1, 2018

@aeschli while I still think an API for this would be valuable (or at the least an API that tells you if the current theme is light or dark), another alternative that might work in certain scenarios would be to expose the set of theme colors as css variables. This would allow the scenario here: #34411 (comment)

In theory I could use an SVG image, with colors pulled from the css variables, allowing me to get theme adaptable icons into the hovers.

Thoughts?

FYI, this is similar to this request #41785, but not just for the webview, for the editor itself.

@aeschli

This comment has been minimized.

Copy link
Contributor

commented Mar 2, 2018

@eamodio Great idea. If using css variables in SVG images works then we should definitely do this. Let's track this in a separate issue!

@DanTup

This comment has been minimized.

Copy link
Contributor

commented Apr 3, 2018

#46940 was closed as a dupe of this, but I'm not sure it's the same request. I have remotely-hosted SVGs I want to include in my markdown tooltips. I need to decide between the black and white versions so need to know whether VS Code considers the current theme light or dark. I don't need access to colours and I cannot modify the SVG.

@eamodio

This comment has been minimized.

Copy link
Contributor

commented Jun 20, 2018

This issue has come up again. In GitLens, I would like to expose 2 colors (hot and cold versions) for the heatmap that GitLens generates, but I am unable to use theme colors, because I need access to the real color values to generate the full palette of colors required.

@vviikk

This comment has been minimized.

Copy link

commented Jun 26, 2018

A lot of extensions would benefit from this. Bracket colorizer, jumpy, gitlens and a zillion others. Would be good to implement this.

@aeschli

This comment has been minimized.

Copy link
Contributor

commented Jun 26, 2018

@piggyslasher Please explain how/where these extension could profit from this.

There is already a story that extensions can declare a new color (by id) and use it in decorations. Users can redefine these colors in themes.

@fabiospampinato

This comment has been minimized.

Copy link
Contributor Author

commented Jun 26, 2018

My use case: I'm making an extension that decorates some tokens, and I'd like them to have the same color that comments have in my theme, the problem is that the regex I use to find those tokens depends on some configurable value, so I cannot pre-compute it in advance and just put it in a .tmLanguage file

@aeschli that's my use case. Sure, I'm already exporting these colors via the settings and users can override them already. But I can't just pick the colors for them automatically depending on what theme they are currently using. Also if someone uses one of those extensions for rotating themes I can't just expect them to update their settings every time.

@vviikk

This comment has been minimized.

Copy link

commented Aug 17, 2018

@aeschli A use case in my scenario is I use the extension called "Bracket pair colorizer" with adds colors to matching brackets, like so:
image
Now, in order for the extension to render the brackets to match my theme, I have to hard code the colors into my settings:

image

Without this, I would end up with something that looks like this out of the box:
image

Notice the color of the brackets. They're from the extension's default preferences because the developer can't access the colors. I hope I'm on the right track.

@aeschli

This comment has been minimized.

Copy link
Contributor

commented Aug 20, 2018

@piggyslasher Did you see that you can define new colors in the extension's package.json?

"contributes": {
  "colors": [{
      "id": "bracketPairColorizer.bracket1",
      "description": "Color for the outermost bracket",
      "defaults": {
          "dark": "#112233",
          "light": "#ddeeff",
          "highContrast": "foreground"
      }
  }]
}

Users can then customize the color in the workspace.colorCustomization setting and even themes can add a default color.

Color default values can be defined for light, dark and high contrast theme and can either be a reference to an existing color or a color hex value.

@tamuratak

This comment has been minimized.

Copy link
Contributor

commented Oct 13, 2018

Hi,
I am working on a PR for LaTeX-Workshop, a VSCode extension for LaTeX editing. In the PR, I make a latex preview in hover available rendering math equations in SVG format with MathJax and embedding the dataurl of generated SVG into a markdown text as an image source.

When rendering math equations, I have to tell MathJax which color to use for each rendering. Otherwise rendered equations are invisible on a certain theme. To work around the lack of color API in VSCode, I have to render equations in a WebView process, not in an extension process because we can know colors form css variables in the WebView process, as suggested by @eamodio. So when LaTeX-Workshop users close a WebView pane, my PR does not work. For LaTeX-Workshop users, opening a WebView pane as a pdf viewer is a usual thing. So the lack of color api seems to be a minor thing.

However, the same can be said for other documents having TeX-like equations. Docstring in python, for example. Let's think about API documents with math equations written in docstring. Python libraries should have tons of such API documents. If we want to render equations in them with MathJax, and to display them with API documents in hover, the same problem happens. And, for python programmers while coding, opening a WebView pane is an unusual thing.

Please notice that, for this purpose, extensions don't have to listen to theme changes. Equations are dynamically rendered each time. MathJax is enough fast to do that.

Regards,

On a light theme.

oct-13-2018 10-46-17

On a dark theme.

oct-13-2018 10-48-16

@Gruntfuggly

This comment has been minimized.

Copy link

commented Jan 30, 2019

@aeschli Unfortunately that doesn't allow the colours to be used outside of the vscode API. 😞

@amueller

This comment has been minimized.

Copy link

commented Feb 19, 2019

@aeschli can you provide a reference for this? I feel like it might solve the @vviikk issue (which I'm also having) but I'm not sure I understand your suggestion.

Basically you're suggesting to use
these? https://code.visualstudio.com/api/references/theme-color

@aeschli

This comment has been minimized.

Copy link
Contributor

commented Feb 20, 2019

Yes, ideally you use one of the existing colors, or define a new color id (see #32813 (comment)) to allow users to theme your color as well.
I know that there are still many gaps with this story, Colors in hover, computed colors...

@CoenraadS

This comment has been minimized.

Copy link
Contributor

commented Feb 24, 2019

If I understand correctly.

User defines a css color as editor.foreground

I need to do a lookup to workbench.colorCustomizations to retrieve the css value

However if I do vscode.workspace.getConfiguration("workbench.colorCustomizations", undefined);

I get only the user defined settings. Is there I way I can get the flattened settings including all the theme colors. (e.g. if user did not specify a custom editor.foreground)

@aeschli

This comment has been minimized.

Copy link
Contributor

commented Feb 24, 2019

No, you can not look up a color, that's the API that's missing.
But you can reference a color by using new ThemeColor('editor.foreground') in certain APIs (status bar, decorators)

@CoenraadS

This comment has been minimized.

Copy link
Contributor

commented Feb 24, 2019

Aah that's the missing link. Awesome 👍 I think it works now

@DanTup

This comment has been minimized.

Copy link
Contributor

commented Feb 28, 2019

I found a way to abuse the WebView to find out whether the theme is Dark or Light. It's a huge hack, but I need to know whether to use light/dark icons and there's no API, so...

It returns a promise with an enum of the theme:

detectTheme(context.subscriptions).then((theme) => {
	if (theme === Theme.Dark) {
		console.log("Using Dark theme!");
	} else if (theme === Theme.Light) {
		console.log("Using Light theme!");
	} else if (theme === Theme.HighContrast) {
		console.log("Using High Contrast theme!");
	} else {
		console.log("Failed to parse theme");
	}
});

It'll automatically resolve with Unknown after a second, in case something has gone wrong. theme_detector.ts looks like this:

import * as vs from "vscode";

export enum Theme {
	Unknown,
	Dark,
	Light,
	HighContrast,
}

export function detectTheme(disposables?: vs.Disposable[]): Promise<Theme> {
	return new Promise((resolve) => {
		let panel = createPanel();
		const messageHandler = (bodyCssClass?: string) => {
			if (panel) {
				panel.dispose();
				panel = undefined;
			}
			resolve(bodyCssClass ? parseClass(bodyCssClass) : Theme.Unknown);
		};

		// After a second, just resolve as unknown.
		setTimeout(() => messageHandler(), 1000);

		panel.webview.onDidReceiveMessage(
			messageHandler,
			undefined,
			disposables,
		);
		panel.webview.html = themeDetectorScript;
	});
}

const themeDetectorScript = `<html><body><script>
	(function() {
		const vscode = acquireVsCodeApi();
		vscode.postMessage(document.body.className);
	})();
</script></body></html>`;

function createPanel() {
	return vs.window.createWebviewPanel(
		"theme-detector",
		"",
		{
			preserveFocus: true,
			viewColumn: vs.ViewColumn.Beside,
		},
		{
			enableScripts: true,
			localResourceRoots: [],
		},
	);
}

function parseClass(bodyCssClass: string): Theme {
	if (bodyCssClass && bodyCssClass.indexOf("vscode-dark") !== -1) {
		return Theme.Dark;
	} else if (bodyCssClass && bodyCssClass.indexOf("vscode-light") !== -1) {
		return Theme.Light;
	} else if (bodyCssClass && bodyCssClass.indexOf("vscode-high-contrast") !== -1) {
		return Theme.HighContrast;
	} else {
		return Theme.Unknown;
	}
}

I don't take any responsibility if you decide to use this, it could be fragile, but since I wrote it I thought I'd share it. I only need the theme, but you should be able to tweak the support extracting colours.

Note: It doesn't do any detection of the theme changing, so you may not want to hold onto the result for too long. Though there is also a visible flicker of the window and it could take up to a second to fail, so you might also not want to call it too much.

@bobbrow

This comment has been minimized.

Copy link
Member

commented May 1, 2019

Can we initialize a ThemeColor based on a "scope" value? For example, if our extension wants to add text decorations based on the theme's setting for the "comment" scope, is it possible?

For the C++ extension, we essentially want to color the code based on our own language service's information instead of TextMate grammars which are not capable of classifying user-defined types correctly.

@aeschli

This comment has been minimized.

Copy link
Contributor

commented May 2, 2019

Can we initialize a ThemeColor based on a "scope" value?

@bobbrow That's currently not possible

@rchiodo

This comment has been minimized.

Copy link

commented May 22, 2019

I'm plus oneing @bobbrow's comment. We need the same thing for the Python Extension. We host the monaco editor in our 'Python Interactive' window and we have no way to get the colors for different scopes. Currently we have a hack of trying to find the current theme's json on disk.

@memeplex

This comment has been minimized.

Copy link

commented Jul 4, 2019

The PDF previewer in LaTeX Workshop could benefit from knowing if the current theme is dark or light in order to decide whether to invert or not the background and foreground colors. In general, I believe the light/dark state is an important piece of information to expose.

@PEZ

This comment has been minimized.

Copy link

commented Jul 8, 2019

I +1000 this one. My use case is that in Clojure there are three types of comments:

  1. Line based, rest of line after a ;.
  2. The (comment) function, returning nil whatever you pass it.
  3. The ignore reader token, #_ resulting in that the reader skips the whole form following it.

These are often used differently, with ; comment being the way to just comment something, (comment) to keep test code in the file, and #_ to disable chunks of code.

To support this use I'm rendering the (comment foo) and #_ stuff as dimmed, with intact syntax highlighting. But some users want those to be rendered the same as line based comments. Which I can't do, since I can't find the theme color value of comments.

Note that I can't solve this with tmLanguage grammar, because dimming can't be done that way.

@DanTup

This comment has been minimized.

Copy link
Contributor

commented Jul 15, 2019

Here's another annoyance with not knowing if the theme is light or dark... Terminal ansi colours...

I had some code that used Grey and White in stack traces so that users own code frames was more visible (white) than framework code (grey). I tested it with both light and dark themes and found that the White ansi color codes produce a dark color in the themes (great!).

Well, until a user with a third party theme found that I was showing white text on a light background 😞

I figured I could raise this as a bug against their theme, however I failed to find any guidance on how themes should handle these, though I did found that in the VS Code code, ansiWhite is inverted for light/dark theme but ansiBlack is not which leaves me more confused.

@anaisbetts

This comment has been minimized.

Copy link

commented Aug 23, 2019

Came here via @DanTup - I'm trying to write a VS Extension to sync your color theme to Windows Terminal, but this bug blocks it. This information is available internally (via "Generate Color Theme from Current Settings"), but I have no sane access to it

@paulyoung

This comment has been minimized.

Copy link

commented Aug 23, 2019

@anaisbetts I’ve done this before. It’s not exactly sane but it worked for me at the time.

const getTokenColorsForTheme = (themeName: string): TokenColors => {
  const tokenColors = new Map();
  let currentThemePath;
  for (const extension of extensions.all) {
    const themes = extension.packageJSON.contributes && extension.packageJSON.contributes.themes;
    const currentTheme = themes && themes.find((theme) => theme.id === themeName);
    if (currentTheme) {
      currentThemePath = path.join(extension.extensionPath, currentTheme.path);
      break;
    }
  }
  const themePaths = [];
  if (currentThemePath) { themePaths.push(currentThemePath); }
  while (themePaths.length > 0) {
    const themePath = themePaths.pop();
    const theme = require(themePath);
    if (theme) {
      if (theme.include) {
        themePaths.push(path.join(path.dirname(themePath), theme.include));
      }
      if (theme.tokenColors) {
        theme.tokenColors.forEach((rule) => {
          if (typeof rule.scope === "string" && !tokenColors.has(rule.scope)) {
            tokenColors.set(rule.scope, rule.settings);
          } else if (rule.scope instanceof Array) {
            rule.scope.forEach((scope) => {
              if (!tokenColors.has(rule.scope)) {
                tokenColors.set(scope, rule.settings);
              }
            });
          }
        });
      }
    }
  }
  return tokenColors;
}

where

type TokenColors = Map<string, TokenColorSettings>

interface TokenColorSettings {
  background?: string
  fontStyle?: string
  foreground?: string
}
@paulyoung

This comment has been minimized.

Copy link

commented Aug 23, 2019

Usage:

const themeName: string | undefined = workspace.getConfiguration("workbench").get("colorTheme");
const tokenColors = getTokenColorsForTheme(themeName);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.