| 
									
										
										
										
											2018-01-28 11:19:31 -05:00
										 |  |  | // CodeMirror, copyright (c) by Marijn Haverbeke and others
 | 
					
						
							| 
									
										
										
										
											2022-10-15 12:22:09 +02:00
										 |  |  | // Distributed under an MIT license: https://codemirror.net/5/LICENSE
 | 
					
						
							| 
									
										
										
										
											2018-01-28 11:19:31 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Highlighting text that matches the selection
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Defines an option highlightSelectionMatches, which, when enabled,
 | 
					
						
							|  |  |  | // will style strings that match the selection throughout the
 | 
					
						
							|  |  |  | // document.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // The option can be set to true to simply enable it, or to a
 | 
					
						
							|  |  |  | // {minChars, style, wordsOnly, showToken, delay} object to explicitly
 | 
					
						
							|  |  |  | // configure it. minChars is the minimum amount of characters that should be
 | 
					
						
							|  |  |  | // selected for the behavior to occur, and style is the token style to
 | 
					
						
							|  |  |  | // apply to the matches. This will be prefixed by "cm-" to create an
 | 
					
						
							|  |  |  | // actual CSS class name. If wordsOnly is enabled, the matches will be
 | 
					
						
							|  |  |  | // highlighted only if the selected text is a word. showToken, when enabled,
 | 
					
						
							|  |  |  | // will cause the current token to be highlighted when nothing is selected.
 | 
					
						
							|  |  |  | // delay is used to specify how much time to wait, in milliseconds, before
 | 
					
						
							| 
									
										
										
										
											2021-04-06 22:16:34 +02:00
										 |  |  | // highlighting the matches. If annotateScrollbar is enabled, the occurrences
 | 
					
						
							| 
									
										
										
										
											2018-01-28 11:19:31 -05:00
										 |  |  | // will be highlighted on the scrollbar via the matchesonscrollbar addon.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (function(mod) { | 
					
						
							|  |  |  |   if (typeof exports == "object" && typeof module == "object") // CommonJS
 | 
					
						
							|  |  |  |     mod(require("../../lib/codemirror"), require("./matchesonscrollbar")); | 
					
						
							|  |  |  |   else if (typeof define == "function" && define.amd) // AMD
 | 
					
						
							|  |  |  |     define(["../../lib/codemirror", "./matchesonscrollbar"], mod); | 
					
						
							|  |  |  |   else // Plain browser env
 | 
					
						
							|  |  |  |     mod(CodeMirror); | 
					
						
							|  |  |  | })(function(CodeMirror) { | 
					
						
							|  |  |  |   "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var defaults = { | 
					
						
							|  |  |  |     style: "matchhighlight", | 
					
						
							|  |  |  |     minChars: 2, | 
					
						
							|  |  |  |     delay: 100, | 
					
						
							|  |  |  |     wordsOnly: false, | 
					
						
							|  |  |  |     annotateScrollbar: false, | 
					
						
							|  |  |  |     showToken: false, | 
					
						
							|  |  |  |     trim: true | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function State(options) { | 
					
						
							|  |  |  |     this.options = {} | 
					
						
							|  |  |  |     for (var name in defaults) | 
					
						
							|  |  |  |       this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name] | 
					
						
							|  |  |  |     this.overlay = this.timeout = null; | 
					
						
							|  |  |  |     this.matchesonscroll = null; | 
					
						
							|  |  |  |     this.active = false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) { | 
					
						
							|  |  |  |     if (old && old != CodeMirror.Init) { | 
					
						
							|  |  |  |       removeOverlay(cm); | 
					
						
							|  |  |  |       clearTimeout(cm.state.matchHighlighter.timeout); | 
					
						
							|  |  |  |       cm.state.matchHighlighter = null; | 
					
						
							|  |  |  |       cm.off("cursorActivity", cursorActivity); | 
					
						
							|  |  |  |       cm.off("focus", onFocus) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (val) { | 
					
						
							|  |  |  |       var state = cm.state.matchHighlighter = new State(val); | 
					
						
							|  |  |  |       if (cm.hasFocus()) { | 
					
						
							|  |  |  |         state.active = true | 
					
						
							|  |  |  |         highlightMatches(cm) | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         cm.on("focus", onFocus) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       cm.on("cursorActivity", cursorActivity); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function cursorActivity(cm) { | 
					
						
							|  |  |  |     var state = cm.state.matchHighlighter; | 
					
						
							|  |  |  |     if (state.active || cm.hasFocus()) scheduleHighlight(cm, state) | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function onFocus(cm) { | 
					
						
							|  |  |  |     var state = cm.state.matchHighlighter | 
					
						
							|  |  |  |     if (!state.active) { | 
					
						
							|  |  |  |       state.active = true | 
					
						
							|  |  |  |       scheduleHighlight(cm, state) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function scheduleHighlight(cm, state) { | 
					
						
							|  |  |  |     clearTimeout(state.timeout); | 
					
						
							|  |  |  |     state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function addOverlay(cm, query, hasBoundary, style) { | 
					
						
							|  |  |  |     var state = cm.state.matchHighlighter; | 
					
						
							|  |  |  |     cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style)); | 
					
						
							|  |  |  |     if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) { | 
					
						
							| 
									
										
										
										
											2020-09-20 21:41:40 +02:00
										 |  |  |       var searchFor = hasBoundary ? new RegExp((/\w/.test(query.charAt(0)) ? "\\b" : "") + | 
					
						
							|  |  |  |                                                query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") + | 
					
						
							|  |  |  |                                                (/\w/.test(query.charAt(query.length - 1)) ? "\\b" : "")) : query; | 
					
						
							| 
									
										
										
										
											2018-01-28 11:19:31 -05:00
										 |  |  |       state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false, | 
					
						
							|  |  |  |         {className: "CodeMirror-selection-highlight-scrollbar"}); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function removeOverlay(cm) { | 
					
						
							|  |  |  |     var state = cm.state.matchHighlighter; | 
					
						
							|  |  |  |     if (state.overlay) { | 
					
						
							|  |  |  |       cm.removeOverlay(state.overlay); | 
					
						
							|  |  |  |       state.overlay = null; | 
					
						
							|  |  |  |       if (state.matchesonscroll) { | 
					
						
							|  |  |  |         state.matchesonscroll.clear(); | 
					
						
							|  |  |  |         state.matchesonscroll = null; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function highlightMatches(cm) { | 
					
						
							|  |  |  |     cm.operation(function() { | 
					
						
							|  |  |  |       var state = cm.state.matchHighlighter; | 
					
						
							|  |  |  |       removeOverlay(cm); | 
					
						
							|  |  |  |       if (!cm.somethingSelected() && state.options.showToken) { | 
					
						
							|  |  |  |         var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken; | 
					
						
							|  |  |  |         var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start; | 
					
						
							|  |  |  |         while (start && re.test(line.charAt(start - 1))) --start; | 
					
						
							|  |  |  |         while (end < line.length && re.test(line.charAt(end))) ++end; | 
					
						
							|  |  |  |         if (start < end) | 
					
						
							|  |  |  |           addOverlay(cm, line.slice(start, end), re, state.options.style); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       var from = cm.getCursor("from"), to = cm.getCursor("to"); | 
					
						
							|  |  |  |       if (from.line != to.line) return; | 
					
						
							|  |  |  |       if (state.options.wordsOnly && !isWord(cm, from, to)) return; | 
					
						
							|  |  |  |       var selection = cm.getRange(from, to) | 
					
						
							|  |  |  |       if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "") | 
					
						
							|  |  |  |       if (selection.length >= state.options.minChars) | 
					
						
							|  |  |  |         addOverlay(cm, selection, false, state.options.style); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function isWord(cm, from, to) { | 
					
						
							|  |  |  |     var str = cm.getRange(from, to); | 
					
						
							|  |  |  |     if (str.match(/^\w+$/) !== null) { | 
					
						
							|  |  |  |         if (from.ch > 0) { | 
					
						
							|  |  |  |             var pos = {line: from.line, ch: from.ch - 1}; | 
					
						
							|  |  |  |             var chr = cm.getRange(pos, from); | 
					
						
							|  |  |  |             if (chr.match(/\W/) === null) return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (to.ch < cm.getLine(from.line).length) { | 
					
						
							|  |  |  |             var pos = {line: to.line, ch: to.ch + 1}; | 
					
						
							|  |  |  |             var chr = cm.getRange(to, pos); | 
					
						
							|  |  |  |             if (chr.match(/\W/) === null) return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } else return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function boundariesAround(stream, re) { | 
					
						
							|  |  |  |     return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) && | 
					
						
							|  |  |  |       (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function makeOverlay(query, hasBoundary, style) { | 
					
						
							|  |  |  |     return {token: function(stream) { | 
					
						
							|  |  |  |       if (stream.match(query) && | 
					
						
							|  |  |  |           (!hasBoundary || boundariesAround(stream, hasBoundary))) | 
					
						
							|  |  |  |         return style; | 
					
						
							|  |  |  |       stream.next(); | 
					
						
							|  |  |  |       stream.skipTo(query.charAt(0)) || stream.skipToEnd(); | 
					
						
							|  |  |  |     }}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); |