text: ﻿// CodeMirror, copyright (c) by Marijn Haverbeke and others
  // Distributed under an MIT license: https://codemirror.net/LICENSE
  
  (function (mod) {
      if (typeof exports == "object" && typeof module == "object") // CommonJS
          mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
      else if (typeof define == "function" && define.amd) // AMD
          define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
      else // Plain browser env
          mod(CodeMirror);
  })(function (CodeMirror) {
      "use strict";
  
      CodeMirror.defineExtension("showMatchesOnScrollbar", function (query, caseFold, options) {
          if (typeof options == "string") options = { className: options };
          if (!options) options = {};
          return new SearchAnnotation(this, query, caseFold, options);
      });
  
      function SearchAnnotation(cm, query, caseFold, options) {
          this.cm = cm;
          this.options = options;
          var annotateOptions = { listenForChanges: false };
          for (var prop in options) annotateOptions[prop] = options[prop];
          if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
          this.annotation = cm.annotateScrollbar(annotateOptions);
          this.query = query;
          this.caseFold = caseFold;
          this.gap = { from: cm.firstLine(), to: cm.lastLine() + 1 };
          this.matches = [];
          this.update = null;
  
          this.findMatches();
          this.annotation.update(this.matches);
  
          var self = this;
          cm.on("change", this.changeHandler = function (_cm, change) { self.onChange(change); });
      }
  
      var MAX_MATCHES = 1000;
  
      SearchAnnotation.prototype.findMatches = function () {
          if (!this.gap) return;
          for (var i = 0; i < this.matches.length; i++) {
              var match = this.matches[i];
              if (match.from.line >= this.gap.to) break;
              if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
          }
          var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), { caseFold: this.caseFold, multiline: this.options.multiline });
          var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;
          while (cursor.findNext()) {
              var match = { from: cursor.from(), to: cursor.to() };
              if (match.from.line >= this.gap.to) break;
              this.matches.splice(i++, 0, match);
              if (this.matches.length > maxMatches) break;
          }
          this.gap = null;
      };
  
      function offsetLine(line, changeStart, sizeChange) {
          if (line <= changeStart) return line;
          return Math.max(changeStart, line + sizeChange);
      }
  
      SearchAnnotation.prototype.onChange = function (change) {
          var startLine = change.from.line;
          var endLine = CodeMirror.changeEnd(change).line;
          var sizeChange = endLine - change.to.line;
          if (this.gap) {
              this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
              this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
          } else {
              this.gap = { from: change.from.line, to: endLine + 1 };
          }
  
          if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
              var match = this.matches[i];
              var newFrom = offsetLine(match.from.line, startLine, sizeChange);
              if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
              var newTo = offsetLine(match.to.line, startLine, sizeChange);
              if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
          }
          clearTimeout(this.update);
          var self = this;
          this.update = setTimeout(function () { self.updateAfterChange(); }, 250);
      };
  
      SearchAnnotation.prototype.updateAfterChange = function () {
          this.findMatches();
          this.annotation.update(this.matches);
      };
  
      SearchAnnotation.prototype.clear = function () {
          this.cm.off("change", this.changeHandler);
          this.annotation.clear();
      };
  });