1/* DokuWiki MoaiEditor Matches.js file 2 Version : 0.5 (May 5, 2026) 3 Author : MoaiTools <info@moaitools.org> 4 License : GPL 3 (http://www.gnu.org/licenses/gpl.html) */ 5 6MoaiEditor.Matches = class { 7 8 constructor(outer) { 9 10 // Handles 11 12 // Settings 13 this.settings = {show:{scrollpoints:false}}; 14 15 // Variables 16 this.matches = []; // array of {type, id, handle, startline, endline, syntax, scroll:{top,bottom}} 17 this.newMatches = []; // newly discovered matches after a preview update (equal to 'this.matches' if partial preview is off) 18 this.startline = 0; // First starting line to be parsed after the preview response has been received 19 20 // Objects 21 this.matcher = new MoaiEditor.Matcher(this); 22 } 23 // ┌───────────────────────────────────┐ 24 // │ Public │ 25 // └───────────────────────────────────┘ 26 27 add (type, handle, startline, endline, syntax=null) { 28 const match = { 29 type : type, 30 handle : handle, 31 id : handle.id, 32 startline : this.startline + startline, 33 endline : this.startline + endline, 34 syntax : syntax, 35 scroll : null, 36 }; 37 this.matches.push(match); // Global list of matches 38 this.newMatches.push(match); // Only new matches 39 } 40 // ──────────────────────────────────── 41 update(top, bottom) { 42 this.start = Date.now(); 43 44 // Get current editor 45 const editor = moaiEditor.editor.current; 46 47 // Get the starting and ending line 48 var startline = 0; 49 if (top !== null) 50 startline = top.endline + 1; 51 this.startline = startline; 52 var endline = editor.watcher.lines.length-1; 53 if (bottom !== null) 54 endline = bottom.startline-1; 55 56 // Remove matches in the changed area 57 this.removeMatches(startline, endline, true); 58 59 // Run matcher 60 this.newMatches = []; 61 this.matcher.onAjax (top, bottom, startline, endline); 62 63 // Sort matches by start line 64 this.matches.sort((a, b) => (a.startline > b.startline) ? 1 : -1); 65 66 // Let the editor process the newly found matches 67 var start = Date.now(); 68 editor.onAjax(this.newMatches); 69 70 // Calc scroll points of new matches 71 for (let match of this.newMatches) { 72 const top = editor.getLineRect(match.startline).top; 73 const bottom = editor.getLineRect(match.endline).bottom; 74 match.scroll = {top:top, bottom:bottom}; 75 } 76 // Show scrollpoints (for debug) 77 this.showScrollPoints('onAjax'); 78 79 const elapsed = Date.now() - start; 80 this.measureFrameTime(" MATCHES.ONAJAX (FRAME TIME)"); 81 } 82 // ──────────────────────────────────── 83 onTextChanged(change) { 84 85 // Get current editor 86 const editor = moaiEditor.editor.current; 87 88 // Remove matches in the changed area 89 var keep = []; 90 const start = change.num.keepfirst; 91 const end = change.index.keeplast-1; 92 this.removeMatches(start, end); 93 94 // Shift the line-numbers and scroll-points of the matches (below the changed zone) 95 var shift = null; 96 for (let match of this.matches) 97 if (match.startline >= change.index.keeplast) { 98 // Shift line numbers 99 match.startline += change.shift; 100 match.endline += change.shift; 101 // Shift the scroll positions 102 if (match.scroll !== null) { 103 if (shift === null) 104 shift = editor.getLineRect(match.startline).top - match.scroll.top; 105 match.scroll.top += shift; 106 match.scroll.bottom += shift; 107 } 108 } 109 // Show scrollpoints (for debug) 110 this.showScrollPoints('onTextChanged'); 111 } 112 // ──────────────────────────────────── 113 removeMatches(startline, endline, editor=false) { 114 var keep = []; 115 for (let match of this.matches) 116 if (!this.collides(match, startline, endline)) 117 keep.push(match); 118 else if (editor) 119 moaiEditor.editor.current.removeMatches(match.startline, match.endline); 120 this.matches = keep; 121 } 122 // ──────────────────────────────────── 123 collides(match, start, end) { 124 if (match.startline >= start && match.startline <= end) 125 return true; 126 if (match.endline >= start && match.endline <= end) 127 return true; 128 if (start >= match.startline && start <= match.endline) 129 return true; 130 if (end >= match.startline && end <= match.endline) 131 return true; 132 return false; 133 } 134 // ──────────────────────────────────── 135 find(handle) { 136 for (let match of this.matches) 137 if (match.handle == handle) 138 return match; 139 return null; 140 } 141 // ──────────────────────────────────── 142 recalcScrollPoints(range=null) { 143 /* @range can be: 144 * null : to recalculate all scroll points (called when the editor size or font changes) 145 * {from, to} : to recalculate scroll points in a range of lines (called when CodeMirror viewport changes) 146 */ 147 this.start = Date.now(); 148 const editor = moaiEditor.editor.current; 149 for (let match of this.matches) { 150 if ( range !== null && !this.collides(match, range.from, range.to) ) 151 continue; 152 const top = editor.getLineRect(match.startline).top; 153 const bottom = editor.getLineRect(match.endline).bottom; 154 match.scroll = {top:top, bottom:bottom}; 155 } 156 } 157 // ──────────────────────────────────── 158 showScrollPoints(caller) { 159 if (!this.settings.show.scrollpoints) 160 return; 161 const container = document.querySelector("#moaied__scrollpoints_overlay"); 162 container.style.display = 'block'; 163 container.innerHTML = ''; 164 const editor = moaiEditor.editor.current; 165 for (let match of this.matches) { 166 const top = editor.getLineRect(match.startline).top; 167 const bottom = editor.getLineRect(match.endline).bottom; 168 var element = moaiEditor.createHTML('<div class="moaied-scrollpoint"></div>'); 169 element.style.top = match.scroll.top+'px'; 170 element.style.height = (match.scroll.bottom - match.scroll.top)+'px'; 171 container.appendChild(element); 172 } 173 } 174 // ──────────────────────────────────── 175 measureFrameTime (text) { 176 requestAnimationFrame(() => { 177 const elapsed = Date.now()-this.start; 178 }); 179 } 180}; // End Class 181 182 183 184 185 186 187