Skip to content

Commit 1ecfa21

Browse files
committed
feat(twoslash/rich): support processHoverInfo and processHoverDocs hook
1 parent 2661779 commit 1ecfa21

File tree

7 files changed

+78
-56
lines changed

7 files changed

+78
-56
lines changed

docs/.vitepress/config.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { DefaultTheme } from 'vitepress'
22
import { defineConfig } from 'vitepress'
33
import { bundledThemes } from 'shikiji'
4-
import { defaultInfoProcessor, transformerTwoslash } from 'vitepress-plugin-twoslash'
4+
import { defaultHoverInfoProcessor, transformerTwoslash } from 'vitepress-plugin-twoslash'
55
import { version } from '../../package.json'
66
import vite from './vite.config'
77

@@ -56,7 +56,7 @@ export default defineConfig({
5656
codeTransformers: [
5757
transformerTwoslash({
5858
processHoverInfo(info) {
59-
return defaultInfoProcessor(info)
59+
return defaultHoverInfoProcessor(info)
6060
// Remove shikiji_core namespace
6161
.replace(/\bshikiji_core\./g, '')
6262
// Remove member access

packages/shikiji-twoslash/src/renderer-rich.ts

+61-14
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,16 @@ export interface RendererRichOptions {
3333
/**
3434
* Custom formatter for the type info text.
3535
* Note that it might not be valid TypeScript syntax.
36+
*
37+
* @default defaultHoverInfoProcessor
3638
*/
37-
formatInfo?(info: string): string
39+
processHoverInfo?: (info: string) => string
40+
/**
41+
* Custom formatter for the docs text (can be markdown).
42+
*
43+
* @default undefined
44+
*/
45+
processHoverDocs?: (docs: string) => string
3846

3947
/**
4048
* Classes added to injected elements
@@ -46,6 +54,11 @@ export interface RendererRichOptions {
4654
* @default the language of the code block
4755
*/
4856
lang?: string
57+
58+
/**
59+
* @deprecated Use `processHoverInfo` instead.
60+
*/
61+
formatInfo?(info: string): string
4962
}
5063

5164
/**
@@ -56,7 +69,9 @@ export function rendererRich(options: RendererRichOptions = {}): TwoSlashRendere
5669
const {
5770
completionIcons = defaultCompletionIcons,
5871
customTagIcons = defaultCustomTagIcons,
59-
formatInfo = info => info,
72+
formatInfo,
73+
processHoverInfo = formatInfo || defaultHoverInfoProcessor,
74+
processHoverDocs = docs => docs,
6075
classExtra = '',
6176
jsdoc = true,
6277
} = options
@@ -69,25 +84,32 @@ export function rendererRich(options: RendererRichOptions = {}): TwoSlashRendere
6984
if (!info.text)
7085
return []
7186

72-
const themedContent = ((codeToHast(formatInfo(info.text), {
87+
const text = processHoverInfo(info.text) ?? info.text
88+
if (!text)
89+
return []
90+
91+
const themedContent = ((codeToHast(text, {
7392
...shikijiOptions,
7493
lang: options.lang || shikijiOptions.lang,
7594
transformers: [],
7695
transforms: undefined,
7796
}).children[0] as Element).children[0] as Element).children
7897

7998
if (jsdoc && info.docs) {
80-
themedContent.push({
81-
type: 'element',
82-
tagName: 'div',
83-
properties: { class: 'twoslash-popup-jsdoc' },
84-
children: [
85-
{
86-
type: 'text',
87-
value: info.docs,
88-
},
89-
],
90-
})
99+
const docs = processHoverDocs(info.docs) ?? info.docs
100+
if (docs) {
101+
themedContent.push({
102+
type: 'element',
103+
tagName: 'div',
104+
properties: { class: 'twoslash-popup-jsdoc' },
105+
children: [
106+
{
107+
type: 'text',
108+
value: docs,
109+
},
110+
],
111+
})
112+
}
91113
}
92114

93115
return themedContent
@@ -299,3 +321,28 @@ export function rendererRich(options: RendererRichOptions = {}): TwoSlashRendere
299321
},
300322
}
301323
}
324+
325+
const regexType = /^[A-Z][a-zA-Z0-9_]*(\<[^\>]*\>)?:/
326+
const regexFunction = /^[a-zA-Z0-9_]*\(/
327+
328+
/**
329+
* The default hover info processor, which will do some basic cleanup
330+
*/
331+
export function defaultHoverInfoProcessor(type: string) {
332+
let content = type
333+
// remove leading `(property)` or `(method)` on each line
334+
.replace(/^\(([\w-]+?)\)\s+/mg, '')
335+
// remove import statement
336+
.replace(/\nimport .*$/, '')
337+
// remove interface or namespace lines with only the name
338+
.replace(/^(interface|namespace) \w+$/mg, '')
339+
.trim()
340+
341+
// Add `type` or `function` keyword if needed
342+
if (content.match(regexType))
343+
content = `type ${content}`
344+
else if (content.match(regexFunction))
345+
content = `function ${content}`
346+
347+
return content
348+
}

packages/shikiji-twoslash/test/out/rich/custom-tags.html

+2-4
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,10 @@
2424
background-color: var(--shiki-light-bg, inherit);
2525
}
2626
</style>
27-
<pre class="shiki shiki-themes min-light min-dark twoslash lsp" style="--shiki-light:#24292eff;--shiki-dark:#b392f0;--shiki-light-bg:#ffffff;--shiki-dark-bg:#1f1f1f" tabindex="0"><code><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">import</span><span> </span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"><span class="twoslash-hover"><span class="twoslash-popup-info"><span class="line"><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">(alias)</span><span> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">function</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHighlighterCore</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">(options</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">HighlighterCoreOptions</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">)</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Promise</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">HighlighterCore</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">></span></span>
28-
<span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">import</span><span> </span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">getHighlighterCore</span></span><div class="twoslash-popup-jsdoc">Create a Shikiji core highlighter instance, with no languages or themes bundled.
27+
<pre class="shiki shiki-themes min-light min-dark twoslash lsp" style="--shiki-light:#24292eff;--shiki-dark:#b392f0;--shiki-light-bg:#ffffff;--shiki-dark-bg:#1f1f1f" tabindex="0"><code><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">import</span><span> </span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"><span class="twoslash-hover"><span class="twoslash-popup-info"><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">function</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHighlighterCore</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">(options</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">HighlighterCoreOptions</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">)</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Promise</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">HighlighterCore</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">></span></span><div class="twoslash-popup-jsdoc">Create a Shikiji core highlighter instance, with no languages or themes bundled.
2928
Wasm and each language and theme must be loaded manually.</div></span>{ getHighlighterCore }</span></span><span> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">from</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#22863A;--shiki-dark:#FFAB70">'shikiji/core'</span></span>
3029
<span class="line"></span>
31-
<span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF"><span class="twoslash-hover"><span class="twoslash-popup-info"><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF">shiki</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">HighlighterCore</span></span></span>shiki</span></span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">await</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"><span class="twoslash-hover"><span class="twoslash-popup-info"><span class="line"><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">(alias)</span><span> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHighlighterCore</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">(options</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">?:</span><span> </span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">HighlighterCoreOptions</span><span> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">|</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF">undefined</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">):</span><span> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF">Promise</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">HighlighterCore</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">></span></span>
32-
<span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">import</span><span> </span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">getHighlighterCore</span></span><div class="twoslash-popup-jsdoc">Create a Shikiji core highlighter instance, with no languages or themes bundled.
30+
<span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF"><span class="twoslash-hover"><span class="twoslash-popup-info"><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF">shiki</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">HighlighterCore</span></span></span>shiki</span></span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">await</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"><span class="twoslash-hover"><span class="twoslash-popup-info"><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">function</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHighlighterCore</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">(options</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">HighlighterCoreOptions</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">|</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF">undefined</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">)</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Promise</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">HighlighterCore</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">></span></span><div class="twoslash-popup-jsdoc">Create a Shikiji core highlighter instance, with no languages or themes bundled.
3331
Wasm and each language and theme must be loaded manually.</div></span>getHighlighterCore</span></span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0">({})</span></span>
3432
<span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF"><span class="twoslash-hover"><span class="twoslash-popup-info"><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF">a</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#F8F8F8">1</span></span></span>a</span></span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#F8F8F8">1</span></span><div class="twoslash-tag-line twoslash-tag-log-line"><span class="twoslash-tag-icon tag-log-icon"><svg viewBox="0 0 32 32"><path fill="currentColor" d="M17 22v-8h-4v2h2v6h-3v2h8v-2zM16 8a1.5 1.5 0 1 0 1.5 1.5A1.5 1.5 0 0 0 16 8"></path><path fill="currentColor" d="M26 28H6a2.002 2.002 0 0 1-2-2V6a2.002 2.002 0 0 1 2-2h20a2.002 2.002 0 0 1 2 2v20a2.002 2.002 0 0 1-2 2M6 6v20h20V6Z"></path></svg></span>Custom log message</div><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF"><span class="twoslash-hover"><span class="twoslash-popup-info"><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF">b</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#F8F8F8">1</span></span></span>b</span></span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#F8F8F8">1</span></span><div class="twoslash-tag-line twoslash-tag-error-line"><span class="twoslash-tag-icon tag-error-icon"><svg viewBox="0 0 32 32"><path fill="currentColor" d="M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2m0 26a12 12 0 1 1 12-12a12 12 0 0 1-12 12"></path><path fill="currentColor" d="M15 8h2v11h-2zm1 14a1.5 1.5 0 1 0 1.5 1.5A1.5 1.5 0 0 0 16 22"></path></svg></span>Custom error message</div><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF"><span class="twoslash-hover"><span class="twoslash-popup-info"><span class="line"><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#79B8FF">c</span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#F8F8F8">1</span></span></span>c</span></span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#D32F2F;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292EFF;--shiki-dark:#B392F0"> </span><span style="--shiki-light:#1976D2;--shiki-dark:#F8F8F8">1</span></span><div class="twoslash-tag-line twoslash-tag-warn-line"><span class="twoslash-tag-icon tag-warn-icon"><svg viewBox="0 0 32 32"><path fill="currentColor" d="M16 23a1.5 1.5 0 1 0 1.5 1.5A1.5 1.5 0 0 0 16 23m-1-11h2v9h-2z"></path><path fill="currentColor" d="M29 30H3a1 1 0 0 1-.887-1.461l13-25a1 1 0 0 1 1.774 0l13 25A1 1 0 0 1 29 30M4.65 28h22.7l.001-.003L16.002 6.17h-.004L4.648 27.997Z"></path></svg></span>Custom warning message</div><div class="twoslash-tag-line twoslash-tag-annotate-line"><span class="twoslash-tag-icon tag-annotate-icon"><svg viewBox="0 0 32 32"><path fill="currentColor" d="M11 24h10v2H11zm2 4h6v2h-6zm3-26A10 10 0 0 0 6 12a9.19 9.19 0 0 0 3.46 7.62c1 .93 1.54 1.46 1.54 2.38h2c0-1.84-1.11-2.87-2.19-3.86A7.2 7.2 0 0 1 8 12a8 8 0 0 1 16 0a7.2 7.2 0 0 1-2.82 6.14c-1.07 1-2.18 2-2.18 3.86h2c0-.92.53-1.45 1.54-2.39A9.18 9.18 0 0 0 26 12A10 10 0 0 0 16 2"></path></svg></span>Custom annotation message</div></code></pre>
3533
<script>

0 commit comments

Comments
 (0)