Passed
Push — master ( fc2d57...76a657 )
by Barry
01:39
created

src/findMdpInsert.js   A

Complexity

Total Complexity 24
Complexity/F 4

Size

Lines of Code 102
Function Count 6

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
c 1
b 0
f 0
nc 1
dl 0
loc 102
rs 10
wmc 24
mnd 3
bc 23
fnc 6
bpm 3.8333
cpm 4
noi 1

4 Functions

Rating   Name   Duplication   Size   Complexity  
A findMdpInsert.js ➔ _findCodeEndingAfter 0 11 3
B findMdpInsert.js ➔ _findMdpEndUnfenced 0 25 5
C findMdpInsert.js ➔ findMdpInsert 0 31 7
B findMdpInsert.js ➔ _findMdpStartUnfenced 0 30 5
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
0 ignored issues
show
Comprehensibility Bug introduced by
The variable e does not seem to be initialized in case the while loop on line 27 is not entered. Are you sure this can never be the case?
Loading history...
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