Skip to content

Commit

Permalink
feat(twoslash): support errorRendering to render error in hover style
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Feb 4, 2024
1 parent 38a9fdc commit 28e325c
Show file tree
Hide file tree
Showing 10 changed files with 328 additions and 158 deletions.
4 changes: 2 additions & 2 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
"@iconify-json/svg-spinners": "^1.1.2",
"@shikijs/transformers": "workspace:*",
"@shikijs/twoslash": "workspace:*",
"@unocss/reset": "^0.58.4",
"@unocss/reset": "^0.58.5",
"@vueuse/core": "^10.7.2",
"floating-vue": "^5.2.2",
"pinia": "^2.1.7",
"shiki": "workspace:*",
"unocss": "^0.58.4",
"unocss": "^0.58.5",
"unplugin-vue-components": "^0.26.0",
"vitepress": "^1.0.0-rc.41",
"vue": "^3.4.15"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"mdast-util-to-hast": "^13.1.0",
"ofetch": "^1.3.3",
"pnpm": "^8.15.1",
"prettier": "^3.2.4",
"prettier": "^3.2.5",
"rimraf": "^5.0.5",
"rollup": "^4.9.6",
"rollup-plugin-copy": "^3.5.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/shiki/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
"@shikijs/core": "workspace:*"
},
"devDependencies": {
"tm-grammars": "^1.1.2",
"tm-grammars": "^1.1.3",
"tm-themes": "^1.1.1",
"vscode-oniguruma": "^1.7.0"
}
Expand Down
2 changes: 1 addition & 1 deletion packages/twoslash/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
},
"dependencies": {
"@shikijs/core": "workspace:*",
"twoslash": "^0.1.0"
"twoslash": "^0.1.1"
},
"devDependencies": {
"@iconify-json/carbon": "^1.1.28",
Expand Down
83 changes: 78 additions & 5 deletions packages/twoslash/src/renderer-rich.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Element, ElementContent, Text } from 'hast'
import type { ShikiTransformerContextCommon } from '@shikijs/core'
import type { NodeHover, NodeQuery } from 'twoslash'
import type { NodeError, NodeHover, NodeQuery } from 'twoslash'
import type { TwoslashRenderer } from './types'
import type { CompletionItem } from './icons'
import { defaultCompletionIcons, defaultCustomTagIcons } from './icons'
Expand Down Expand Up @@ -47,6 +47,16 @@ export interface RendererRichOptions {
*/
processHoverDocs?: (docs: string) => string

/**
* The way errors should be rendered.
*
* - `'line'`: Render the error line after the line of code
* - `'hover'`: Render the error in the hover popup
*
* @default 'line'
*/
errorRendering?: 'line' | 'hover'

/**
* Classes added to injected elements
*/
Expand Down Expand Up @@ -137,6 +147,16 @@ export interface RendererRichOptions {
* The token for the error informaton.
*/
errorToken?: HastExtension
/**
* The container of the error popup.
* Only used when `errorRendering` is set to `'hover'`.
*/
errorPopup?: HastExtension
/**
* Custom function to compose the error token.
* Only used when `errorRendering` is set to `'hover'`.
*/
errorCompose?: (parts: { popup: Element, token: Text | Element }) => ElementContent[]
/**
* The wrapper for the highlighted nodes.
*/
Expand Down Expand Up @@ -188,6 +208,7 @@ export function rendererRich(options: RendererRichOptions = {}): TwoslashRendere
processHoverDocs = docs => docs,
classExtra = '',
jsdoc = true,
errorRendering = 'line',
renderMarkdown = renderMarkdownPassThrough,
renderMarkdownInline = renderMarkdownPassThrough,
hast,
Expand Down Expand Up @@ -474,27 +495,66 @@ export function rendererRich(options: RendererRichOptions = {}): TwoslashRendere
)
},

nodeError(_, node) {
nodeError(error, node) {
if (errorRendering !== 'hover') {
return extend(
hast?.errorToken,
{
type: 'element',
tagName: 'span',
properties: {
class: [`twoslash-error`, getErrorLevelClass(error)].filter(Boolean).join(' '),
},
children: [node],
},
)
}

const popup = extend(
hast?.errorPopup,
{
type: 'element',
tagName: 'span',
properties: {
class: ['twoslash-popup-container', classExtra].filter(Boolean).join(' '),
},
children: [
{
type: 'element',
tagName: 'div',
properties: {
class: 'twoslash-popup-error',
},
children: renderMarkdown.call(this, error.text),
},
],
},
)

return extend(
hast?.errorToken,
{
type: 'element',
tagName: 'span',
properties: {
class: 'twoslash-error',
class: `twoslash-error twoslash-error-hover ${getErrorLevelClass(error)}`,
},
children: [node],
children: hast?.errorCompose
? hast?.errorCompose({ popup, token: node })
: [popup, node],
},
)
},

lineError(error) {
if (errorRendering !== 'line')
return []
return [
{
type: 'element',
tagName: 'div',
properties: {
class: ['twoslash-meta-line twoslash-error-line', classExtra].filter(Boolean).join(' '),
class: ['twoslash-meta-line twoslash-error-line', getErrorLevelClass(error), classExtra].filter(Boolean).join(' '),
},
children: [
{
Expand Down Expand Up @@ -576,3 +636,16 @@ export function defaultHoverInfoProcessor(type: string) {

return content
}

function getErrorLevelClass(error: NodeError) {
switch (error.level) {
case 0:
return 'twoslash-error-level-warning'
case 1:
return '' // 'twoslash-error-level-error'
case 2:
return 'twoslash-error-level-suggestion'
default:
return 'twoslash-error-level-info'
}
}
29 changes: 26 additions & 3 deletions packages/twoslash/style-rich.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
--twoslash-highlighted-bg: #c37d0d20;
--twoslash-popup-bg: #f8f8f8;
--twoslash-popup-color: inherit;
--twoslash-popup-shadow: rgba(0, 0, 0.08) 0px 1px 4px;
--twoslash-popup-shadow: rgba(0, 0, 0, 0.08) 0px 1px 4px;
--twoslash-docs-color: #888;
--twoslash-docs-font: sans-serif;
--twoslash-code-font: inherit;
Expand All @@ -16,10 +16,12 @@
--twoslash-cursor-color: #8888;
--twoslash-error-color: #d45656;
--twoslash-error-bg: #d4565620;
--twoslash-warn-color: #c37d0d;
--twoslash-warn-bg: #c37d0d20;
--twoslash-tag-color: #3772cf;
--twoslash-tag-bg: #3772cf20;
--twoslash-tag-warn-color: #c37d0d;
--twoslash-tag-warn-bg: #c37d0d20;
--twoslash-tag-warn-color: var(--twoslash-warn-color);
--twoslash-tag-warn-bg: var(--twoslash-warn-bg);
--twoslash-tag-annotate-color: #1ba673;
--twoslash-tag-annotate-bg: #1ba67320;
}
Expand Down Expand Up @@ -67,6 +69,7 @@
}

.twoslash .twoslash-hover:hover .twoslash-popup-container,
.twoslash .twoslash-error-hover:hover .twoslash-popup-container,
.twoslash .twoslash-query-presisted .twoslash-popup-container {
opacity: 1;
pointer-events: auto;
Expand All @@ -90,6 +93,7 @@
}

.twoslash .twoslash-popup-code,
.twoslash .twoslash-popup-error,
.twoslash .twoslash-popup-docs {
padding: 6px 8px !important;
}
Expand All @@ -106,6 +110,13 @@
border-top: 1px solid var(--twoslash-border-color);
}

.twoslash .twoslash-popup-error {
color: var(--twoslash-error-color);
background-color: var(--twoslash-error-bg);
font-family: var(--twoslash-docs-font);
font-size: 0.8em;
}

.twoslash .twoslash-popup-docs-tags {
display: flex;
flex-direction: column;
Expand All @@ -131,12 +142,24 @@
margin: 0.2em 0;
}

.twoslash .twoslash-error-line.twoslash-error-level-warning {
background-color: var(--twoslash-warn-bg);
border-left: 3px solid var(--twoslash-warn-color);
color: var(--twoslash-warn-color);
}

.twoslash .twoslash-error {
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23c94824'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
repeat-x bottom left;
padding-bottom: 2px;
}

.twoslash .twoslash-error.twoslash-error-level-warning {
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23c37d0d'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
repeat-x bottom left;
padding-bottom: 2px;
}

/* ===== Completeions ===== */
.twoslash .twoslash-completion-cursor {
position: relative;
Expand Down
35 changes: 35 additions & 0 deletions packages/twoslash/test/out/rich/rich-error-hover.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 31 additions & 2 deletions packages/twoslash/test/rich.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Number.parseInt(todo.title, 10);

expect(styleTag + html + colorToggle).toMatchFileSnapshot('./out/rich/rich.html')

const html2 = await codeToHtml(code, {
const htmlNoTheme = await codeToHtml(code, {
lang: 'ts',
theme: 'none',
transformers: [
Expand All @@ -87,7 +87,36 @@ Number.parseInt(todo.title, 10);
],
})

expect(styleTag + html2).toMatchFileSnapshot('./out/rich/rich-none-theme.html')
expect(styleTag + htmlNoTheme).toMatchFileSnapshot('./out/rich/rich-none-theme.html')
})

it('error rendering hover', async () => {
const code = `
// @errors: 2540
interface Todo {
title: string;
}
const todo: Readonly<Todo> = {
title: "Delete inactive users".toUpperCase(),
};
todo.title = "Hello";
`.trim()

const htmlErrorsHover = await codeToHtml(code, {
lang: 'ts',
theme: 'vitesse-light',
transformers: [
transformerTwoslash({
renderer: rendererRich({
errorRendering: 'hover',
}),
}),
],
})

expect(styleTag + htmlErrorsHover).toMatchFileSnapshot('./out/rich/rich-error-hover.html')
})

it('no-icons', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/vitepress-twoslash/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"mdast-util-gfm": "^3.0.0",
"mdast-util-to-hast": "^13.1.0",
"shiki": "workspace:*",
"twoslash-vue": "^0.1.0",
"twoslash-vue": "^0.1.1",
"vue": "^3.4.15"
}
}
Loading

0 comments on commit 28e325c

Please sign in to comment.