colors: add support for exporting colors

Make color definitions introspectable, where previously there was data
hidden in closures. This allows colors to be exported and used in
other tools.
This commit is contained in:
Connor Peet 2021-05-28 14:15:17 -07:00
parent 2c50328192
commit 950ad4ffec
No known key found for this signature in database
GPG key ID: CF8FD2EA0DBC61BD
8 changed files with 100 additions and 69 deletions

View file

@ -12,7 +12,7 @@ set NAMESHORT=%NAMESHORT:"=%.exe
set CODE=".build\electron\%NAMESHORT%" set CODE=".build\electron\%NAMESHORT%"
:: Download Electron if needed :: Download Electron if needed
node build\lib\electron.js call node build\lib\electron.js
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
:: Run tests :: Run tests

View file

@ -11,6 +11,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { RunOnceScheduler } from 'vs/base/common/async'; import { RunOnceScheduler } from 'vs/base/common/async';
import { assertNever } from 'vs/base/common/types';
// ------ API types // ------ API types
@ -24,11 +25,21 @@ export interface ColorContribution {
readonly deprecationMessage: string | undefined; readonly deprecationMessage: string | undefined;
} }
export const enum ColorTransformType {
export interface ColorFunction { Darken,
(theme: IColorTheme): Color | undefined; Lighten,
Transparent,
OneOf,
LessProminent,
} }
export type ColorTransform =
| { op: ColorTransformType.Darken; value: ColorValue; factor: number }
| { op: ColorTransformType.Lighten; value: ColorValue; factor: number }
| { op: ColorTransformType.Transparent; value: ColorValue; factor: number }
| { op: ColorTransformType.OneOf; values: readonly ColorValue[] }
| { op: ColorTransformType.LessProminent; value: ColorValue; background: ColorValue; factor: number; transparency: number };
export interface ColorDefaults { export interface ColorDefaults {
light: ColorValue | null; light: ColorValue | null;
dark: ColorValue | null; dark: ColorValue | null;
@ -38,7 +49,7 @@ export interface ColorDefaults {
/** /**
* A Color Value is either a color literal, a reference to an other color or a derived color * A Color Value is either a color literal, a reference to an other color or a derived color
*/ */
export type ColorValue = Color | string | ColorIdentifier | ColorFunction; export type ColorValue = Color | string | ColorIdentifier | ColorTransform;
// color registry // color registry
export const Extensions = { export const Extensions = {
@ -495,63 +506,63 @@ export const chartsPurple = registerColor('charts.purple', { dark: '#B180D7', li
// ----- color functions // ----- color functions
export function darken(colorValue: ColorValue, factor: number): ColorFunction { export function executeTransform(transform: ColorTransform, theme: IColorTheme) {
return (theme) => { switch (transform.op) {
let color = resolveColorValue(colorValue, theme); case ColorTransformType.Darken:
if (color) { return resolveColorValue(transform.value, theme)?.darken(transform.factor);
return color.darken(factor);
}
return undefined;
};
}
export function lighten(colorValue: ColorValue, factor: number): ColorFunction { case ColorTransformType.Lighten:
return (theme) => { return resolveColorValue(transform.value, theme)?.lighten(transform.factor);
let color = resolveColorValue(colorValue, theme);
if (color) {
return color.lighten(factor);
}
return undefined;
};
}
export function transparent(colorValue: ColorValue, factor: number): ColorFunction { case ColorTransformType.Transparent:
return (theme) => { return resolveColorValue(transform.value, theme)?.transparent(transform.factor);
let color = resolveColorValue(colorValue, theme);
if (color) {
return color.transparent(factor);
}
return undefined;
};
}
export function oneOf(...colorValues: ColorValue[]): ColorFunction { case ColorTransformType.OneOf:
return (theme) => { for (const candidate of transform.values) {
for (let colorValue of colorValues) { const color = resolveColorValue(candidate, theme);
let color = resolveColorValue(colorValue, theme); if (color) {
if (color) { return color;
return color;
}
}
return undefined;
};
}
function lessProminent(colorValue: ColorValue, backgroundColorValue: ColorValue, factor: number, transparency: number): ColorFunction {
return (theme) => {
let from = resolveColorValue(colorValue, theme);
if (from) {
let backgroundColor = resolveColorValue(backgroundColorValue, theme);
if (backgroundColor) {
if (from.isDarkerThan(backgroundColor)) {
return Color.getLighterColor(from, backgroundColor, factor).transparent(transparency);
} }
return Color.getDarkerColor(from, backgroundColor, factor).transparent(transparency);
} }
return from.transparent(factor * transparency); return undefined;
}
return undefined; case ColorTransformType.LessProminent:
}; const from = resolveColorValue(transform.value, theme);
if (!from) {
return undefined;
}
const backgroundColor = resolveColorValue(transform.background, theme);
if (!backgroundColor) {
return from.transparent(transform.factor * transform.transparency);
}
return from.isDarkerThan(backgroundColor)
? Color.getLighterColor(from, backgroundColor, transform.factor).transparent(transform.transparency)
: Color.getDarkerColor(from, backgroundColor, transform.factor).transparent(transform.transparency);
default:
throw assertNever(transform);
}
}
export function darken(colorValue: ColorValue, factor: number): ColorTransform {
return { op: ColorTransformType.Darken, value: colorValue, factor };
}
export function lighten(colorValue: ColorValue, factor: number): ColorTransform {
return { op: ColorTransformType.Lighten, value: colorValue, factor };
}
export function transparent(colorValue: ColorValue, factor: number): ColorTransform {
return { op: ColorTransformType.Transparent, value: colorValue, factor };
}
export function oneOf(...colorValues: ColorValue[]): ColorTransform {
return { op: ColorTransformType.OneOf, values: colorValues };
}
function lessProminent(colorValue: ColorValue, backgroundColorValue: ColorValue, factor: number, transparency: number): ColorTransform {
return { op: ColorTransformType.LessProminent, value: colorValue, background: backgroundColorValue, factor, transparency };
} }
// ----- implementation // ----- implementation
@ -569,8 +580,8 @@ export function resolveColorValue(colorValue: ColorValue | null, theme: IColorTh
return theme.getColor(colorValue); return theme.getColor(colorValue);
} else if (colorValue instanceof Color) { } else if (colorValue instanceof Color) {
return colorValue; return colorValue;
} else if (typeof colorValue === 'function') { } else if (typeof colorValue === 'object') {
return colorValue(theme); return executeTransform(colorValue, theme);
} }
return undefined; return undefined;
} }

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, listFocusOutline, listInactiveFocusOutline, tableColumnsBorder, quickInputListFocusBackground, buttonBorder, keybindingLabelForeground, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, quickInputListFocusForeground } from 'vs/platform/theme/common/colorRegistry'; import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, listFocusOutline, listInactiveFocusOutline, tableColumnsBorder, quickInputListFocusBackground, buttonBorder, keybindingLabelForeground, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, quickInputListFocusForeground, ColorTransform } from 'vs/platform/theme/common/colorRegistry';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
import { IThemable, styleFn } from 'vs/base/common/styler'; import { IThemable, styleFn } from 'vs/base/common/styler';
@ -269,7 +269,7 @@ export function attachStylerCallback(themeService: IThemeService, colors: { [nam
} }
export interface IBreadcrumbsWidgetStyleOverrides extends IColorMapping { export interface IBreadcrumbsWidgetStyleOverrides extends IColorMapping {
breadcrumbsBackground?: ColorIdentifier | ColorFunction; breadcrumbsBackground?: ColorIdentifier | ColorTransform;
breadcrumbsForeground?: ColorIdentifier; breadcrumbsForeground?: ColorIdentifier;
breadcrumbsHoverForeground?: ColorIdentifier; breadcrumbsHoverForeground?: ColorIdentifier;
breadcrumbsFocusForeground?: ColorIdentifier; breadcrumbsFocusForeground?: ColorIdentifier;

View file

@ -24,7 +24,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IListService, WorkbenchDataTree, WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; import { IListService, WorkbenchDataTree, WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { ColorIdentifier, ColorFunction } from 'vs/platform/theme/common/colorRegistry'; import { ColorIdentifier, ColorTransform } from 'vs/platform/theme/common/colorRegistry';
import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ResourceLabel } from 'vs/workbench/browser/labels'; import { ResourceLabel } from 'vs/workbench/browser/labels';
@ -146,7 +146,7 @@ export interface IBreadcrumbsControlOptions {
showFileIcons: boolean; showFileIcons: boolean;
showSymbolIcons: boolean; showSymbolIcons: boolean;
showDecorationColors: boolean; showDecorationColors: boolean;
breadcrumbsBackground: ColorIdentifier | ColorFunction; breadcrumbsBackground: ColorIdentifier | ColorTransform;
showPlaceholder: boolean; showPlaceholder: boolean;
} }

View file

@ -30,7 +30,7 @@ import { IMenu, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/co
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IOpenerService } from 'vs/platform/opener/common/opener';
import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, resolveColorValue, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IColorTheme, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions'; import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions';
import { CommentGlyphWidget } from 'vs/workbench/contrib/comments/browser/commentGlyphWidget'; import { CommentGlyphWidget } from 'vs/workbench/contrib/comments/browser/commentGlyphWidget';
@ -805,7 +805,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
renderOptions: { renderOptions: {
after: { after: {
contentText: placeholder, contentText: placeholder,
color: `${transparent(editorForeground, 0.4)(this.themeService.getColorTheme())}` color: `${resolveColorValue(editorForeground, this.themeService.getColorTheme())?.transparent(0.4)}`
} }
} }
}]; }];

View file

@ -33,7 +33,7 @@ import { createAndBindHistoryNavigationWidgetScopedContextKeyService } from 'vs/
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { getSimpleEditorOptions, getSimpleCodeEditorWidgetOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; import { getSimpleEditorOptions, getSimpleCodeEditorWidgetOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
import { IDecorationOptions } from 'vs/editor/common/editorCommon'; import { IDecorationOptions } from 'vs/editor/common/editorCommon';
import { transparent, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems';
import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItem, completionKindFromString, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes'; import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItem, completionKindFromString, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes';
@ -657,7 +657,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
const decorations: IDecorationOptions[] = []; const decorations: IDecorationOptions[] = [];
if (this.isReadonly && this.replInput.hasTextFocus() && !this.replInput.getValue()) { if (this.isReadonly && this.replInput.hasTextFocus() && !this.replInput.getValue()) {
const transparentForeground = transparent(editorForeground, 0.4)(this.themeService.getColorTheme()); const transparentForeground = resolveColorValue(editorForeground, this.themeService.getColorTheme())?.transparent(0.4);
decorations.push({ decorations.push({
range: { range: {
startLineNumber: 0, startLineNumber: 0,

View file

@ -13,7 +13,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity';
import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { editorWidgetBackground, editorWidgetForeground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, textLinkForeground, contrastBorder, darken } from 'vs/platform/theme/common/colorRegistry'; import { editorWidgetBackground, editorWidgetForeground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, textLinkForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { Button } from 'vs/base/browser/ui/button/button'; import { Button } from 'vs/base/browser/ui/button/button';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@ -142,7 +142,7 @@ export class FeedbackWidget extends Dropdown {
if (darkenFactor) { if (darkenFactor) {
const backgroundBaseColor = theme.getColor(editorWidgetBackground); const backgroundBaseColor = theme.getColor(editorWidgetBackground);
if (backgroundBaseColor) { if (backgroundBaseColor) {
const backgroundColor = darken(backgroundBaseColor, darkenFactor)(theme); const backgroundColor = backgroundBaseColor.darken(darkenFactor);
if (backgroundColor) { if (backgroundColor) {
closeBtn.style.backgroundColor = backgroundColor.toString(); closeBtn.style.backgroundColor = backgroundColor.toString();
} }

View file

@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Color } from 'vs/base/common/color';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry';
suite('ColorRegistry', () => {
if (process.env.VSCODE_COLOR_REGISTRY_EXPORT) {
test('exports', () => {
const themingRegistry = Registry.as<IColorRegistry>(Extensions.ColorContribution);
const colors = themingRegistry.getColors();
const replacer = (_key: string, value: unknown) =>
value instanceof Color ? Color.Format.CSS.formatHexA(value) : value;
console.log(`#colors:${JSON.stringify(colors, replacer)}\n`);
});
}
});