3df69859849c99883d3a704027fc63d55947c9c1
[myslice.git] / third-party / codemirror-3.15 / addon / search / match-highlighter.js
1 // Highlighting text that matches the selection
2 //
3 // Defines an option highlightSelectionMatches, which, when enabled,
4 // will style strings that match the selection throughout the
5 // document.
6 //
7 // The option can be set to true to simply enable it, or to a
8 // {minChars, style, showToken} object to explicitly configure it.
9 // minChars is the minimum amount of characters that should be
10 // selected for the behavior to occur, and style is the token style to
11 // apply to the matches. This will be prefixed by "cm-" to create an
12 // actual CSS class name. showToken, when enabled, will cause the
13 // current token to be highlighted when nothing is selected.
14
15 (function() {
16   var DEFAULT_MIN_CHARS = 2;
17   var DEFAULT_TOKEN_STYLE = "matchhighlight";
18
19   function State(options) {
20     if (typeof options == "object") {
21       this.minChars = options.minChars;
22       this.style = options.style;
23       this.showToken = options.showToken;
24     }
25     if (this.style == null) this.style = DEFAULT_TOKEN_STYLE;
26     if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS;
27     this.overlay = this.timeout = null;
28   }
29
30   CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
31     if (old && old != CodeMirror.Init) {
32       var over = cm.state.matchHighlighter.overlay;
33       if (over) cm.removeOverlay(over);
34       clearTimeout(cm.state.matchHighlighter.timeout);
35       cm.state.matchHighlighter = null;
36       cm.off("cursorActivity", cursorActivity);
37     }
38     if (val) {
39       cm.state.matchHighlighter = new State(val);
40       highlightMatches(cm);
41       cm.on("cursorActivity", cursorActivity);
42     }
43   });
44
45   function cursorActivity(cm) {
46     var state = cm.state.matchHighlighter;
47     clearTimeout(state.timeout);
48     state.timeout = setTimeout(function() {highlightMatches(cm);}, 100);
49   }
50
51   function highlightMatches(cm) {
52     cm.operation(function() {
53       var state = cm.state.matchHighlighter;
54       if (state.overlay) {
55         cm.removeOverlay(state.overlay);
56         state.overlay = null;
57       }
58       if (!cm.somethingSelected() && state.showToken) {
59         var re = state.showToken === true ? /[\w$]/ : state.showToken;
60         var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
61         while (start && re.test(line.charAt(start - 1))) --start;
62         while (end < line.length && re.test(line.charAt(end))) ++end;
63         if (start < end)
64           cm.addOverlay(state.overlay = makeOverlay(line.slice(start, end), re, state.style));
65         return;
66       }
67       if (cm.getCursor("head").line != cm.getCursor("anchor").line) return;
68       var selection = cm.getSelection().replace(/^\s+|\s+$/g, "");
69       if (selection.length >= state.minChars)
70         cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style));
71     });
72   }
73
74   function boundariesAround(stream, re) {
75     return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
76       (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
77   }
78
79   function makeOverlay(query, hasBoundary, style) {
80     return {token: function(stream) {
81       if (stream.match(query) &&
82           (!hasBoundary || boundariesAround(stream, hasBoundary)))
83         return style;
84       stream.next();
85       stream.skipTo(query.charAt(0)) || stream.skipToEnd();
86     }};
87   }
88 })();