JAVASCRIPT 8
Search.js Guest on 20th November 2020 03:21:38 PM
  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, onEnter, onKeyDown) {
  61.     cm.openDialog(text, onEnter, {
  62.       value: deflt,
  63.       selectValueOnOpen: true,
  64.       closeOnEnter: false,
  65.       onClose: function() { clearSearch(cm); },
  66.       onKeyDown: onKeyDown
  67.     });
  68.   }
  69.  
  70.   function dialog(cm, text, shortText, deflt, f) {
  71.     if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
  72.     else f(prompt(shortText, deflt));
  73.   }
  74.  
  75.   function confirmDialog(cm, text, shortText, fs) {
  76.     if (cm.openConfirm) cm.openConfirm(text, fs);
  77.     else if (confirm(shortText)) fs[0]();
  78.   }
  79.  
  80.   function parseString(string) {
  81.     return string.replace(/\\(.)/g, function(_, ch) {
  82.       if (ch == "n") return "\n"
  83.       if (ch == "r") return "\r"
  84.       return ch
  85.     })
  86.   }
  87.  
  88.   function parseQuery(query) {
  89.     var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
  90.     if (isRE) {
  91.       try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
  92.       catch(e) {} // Not a regular expression after all, do a string search
  93.     } else {
  94.       query = parseString(query)
  95.     }
  96.     if (typeof query == "string" ? query == "" : query.test(""))
  97.       query = /x^/;
  98.     return query;
  99.   }
  100.  
  101.   var queryDialog =
  102.     '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>';
  103.  
  104.   function startSearch(cm, state, query) {
  105.     state.queryText = query;
  106.     state.query = parseQuery(query);
  107.     cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
  108.     state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
  109.     cm.addOverlay(state.overlay);
  110.     if (cm.showMatchesOnScrollbar) {
  111.       if (state.annotate) { state.annotate.clear(); state.annotate = null; }
  112.       state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
  113.     }
  114.   }
  115.  
  116.   function doSearch(cm, rev, persistent, immediate) {
  117.     var state = getSearchState(cm);
  118.     if (state.query) return findNext(cm, rev);
  119.     var q = cm.getSelection() || state.lastQuery;
  120.     if (persistent && cm.openDialog) {
  121.       var hiding = null
  122.       var searchNext = function(query, event) {
  123.         CodeMirror.e_stop(event);
  124.         if (!query) return;
  125.         if (query != state.queryText) {
  126.           startSearch(cm, state, query);
  127.           state.posFrom = state.posTo = cm.getCursor();
  128.         }
  129.         if (hiding) hiding.style.opacity = 1
  130.         findNext(cm, event.shiftKey, function(_, to) {
  131.           var dialog
  132.           if (to.line < 3 && document.querySelector &&
  133.               (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
  134.               dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
  135.             (hiding = dialog).style.opacity = .4
  136.         })
  137.       };
  138.       persistentDialog(cm, queryDialog, q, searchNext, function(event, query) {
  139.         var keyName = CodeMirror.keyName(event)
  140.         var cmd = CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
  141.         if (!cmd) cmd = cm.getOption('extraKeys')[keyName]
  142.         if (cmd == "findNext" || cmd == "findPrev" ||
  143.           cmd == "findPersistentNext" || cmd == "findPersistentPrev") {
  144.           CodeMirror.e_stop(event);
  145.           startSearch(cm, getSearchState(cm), query);
  146.           cm.execCommand(cmd);
  147.         } else if (cmd == "find" || cmd == "findPersistent") {
  148.           CodeMirror.e_stop(event);
  149.           searchNext(query, event);
  150.         }
  151.       });
  152.       if (immediate && q) {
  153.         startSearch(cm, state, q);
  154.         findNext(cm, rev);
  155.       }
  156.     } else {
  157.       dialog(cm, queryDialog, "Search for:", q, function(query) {
  158.         if (query && !state.query) cm.operation(function() {
  159.           startSearch(cm, state, query);
  160.           state.posFrom = state.posTo = cm.getCursor();
  161.           findNext(cm, rev);
  162.         });
  163.       });
  164.     }
  165.   }
  166.  
  167.   function findNext(cm, rev, callback) {cm.operation(function() {
  168.     var state = getSearchState(cm);
  169.     var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
  170.     if (!cursor.find(rev)) {
  171.       cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
  172.       if (!cursor.find(rev)) return;
  173.     }
  174.     cm.setSelection(cursor.from(), cursor.to());
  175.     cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
  176.     state.posFrom = cursor.from(); state.posTo = cursor.to();
  177.     if (callback) callback(cursor.from(), cursor.to())
  178.   });}
  179.  
  180.   function clearSearch(cm) {cm.operation(function() {
  181.     var state = getSearchState(cm);
  182.     state.lastQuery = state.query;
  183.     if (!state.query) return;
  184.     state.query = state.queryText = null;
  185.     cm.removeOverlay(state.overlay);
  186.     if (state.annotate) { state.annotate.clear(); state.annotate = null; }
  187.   });}
  188.  
  189.   var replaceQueryDialog =
  190.     ' <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>';
  191.   var replacementQueryDialog = 'With: <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
  192.   var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>";
  193.  
  194.   function replaceAll(cm, query, text) {
  195.     cm.operation(function() {
  196.       for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
  197.         if (typeof query != "string") {
  198.           var match = cm.getRange(cursor.from(), cursor.to()).match(query);
  199.           cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
  200.         } else cursor.replace(text);
  201.       }
  202.     });
  203.   }
  204.  
  205.   function replace(cm, all) {
  206.     if (cm.getOption("readOnly")) return;
  207.     var query = cm.getSelection() || getSearchState(cm).lastQuery;
  208.     var dialogText = all ? "Replace all:" : "Replace:"
  209.     dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
  210.       if (!query) return;
  211.       query = parseQuery(query);
  212.       dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
  213.         text = parseString(text)
  214.         if (all) {
  215.           replaceAll(cm, query, text)
  216.         } else {
  217.           clearSearch(cm);
  218.           var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
  219.           var advance = function() {
  220.             var start = cursor.from(), match;
  221.             if (!(match = cursor.findNext())) {
  222.               cursor = getSearchCursor(cm, query);
  223.               if (!(match = cursor.findNext()) ||
  224.                   (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
  225.             }
  226.             cm.setSelection(cursor.from(), cursor.to());
  227.             cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
  228.             confirmDialog(cm, doReplaceConfirm, "Replace?",
  229.                           [function() {doReplace(match);}, advance,
  230.                            function() {replaceAll(cm, query, text)}]);
  231.           };
  232.           var doReplace = function(match) {
  233.             cursor.replace(typeof query == "string" ? text :
  234.                            text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
  235.             advance();
  236.           };
  237.           advance();
  238.         }
  239.       });
  240.     });
  241.   }
  242.  
  243.   CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
  244.   CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
  245.   CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
  246.   CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
  247.   CodeMirror.commands.findNext = doSearch;
  248.   CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
  249.   CodeMirror.commands.clearSearch = clearSearch;
  250.   CodeMirror.commands.replace = replace;
  251.   CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
  252. });

Paste is for source code and general debugging text.

Login or Register to edit, delete and keep track of your pastes and more.

Raw Paste

Login or Register to edit or fork this paste. It's free.