|
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(mod) { |
|
16 if (typeof exports == "object" && typeof module == "object") // CommonJS |
|
17 mod(require("../../lib/codemirror")); |
|
18 else if (typeof define == "function" && define.amd) // AMD |
|
19 define(["../../lib/codemirror"], mod); |
|
20 else // Plain browser env |
|
21 mod(CodeMirror); |
|
22 })(function(CodeMirror) { |
|
23 "use strict"; |
|
24 |
|
25 var DEFAULT_MIN_CHARS = 2; |
|
26 var DEFAULT_TOKEN_STYLE = "matchhighlight"; |
|
27 var DEFAULT_DELAY = 100; |
|
28 |
|
29 function State(options) { |
|
30 if (typeof options == "object") { |
|
31 this.minChars = options.minChars; |
|
32 this.style = options.style; |
|
33 this.showToken = options.showToken; |
|
34 this.delay = options.delay; |
|
35 } |
|
36 if (this.style == null) this.style = DEFAULT_TOKEN_STYLE; |
|
37 if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS; |
|
38 if (this.delay == null) this.delay = DEFAULT_DELAY; |
|
39 this.overlay = this.timeout = null; |
|
40 } |
|
41 |
|
42 CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) { |
|
43 if (old && old != CodeMirror.Init) { |
|
44 var over = cm.state.matchHighlighter.overlay; |
|
45 if (over) cm.removeOverlay(over); |
|
46 clearTimeout(cm.state.matchHighlighter.timeout); |
|
47 cm.state.matchHighlighter = null; |
|
48 cm.off("cursorActivity", cursorActivity); |
|
49 } |
|
50 if (val) { |
|
51 cm.state.matchHighlighter = new State(val); |
|
52 highlightMatches(cm); |
|
53 cm.on("cursorActivity", cursorActivity); |
|
54 } |
|
55 }); |
|
56 |
|
57 function cursorActivity(cm) { |
|
58 var state = cm.state.matchHighlighter; |
|
59 clearTimeout(state.timeout); |
|
60 state.timeout = setTimeout(function() {highlightMatches(cm);}, state.delay); |
|
61 } |
|
62 |
|
63 function highlightMatches(cm) { |
|
64 cm.operation(function() { |
|
65 var state = cm.state.matchHighlighter; |
|
66 if (state.overlay) { |
|
67 cm.removeOverlay(state.overlay); |
|
68 state.overlay = null; |
|
69 } |
|
70 if (!cm.somethingSelected() && state.showToken) { |
|
71 var re = state.showToken === true ? /[\w$]/ : state.showToken; |
|
72 var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start; |
|
73 while (start && re.test(line.charAt(start - 1))) --start; |
|
74 while (end < line.length && re.test(line.charAt(end))) ++end; |
|
75 if (start < end) |
|
76 cm.addOverlay(state.overlay = makeOverlay(line.slice(start, end), re, state.style)); |
|
77 return; |
|
78 } |
|
79 if (cm.getCursor("head").line != cm.getCursor("anchor").line) return; |
|
80 var selection = cm.getSelections()[0].replace(/^\s+|\s+$/g, ""); |
|
81 if (selection.length >= state.minChars) |
|
82 cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style)); |
|
83 }); |
|
84 } |
|
85 |
|
86 function boundariesAround(stream, re) { |
|
87 return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) && |
|
88 (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos))); |
|
89 } |
|
90 |
|
91 function makeOverlay(query, hasBoundary, style) { |
|
92 return {token: function(stream) { |
|
93 if (stream.match(query) && |
|
94 (!hasBoundary || boundariesAround(stream, hasBoundary))) |
|
95 return style; |
|
96 stream.next(); |
|
97 stream.skipTo(query.charAt(0)) || stream.skipToEnd(); |
|
98 }}; |
|
99 } |
|
100 }); |