github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/app/public/codemirror/addon/search/search.js (about)

     1  // CodeMirror, copyright (c) by Marijn Haverbeke and others
     2  // Distributed under an MIT license: http://codemirror.net/LICENSE
     3  
     4  // Define search commands. Depends on dialog.js or another
     5  // implementation of the openDialog method.
     6  
     7  // Replace works a little oddly -- it will do the replace on the next
     8  // Ctrl-G (or whatever is bound to findNext) press. You prevent a
     9  // replace by making sure the match is no longer selected when hitting
    10  // Ctrl-G.
    11  
    12  (function(mod) {
    13    if (typeof exports == "object" && typeof module == "object") // CommonJS
    14      mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
    15    else if (typeof define == "function" && define.amd) // AMD
    16      define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
    17    else // Plain browser env
    18      mod(CodeMirror);
    19  })(function(CodeMirror) {
    20    "use strict";
    21  
    22    function searchOverlay(query, caseInsensitive) {
    23      if (typeof query == "string")
    24        query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
    25      else if (!query.global)
    26        query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
    27  
    28      return {token: function(stream) {
    29        query.lastIndex = stream.pos;
    30        var match = query.exec(stream.string);
    31        if (match && match.index == stream.pos) {
    32          stream.pos += match[0].length || 1;
    33          return "searching";
    34        } else if (match) {
    35          stream.pos = match.index;
    36        } else {
    37          stream.skipToEnd();
    38        }
    39      }};
    40    }
    41  
    42    function SearchState() {
    43      this.posFrom = this.posTo = this.lastQuery = this.query = null;
    44      this.overlay = null;
    45    }
    46  
    47    function getSearchState(cm) {
    48      return cm.state.search || (cm.state.search = new SearchState());
    49    }
    50  
    51    function queryCaseInsensitive(query) {
    52      return typeof query == "string" && query == query.toLowerCase();
    53    }
    54  
    55    function getSearchCursor(cm, query, pos) {
    56      // Heuristic: if the query string is all lowercase, do a case insensitive search.
    57      return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
    58    }
    59  
    60    function persistentDialog(cm, text, deflt, f) {
    61      cm.openDialog(text, f, {
    62        value: deflt,
    63        selectValueOnOpen: true,
    64        closeOnEnter: false,
    65        onClose: function() { clearSearch(cm); }
    66      });
    67    }
    68  
    69    function dialog(cm, text, shortText, deflt, f) {
    70      if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
    71      else f(prompt(shortText, deflt));
    72    }
    73  
    74    function confirmDialog(cm, text, shortText, fs) {
    75      if (cm.openConfirm) cm.openConfirm(text, fs);
    76      else if (confirm(shortText)) fs[0]();
    77    }
    78  
    79    function parseString(string) {
    80      return string.replace(/\\(.)/g, function(_, ch) {
    81        if (ch == "n") return "\n"
    82        if (ch == "r") return "\r"
    83        return ch
    84      })
    85    }
    86  
    87    function parseQuery(query) {
    88      var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
    89      if (isRE) {
    90        try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
    91        catch(e) {} // Not a regular expression after all, do a string search
    92      } else {
    93        query = parseString(query)
    94      }
    95      if (typeof query == "string" ? query == "" : query.test(""))
    96        query = /x^/;
    97      return query;
    98    }
    99  
   100    var queryDialog =
   101      'Search: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
   102  
   103    function startSearch(cm, state, query) {
   104      state.queryText = query;
   105      state.query = parseQuery(query);
   106      cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
   107      state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
   108      cm.addOverlay(state.overlay);
   109      if (cm.showMatchesOnScrollbar) {
   110        if (state.annotate) { state.annotate.clear(); state.annotate = null; }
   111        state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
   112      }
   113    }
   114  
   115    function doSearch(cm, rev, persistent) {
   116      var state = getSearchState(cm);
   117      if (state.query) return findNext(cm, rev);
   118      var q = cm.getSelection() || state.lastQuery;
   119      if (persistent && cm.openDialog) {
   120        var hiding = null
   121        persistentDialog(cm, queryDialog, q, function(query, event) {
   122          CodeMirror.e_stop(event);
   123          if (!query) return;
   124          if (query != state.queryText) startSearch(cm, state, query);
   125          if (hiding) hiding.style.opacity = 1
   126          findNext(cm, event.shiftKey, function(_, to) {
   127            var dialog
   128            if (to.line < 3 && document.querySelector &&
   129                (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
   130                dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
   131              (hiding = dialog).style.opacity = .4
   132          })
   133        });
   134      } else {
   135        dialog(cm, queryDialog, "Search for:", q, function(query) {
   136          if (query && !state.query) cm.operation(function() {
   137            startSearch(cm, state, query);
   138            state.posFrom = state.posTo = cm.getCursor();
   139            findNext(cm, rev);
   140          });
   141        });
   142      }
   143    }
   144  
   145    function findNext(cm, rev, callback) {cm.operation(function() {
   146      var state = getSearchState(cm);
   147      var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
   148      if (!cursor.find(rev)) {
   149        cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
   150        if (!cursor.find(rev)) return;
   151      }
   152      cm.setSelection(cursor.from(), cursor.to());
   153      cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
   154      state.posFrom = cursor.from(); state.posTo = cursor.to();
   155      if (callback) callback(cursor.from(), cursor.to())
   156    });}
   157  
   158    function clearSearch(cm) {cm.operation(function() {
   159      var state = getSearchState(cm);
   160      state.lastQuery = state.query;
   161      if (!state.query) return;
   162      state.query = state.queryText = null;
   163      cm.removeOverlay(state.overlay);
   164      if (state.annotate) { state.annotate.clear(); state.annotate = null; }
   165    });}
   166  
   167    var replaceQueryDialog =
   168      ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
   169    var replacementQueryDialog = 'With: <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
   170    var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>";
   171  
   172    function replaceAll(cm, query, text) {
   173      cm.operation(function() {
   174        for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
   175          if (typeof query != "string") {
   176            var match = cm.getRange(cursor.from(), cursor.to()).match(query);
   177            cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
   178          } else cursor.replace(text);
   179        }
   180      });
   181    }
   182  
   183    function replace(cm, all) {
   184      if (cm.getOption("readOnly")) return;
   185      var query = cm.getSelection() || getSearchState(cm).lastQuery;
   186      var dialogText = all ? "Replace all:" : "Replace:"
   187      dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
   188        if (!query) return;
   189        query = parseQuery(query);
   190        dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
   191          text = parseString(text)
   192          if (all) {
   193            replaceAll(cm, query, text)
   194          } else {
   195            clearSearch(cm);
   196            var cursor = getSearchCursor(cm, query, cm.getCursor());
   197            var advance = function() {
   198              var start = cursor.from(), match;
   199              if (!(match = cursor.findNext())) {
   200                cursor = getSearchCursor(cm, query);
   201                if (!(match = cursor.findNext()) ||
   202                    (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
   203              }
   204              cm.setSelection(cursor.from(), cursor.to());
   205              cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
   206              confirmDialog(cm, doReplaceConfirm, "Replace?",
   207                            [function() {doReplace(match);}, advance,
   208                             function() {replaceAll(cm, query, text)}]);
   209            };
   210            var doReplace = function(match) {
   211              cursor.replace(typeof query == "string" ? text :
   212                             text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
   213              advance();
   214            };
   215            advance();
   216          }
   217        });
   218      });
   219    }
   220  
   221    CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
   222    CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
   223    CodeMirror.commands.findNext = doSearch;
   224    CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
   225    CodeMirror.commands.clearSearch = clearSearch;
   226    CodeMirror.commands.replace = replace;
   227    CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
   228  });