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

     1  // CodeMirror, copyright (c) by Marijn Haverbeke and others
     2  // Distributed under an MIT license: http://codemirror.net/LICENSE
     3  
     4  // Highlighting text that matches the selection
     5  //
     6  // Defines an option highlightSelectionMatches, which, when enabled,
     7  // will style strings that match the selection throughout the
     8  // document.
     9  //
    10  // The option can be set to true to simply enable it, or to a
    11  // {minChars, style, wordsOnly, showToken, delay} object to explicitly
    12  // configure it. minChars is the minimum amount of characters that should be
    13  // selected for the behavior to occur, and style is the token style to
    14  // apply to the matches. This will be prefixed by "cm-" to create an
    15  // actual CSS class name. If wordsOnly is enabled, the matches will be
    16  // highlighted only if the selected text is a word. showToken, when enabled,
    17  // will cause the current token to be highlighted when nothing is selected.
    18  // delay is used to specify how much time to wait, in milliseconds, before
    19  // highlighting the matches.
    20  
    21  (function(mod) {
    22    if (typeof exports == "object" && typeof module == "object") // CommonJS
    23      mod(require("../../lib/codemirror"));
    24    else if (typeof define == "function" && define.amd) // AMD
    25      define(["../../lib/codemirror"], mod);
    26    else // Plain browser env
    27      mod(CodeMirror);
    28  })(function(CodeMirror) {
    29    "use strict";
    30  
    31    var DEFAULT_MIN_CHARS = 2;
    32    var DEFAULT_TOKEN_STYLE = "matchhighlight";
    33    var DEFAULT_DELAY = 100;
    34    var DEFAULT_WORDS_ONLY = false;
    35  
    36    function State(options) {
    37      if (typeof options == "object") {
    38        this.minChars = options.minChars;
    39        this.style = options.style;
    40        this.showToken = options.showToken;
    41        this.delay = options.delay;
    42        this.wordsOnly = options.wordsOnly;
    43      }
    44      if (this.style == null) this.style = DEFAULT_TOKEN_STYLE;
    45      if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS;
    46      if (this.delay == null) this.delay = DEFAULT_DELAY;
    47      if (this.wordsOnly == null) this.wordsOnly = DEFAULT_WORDS_ONLY;
    48      this.overlay = this.timeout = null;
    49    }
    50  
    51    CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
    52      if (old && old != CodeMirror.Init) {
    53        var over = cm.state.matchHighlighter.overlay;
    54        if (over) cm.removeOverlay(over);
    55        clearTimeout(cm.state.matchHighlighter.timeout);
    56        cm.state.matchHighlighter = null;
    57        cm.off("cursorActivity", cursorActivity);
    58      }
    59      if (val) {
    60        cm.state.matchHighlighter = new State(val);
    61        highlightMatches(cm);
    62        cm.on("cursorActivity", cursorActivity);
    63      }
    64    });
    65  
    66    function cursorActivity(cm) {
    67      var state = cm.state.matchHighlighter;
    68      clearTimeout(state.timeout);
    69      state.timeout = setTimeout(function() {highlightMatches(cm);}, state.delay);
    70    }
    71  
    72    function highlightMatches(cm) {
    73      cm.operation(function() {
    74        var state = cm.state.matchHighlighter;
    75        if (state.overlay) {
    76          cm.removeOverlay(state.overlay);
    77          state.overlay = null;
    78        }
    79        if (!cm.somethingSelected() && state.showToken) {
    80          var re = state.showToken === true ? /[\w$]/ : state.showToken;
    81          var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
    82          while (start && re.test(line.charAt(start - 1))) --start;
    83          while (end < line.length && re.test(line.charAt(end))) ++end;
    84          if (start < end)
    85            cm.addOverlay(state.overlay = makeOverlay(line.slice(start, end), re, state.style));
    86          return;
    87        }
    88        var from = cm.getCursor("from"), to = cm.getCursor("to");
    89        if (from.line != to.line) return;
    90        if (state.wordsOnly && !isWord(cm, from, to)) return;
    91        var selection = cm.getRange(from, to).replace(/^\s+|\s+$/g, "");
    92        if (selection.length >= state.minChars)
    93          cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style));
    94      });
    95    }
    96  
    97    function isWord(cm, from, to) {
    98      var str = cm.getRange(from, to);
    99      if (str.match(/^\w+$/) !== null) {
   100          if (from.ch > 0) {
   101              var pos = {line: from.line, ch: from.ch - 1};
   102              var chr = cm.getRange(pos, from);
   103              if (chr.match(/\W/) === null) return false;
   104          }
   105          if (to.ch < cm.getLine(from.line).length) {
   106              var pos = {line: to.line, ch: to.ch + 1};
   107              var chr = cm.getRange(to, pos);
   108              if (chr.match(/\W/) === null) return false;
   109          }
   110          return true;
   111      } else return false;
   112    }
   113  
   114    function boundariesAround(stream, re) {
   115      return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
   116        (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
   117    }
   118  
   119    function makeOverlay(query, hasBoundary, style) {
   120      return {token: function(stream) {
   121        if (stream.match(query) &&
   122            (!hasBoundary || boundariesAround(stream, hasBoundary)))
   123          return style;
   124        stream.next();
   125        stream.skipTo(query.charAt(0)) || stream.skipToEnd();
   126      }};
   127    }
   128  });