-
Notifications
You must be signed in to change notification settings - Fork 3
/
prism-js-fold.js
98 lines (88 loc) · 3.72 KB
/
prism-js-fold.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
// inspects code elements from prism during pre-render hook. if it's JS or JSON we try to insert details/summary tags to
// allow code folding
const lastLineWrapperOpen = "<span class='ll'>".split() // wrap portion of last line preceding the closing symbol so we can conditionally hide it, needed to make non-square hidden regions collapse nicely.
const lastLineWrapperClose = "</span>".split()
const firstLineContentWrapperOpen = "<span class='fl'>".split() // wrapper beginning at first **visible** character on first line where opening symbol is found. needed so we can align opening toggle nicely
const firstLineContentWrapperClose = "</span>".split()
const detailsOpenFragmentActive = "<details open><summary>".split()
const detailsOpenFragmentInactive = "<details><summary>".split()
const summaryCloseFragment = "</summary>".split()
const detailsCloseFragment = "</details>".split()
const symbolPairMap = {
'{': '}',
'[': ']'
}
// folding `context` type contains:
// - minimumDepth:int - depth value that must be met or exceeded for folding to occur
function insertFold(inputBuffer, depth, context) {
const output = []
let remaining = inputBuffer
let current
function createFold(symbol) {
const [result, resultRemaining] = insertFold(remaining, depth + 1, context)
const currentLineEndIndex = result.indexOf('\n')
// only create fold if symbol pair crossed a '\n'. only insert fold if at required depth.
if (currentLineEndIndex >= 0 && depth >= context.minimumDepth) {
const currentLineStartIndex = output.lastIndexOf('\n')
const currentLineStart = output.splice(currentLineStartIndex + 1)
const currentLineCharacterStartIndex = currentLineStart.findIndex((c) => /[^\s]{1}/.test(c))
const currentLineStartWhiteSpace = currentLineStart.splice(0, currentLineCharacterStartIndex)
const currentLineEnd = result.splice(0, currentLineEndIndex)
const resultLastLineIndex = result.lastIndexOf('\n')
const resultLastLine = result.splice(resultLastLineIndex + 1)
output.push(
...(context.lineCount >= 40 ? detailsOpenFragmentInactive : detailsOpenFragmentActive),
...currentLineStartWhiteSpace,
...firstLineContentWrapperOpen,
...currentLineStart,
...currentLineEnd,
...firstLineContentWrapperClose,
...summaryCloseFragment,
...result,
...detailsCloseFragment,
...lastLineWrapperOpen,
...resultLastLine,
...lastLineWrapperClose,
symbolPairMap[symbol]
)
remaining = resultRemaining
} else {
output.push(...result, symbolPairMap[symbol])
remaining = resultRemaining
}
}
while ((current = remaining.shift()) !== undefined) {
switch (current) {
case '[':
case '{':
output.push(current)
createFold(current);
break;
case ']':
case '}':
return [output, remaining]
default:
output.push(current)
}
}
return [output, remaining]
}
// takes a code element and begins recursion if it's a parsable format
function insertFolds(codeElement) {
const parseable = Array.from(codeElement.classList).find(
(cls) => cls.endsWith('json') || cls.endsWith('js') || cls.endsWith('javascript')
) !== undefined
if (parseable) {
const inputBuffer = codeElement.innerText.split('')
const [result] = insertFold(inputBuffer, 1, {
minimumDepth: 2,
lineCount: inputBuffer.filter((c) => c === '\n').length
})
codeElement.innerHTML = result.join('')
}
}
if (Prism) {
Prism.hooks.add('before-all-elements-highlight', ({ elements }) => elements.forEach(insertFolds))
} else {
console.warn('prism-js-fold: Prism was not loaded so we could not add the Prism hook needed for code folding insertion.')
}