Skip to content

Commit 04ab0eb

Browse files
innocenzibrc-dd
andauthoredOct 25, 2022
feat: support focus, colored diffs, error highlights in code blocks (#1534)
Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>
1 parent a29a4a6 commit 04ab0eb

File tree

6 files changed

+269
-16
lines changed

6 files changed

+269
-16
lines changed
 

‎docs/guide/markdown.md

+129-4
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,11 @@ For more details, see [Frontmatter](./frontmatter).
7171
**Input**
7272

7373
```
74-
| Tables | Are | Cool |
75-
| ------------- |:-------------:| -----:|
74+
| Tables | Are | Cool |
75+
| ------------- | :-----------: | ----: |
7676
| col 3 is | right-aligned | $1600 |
77-
| col 2 is | centered | $12 |
78-
| zebra stripes | are neat | $1 |
77+
| col 2 is | centered | $12 |
78+
| zebra stripes | are neat | $1 |
7979
```
8080

8181
**Output**
@@ -351,6 +351,131 @@ export default { // Highlighted
351351
}
352352
```
353353

354+
Alternatively, it's possible to highlight directly in the line by using the `// [!code hl]` comment.
355+
356+
**Input**
357+
358+
````
359+
```js
360+
export default {
361+
data () {
362+
return {
363+
msg: 'Highlighted!' // [!codeㅤ hl]
364+
}
365+
}
366+
}
367+
```
368+
````
369+
370+
**Output**
371+
372+
```js
373+
export default {
374+
data () {
375+
return {
376+
msg: 'Highlighted!' // [!code hl]
377+
}
378+
}
379+
}
380+
```
381+
382+
## Focus in Code Blocks
383+
384+
Adding the `// [!code focus]` comment on a line will focus it and blur the other parts of the code.
385+
386+
Additionally, you can define a number of lines to focus using `// [!code focus:<lines>]`.
387+
388+
**Input**
389+
390+
````
391+
```js
392+
export default {
393+
data () {
394+
return {
395+
msg: 'Focused!' // [!codeㅤ focus]
396+
}
397+
}
398+
}
399+
```
400+
````
401+
402+
**Output**
403+
404+
```js
405+
export default {
406+
data () {
407+
return {
408+
msg: 'Focused!' // [!code focus]
409+
}
410+
}
411+
}
412+
```
413+
414+
## Colored diffs in Code Blocks
415+
416+
Adding the `// [!code --]` or `// [!code ++]` comments on a line will create a diff of that line, while keeping the colors of the codeblock.
417+
418+
**Input**
419+
420+
````
421+
```js
422+
export default {
423+
data () {
424+
return {
425+
msg: 'Removed' // [!codeㅤ --]
426+
msg: 'Added' // [!codeㅤ ++]
427+
}
428+
}
429+
}
430+
```
431+
````
432+
433+
**Output**
434+
435+
```js
436+
export default {
437+
data () {
438+
return {
439+
msg: 'Removed' // [!code --]
440+
msg: 'Added' // [!code ++]
441+
}
442+
}
443+
}
444+
```
445+
446+
## Errors and warnings
447+
448+
Adding the `// [!code warning]` or `// [!code error]` comments on a line will color it accordingly.
449+
450+
**Input**
451+
452+
````
453+
```js
454+
export default {
455+
data () {
456+
return {
457+
msg: 'Error', // [!codeㅤ error]
458+
msg: 'Warning' // [!codeㅤ warning]
459+
}
460+
}
461+
}
462+
```
463+
````
464+
465+
**Output**
466+
467+
```js
468+
export default {
469+
data () {
470+
return {
471+
msg: 'Error', // [!code error]
472+
msg: 'Warning' // [!code warning]
473+
}
474+
}
475+
}
476+
```
477+
478+
354479
## Line Numbers
355480

356481
You can enable line numbers for each code blocks via config:

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"@vueuse/core": "^9.3.0",
8989
"body-scroll-lock": "4.0.0-beta.0",
9090
"shiki": "^0.11.1",
91+
"shiki-processor": "^0.1.0",
9192
"vite": "^3.1.6",
9293
"vue": "^3.2.40"
9394
},

‎pnpm-lock.yaml

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/client/theme-default/styles/components/vp-doc.css

+56
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,62 @@
329329
display: inline-block;
330330
}
331331

332+
.vp-doc [class*='language-'] code .highlighted.error {
333+
background-color: var(--vp-code-line-error-color);
334+
}
335+
336+
.vp-doc [class*='language-'] code .highlighted.warning {
337+
background-color: var(--vp-code-line-warning-color);
338+
}
339+
340+
.vp-doc [class*='language-'] code .diff {
341+
transition: background-color 0.5s;
342+
margin: 0 -24px;
343+
padding: 0 24px;
344+
width: calc(100% + 2 * 24px);
345+
display: inline-block;
346+
}
347+
348+
.vp-doc [class*='language-'] code .diff::before {
349+
position: absolute;
350+
left: 1rem;
351+
}
352+
353+
.vp-doc [class*='language-'] .has-focused-lines .line:not(.has-focus) {
354+
filter: blur(0.095rem);
355+
opacity: 0.4;
356+
transition: filter 0.35s, opacity 0.35s;
357+
}
358+
359+
.vp-doc [class*='language-'] .has-focused-lines .line:not(.has-focus) {
360+
opacity: 0.7;
361+
transition: filter 0.35s, opacity 0.35s;
362+
}
363+
364+
.vp-doc [class*='language-']:hover .has-focused-lines .line:not(.has-focus) {
365+
filter: blur(0);
366+
opacity: 1;
367+
}
368+
369+
.vp-doc [class*='language-'] code .diff.remove {
370+
background-color: var(--vp-code-line-diff-remove-color);
371+
opacity: 0.7;
372+
}
373+
374+
.vp-doc [class*='language-'] code .diff.remove::before {
375+
content: '-';
376+
color: var(--vp-code-line-diff-remove-symbol-color);
377+
}
378+
379+
.vp-doc [class*='language-'] code .diff.add {
380+
background-color: var(--vp-code-line-diff-add-color);
381+
}
382+
383+
.vp-doc [class*='language-'] code .diff.add::before {
384+
content: '+';
385+
color: var(--vp-code-line-diff-add-symbol-color);
386+
}
387+
332388
.vp-doc div[class*='language-'].line-numbers-mode {
333389
padding-left: 32px;
334390
}

‎src/client/theme-default/styles/vars.css

+9
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,15 @@
209209
--vp-code-line-highlight-color: rgba(0, 0, 0, 0.5);
210210
--vp-code-line-number-color: var(--vp-c-text-dark-3);
211211

212+
--vp-code-line-diff-add-color: rgba(125, 191, 123, 0.1);
213+
--vp-code-line-diff-add-symbol-color: rgba(125, 191, 123, 0.5);
214+
215+
--vp-code-line-diff-remove-color: rgba(255, 128, 128, 0.05);
216+
--vp-code-line-diff-remove-symbol-color: rgba(255, 128, 128, 0.5);
217+
218+
--vp-code-line-error-color: var(--vp-c-red-dimm-2);
219+
--vp-code-line-warning-color: var(--vp-c-yellow-dimm-2);
220+
212221
--vp-code-copy-code-hover-bg: rgba(255, 255, 255, 0.05);
213222
--vp-code-copy-code-active-text: var(--vp-c-text-dark-2);
214223
}

‎src/node/markdown/plugins/highlight.ts

+64-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import { IThemeRegistration, getHighlighter, HtmlRendererOptions } from 'shiki'
1+
import { IThemeRegistration, HtmlRendererOptions } from 'shiki'
2+
import {
3+
createDiffProcessor,
4+
createFocusProcessor,
5+
createHighlightProcessor,
6+
createRangeProcessor,
7+
getHighlighter,
8+
Processor,
9+
addClass,
10+
defineProcessor
11+
} from 'shiki-processor'
212
import type { ThemeOptions } from '../markdown'
313

414
/**
@@ -32,38 +42,80 @@ const attrsToLines = (attrs: string): HtmlRendererOptions['lineOptions'] => {
3242
}))
3343
}
3444

45+
const errorLevelProcessor = defineProcessor({
46+
name: 'error-level',
47+
handler: createRangeProcessor({
48+
error: ['highlighted', 'error'],
49+
warning: ['highlighted', 'warning']
50+
})
51+
})
52+
3553
export async function highlight(
3654
theme: ThemeOptions = 'material-palenight'
3755
): Promise<(str: string, lang: string, attrs: string) => string> {
3856
const hasSingleTheme = typeof theme === 'string' || 'name' in theme
3957
const getThemeName = (themeValue: IThemeRegistration) =>
4058
typeof themeValue === 'string' ? themeValue : themeValue.name
4159

60+
const processors: Processor[] = [
61+
createFocusProcessor(),
62+
createHighlightProcessor({ hasHighlightClass: 'highlighted' }),
63+
createDiffProcessor(),
64+
errorLevelProcessor
65+
]
66+
4267
const highlighter = await getHighlighter({
43-
themes: hasSingleTheme ? [theme] : [theme.dark, theme.light]
68+
themes: hasSingleTheme ? [theme] : [theme.dark, theme.light],
69+
processors
4470
})
45-
const preRE = /^<pre.*?>/
71+
72+
const styleRE = /<pre .* (style=".*")><code>/
73+
const preRE = /^<pre(.*?)>/
4674
const vueRE = /-vue$/
4775

4876
return (str: string, lang: string, attrs: string) => {
4977
const vPre = vueRE.test(lang) ? '' : 'v-pre'
5078
lang = lang.replace(vueRE, '').toLowerCase()
5179

5280
const lineOptions = attrsToLines(attrs)
81+
const cleanup = (str: string) =>
82+
str
83+
.replace(preRE, (_, attributes) => `<pre ${vPre}${attributes}>`)
84+
.replace(styleRE, (_, style) => _.replace(style, ''))
5385

5486
if (hasSingleTheme) {
55-
return highlighter
56-
.codeToHtml(str, { lang, lineOptions, theme: getThemeName(theme) })
57-
.replace(preRE, `<pre ${vPre}>`)
87+
return cleanup(
88+
highlighter.codeToHtml(str, {
89+
lang,
90+
lineOptions,
91+
theme: getThemeName(theme)
92+
})
93+
)
5894
}
5995

60-
const dark = highlighter
61-
.codeToHtml(str, { lang, lineOptions, theme: getThemeName(theme.dark) })
62-
.replace(preRE, `<pre ${vPre} class="vp-code-dark">`)
96+
const dark = addClass(
97+
cleanup(
98+
highlighter.codeToHtml(str, {
99+
lang,
100+
lineOptions,
101+
theme: getThemeName(theme.dark)
102+
})
103+
),
104+
'vp-code-dark',
105+
'pre'
106+
)
63107

64-
const light = highlighter
65-
.codeToHtml(str, { lang, lineOptions, theme: getThemeName(theme.light) })
66-
.replace(preRE, `<pre ${vPre} class="vp-code-light">`)
108+
const light = addClass(
109+
cleanup(
110+
highlighter.codeToHtml(str, {
111+
lang,
112+
lineOptions,
113+
theme: getThemeName(theme.light)
114+
})
115+
),
116+
'vp-code-light',
117+
'pre'
118+
)
67119

68120
return dark + light
69121
}

0 commit comments

Comments
 (0)
Please sign in to comment.