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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/sourceeditor/codemirror/search/search.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,167 @@
     1.4 +// Define search commands. Depends on dialog.js or another
     1.5 +// implementation of the openDialog method.
     1.6 +
     1.7 +// Replace works a little oddly -- it will do the replace on the next
     1.8 +// Ctrl-G (or whatever is bound to findNext) press. You prevent a
     1.9 +// replace by making sure the match is no longer selected when hitting
    1.10 +// Ctrl-G.
    1.11 +
    1.12 +(function(mod) {
    1.13 +  if (typeof exports == "object" && typeof module == "object") // CommonJS
    1.14 +    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
    1.15 +  else if (typeof define == "function" && define.amd) // AMD
    1.16 +    define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
    1.17 +  else // Plain browser env
    1.18 +    mod(CodeMirror);
    1.19 +})(function(CodeMirror) {
    1.20 +  "use strict";
    1.21 +  function searchOverlay(query, caseInsensitive) {
    1.22 +    var startChar;
    1.23 +    if (typeof query == "string") {
    1.24 +      startChar = query.charAt(0);
    1.25 +      query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"),
    1.26 +                         caseInsensitive ? "i" : "");
    1.27 +    } else {
    1.28 +      query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : "");
    1.29 +    }
    1.30 +    return {token: function(stream) {
    1.31 +      if (stream.match(query)) return "searching";
    1.32 +      while (!stream.eol()) {
    1.33 +        stream.next();
    1.34 +        if (startChar && !caseInsensitive)
    1.35 +          stream.skipTo(startChar) || stream.skipToEnd();
    1.36 +        if (stream.match(query, false)) break;
    1.37 +      }
    1.38 +    }};
    1.39 +  }
    1.40 +
    1.41 +  function SearchState() {
    1.42 +    this.posFrom = this.posTo = this.query = null;
    1.43 +    this.overlay = null;
    1.44 +  }
    1.45 +  function getSearchState(cm) {
    1.46 +    return cm.state.search || (cm.state.search = new SearchState());
    1.47 +  }
    1.48 +  function queryCaseInsensitive(query) {
    1.49 +    return typeof query == "string" && query == query.toLowerCase();
    1.50 +  }
    1.51 +  function getSearchCursor(cm, query, pos) {
    1.52 +    // Heuristic: if the query string is all lowercase, do a case insensitive search.
    1.53 +    return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
    1.54 +  }
    1.55 +  function dialog(cm, text, shortText, deflt, f) {
    1.56 +    if (cm.openDialog) cm.openDialog(text, f, {value: deflt});
    1.57 +    else f(prompt(shortText, deflt));
    1.58 +  }
    1.59 +  function confirmDialog(cm, text, shortText, fs) {
    1.60 +    if (cm.openConfirm) cm.openConfirm(text, fs);
    1.61 +    else if (confirm(shortText)) fs[0]();
    1.62 +  }
    1.63 +  function parseQuery(query) {
    1.64 +    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
    1.65 +    if (isRE) {
    1.66 +      query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i");
    1.67 +      if (query.test("")) query = /x^/;
    1.68 +    } else if (query == "") {
    1.69 +      query = /x^/;
    1.70 +    }
    1.71 +    return query;
    1.72 +  }
    1.73 +  var queryDialog;
    1.74 +  function doSearch(cm, rev) {
    1.75 +    if (!queryDialog) {
    1.76 +      let doc = cm.getWrapperElement().ownerDocument;
    1.77 +      let inp = doc.createElement("input");
    1.78 +      let txt = doc.createTextNode(cm.l10n("findCmd.promptMessage"));
    1.79 +
    1.80 +      inp.type = "text";
    1.81 +      inp.style.width = "10em";
    1.82 +      inp.style.MozMarginStart = "1em";
    1.83 +
    1.84 +      queryDialog = doc.createElement("div");
    1.85 +      queryDialog.appendChild(txt);
    1.86 +      queryDialog.appendChild(inp);
    1.87 +    }
    1.88 +    var state = getSearchState(cm);
    1.89 +    if (state.query) return findNext(cm, rev);
    1.90 +    dialog(cm, queryDialog, "Search for:", cm.getSelection(), function(query) {
    1.91 +      cm.operation(function() {
    1.92 +        if (!query || state.query) return;
    1.93 +        state.query = parseQuery(query);
    1.94 +        cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
    1.95 +        state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
    1.96 +        cm.addOverlay(state.overlay);
    1.97 +        state.posFrom = state.posTo = cm.getCursor();
    1.98 +        findNext(cm, rev);
    1.99 +      });
   1.100 +    });
   1.101 +  }
   1.102 +  function findNext(cm, rev) {cm.operation(function() {
   1.103 +    var state = getSearchState(cm);
   1.104 +    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
   1.105 +    if (!cursor.find(rev)) {
   1.106 +      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
   1.107 +      if (!cursor.find(rev)) return;
   1.108 +    }
   1.109 +    cm.setSelection(cursor.from(), cursor.to());
   1.110 +    cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
   1.111 +    state.posFrom = cursor.from(); state.posTo = cursor.to();
   1.112 +  });}
   1.113 +  function clearSearch(cm) {cm.operation(function() {
   1.114 +    var state = getSearchState(cm);
   1.115 +    if (!state.query) return;
   1.116 +    state.query = null;
   1.117 +    cm.removeOverlay(state.overlay);
   1.118 +  });}
   1.119 +
   1.120 +  var replaceQueryDialog =
   1.121 +    'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
   1.122 +  var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
   1.123 +  var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
   1.124 +  function replace(cm, all) {
   1.125 +    dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) {
   1.126 +      if (!query) return;
   1.127 +      query = parseQuery(query);
   1.128 +      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
   1.129 +        if (all) {
   1.130 +          cm.operation(function() {
   1.131 +            for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
   1.132 +              if (typeof query != "string") {
   1.133 +                var match = cm.getRange(cursor.from(), cursor.to()).match(query);
   1.134 +                cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
   1.135 +              } else cursor.replace(text);
   1.136 +            }
   1.137 +          });
   1.138 +        } else {
   1.139 +          clearSearch(cm);
   1.140 +          var cursor = getSearchCursor(cm, query, cm.getCursor());
   1.141 +          var advance = function() {
   1.142 +            var start = cursor.from(), match;
   1.143 +            if (!(match = cursor.findNext())) {
   1.144 +              cursor = getSearchCursor(cm, query);
   1.145 +              if (!(match = cursor.findNext()) ||
   1.146 +                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
   1.147 +            }
   1.148 +            cm.setSelection(cursor.from(), cursor.to());
   1.149 +            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
   1.150 +            confirmDialog(cm, doReplaceConfirm, "Replace?",
   1.151 +                          [function() {doReplace(match);}, advance]);
   1.152 +          };
   1.153 +          var doReplace = function(match) {
   1.154 +            cursor.replace(typeof query == "string" ? text :
   1.155 +                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
   1.156 +            advance();
   1.157 +          };
   1.158 +          advance();
   1.159 +        }
   1.160 +      });
   1.161 +    });
   1.162 +  }
   1.163 +
   1.164 +  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
   1.165 +  CodeMirror.commands.findNext = doSearch;
   1.166 +  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
   1.167 +  CodeMirror.commands.clearSearch = clearSearch;
   1.168 +  CodeMirror.commands.replace = replace;
   1.169 +  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
   1.170 +});

mercurial