brace-fold.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. ;(function (mod) {
  4. if (typeof exports == 'object' && typeof module == 'object')
  5. // CommonJS
  6. mod(require('../../lib/codemirror'))
  7. else if (typeof define == 'function' && define.amd)
  8. // AMD
  9. define(['../../lib/codemirror'], mod)
  10. // Plain browser env
  11. else mod(CodeMirror)
  12. })(function (CodeMirror) {
  13. 'use strict'
  14. function bracketFolding(pairs) {
  15. return function (cm, start) {
  16. var line = start.line,
  17. lineText = cm.getLine(line)
  18. function findOpening(pair) {
  19. var tokenType
  20. for (var at = start.ch, pass = 0; ; ) {
  21. var found = at <= 0 ? -1 : lineText.lastIndexOf(pair[0], at - 1)
  22. if (found == -1) {
  23. if (pass == 1) break
  24. pass = 1
  25. at = lineText.length
  26. continue
  27. }
  28. if (pass == 1 && found < start.ch) break
  29. tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))
  30. if (!/^(comment|string)/.test(tokenType)) return { ch: found + 1, tokenType: tokenType, pair: pair }
  31. at = found - 1
  32. }
  33. }
  34. function findRange(found) {
  35. var count = 1,
  36. lastLine = cm.lastLine(),
  37. end,
  38. startCh = found.ch,
  39. endCh
  40. outer: for (var i = line; i <= lastLine; ++i) {
  41. var text = cm.getLine(i),
  42. pos = i == line ? startCh : 0
  43. for (;;) {
  44. var nextOpen = text.indexOf(found.pair[0], pos),
  45. nextClose = text.indexOf(found.pair[1], pos)
  46. if (nextOpen < 0) nextOpen = text.length
  47. if (nextClose < 0) nextClose = text.length
  48. pos = Math.min(nextOpen, nextClose)
  49. if (pos == text.length) break
  50. if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == found.tokenType) {
  51. if (pos == nextOpen) ++count
  52. else if (!--count) {
  53. end = i
  54. endCh = pos
  55. break outer
  56. }
  57. }
  58. ++pos
  59. }
  60. }
  61. if (end == null || line == end) return null
  62. return { from: CodeMirror.Pos(line, startCh), to: CodeMirror.Pos(end, endCh) }
  63. }
  64. var found = []
  65. for (var i = 0; i < pairs.length; i++) {
  66. var open = findOpening(pairs[i])
  67. if (open) found.push(open)
  68. }
  69. found.sort(function (a, b) {
  70. return a.ch - b.ch
  71. })
  72. for (var i = 0; i < found.length; i++) {
  73. var range = findRange(found[i])
  74. if (range) return range
  75. }
  76. return null
  77. }
  78. }
  79. CodeMirror.registerHelper(
  80. 'fold',
  81. 'brace',
  82. bracketFolding([
  83. ['{', '}'],
  84. ['[', ']'],
  85. ])
  86. )
  87. CodeMirror.registerHelper(
  88. 'fold',
  89. 'brace-paren',
  90. bracketFolding([
  91. ['{', '}'],
  92. ['[', ']'],
  93. ['(', ')'],
  94. ])
  95. )
  96. CodeMirror.registerHelper('fold', 'import', function (cm, start) {
  97. function hasImport(line) {
  98. if (line < cm.firstLine() || line > cm.lastLine()) return null
  99. var start = cm.getTokenAt(CodeMirror.Pos(line, 1))
  100. if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1))
  101. if (start.type != 'keyword' || start.string != 'import') return null
  102. // Now find closing semicolon, return its position
  103. for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
  104. var text = cm.getLine(i),
  105. semi = text.indexOf(';')
  106. if (semi != -1) return { startCh: start.end, end: CodeMirror.Pos(i, semi) }
  107. }
  108. }
  109. var startLine = start.line,
  110. has = hasImport(startLine),
  111. prev
  112. if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1)) return null
  113. for (var end = has.end; ; ) {
  114. var next = hasImport(end.line + 1)
  115. if (next == null) break
  116. end = next.end
  117. }
  118. return { from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end }
  119. })
  120. CodeMirror.registerHelper('fold', 'include', function (cm, start) {
  121. function hasInclude(line) {
  122. if (line < cm.firstLine() || line > cm.lastLine()) return null
  123. var start = cm.getTokenAt(CodeMirror.Pos(line, 1))
  124. if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1))
  125. if (start.type == 'meta' && start.string.slice(0, 8) == '#include') return start.start + 8
  126. }
  127. var startLine = start.line,
  128. has = hasInclude(startLine)
  129. if (has == null || hasInclude(startLine - 1) != null) return null
  130. for (var end = startLine; ; ) {
  131. var next = hasInclude(end + 1)
  132. if (next == null) break
  133. ++end
  134. }
  135. return { from: CodeMirror.Pos(startLine, has + 1), to: cm.clipPos(CodeMirror.Pos(end)) }
  136. })
  137. })