1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others |
---|
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE |
---|
3 | |
---|
4 | (function(mod) { |
---|
5 | if (typeof exports == "object" && typeof module == "object") // CommonJS |
---|
6 | mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar")); |
---|
7 | else if (typeof define == "function" && define.amd) // AMD |
---|
8 | define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod); |
---|
9 | else // Plain browser env |
---|
10 | mod(CodeMirror); |
---|
11 | })(function(CodeMirror) { |
---|
12 | "use strict"; |
---|
13 | |
---|
14 | CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) { |
---|
15 | if (typeof options == "string") options = {className: options}; |
---|
16 | if (!options) options = {}; |
---|
17 | return new SearchAnnotation(this, query, caseFold, options); |
---|
18 | }); |
---|
19 | |
---|
20 | function SearchAnnotation(cm, query, caseFold, options) { |
---|
21 | this.cm = cm; |
---|
22 | var annotateOptions = {listenForChanges: false}; |
---|
23 | for (var prop in options) annotateOptions[prop] = options[prop]; |
---|
24 | if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match"; |
---|
25 | this.annotation = cm.annotateScrollbar(annotateOptions); |
---|
26 | this.query = query; |
---|
27 | this.caseFold = caseFold; |
---|
28 | this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1}; |
---|
29 | this.matches = []; |
---|
30 | this.update = null; |
---|
31 | |
---|
32 | this.findMatches(); |
---|
33 | this.annotation.update(this.matches); |
---|
34 | |
---|
35 | var self = this; |
---|
36 | cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); }); |
---|
37 | } |
---|
38 | |
---|
39 | var MAX_MATCHES = 1000; |
---|
40 | |
---|
41 | SearchAnnotation.prototype.findMatches = function() { |
---|
42 | if (!this.gap) return; |
---|
43 | for (var i = 0; i < this.matches.length; i++) { |
---|
44 | var match = this.matches[i]; |
---|
45 | if (match.from.line >= this.gap.to) break; |
---|
46 | if (match.to.line >= this.gap.from) this.matches.splice(i--, 1); |
---|
47 | } |
---|
48 | var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), this.caseFold); |
---|
49 | while (cursor.findNext()) { |
---|
50 | var match = {from: cursor.from(), to: cursor.to()}; |
---|
51 | if (match.from.line >= this.gap.to) break; |
---|
52 | this.matches.splice(i++, 0, match); |
---|
53 | if (this.matches.length > MAX_MATCHES) break; |
---|
54 | } |
---|
55 | this.gap = null; |
---|
56 | }; |
---|
57 | |
---|
58 | function offsetLine(line, changeStart, sizeChange) { |
---|
59 | if (line <= changeStart) return line; |
---|
60 | return Math.max(changeStart, line + sizeChange); |
---|
61 | } |
---|
62 | |
---|
63 | SearchAnnotation.prototype.onChange = function(change) { |
---|
64 | var startLine = change.from.line; |
---|
65 | var endLine = CodeMirror.changeEnd(change).line; |
---|
66 | var sizeChange = endLine - change.to.line; |
---|
67 | if (this.gap) { |
---|
68 | this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line); |
---|
69 | this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line); |
---|
70 | } else { |
---|
71 | this.gap = {from: change.from.line, to: endLine + 1}; |
---|
72 | } |
---|
73 | |
---|
74 | if (sizeChange) for (var i = 0; i < this.matches.length; i++) { |
---|
75 | var match = this.matches[i]; |
---|
76 | var newFrom = offsetLine(match.from.line, startLine, sizeChange); |
---|
77 | if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch); |
---|
78 | var newTo = offsetLine(match.to.line, startLine, sizeChange); |
---|
79 | if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch); |
---|
80 | } |
---|
81 | clearTimeout(this.update); |
---|
82 | var self = this; |
---|
83 | this.update = setTimeout(function() { self.updateAfterChange(); }, 250); |
---|
84 | }; |
---|
85 | |
---|
86 | SearchAnnotation.prototype.updateAfterChange = function() { |
---|
87 | this.findMatches(); |
---|
88 | this.annotation.update(this.matches); |
---|
89 | }; |
---|
90 | |
---|
91 | SearchAnnotation.prototype.clear = function() { |
---|
92 | this.cm.off("change", this.changeHandler); |
---|
93 | this.annotation.clear(); |
---|
94 | }; |
---|
95 | }); |
---|