| b69ab31 | | | 1 | /** |
| b69ab31 | | | 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| b69ab31 | | | 3 | * |
| b69ab31 | | | 4 | * This source code is licensed under the MIT license found in the |
| b69ab31 | | | 5 | * LICENSE file in the root directory of this source tree. |
| b69ab31 | | | 6 | */ |
| b69ab31 | | | 7 | |
| b69ab31 | | | 8 | /** |
| b69ab31 | | | 9 | * This is the prefix used by Monaco for the CSS classes it uses for syntax |
| b69ab31 | | | 10 | * highlighting. Each token returned by `IGrammar.tokenizeLine2()` has a |
| b69ab31 | | | 11 | * `number` associated with it. To construct the appropriate CSS class name for |
| b69ab31 | | | 12 | * color number `n`, do: `${CSS_CLASS_PREFIX}${n}`. |
| b69ab31 | | | 13 | */ |
| b69ab31 | | | 14 | export const CSS_CLASS_PREFIX = 'mtk'; |
| b69ab31 | | | 15 | |
| b69ab31 | | | 16 | /** |
| b69ab31 | | | 17 | * Updates the <style> element on the page to define the CSS rules necessary to |
| b69ab31 | | | 18 | * honor the user's selected theme. |
| b69ab31 | | | 19 | * @param colorMap as returned by `Registry.getColorMap()` where each value in |
| b69ab31 | | | 20 | * the array is a CSS hex value, such as "#AA0000". |
| b69ab31 | | | 21 | */ |
| b69ab31 | | | 22 | export function updateTextMateGrammarCSS(colorMap: string[]): void { |
| b69ab31 | | | 23 | // Note that if the Monaco editor is used on the page, then we also need to do |
| b69ab31 | | | 24 | // something like: |
| b69ab31 | | | 25 | // |
| b69ab31 | | | 26 | // const colorMap = cssColors.map(Color.Format.CSS.parseHex); |
| b69ab31 | | | 27 | // TokenizationRegistry.setColorMap(colorMap); |
| b69ab31 | | | 28 | // |
| b69ab31 | | | 29 | // though that will require loading code from the monaco-editor npm module. |
| b69ab31 | | | 30 | |
| b69ab31 | | | 31 | const css = generateTokensCSSForColorMap(colorMap); |
| b69ab31 | | | 32 | const style = getOrCreateStyleElementForColorsCSS(); |
| b69ab31 | | | 33 | style.innerHTML = css; |
| b69ab31 | | | 34 | } |
| b69ab31 | | | 35 | |
| b69ab31 | | | 36 | let styleElementForTextMateCSS: HTMLStyleElement | null = null; |
| b69ab31 | | | 37 | |
| b69ab31 | | | 38 | function getOrCreateStyleElementForColorsCSS(): HTMLStyleElement { |
| b69ab31 | | | 39 | // If there is an existing <style> element, then overwrite its contents |
| b69ab31 | | | 40 | // rather than create a new one. (Yes, this means that we support only one |
| b69ab31 | | | 41 | // theme globally on the page at a time, at least for now.) |
| b69ab31 | | | 42 | if (styleElementForTextMateCSS != null) { |
| b69ab31 | | | 43 | return styleElementForTextMateCSS; |
| b69ab31 | | | 44 | } |
| b69ab31 | | | 45 | |
| b69ab31 | | | 46 | // We want to ensure that our <style> element appears after Monaco's so that |
| b69ab31 | | | 47 | // we can override some styles it inserted for the default theme. |
| b69ab31 | | | 48 | styleElementForTextMateCSS = document.createElement('style'); |
| b69ab31 | | | 49 | |
| b69ab31 | | | 50 | // If an instance of the Monaco editor is being used on the page, then it will |
| b69ab31 | | | 51 | // have injected a stylesheet that we need to override. We expect these styles |
| b69ab31 | | | 52 | // to be in an element with the class name 'monaco-colors' based on: |
| b69ab31 | | | 53 | // https://github.com/microsoft/vscode/blob/f78d84606cd16d75549c82c68888de91d8bdec9f/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts#L206-L214 |
| b69ab31 | | | 54 | // |
| b69ab31 | | | 55 | // However, .monaco-colors may not have been inserted yet (this could be the |
| b69ab31 | | | 56 | // case depending on where Monaco is in its own initialization process), so we |
| b69ab31 | | | 57 | // just append the <style> tag to <body> so that when .monaco-colors is |
| b69ab31 | | | 58 | // inserted to the <head> tag, our <style> tag will be guaranteed to appear |
| b69ab31 | | | 59 | // later in the DOM and therefore its styles will take precedence. |
| b69ab31 | | | 60 | document.body.appendChild(styleElementForTextMateCSS); |
| b69ab31 | | | 61 | |
| b69ab31 | | | 62 | return styleElementForTextMateCSS; |
| b69ab31 | | | 63 | } |
| b69ab31 | | | 64 | |
| b69ab31 | | | 65 | /** |
| b69ab31 | | | 66 | * Adapted from the `generateTokensCSSForColorMap()` implementation in |
| b69ab31 | | | 67 | * `monaco-editor/esm/vs/editor/common/modes/supports/tokenization.js`. |
| b69ab31 | | | 68 | * Note that the original takes an Array<Color>, but while the Color class has |
| b69ab31 | | | 69 | * all sorts of fancy methods like `getRelativeLuminance()` and `blend()`, the |
| b69ab31 | | | 70 | * only thing this function needs is its `toString()` method, which formats it |
| b69ab31 | | | 71 | * as a CSS hex value, which is what `registry.getColorMap()` returned in the |
| b69ab31 | | | 72 | * first place! |
| b69ab31 | | | 73 | */ |
| b69ab31 | | | 74 | function generateTokensCSSForColorMap(cssColors: readonly string[]): string { |
| b69ab31 | | | 75 | const rules: string[] = []; |
| b69ab31 | | | 76 | for (let i = 1, len = cssColors.length; i < len; i++) { |
| b69ab31 | | | 77 | const color = cssColors[i]; |
| b69ab31 | | | 78 | rules[i] = `.${CSS_CLASS_PREFIX}${i} { color: ${color}; }`; |
| b69ab31 | | | 79 | } |
| b69ab31 | | | 80 | rules.push(`.${CSS_CLASS_PREFIX}i { font-style: italic; }`); |
| b69ab31 | | | 81 | rules.push(`.${CSS_CLASS_PREFIX}b { font-weight: bold; }`); |
| b69ab31 | | | 82 | rules.push( |
| b69ab31 | | | 83 | `.${CSS_CLASS_PREFIX}u { text-decoration: underline; text-underline-position: under; }`, |
| b69ab31 | | | 84 | ); |
| b69ab31 | | | 85 | rules.push(`.${CSS_CLASS_PREFIX}s { text-decoration: line-through; }`); |
| b69ab31 | | | 86 | rules.push( |
| b69ab31 | | | 87 | `.${CSS_CLASS_PREFIX}s.${CSS_CLASS_PREFIX}u { text-decoration: underline line-through; text-underline-position: under; }`, |
| b69ab31 | | | 88 | ); |
| b69ab31 | | | 89 | return rules.join('\n'); |
| b69ab31 | | | 90 | } |