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 });