1 // Highlighting text that matches the selection
3 // Defines an option highlightSelectionMatches, which, when enabled,
4 // will style strings that match the selection throughout the
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.
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
22 })(function(CodeMirror
) {
25 var DEFAULT_MIN_CHARS
= 2;
26 var DEFAULT_TOKEN_STYLE
= "matchhighlight";
27 var DEFAULT_DELAY
= 100;
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
;
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;
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
);
51 cm
.state
.matchHighlighter
= new State(val
);
53 cm
.on("cursorActivity", cursorActivity
);
57 function cursorActivity(cm
) {
58 var state
= cm
.state
.matchHighlighter
;
59 clearTimeout(state
.timeout
);
60 state
.timeout
= setTimeout(function() {highlightMatches(cm
);}, state
.delay
);
63 function highlightMatches(cm
) {
64 cm
.operation(function() {
65 var state
= cm
.state
.matchHighlighter
;
67 cm
.removeOverlay(state
.overlay
);
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
;
76 cm
.addOverlay(state
.overlay
= makeOverlay(line
.slice(start
, end
), re
, state
.style
));
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
));
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
)));
91 function makeOverlay(query
, hasBoundary
, style
) {
92 return {token: function(stream
) {
93 if (stream
.match(query
) &&
94 (!hasBoundary
|| boundariesAround(stream
, hasBoundary
)))
97 stream
.skipTo(query
.charAt(0)) || stream
.skipToEnd();