addons/shared/textmate-lib/textmateStyles.tsblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318/**
b69ab319 * This is the prefix used by Monaco for the CSS classes it uses for syntax
b69ab3110 * highlighting. Each token returned by `IGrammar.tokenizeLine2()` has a
b69ab3111 * `number` associated with it. To construct the appropriate CSS class name for
b69ab3112 * color number `n`, do: `${CSS_CLASS_PREFIX}${n}`.
b69ab3113 */
b69ab3114export const CSS_CLASS_PREFIX = 'mtk';
b69ab3115
b69ab3116/**
b69ab3117 * Updates the <style> element on the page to define the CSS rules necessary to
b69ab3118 * honor the user's selected theme.
b69ab3119 * @param colorMap as returned by `Registry.getColorMap()` where each value in
b69ab3120 * the array is a CSS hex value, such as "#AA0000".
b69ab3121 */
b69ab3122export function updateTextMateGrammarCSS(colorMap: string[]): void {
b69ab3123 // Note that if the Monaco editor is used on the page, then we also need to do
b69ab3124 // something like:
b69ab3125 //
b69ab3126 // const colorMap = cssColors.map(Color.Format.CSS.parseHex);
b69ab3127 // TokenizationRegistry.setColorMap(colorMap);
b69ab3128 //
b69ab3129 // though that will require loading code from the monaco-editor npm module.
b69ab3130
b69ab3131 const css = generateTokensCSSForColorMap(colorMap);
b69ab3132 const style = getOrCreateStyleElementForColorsCSS();
b69ab3133 style.innerHTML = css;
b69ab3134}
b69ab3135
b69ab3136let styleElementForTextMateCSS: HTMLStyleElement | null = null;
b69ab3137
b69ab3138function getOrCreateStyleElementForColorsCSS(): HTMLStyleElement {
b69ab3139 // If there is an existing <style> element, then overwrite its contents
b69ab3140 // rather than create a new one. (Yes, this means that we support only one
b69ab3141 // theme globally on the page at a time, at least for now.)
b69ab3142 if (styleElementForTextMateCSS != null) {
b69ab3143 return styleElementForTextMateCSS;
b69ab3144 }
b69ab3145
b69ab3146 // We want to ensure that our <style> element appears after Monaco's so that
b69ab3147 // we can override some styles it inserted for the default theme.
b69ab3148 styleElementForTextMateCSS = document.createElement('style');
b69ab3149
b69ab3150 // If an instance of the Monaco editor is being used on the page, then it will
b69ab3151 // have injected a stylesheet that we need to override. We expect these styles
b69ab3152 // to be in an element with the class name 'monaco-colors' based on:
b69ab3153 // https://github.com/microsoft/vscode/blob/f78d84606cd16d75549c82c68888de91d8bdec9f/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts#L206-L214
b69ab3154 //
b69ab3155 // However, .monaco-colors may not have been inserted yet (this could be the
b69ab3156 // case depending on where Monaco is in its own initialization process), so we
b69ab3157 // just append the <style> tag to <body> so that when .monaco-colors is
b69ab3158 // inserted to the <head> tag, our <style> tag will be guaranteed to appear
b69ab3159 // later in the DOM and therefore its styles will take precedence.
b69ab3160 document.body.appendChild(styleElementForTextMateCSS);
b69ab3161
b69ab3162 return styleElementForTextMateCSS;
b69ab3163}
b69ab3164
b69ab3165/**
b69ab3166 * Adapted from the `generateTokensCSSForColorMap()` implementation in
b69ab3167 * `monaco-editor/esm/vs/editor/common/modes/supports/tokenization.js`.
b69ab3168 * Note that the original takes an Array<Color>, but while the Color class has
b69ab3169 * all sorts of fancy methods like `getRelativeLuminance()` and `blend()`, the
b69ab3170 * only thing this function needs is its `toString()` method, which formats it
b69ab3171 * as a CSS hex value, which is what `registry.getColorMap()` returned in the
b69ab3172 * first place!
b69ab3173 */
b69ab3174function generateTokensCSSForColorMap(cssColors: readonly string[]): string {
b69ab3175 const rules: string[] = [];
b69ab3176 for (let i = 1, len = cssColors.length; i < len; i++) {
b69ab3177 const color = cssColors[i];
b69ab3178 rules[i] = `.${CSS_CLASS_PREFIX}${i} { color: ${color}; }`;
b69ab3179 }
b69ab3180 rules.push(`.${CSS_CLASS_PREFIX}i { font-style: italic; }`);
b69ab3181 rules.push(`.${CSS_CLASS_PREFIX}b { font-weight: bold; }`);
b69ab3182 rules.push(
b69ab3183 `.${CSS_CLASS_PREFIX}u { text-decoration: underline; text-underline-position: under; }`,
b69ab3184 );
b69ab3185 rules.push(`.${CSS_CLASS_PREFIX}s { text-decoration: line-through; }`);
b69ab3186 rules.push(
b69ab3187 `.${CSS_CLASS_PREFIX}s.${CSS_CLASS_PREFIX}u { text-decoration: underline line-through; text-underline-position: under; }`,
b69ab3188 );
b69ab3189 return rules.join('\n');
b69ab3190}