Skip to content

Commit 5c8fc77

Browse files
committed
fix(twoslash): fix popover offset calculation
1 parent f6c8127 commit 5c8fc77

File tree

8 files changed

+55
-82
lines changed

8 files changed

+55
-82
lines changed

packages/shikiji-twoslash/src/index.ts

+20-11
Original file line numberDiff line numberDiff line change
@@ -91,39 +91,48 @@ export function transformerTwoSlash(options: TransformerTwoSlashOptions = {}): S
9191
throw new Error(`[shikiji-twoslash] Cannot find token at L${line}:${character}`)
9292
}
9393

94+
const skipTokens = new Set<Element | Text>()
95+
9496
for (const error of twoslash.errors) {
9597
if (error.line == null || error.character == null)
9698
return
9799
const token = locateTextToken(error.line, error.character)
98100
if (!token)
99101
continue
100102

103+
skipTokens.add(token)
104+
101105
const clone = { ...token }
102106
Object.assign(token, renderer.nodeError.call(this, error, clone))
103107

104108
insertAfterLine(error.line, renderer.lineError.call(this, error))
105109
}
106110

111+
for (const query of twoslash.queries) {
112+
if (query.kind === 'completions') {
113+
insertAfterLine(query.line, renderer.lineCompletions.call(this, query))
114+
}
115+
else if (query.kind === 'query') {
116+
const token = locateTextToken(query.line - 1, query.offset)
117+
if (token)
118+
skipTokens.add(token)
119+
insertAfterLine(query.line, renderer.lineQuery.call(this, query, token))
120+
}
121+
}
122+
107123
for (const info of twoslash.staticQuickInfos) {
108124
const token = locateTextToken(info.line, info.character)
109125
if (!token || token.type !== 'text')
110126
continue
111127

128+
// If it's already rendered as popup or error, skip it
129+
if (skipTokens.has(token))
130+
continue
131+
112132
const clone = { ...token }
113133
Object.assign(token, renderer.nodeStaticInfo.call(this, info, clone))
114134
}
115135

116-
for (const query of twoslash.queries) {
117-
insertAfterLine(
118-
query.line,
119-
query.kind === 'completions'
120-
? renderer.lineCompletions.call(this, query)
121-
: query.kind === 'query'
122-
? renderer.lineQuery.call(this, query, locateTextToken(query.line - 1, query.offset))
123-
: [],
124-
)
125-
}
126-
127136
for (const tag of twoslash.tags)
128137
insertAfterLine(tag.line, renderer.lineCustomTag.call(this, tag))
129138
},

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ export const rendererClassic: TwoSlashRenderers = {
133133

134134
lineQuery(query, targetNode) {
135135
const targetText = targetNode?.type === 'text' ? targetNode.value : ''
136-
const offset = Math.max(0, (query.offset || 0) - Math.round(targetText.length / 2) - 1)
136+
const offset = Math.max(0, (query.offset || 0) + Math.floor(targetText.length / 2) - 1)
137+
137138
return [
138139
{
139140
type: 'element',

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

+17-16
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@ import type { TwoSlashRenderers } from './types'
77
*/
88
export const rendererRich: TwoSlashRenderers = {
99
nodeStaticInfo(info, node) {
10-
let themed: ElementContent
10+
let themedContent: ElementContent[]
1111

1212
try {
13-
themed = (this.codeToHast(info.text, {
13+
themedContent = ((this.codeToHast(info.text, {
1414
...this.options,
1515
transformers: [],
1616
transforms: undefined,
17-
}).children[0] as Element).children[0]
17+
}).children[0] as Element).children[0] as Element).children
1818
}
1919
catch (e) {
20-
themed = {
20+
themedContent = [{
2121
type: 'text',
2222
value: info.text,
23-
}
23+
}]
2424
}
2525

2626
return {
@@ -37,9 +37,7 @@ export const rendererRich: TwoSlashRenderers = {
3737
properties: {
3838
class: 'twoslash-hover-info',
3939
},
40-
children: [
41-
themed,
42-
],
40+
children: themedContent,
4341
},
4442
],
4543
}
@@ -131,23 +129,26 @@ export const rendererRich: TwoSlashRenderers = {
131129
},
132130

133131
lineQuery(query, targetNode) {
132+
if (!query.text)
133+
return []
134+
134135
const targetText = targetNode?.type === 'text' ? targetNode.value : ''
135-
const offset = Math.max(0, (query.offset || 0) - Math.round(targetText.length / 2) - 1)
136+
const offset = Math.max(0, (query.offset || 0) + Math.floor(targetText.length / 2) - 1)
136137

137-
let themed: ElementContent
138+
let themedContent: ElementContent[]
138139

139140
try {
140-
themed = (this.codeToHast(query.text || '', {
141+
themedContent = ((this.codeToHast(query.text, {
141142
...this.options,
142143
transformers: [],
143144
transforms: undefined,
144-
}).children[0] as Element).children[0]
145+
}).children[0] as Element).children[0] as Element).children
145146
}
146147
catch (e) {
147-
themed = {
148+
themedContent = [{
148149
type: 'text',
149-
value: query.text || '',
150-
}
150+
value: query.text,
151+
}]
151152
}
152153

153154
return [
@@ -168,7 +169,7 @@ export const rendererRich: TwoSlashRenderers = {
168169
properties: { class: 'twoslash-popover-arrow' },
169170
children: [],
170171
},
171-
themed,
172+
...themedContent,
172173
],
173174
},
174175
],

packages/shikiji-twoslash/style-rich.css

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* ===== Basic ===== */
22
.twoslash {
3-
--twoslash-border-color: #8885;
4-
--twoslash-underline-color: #888;
3+
--twoslash-border-color: #8888;
4+
--twoslash-underline-color: currentColor;
55
--twoslash-popup-bg: #f8f8f8;
66
--twoslash-popup-shadow: rgba(0, 0, 0, 0.08) 0px 1px 4px;
77
--twoslash-matched-color: #2e8f82;
@@ -20,7 +20,7 @@
2020

2121
/* ===== Hover Info ===== */
2222
.twoslash:hover .twoslash-hover {
23-
border-color: var(--twoslash-border-color);
23+
border-color: var(--twoslash-underline-color);
2424
}
2525

2626
.twoslash .twoslash-hover {
@@ -76,7 +76,7 @@
7676
border: 1px solid var(--twoslash-border-color);
7777
border-radius: 4px;
7878
padding: 6px 8px;
79-
margin: 0 -0.5em;
79+
margin: 0 -20px;
8080
z-index: 9;
8181
user-select: none;
8282
box-shadow: var(--twoslash-popup-shadow);

packages/shikiji-twoslash/test/out/classic/cuts_out_unnecessary_code.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
<span class="line"><span style="color:#DBD7CAEE"> </span><span style="color:#4D9375">throw</span><span style="color:#DBD7CAEE"> </span><span style="color:#C98A7D99">"</span><span style="color:#C98A7D">unimplemented</span><span style="color:#C98A7D99">"</span></span>
99
<span class="line"><span style="color:#666666">}</span></span>
1010
<span class="line"></span>
11-
<span class="line"><span style="color:#CB7676">let </span><span style="color:#BD976A"><data-lsp lsp="let a: NameLabel">a</data-lsp></span><span style="color:#CB7676"> </span><span style="color:#666666">=</span><span style="color:#CB7676"> </span><span style="color:#80A665"><data-lsp lsp="function createLabel<&#x22;typescript&#x22;>(idOrName: &#x22;typescript&#x22;): NameLabel">createLabel</data-lsp></span><span style="color:#666666">(</span><span style="color:#C98A7D99">"</span><span style="color:#C98A7D">typescript</span><span style="color:#C98A7D99">"</span><span style="color:#666666">)</span></span>
12-
<span class="line"></span><div class="meta-line"> <span class="popover"><div class="arrow"></div>let a: NameLabel</span></div><span class="line"><span style="color:#CB7676">let </span><span style="color:#BD976A"><data-lsp lsp="let b: IdLabel">b</data-lsp></span><span style="color:#CB7676"> </span><span style="color:#666666">=</span><span style="color:#CB7676"> </span><span style="color:#80A665"><data-lsp lsp="function createLabel<2.8>(idOrName: 2.8): IdLabel">createLabel</data-lsp></span><span style="color:#666666">(</span><span style="color:#4C9A91">2.8</span><span style="color:#666666">)</span></span>
13-
<span class="line"></span><div class="meta-line"> <span class="popover"><div class="arrow"></div>function createLabel&#x3C;2.8>(idOrName: 2.8): IdLabel</span></div><span class="line"><span style="color:#CB7676">let </span><span style="color:#BD976A"><data-lsp lsp="let c: IdLabel | NameLabel">c</data-lsp></span><span style="color:#CB7676"> </span><span style="color:#666666">=</span><span style="color:#CB7676"> </span><span style="color:#80A665"><data-lsp lsp="function createLabel<&#x22;hello&#x22; | 42>(idOrName: &#x22;hello&#x22; | 42): IdLabel | NameLabel">createLabel</data-lsp></span><span style="color:#666666">(</span><span style="color:#BD976A"><data-lsp lsp="var Math: Math">Math</data-lsp></span><span style="color:#666666">.</span><span style="color:#80A665"><data-lsp lsp="(method) Math.random(): number">random</data-lsp></span><span style="color:#666666">()</span><span style="color:#CB7676"> ? </span><span style="color:#C98A7D99">"</span><span style="color:#C98A7D">hello</span><span style="color:#C98A7D99">"</span><span style="color:#CB7676"> : </span><span style="color:#4C9A91">42</span><span style="color:#666666">)</span></span><div class="meta-line"> <span class="inline-completions"><ul class="dropdown"><li><span><span class="result-found">r</span>andom</span></li><li><span><span class="result-found">r</span>ound</span></li></ul></span></div></code></pre>
11+
<span class="line"><span style="color:#CB7676">let </span><span style="color:#BD976A">a</span><span style="color:#CB7676"> </span><span style="color:#666666">=</span><span style="color:#CB7676"> </span><span style="color:#80A665"><data-lsp lsp="function createLabel<&#x22;typescript&#x22;>(idOrName: &#x22;typescript&#x22;): NameLabel">createLabel</data-lsp></span><span style="color:#666666">(</span><span style="color:#C98A7D99">"</span><span style="color:#C98A7D">typescript</span><span style="color:#C98A7D99">"</span><span style="color:#666666">)</span></span>
12+
<span class="line"></span><div class="meta-line"> <span class="popover"><div class="arrow"></div>let a: NameLabel</span></div><span class="line"><span style="color:#CB7676">let </span><span style="color:#BD976A"><data-lsp lsp="let b: IdLabel">b</data-lsp></span><span style="color:#CB7676"> </span><span style="color:#666666">=</span><span style="color:#CB7676"> </span><span style="color:#80A665">createLabel</span><span style="color:#666666">(</span><span style="color:#4C9A91">2.8</span><span style="color:#666666">)</span></span>
13+
<span class="line"></span><div class="meta-line"> <span class="popover"><div class="arrow"></div>function createLabel&#x3C;2.8>(idOrName: 2.8): IdLabel</span></div><span class="line"><span style="color:#CB7676">let </span><span style="color:#BD976A"><data-lsp lsp="let c: IdLabel | NameLabel">c</data-lsp></span><span style="color:#CB7676"> </span><span style="color:#666666">=</span><span style="color:#CB7676"> </span><span style="color:#80A665"><data-lsp lsp="function createLabel<&#x22;hello&#x22; | 42>(idOrName: &#x22;hello&#x22; | 42): IdLabel | NameLabel">createLabel</data-lsp></span><span style="color:#666666">(</span><span style="color:#BD976A"><data-lsp lsp="var Math: Math">Math</data-lsp></span><span style="color:#666666">.</span><span style="color:#80A665"><data-lsp lsp="(method) Math.random(): number">random</data-lsp></span><span style="color:#666666">()</span><span style="color:#CB7676"> ? </span><span style="color:#C98A7D99">"</span><span style="color:#C98A7D">hello</span><span style="color:#C98A7D99">"</span><span style="color:#CB7676"> : </span><span style="color:#4C9A91">42</span><span style="color:#666666">)</span></span><div class="meta-line"> <span class="inline-completions"><ul class="dropdown"><li><span><span class="result-found">r</span>andom</span></li><li><span><span class="result-found">r</span>ound</span></li></ul></span></div></code></pre>

packages/shikiji-twoslash/test/out/markdown-it/works.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
</style>
1818
<h1>Hello</h1>
1919
<p>Code block with twoslash:</p>
20-
<pre class="shiki shiki-themes vitesse-light vitesse-dark twoslash lsp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212" tabindex="0"><code class="language-ts"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const </span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"><span class="twoslash-hover">a<span class="twoslash-hover-info"><code><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const </span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">a</span><span style="--shiki-light:#999999;--shiki-dark:#666666">: </span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">123</span></span></code></span></span></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> </span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">123</span></span>
21-
<span class="line"></span><div class="twoslash-meta-line twoslash-popover-line"> <span class="twoslash-popover"><div class="twoslash-popover-arrow"></div><code><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const </span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">a</span><span style="--shiki-light:#999999;--shiki-dark:#666666">: </span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">123</span></span></code></span></div></code></pre>
20+
<pre class="shiki shiki-themes vitesse-light vitesse-dark twoslash lsp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212" tabindex="0"><code class="language-ts"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const </span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">a</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> </span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">123</span></span>
21+
<span class="line"></span><div class="twoslash-meta-line twoslash-popover-line"> <span class="twoslash-popover"><div class="twoslash-popover-arrow"></div><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const </span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">a</span><span style="--shiki-light:#999999;--shiki-dark:#666666">: </span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">123</span></span></span></div></code></pre>
2222
<p>Code block without twoslash:</p>
2323
<pre class="shiki shiki-themes vitesse-light vitesse-dark" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212" tabindex="0"><code class="language-ts"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const </span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">a</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 123</span></span>
2424
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// ^?</span></span>

0 commit comments

Comments
 (0)