1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | (function(mod) {
|
23 | if (typeof exports == "object" && typeof module == "object")
|
24 | mod(require("../../lib/codemirror"), require("./matchesonscrollbar"));
|
25 | else if (typeof define == "function" && define.amd)
|
26 | define(["../../lib/codemirror", "./matchesonscrollbar"], mod);
|
27 | else
|
28 | mod(CodeMirror);
|
29 | })(function(CodeMirror) {
|
30 | "use strict";
|
31 |
|
32 | var defaults = {
|
33 | style: "matchhighlight",
|
34 | minChars: 2,
|
35 | delay: 100,
|
36 | wordsOnly: false,
|
37 | annotateScrollbar: false,
|
38 | showToken: false,
|
39 | trim: true
|
40 | }
|
41 |
|
42 | function State(options) {
|
43 | this.options = {}
|
44 | for (var name in defaults)
|
45 | this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name]
|
46 | this.overlay = this.timeout = null;
|
47 | this.matchesonscroll = null;
|
48 | this.active = false;
|
49 | }
|
50 |
|
51 | CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
|
52 | if (old && old != CodeMirror.Init) {
|
53 | removeOverlay(cm);
|
54 | clearTimeout(cm.state.matchHighlighter.timeout);
|
55 | cm.state.matchHighlighter = null;
|
56 | cm.off("cursorActivity", cursorActivity);
|
57 | cm.off("focus", onFocus)
|
58 | }
|
59 | if (val) {
|
60 | var state = cm.state.matchHighlighter = new State(val);
|
61 | if (cm.hasFocus()) {
|
62 | state.active = true
|
63 | highlightMatches(cm)
|
64 | } else {
|
65 | cm.on("focus", onFocus)
|
66 | }
|
67 | cm.on("cursorActivity", cursorActivity);
|
68 | }
|
69 | });
|
70 |
|
71 | function cursorActivity(cm) {
|
72 | var state = cm.state.matchHighlighter;
|
73 | if (state.active || cm.hasFocus()) scheduleHighlight(cm, state)
|
74 | }
|
75 |
|
76 | function onFocus(cm) {
|
77 | var state = cm.state.matchHighlighter
|
78 | if (!state.active) {
|
79 | state.active = true
|
80 | scheduleHighlight(cm, state)
|
81 | }
|
82 | }
|
83 |
|
84 | function scheduleHighlight(cm, state) {
|
85 | clearTimeout(state.timeout);
|
86 | state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay);
|
87 | }
|
88 |
|
89 | function addOverlay(cm, query, hasBoundary, style) {
|
90 | var state = cm.state.matchHighlighter;
|
91 | cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
|
92 | if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
|
93 | var searchFor = hasBoundary ? new RegExp("\\b" + query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") + "\\b") : query;
|
94 | state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
|
95 | {className: "CodeMirror-selection-highlight-scrollbar"});
|
96 | }
|
97 | }
|
98 |
|
99 | function removeOverlay(cm) {
|
100 | var state = cm.state.matchHighlighter;
|
101 | if (state.overlay) {
|
102 | cm.removeOverlay(state.overlay);
|
103 | state.overlay = null;
|
104 | if (state.matchesonscroll) {
|
105 | state.matchesonscroll.clear();
|
106 | state.matchesonscroll = null;
|
107 | }
|
108 | }
|
109 | }
|
110 |
|
111 | function highlightMatches(cm) {
|
112 | cm.operation(function() {
|
113 | var state = cm.state.matchHighlighter;
|
114 | removeOverlay(cm);
|
115 | if (!cm.somethingSelected() && state.options.showToken) {
|
116 | var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
|
117 | var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
|
118 | while (start && re.test(line.charAt(start - 1))) --start;
|
119 | while (end < line.length && re.test(line.charAt(end))) ++end;
|
120 | if (start < end)
|
121 | addOverlay(cm, line.slice(start, end), re, state.options.style);
|
122 | return;
|
123 | }
|
124 | var from = cm.getCursor("from"), to = cm.getCursor("to");
|
125 | if (from.line != to.line) return;
|
126 | if (state.options.wordsOnly && !isWord(cm, from, to)) return;
|
127 | var selection = cm.getRange(from, to)
|
128 | if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "")
|
129 | if (selection.length >= state.options.minChars)
|
130 | addOverlay(cm, selection, false, state.options.style);
|
131 | });
|
132 | }
|
133 |
|
134 | function isWord(cm, from, to) {
|
135 | var str = cm.getRange(from, to);
|
136 | if (str.match(/^\w+$/) !== null) {
|
137 | if (from.ch > 0) {
|
138 | var pos = {line: from.line, ch: from.ch - 1};
|
139 | var chr = cm.getRange(pos, from);
|
140 | if (chr.match(/\W/) === null) return false;
|
141 | }
|
142 | if (to.ch < cm.getLine(from.line).length) {
|
143 | var pos = {line: to.line, ch: to.ch + 1};
|
144 | var chr = cm.getRange(to, pos);
|
145 | if (chr.match(/\W/) === null) return false;
|
146 | }
|
147 | return true;
|
148 | } else return false;
|
149 | }
|
150 |
|
151 | function boundariesAround(stream, re) {
|
152 | return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
|
153 | (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
|
154 | }
|
155 |
|
156 | function makeOverlay(query, hasBoundary, style) {
|
157 | return {token: function(stream) {
|
158 | if (stream.match(query) &&
|
159 | (!hasBoundary || boundariesAround(stream, hasBoundary)))
|
160 | return style;
|
161 | stream.next();
|
162 | stream.skipTo(query.charAt(0)) || stream.skipToEnd();
|
163 | }};
|
164 | }
|
165 | });
|