/
codePlugin.ts
147 lines (126 loc) · 4.09 KB
/
codePlugin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import type { PluginWithOptions } from 'markdown-it'
import {
isHighlightLine,
resolveHighlightLines,
} from './resolveHighlightLines.js'
import { resolveLanguage } from './resolveLanguage.js'
import { resolveLineNumbers } from './resolveLineNumbers.js'
import { resolveVPre } from './resolveVPre.js'
export interface CodePluginOptions {
/**
* Enable highlight lines or not
*/
highlightLines?: boolean
/**
* Enable line numbers or not
*
* - A `boolean` value is to enable line numbers or not.
* - A `number` value is the minimum number of lines to enable line numbers
*/
lineNumbers?: boolean | number
/**
* Wrap the `<pre>` tag with an extra `<div>` or not. Do not disable it unless you
* understand what's it for
*
* - Required for `highlightLines`
* - Required for `lineNumbers`
* - Required for language display of default theme
*/
preWrapper?: boolean
/**
* Add `v-pre` directive or not
*/
vPre?: {
/**
* Add `v-pre` directive to `<pre>` tag of code block or not
*/
block?: boolean
/**
* Add `v-pre` directive to `<code>` tag of inline code or not
*/
inline?: boolean
}
}
/**
* Code plugin
*/
export const codePlugin: PluginWithOptions<CodePluginOptions> = (
md,
{
highlightLines = true,
lineNumbers = true,
preWrapper = true,
vPre: { block: vPreBlock = true, inline: vPreInline = true } = {},
}: CodePluginOptions = {}
): void => {
// override default fence renderer
md.renderer.rules.fence = (tokens, idx, options, env, slf) => {
const token = tokens[idx]
// get token info
const info = token.info ? md.utils.unescapeAll(token.info).trim() : ''
// resolve language from token info
const language = resolveLanguage(info)
const languageClass = `${options.langPrefix}${language.name}`
// try to get highlighted code
const code =
options.highlight?.(token.content, language.name, '') ||
md.utils.escapeHtml(token.content)
token.attrJoin('class', languageClass)
// wrap highlighted code with `<pre>` and `<code>`
let result = code.startsWith('<pre')
? code
: `<pre${slf.renderAttrs(token)}><code>${code}</code></pre>`
// resolve v-pre mark from token info
const useVPre = resolveVPre(info) ?? vPreBlock
if (useVPre) {
result = `<pre v-pre${result.slice('<pre'.length)}`
}
// if `preWrapper` is disabled, return directly
if (!preWrapper) {
return result
}
// code fences always have an ending `\n`, so we should trim the last line
const lines = code.split('\n').slice(0, -1)
// resolve highlight line ranges from token info
const highlightLinesRanges = highlightLines
? resolveHighlightLines(info)
: null
// generate highlight lines
if (highlightLinesRanges) {
const highlightLinesCode = lines
.map((_, index) => {
if (isHighlightLine(index + 1, highlightLinesRanges)) {
return '<div class="highlight-line"> </div>'
}
return '<br>'
})
.join('')
result = `${result}<div class="highlight-lines">${highlightLinesCode}</div>`
}
// resolve line-numbers mark from token info
const useLineNumbers =
resolveLineNumbers(info) ??
(typeof lineNumbers === 'number'
? lines.length >= lineNumbers
: lineNumbers)
// generate line numbers
if (useLineNumbers) {
// generate line numbers code
const lineNumbersCode = lines
.map(() => `<div class="line-number"></div>`)
.join('')
result = `${result}<div class="line-numbers" aria-hidden="true">${lineNumbersCode}</div>`
}
result = `<div class="${languageClass}${
useLineNumbers ? ' line-numbers-mode' : ''
}" data-ext="${language.ext}">${result}</div>`
return result
}
if (vPreInline) {
const rawInlineCodeRule = md.renderer.rules.code_inline!
md.renderer.rules.code_inline = (tokens, idx, options, env, slf) => {
const result = rawInlineCodeRule(tokens, idx, options, env, slf)
return `<code v-pre${result.slice('<code'.length)}`
}
}
}