From f4456dc5eae23c16336b65c16374f429395a3cfd Mon Sep 17 00:00:00 2001 From: Michael Moore Date: Tue, 12 Nov 2024 17:57:21 -0600 Subject: [PATCH] feat: add explicitTrigger option for transformerColorizedBrackets --- docs/packages/colorized-brackets.md | 32 +++++++++-- packages/colorized-brackets/src/index.ts | 15 +++++ packages/colorized-brackets/src/types.ts | 33 ++++++----- .../test/explicit-trigger.test.ts | 57 +++++++++++++++++++ 4 files changed, 117 insertions(+), 20 deletions(-) create mode 100644 packages/colorized-brackets/test/explicit-trigger.test.ts diff --git a/docs/packages/colorized-brackets.md b/docs/packages/colorized-brackets.md index b3037b80..fca353c2 100644 --- a/docs/packages/colorized-brackets.md +++ b/docs/packages/colorized-brackets.md @@ -18,7 +18,7 @@ npm i -D @shikijs/colorized-brackets Add to your Shiki transformers: -```ts +```ts colorize-brackets import { transformerColorizedBrackets } from '@shikijs/colorized-brackets' import { codeToHtml } from 'shiki' @@ -33,7 +33,7 @@ const html = await codeToHtml('let values: number[] = [];', { Brackets are automatically colored according to your Shiki theme (or themes if using [dual themes](https://shiki.style/guide/dual-themes)), with support for all of Shiki's built-in themes. However, you can customize colors if you've added custom themes to Shiki, or if you want to override the colors of a built-in theme: -```ts +```ts colorize-brackets const html = await codeToHtml('let values: number[] = [];', { lang: 'ts', theme: myCustomTheme, @@ -53,7 +53,7 @@ If no bracket colors are found for a theme, it falls back to the default `dark-p You can customize the bracket pairs: -```ts +```ts colorize-brackets const transformer = transformerColorizedBrackets({ bracketPairs: [{ opener: '{', closer: '}' }], }) @@ -63,7 +63,7 @@ The above would only colorize `{}` curly brackets. The default config colorizes For advanced usage, you can specify which TextMate scopes a bracket pair is allowed or denied in, using `scopesAllowList` and `scopesDenyList`. For example, the default config for `<>` angle brackets is: -```ts +```ts colorize-brackets const bracketPair = { opener: '<', closer: '>', @@ -78,8 +78,30 @@ const bracketPair = { All settings can be overridden for specific languages using the `langs` option: -```ts +```ts colorize-brackets const transformer = transformerColorizedBrackets({ langs: { ts: myCustomTypescriptConfig }, }) ``` + +### Explicit Trigger + +If you do not want colorized brackets for all code blocks, you can enable the `explicitTrigger` option: + +```ts colorize-brackets +const transformer = transformerColorizedBrackets({ + explicitTrigger: true, +}) +``` + +Then, only code blocks with the `colorize-brackets` [meta string](/guide/transformers#meta) will have bracket colorizing enabled. + +````md +```ts +// no bracket colorizing +``` + +```ts colorize-brackets +// brackets will be colorized +``` +```` diff --git a/packages/colorized-brackets/src/index.ts b/packages/colorized-brackets/src/index.ts index 70cab850..467c7300 100644 --- a/packages/colorized-brackets/src/index.ts +++ b/packages/colorized-brackets/src/index.ts @@ -57,15 +57,25 @@ export function transformerColorizedBrackets( liquid: { bracketPairs: jinjaLikeBracketPairs }, ...options.langs, }, + explicitTrigger: options.explicitTrigger ?? false, } + const transformer: ShikiTransformer = { name: 'colorizedBrackets', preprocess(code, options) { + if (!isEnabled(config, this.options.meta?.__raw)) { + return + } + // includeExplanation is a valid option for codeToTokens // but is missing from the type definition here (options as CodeToTokensOptions).includeExplanation ||= 'scopeName' }, tokens: function transformTokens(tokens) { + if (!isEnabled(config, this.options.meta?.__raw)) { + return + } + const lang = this.options.lang for (let lineIndex = 0; lineIndex < tokens.length; lineIndex++) { @@ -81,3 +91,8 @@ export function transformerColorizedBrackets( } return transformer } + +const EXPLICIT_TRIGGER_REGEX = /(^|\s)colorize-brackets($|\s)/ +function isEnabled(config: TransformerColorizedBracketsOptions, meta: string | undefined): boolean { + return !config.explicitTrigger || meta?.match(EXPLICIT_TRIGGER_REGEX) != null +} diff --git a/packages/colorized-brackets/src/types.ts b/packages/colorized-brackets/src/types.ts index 46fae593..1406459a 100644 --- a/packages/colorized-brackets/src/types.ts +++ b/packages/colorized-brackets/src/types.ts @@ -4,31 +4,34 @@ * @property themes - a record of theme names to bracket CSS colors; the final color is the unexpected bracket color * @property bracketPairs - bracket pair definitions * @property langs - language-specific configs that are merged with the base config + * @property explicitTrigger - if true, the transformer only runs for code blocks with the `colorize-brackets` meta string */ - export interface TransformerColorizedBracketsOptions { themes: Record bracketPairs: BracketPair[] langs: Record -}/** - * Language-specific config - * - * @property themes - language-specific theme customizations; if not defined, it uses the theme customizations from the base config - * @property bracketPairs - language-specific bracket pairs; if not defined, it uses the bracket from the base config - */ + explicitTrigger?: boolean +} +/** + * Language-specific config + * + * @property themes - language-specific theme customizations; if not defined, it uses the theme customizations from the base config + * @property bracketPairs - language-specific bracket pairs; if not defined, it uses the bracket from the base config + */ export interface ColorizedBracketsLangConfig { themes?: Record bracketPairs?: BracketPair[] -}/** - * Defines opening and closing brackets, and allowed Textmate scopes - * - * @property opener - the string that opens a bracket pair; multi-character strings are not yet supported - * @property closer - the string that closes a bracket pair; multi-character strings are not yet supported - * @property scopesAllowList - if defined, brackets will only be colored if at least 1 of their scopes matches a scope from this list - * @property scopesDenyList - if defined, brackets will not be colored if any of their scopes match a scope from this list - */ +} +/** + * Defines opening and closing brackets, and allowed Textmate scopes + * + * @property opener - the string that opens a bracket pair; multi-character strings are not yet supported + * @property closer - the string that closes a bracket pair; multi-character strings are not yet supported + * @property scopesAllowList - if defined, brackets will only be colored if at least 1 of their scopes matches a scope from this list + * @property scopesDenyList - if defined, brackets will not be colored if any of their scopes match a scope from this list + */ export interface BracketPair { opener: string closer: string diff --git a/packages/colorized-brackets/test/explicit-trigger.test.ts b/packages/colorized-brackets/test/explicit-trigger.test.ts new file mode 100644 index 00000000..1df9f69c --- /dev/null +++ b/packages/colorized-brackets/test/explicit-trigger.test.ts @@ -0,0 +1,57 @@ +import { createHighlighter } from 'shiki' +import { describe, expect, it } from 'vitest' +import { transformerColorizedBrackets } from '../src' + +describe('explicitTrigger', async () => { + const lang = 'ts' + const theme = 'dark-plus' + const highlighter = await createHighlighter({ + langs: [lang], + themes: [theme], + }) + + const validMetaStrings = [ + ['colorize-brackets'], + ['foo colorize-brackets'], + ['foo colorize-brackets bar'], + ['colorize-brackets bar'], + ] + it.each(validMetaStrings)('should colorize brackets for meta string "%s"', (meta) => { + const code = 'let values: number[] = [1, 2, 3];' + expect( + highlighter.codeToHtml(code, { + lang, + theme, + transformers: [ + transformerColorizedBrackets({ + themes: { 'dark-plus': ['Y', 'P', 'B', 'R'] }, + explicitTrigger: true, + }), + ], + meta: { __raw: meta }, + }), + ).toContain('[]') + }) + + const invalidMetaStrings = [ + [''], + ['colorize-brackets-no-word-break'], + ['no-word-break-colorize-brackets'], + ] + it.each(invalidMetaStrings)('should not colorize brackets for meta string "%s"', (meta) => { + const code = 'let values: number[] = [1, 2, 3];' + expect( + highlighter.codeToHtml(code, { + lang, + theme, + transformers: [ + transformerColorizedBrackets({ + themes: { 'dark-plus': ['Y', 'P', 'B', 'R'] }, + explicitTrigger: true, + }), + ], + meta: { __raw: meta }, + }), + ).not.toContain('[]') + }) +})