matchesonscrollbar.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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'), require('./searchcursor'), require('../scroll/annotatescrollbar'))
  7. else if (typeof define == 'function' && define.amd)
  8. // AMD
  9. define(['../../lib/codemirror', './searchcursor', '../scroll/annotatescrollbar'], mod)
  10. // Plain browser env
  11. else mod(CodeMirror)
  12. })(function (CodeMirror) {
  13. 'use strict'
  14. CodeMirror.defineExtension('showMatchesOnScrollbar', function (query, caseFold, options) {
  15. if (typeof options == 'string') options = { className: options }
  16. if (!options) options = {}
  17. return new SearchAnnotation(this, query, caseFold, options)
  18. })
  19. function SearchAnnotation(cm, query, caseFold, options) {
  20. this.cm = cm
  21. this.options = options
  22. var annotateOptions = { listenForChanges: false }
  23. for (var prop in options) annotateOptions[prop] = options[prop]
  24. if (!annotateOptions.className) annotateOptions.className = 'CodeMirror-search-match'
  25. this.annotation = cm.annotateScrollbar(annotateOptions)
  26. this.query = query
  27. this.caseFold = caseFold
  28. this.gap = { from: cm.firstLine(), to: cm.lastLine() + 1 }
  29. this.matches = []
  30. this.update = null
  31. this.findMatches()
  32. this.annotation.update(this.matches)
  33. var self = this
  34. cm.on(
  35. 'change',
  36. (this.changeHandler = function (_cm, change) {
  37. self.onChange(change)
  38. })
  39. )
  40. }
  41. var MAX_MATCHES = 1000
  42. SearchAnnotation.prototype.findMatches = function () {
  43. if (!this.gap) return
  44. for (var i = 0; i < this.matches.length; i++) {
  45. var match = this.matches[i]
  46. if (match.from.line >= this.gap.to) break
  47. if (match.to.line >= this.gap.from) this.matches.splice(i--, 1)
  48. }
  49. var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), { caseFold: this.caseFold, multiline: this.options.multiline })
  50. var maxMatches = (this.options && this.options.maxMatches) || MAX_MATCHES
  51. while (cursor.findNext()) {
  52. var match = { from: cursor.from(), to: cursor.to() }
  53. if (match.from.line >= this.gap.to) break
  54. this.matches.splice(i++, 0, match)
  55. if (this.matches.length > maxMatches) break
  56. }
  57. this.gap = null
  58. }
  59. function offsetLine(line, changeStart, sizeChange) {
  60. if (line <= changeStart) return line
  61. return Math.max(changeStart, line + sizeChange)
  62. }
  63. SearchAnnotation.prototype.onChange = function (change) {
  64. var startLine = change.from.line
  65. var endLine = CodeMirror.changeEnd(change).line
  66. var sizeChange = endLine - change.to.line
  67. if (this.gap) {
  68. this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line)
  69. this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line)
  70. } else {
  71. this.gap = { from: change.from.line, to: endLine + 1 }
  72. }
  73. if (sizeChange)
  74. for (var i = 0; i < this.matches.length; i++) {
  75. var match = this.matches[i]
  76. var newFrom = offsetLine(match.from.line, startLine, sizeChange)
  77. if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch)
  78. var newTo = offsetLine(match.to.line, startLine, sizeChange)
  79. if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch)
  80. }
  81. clearTimeout(this.update)
  82. var self = this
  83. this.update = setTimeout(function () {
  84. self.updateAfterChange()
  85. }, 250)
  86. }
  87. SearchAnnotation.prototype.updateAfterChange = function () {
  88. this.findMatches()
  89. this.annotation.update(this.matches)
  90. }
  91. SearchAnnotation.prototype.clear = function () {
  92. this.cm.off('change', this.changeHandler)
  93. this.annotation.clear()
  94. }
  95. })