1
|
|
|
/** findMdpInsert and findCode functions use a similar layout to return the location and contents |
2
|
|
|
* .start => points at the character in the string why the other item starts (ie. comment or code block) |
3
|
|
|
* .length => is the overall length of the comment or code block. |
4
|
|
|
* .internalStart => points at the character in the string where the internal payload starts |
5
|
|
|
* .internalLength => is the length of the internal payload |
6
|
|
|
* .commandString => is the command string found within the particular item |
7
|
|
|
* .info => is a structure containing further info about what was found |
8
|
|
|
* if start is returned as -1 then nothing was found |
9
|
|
|
* |
10
|
|
|
* The internalStart/internaLength defines the internal content which will be replaced. This does not include |
11
|
|
|
* leading and lagging CRLF/LF. So the replacement text is not required to have either leading or lagging line |
12
|
|
|
* endings. However, if the internalLength is negative this means that leading CRLF or LF must be added by the insertion |
13
|
|
|
* routine. The reason for this is that it allows insertions between code fences or mdpInsert pairs which have zero lines |
14
|
|
|
* between them. |
15
|
|
|
* |
16
|
|
|
**/ |
17
|
|
|
|
18
|
|
|
const {findCode} = require('./findCode.js') |
19
|
|
|
|
20
|
|
|
export function findMdpInsert (txt, start) { |
21
|
|
|
let s = _findMdpStartUnfenced(txt, start) |
22
|
|
|
if (s.start === -1) { return s } |
23
|
|
|
let s1 = JSON.parse(JSON.stringify(s)) // create copy |
24
|
|
|
let depth = 1 |
25
|
|
|
let e |
26
|
|
|
let posn = s1.internalStart - 2 |
27
|
|
|
while (depth !== 0) { |
28
|
|
|
e = _findMdpEndUnfenced(txt, s, posn) |
29
|
|
|
if (e.start === -1) { |
30
|
|
|
// we have not found any more ends so we need to return a fail |
31
|
|
|
return e |
32
|
|
|
} |
33
|
|
|
s1 = _findMdpStartUnfenced(txt, posn) |
34
|
|
|
if (s1.start !== -1) { |
35
|
|
|
// we have found another start pattern |
36
|
|
|
if (s1.start < (e.internalStart + e.internalLength)) { |
37
|
|
|
depth++ |
38
|
|
|
posn = s1.internalStart - 2 |
39
|
|
|
} else { |
40
|
|
|
depth-- |
41
|
|
|
posn = e.start + e.length |
42
|
|
|
} |
43
|
|
|
} else { |
44
|
|
|
depth-- |
45
|
|
|
posn = e.start + e.length |
46
|
|
|
} |
47
|
|
|
if (depth > 5) { return {start: -1} } |
48
|
|
|
} |
49
|
|
|
return e |
|
|
|
|
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
function _findMdpStartUnfenced (txt, start) { |
53
|
|
|
let lookFrom = start |
54
|
|
|
let m, c |
55
|
|
|
while (true) { |
56
|
|
|
m = _findMdpStart(txt, lookFrom) |
57
|
|
|
if (m.start === -1) { return m } |
58
|
|
|
// we need to find the first code which ends after the mdp start |
59
|
|
|
c = _findCodeEndingAfter(txt, lookFrom, m.start) |
60
|
|
|
if (c.start === -1 || m.start < c.start) { |
61
|
|
|
// the mdp start we've found is not within a code fence |
62
|
|
|
break |
63
|
|
|
} |
64
|
|
|
// the mdp start we've found is within a code fence so find the next one |
65
|
|
|
lookFrom = c.start + c.length |
66
|
|
|
} |
67
|
|
|
return m |
68
|
|
|
|
69
|
|
|
function _findMdpStart (txt, start) { |
70
|
|
|
let regex = /(\r\n|\n|^)([ ]{0,3}\[>[^\r\n\t\0[\]]*\]: # (\([^\r\n\t\0]*\)|"[^\r\n\t\0]*"|'[^\r\n\t\0]*'))(\r\n|\n)/g |
71
|
|
|
regex.lastIndex = start |
72
|
|
|
let regexResult = regex.exec(txt) |
73
|
|
|
if (regexResult === null) { return {start: -1} } |
74
|
|
|
let r = { |
75
|
|
|
start: regexResult.index + regexResult[1].length, |
76
|
|
|
internalStart: regexResult.index + regexResult[0].length, |
77
|
|
|
commandString: regexResult[3].substring(1, regexResult[3].length - 1) |
78
|
|
|
} |
79
|
|
|
return r |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
function _findMdpEndUnfenced (txt, opening, start) { |
84
|
|
|
let lookFrom = start |
85
|
|
|
let m, c |
86
|
|
|
while (true) { |
87
|
|
|
m = _findMdpEnd(txt, opening, lookFrom - 2) |
88
|
|
|
if (m.start === -1) { return m } |
89
|
|
|
// we need to find the first code which ends after the mdpEnd starts |
90
|
|
|
c = _findCodeEndingAfter(txt, lookFrom, (m.internalStart + m.internalLength)) |
91
|
|
|
if (c.start === -1 || (m.internalStart + m.internalLength) < c.start) { break } // the mdp end we've found is not within a code fence |
92
|
|
|
// the mdp end we've found is within a code fence so find the next one |
93
|
|
|
lookFrom = c.start + c.length |
94
|
|
|
} |
95
|
|
|
return m |
96
|
|
|
|
97
|
|
|
function _findMdpEnd (txt, opening, start) { |
98
|
|
|
let r = JSON.parse(JSON.stringify(opening)) // create copy of opening structure passed in |
99
|
|
|
let regex = /(\r\n|\n)([ ]{0,3}\[<[^\r\n\t\0[\]]*\]: #)(\r\n|\n|$)/g |
100
|
|
|
regex.lastIndex = start |
101
|
|
|
let regexResult = regex.exec(txt) |
102
|
|
|
if (regexResult === null) { return {start: -1} } |
103
|
|
|
r.internalLength = regexResult.index - r.internalStart |
104
|
|
|
r.length = regexResult.index + regexResult[0].length - regexResult[3].length - r.start |
105
|
|
|
return r |
106
|
|
|
} |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
function _findCodeEndingAfter (txt, start, endingAfter) { |
110
|
|
|
// returns the first code block which ends after the position specified |
111
|
|
|
// the search starts at start |
112
|
|
|
let pos = start |
113
|
|
|
let c |
114
|
|
|
do { |
115
|
|
|
c = findCode(txt, pos) |
116
|
|
|
pos = c.start + c.length |
117
|
|
|
} while (c.start !== -1 && pos <= endingAfter) |
118
|
|
|
return c |
119
|
|
|
} |
120
|
|
|
|