browser/devtools/sourceeditor/codemirror/search/searchcursor.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/sourceeditor/codemirror/search/searchcursor.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,186 @@
     1.4 +(function(mod) {
     1.5 +  if (typeof exports == "object" && typeof module == "object") // CommonJS
     1.6 +    mod(require("../../lib/codemirror"));
     1.7 +  else if (typeof define == "function" && define.amd) // AMD
     1.8 +    define(["../../lib/codemirror"], mod);
     1.9 +  else // Plain browser env
    1.10 +    mod(CodeMirror);
    1.11 +})(function(CodeMirror) {
    1.12 +  "use strict";
    1.13 +  var Pos = CodeMirror.Pos;
    1.14 +
    1.15 +  function SearchCursor(doc, query, pos, caseFold) {
    1.16 +    this.atOccurrence = false; this.doc = doc;
    1.17 +    if (caseFold == null && typeof query == "string") caseFold = false;
    1.18 +
    1.19 +    pos = pos ? doc.clipPos(pos) : Pos(0, 0);
    1.20 +    this.pos = {from: pos, to: pos};
    1.21 +
    1.22 +    // The matches method is filled in based on the type of query.
    1.23 +    // It takes a position and a direction, and returns an object
    1.24 +    // describing the next occurrence of the query, or null if no
    1.25 +    // more matches were found.
    1.26 +    if (typeof query != "string") { // Regexp match
    1.27 +      if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g");
    1.28 +      this.matches = function(reverse, pos) {
    1.29 +        if (reverse) {
    1.30 +          query.lastIndex = 0;
    1.31 +          var line = doc.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start;
    1.32 +          for (;;) {
    1.33 +            query.lastIndex = cutOff;
    1.34 +            var newMatch = query.exec(line);
    1.35 +            if (!newMatch) break;
    1.36 +            match = newMatch;
    1.37 +            start = match.index;
    1.38 +            cutOff = match.index + (match[0].length || 1);
    1.39 +            if (cutOff == line.length) break;
    1.40 +          }
    1.41 +          var matchLen = (match && match[0].length) || 0;
    1.42 +          if (!matchLen) {
    1.43 +            if (start == 0 && line.length == 0) {match = undefined;}
    1.44 +            else if (start != doc.getLine(pos.line).length) {
    1.45 +              matchLen++;
    1.46 +            }
    1.47 +          }
    1.48 +        } else {
    1.49 +          query.lastIndex = pos.ch;
    1.50 +          var line = doc.getLine(pos.line), match = query.exec(line);
    1.51 +          var matchLen = (match && match[0].length) || 0;
    1.52 +          var start = match && match.index;
    1.53 +          if (start + matchLen != line.length && !matchLen) matchLen = 1;
    1.54 +        }
    1.55 +        if (match && matchLen)
    1.56 +          return {from: Pos(pos.line, start),
    1.57 +                  to: Pos(pos.line, start + matchLen),
    1.58 +                  match: match};
    1.59 +      };
    1.60 +    } else { // String query
    1.61 +      var origQuery = query;
    1.62 +      if (caseFold) query = query.toLowerCase();
    1.63 +      var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
    1.64 +      var target = query.split("\n");
    1.65 +      // Different methods for single-line and multi-line queries
    1.66 +      if (target.length == 1) {
    1.67 +        if (!query.length) {
    1.68 +          // Empty string would match anything and never progress, so
    1.69 +          // we define it to match nothing instead.
    1.70 +          this.matches = function() {};
    1.71 +        } else {
    1.72 +          this.matches = function(reverse, pos) {
    1.73 +            if (reverse) {
    1.74 +              var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig);
    1.75 +              var match = line.lastIndexOf(query);
    1.76 +              if (match > -1) {
    1.77 +                match = adjustPos(orig, line, match);
    1.78 +                return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
    1.79 +              }
    1.80 +             } else {
    1.81 +               var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig);
    1.82 +               var match = line.indexOf(query);
    1.83 +               if (match > -1) {
    1.84 +                 match = adjustPos(orig, line, match) + pos.ch;
    1.85 +                 return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
    1.86 +               }
    1.87 +            }
    1.88 +          };
    1.89 +        }
    1.90 +      } else {
    1.91 +        var origTarget = origQuery.split("\n");
    1.92 +        this.matches = function(reverse, pos) {
    1.93 +          var last = target.length - 1;
    1.94 +          if (reverse) {
    1.95 +            if (pos.line - (target.length - 1) < doc.firstLine()) return;
    1.96 +            if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return;
    1.97 +            var to = Pos(pos.line, origTarget[last].length);
    1.98 +            for (var ln = pos.line - 1, i = last - 1; i >= 1; --i, --ln)
    1.99 +              if (target[i] != fold(doc.getLine(ln))) return;
   1.100 +            var line = doc.getLine(ln), cut = line.length - origTarget[0].length;
   1.101 +            if (fold(line.slice(cut)) != target[0]) return;
   1.102 +            return {from: Pos(ln, cut), to: to};
   1.103 +          } else {
   1.104 +            if (pos.line + (target.length - 1) > doc.lastLine()) return;
   1.105 +            var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length;
   1.106 +            if (fold(line.slice(cut)) != target[0]) return;
   1.107 +            var from = Pos(pos.line, cut);
   1.108 +            for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln)
   1.109 +              if (target[i] != fold(doc.getLine(ln))) return;
   1.110 +            if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return;
   1.111 +            return {from: from, to: Pos(ln, origTarget[last].length)};
   1.112 +          }
   1.113 +        };
   1.114 +      }
   1.115 +    }
   1.116 +  }
   1.117 +
   1.118 +  SearchCursor.prototype = {
   1.119 +    findNext: function() {return this.find(false);},
   1.120 +    findPrevious: function() {return this.find(true);},
   1.121 +
   1.122 +    find: function(reverse) {
   1.123 +      var self = this, pos = this.doc.clipPos(reverse ? this.pos.from : this.pos.to);
   1.124 +      function savePosAndFail(line) {
   1.125 +        var pos = Pos(line, 0);
   1.126 +        self.pos = {from: pos, to: pos};
   1.127 +        self.atOccurrence = false;
   1.128 +        return false;
   1.129 +      }
   1.130 +
   1.131 +      for (;;) {
   1.132 +        if (this.pos = this.matches(reverse, pos)) {
   1.133 +          this.atOccurrence = true;
   1.134 +          return this.pos.match || true;
   1.135 +        }
   1.136 +        if (reverse) {
   1.137 +          if (!pos.line) return savePosAndFail(0);
   1.138 +          pos = Pos(pos.line-1, this.doc.getLine(pos.line-1).length);
   1.139 +        }
   1.140 +        else {
   1.141 +          var maxLine = this.doc.lineCount();
   1.142 +          if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
   1.143 +          pos = Pos(pos.line + 1, 0);
   1.144 +        }
   1.145 +      }
   1.146 +    },
   1.147 +
   1.148 +    from: function() {if (this.atOccurrence) return this.pos.from;},
   1.149 +    to: function() {if (this.atOccurrence) return this.pos.to;},
   1.150 +
   1.151 +    replace: function(newText) {
   1.152 +      if (!this.atOccurrence) return;
   1.153 +      var lines = CodeMirror.splitLines(newText);
   1.154 +      this.doc.replaceRange(lines, this.pos.from, this.pos.to);
   1.155 +      this.pos.to = Pos(this.pos.from.line + lines.length - 1,
   1.156 +                        lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
   1.157 +    }
   1.158 +  };
   1.159 +
   1.160 +  // Maps a position in a case-folded line back to a position in the original line
   1.161 +  // (compensating for codepoints increasing in number during folding)
   1.162 +  function adjustPos(orig, folded, pos) {
   1.163 +    if (orig.length == folded.length) return pos;
   1.164 +    for (var pos1 = Math.min(pos, orig.length);;) {
   1.165 +      var len1 = orig.slice(0, pos1).toLowerCase().length;
   1.166 +      if (len1 < pos) ++pos1;
   1.167 +      else if (len1 > pos) --pos1;
   1.168 +      else return pos1;
   1.169 +    }
   1.170 +  }
   1.171 +
   1.172 +  CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
   1.173 +    return new SearchCursor(this.doc, query, pos, caseFold);
   1.174 +  });
   1.175 +  CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
   1.176 +    return new SearchCursor(this, query, pos, caseFold);
   1.177 +  });
   1.178 +
   1.179 +  CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
   1.180 +    var ranges = [], next;
   1.181 +    var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold);
   1.182 +    while (next = cur.findNext()) {
   1.183 +      if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break;
   1.184 +      ranges.push({anchor: cur.from(), head: cur.to()});
   1.185 +    }
   1.186 +    if (ranges.length)
   1.187 +      this.setSelections(ranges, 0);
   1.188 +  });
   1.189 +});

mercurial