-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
Copy pathsnippet.js
150 lines (125 loc) Β· 4.17 KB
/
snippet.js
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
148
149
150
const { fs } = require('@vuepress/shared-utils')
const TRANSCLUDE_WITH = 'TRANSCLUDE_WITH'
const TRANSCLUDE_LINE = 'TRANSCLUDE_LINE'
const TRANSCLUDE_TAG = 'TRANSCLUDE_TAG'
module.exports = function (md, options) {
const _root = options && options.root ? options.root : process.cwd()
const fileExists = f => {
return fs.existsSync(f)
}
const readFileSync = f => {
return fileExists(f) ? fs.readFileSync(f).toString() : `Not Found: ${f}`
}
const parseOptions = opts => {
const _t = {}
opts.trim().split(' ').forEach(pair => {
const [opt, value] = pair.split('=')
_t[opt] = value
})
return _t
}
const dataFactory = (state, pos, max) => {
const start = pos + 6
const end = state.skipSpacesBack(max, pos) - 1
const [opts, fullpathWithAtSym] = state.src.slice(start, end).trim().split('](')
const fullpath = fullpathWithAtSym.replace(/^@/, _root).trim()
const pathParts = fullpath.split('/')
const fileParts = pathParts[pathParts.length - 1].split('.')
return {
file: {
resolve: fullpath,
path: pathParts.slice(0, pathParts.length - 1).join('/'),
name: fileParts.slice(0, fileParts.length - 1).join('.'),
ext: fileParts[fileParts.length - 1]
},
options: parseOptions(opts),
content: readFileSync(fullpath),
fileExists: fileExists(fullpath)
}
}
const optionsMap = ({
options
}) => ({
hasHighlight: options.highlight || false,
hasTransclusion: options.transclude || options.transcludeWith || options.transcludeTag || false,
get transclusionType () {
if (options.transcludeWith) return TRANSCLUDE_WITH
if (options.transcludeTag) return TRANSCLUDE_TAG
if (options.transclude) return TRANSCLUDE_LINE
},
get meta () {
return this.hasHighlight ? options.highlight : ''
}
})
const contentTransclusion = ({
content,
options
}, transcludeType) => {
const lines = content.split('\n')
let _content = ''
if (transcludeType === TRANSCLUDE_LINE) {
const [tStart, tEnd] = options.transclude.replace(/[^\d|-]/g, '').split('-')
lines.forEach((line, idx) => {
const i = idx + 1
if (i >= tStart && i <= tEnd) {
_content += line + '\n'
}
})
} else if (transcludeType === TRANSCLUDE_TAG) {
const t = options.transcludeTag
const tag = new RegExp(`${t}>$|^<${t}`)
let matched = false
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
if (matched && tag.test(line)) {
_content += line + '\n'
break
} else if (matched) {
_content += line + '\n'
} else if (tag.test(line)) {
_content += line + '\n'
matched = true
}
}
} else if (transcludeType === TRANSCLUDE_WITH) {
const t = options.transcludeWith
const tag = new RegExp(t)
let matched = false
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
if (tag.test(line)) {
matched = !matched
continue
}
if (matched) {
_content += line + '\n'
}
}
}
return _content === '' ? 'No lines matched.' : _content
}
function parser (state, startLine, endLine, silent) {
const matcher = [64, 91, 99, 111, 100, 101]
const pos = state.bMarks[startLine] + state.tShift[startLine]
const max = state.eMarks[startLine]
if (state.sCount[startLine] - state.blkIndent >= 4) {
return false
}
for (let i = 0; i < 6; ++i) {
const ch = state.src.charCodeAt(pos + i)
if (ch !== matcher[i] || pos + i >= max) return false
}
if (silent) return true
// handle code snippet include
const d = dataFactory(state, pos, max)
const opts = optionsMap(d)
const token = state.push('fence', 'code', 0)
token.info = (d.options.lang || d.file.ext) + opts.meta
token.content = d.fileExists && opts.hasTransclusion ? contentTransclusion(d, opts.transclusionType) : d.content
token.markup = '```'
token.map = [startLine, startLine + 1]
state.line = startLine + 1
return true
}
md.block.ruler.before('fence', 'snippet', parser)
}