Mercurial
comparison .cms/lib/codemirror/addon/search/match-highlighter.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78edf6b517a0 |
---|---|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others | |
2 // Distributed under an MIT license: https://codemirror.net/5/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. If annotateScrollbar is enabled, the occurrences | |
20 // will be highlighted on the scrollbar via the matchesonscrollbar addon. | |
21 | |
22 (function(mod) { | |
23 if (typeof exports == "object" && typeof module == "object") // CommonJS | |
24 mod(require("../../lib/codemirror"), require("./matchesonscrollbar")); | |
25 else if (typeof define == "function" && define.amd) // AMD | |
26 define(["../../lib/codemirror", "./matchesonscrollbar"], mod); | |
27 else // Plain browser env | |
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((/\w/.test(query.charAt(0)) ? "\\b" : "") + | |
94 query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") + | |
95 (/\w/.test(query.charAt(query.length - 1)) ? "\\b" : "")) : query; | |
96 state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false, | |
97 {className: "CodeMirror-selection-highlight-scrollbar"}); | |
98 } | |
99 } | |
100 | |
101 function removeOverlay(cm) { | |
102 var state = cm.state.matchHighlighter; | |
103 if (state.overlay) { | |
104 cm.removeOverlay(state.overlay); | |
105 state.overlay = null; | |
106 if (state.matchesonscroll) { | |
107 state.matchesonscroll.clear(); | |
108 state.matchesonscroll = null; | |
109 } | |
110 } | |
111 } | |
112 | |
113 function highlightMatches(cm) { | |
114 cm.operation(function() { | |
115 var state = cm.state.matchHighlighter; | |
116 removeOverlay(cm); | |
117 if (!cm.somethingSelected() && state.options.showToken) { | |
118 var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken; | |
119 var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start; | |
120 while (start && re.test(line.charAt(start - 1))) --start; | |
121 while (end < line.length && re.test(line.charAt(end))) ++end; | |
122 if (start < end) | |
123 addOverlay(cm, line.slice(start, end), re, state.options.style); | |
124 return; | |
125 } | |
126 var from = cm.getCursor("from"), to = cm.getCursor("to"); | |
127 if (from.line != to.line) return; | |
128 if (state.options.wordsOnly && !isWord(cm, from, to)) return; | |
129 var selection = cm.getRange(from, to) | |
130 if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "") | |
131 if (selection.length >= state.options.minChars) | |
132 addOverlay(cm, selection, false, state.options.style); | |
133 }); | |
134 } | |
135 | |
136 function isWord(cm, from, to) { | |
137 var str = cm.getRange(from, to); | |
138 if (str.match(/^\w+$/) !== null) { | |
139 if (from.ch > 0) { | |
140 var pos = {line: from.line, ch: from.ch - 1}; | |
141 var chr = cm.getRange(pos, from); | |
142 if (chr.match(/\W/) === null) return false; | |
143 } | |
144 if (to.ch < cm.getLine(from.line).length) { | |
145 var pos = {line: to.line, ch: to.ch + 1}; | |
146 var chr = cm.getRange(to, pos); | |
147 if (chr.match(/\W/) === null) return false; | |
148 } | |
149 return true; | |
150 } else return false; | |
151 } | |
152 | |
153 function boundariesAround(stream, re) { | |
154 return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) && | |
155 (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos))); | |
156 } | |
157 | |
158 function makeOverlay(query, hasBoundary, style) { | |
159 return {token: function(stream) { | |
160 if (stream.match(query) && | |
161 (!hasBoundary || boundariesAround(stream, hasBoundary))) | |
162 return style; | |
163 stream.next(); | |
164 stream.skipTo(query.charAt(0)) || stream.skipToEnd(); | |
165 }}; | |
166 } | |
167 }); |