Skip to content

Commit

Permalink
feat: add explicitTrigger option for transformerColorizedBrackets
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelMakesGames committed Nov 12, 2024
1 parent d3cb69b commit f4456dc
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 20 deletions.
32 changes: 27 additions & 5 deletions docs/packages/colorized-brackets.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -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,
Expand All @@ -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: '}' }],
})
Expand All @@ -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: '>',
Expand All @@ -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
```
````
15 changes: 15 additions & 0 deletions packages/colorized-brackets/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand All @@ -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
}
33 changes: 18 additions & 15 deletions packages/colorized-brackets/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string[]>
bracketPairs: BracketPair[]
langs: Record<string, ColorizedBracketsLangConfig>
}/**
* 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<string, string[]>
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
Expand Down
57 changes: 57 additions & 0 deletions packages/colorized-brackets/test/explicit-trigger.test.ts
Original file line number Diff line number Diff line change
@@ -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('<span style="color:Y">[</span><span style="color:Y">]</span>')
})

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('<span style="color:Y">[</span><span style="color:Y">]</span>')
})
})

0 comments on commit f4456dc

Please sign in to comment.