/* DokuWiki MoaiEditor Matches.js file Version : 0.5 (May 5, 2026) Author : MoaiTools License : GPL 3 (http://www.gnu.org/licenses/gpl.html) */ MoaiEditor.Matches = class { constructor(outer) { // Handles // Settings this.settings = {show:{scrollpoints:false}}; // Variables this.matches = []; // array of {type, id, handle, startline, endline, syntax, scroll:{top,bottom}} this.newMatches = []; // newly discovered matches after a preview update (equal to 'this.matches' if partial preview is off) this.startline = 0; // First starting line to be parsed after the preview response has been received // Objects this.matcher = new MoaiEditor.Matcher(this); } // ┌───────────────────────────────────┐ // │ Public │ // └───────────────────────────────────┘ add (type, handle, startline, endline, syntax=null) { const match = { type : type, handle : handle, id : handle.id, startline : this.startline + startline, endline : this.startline + endline, syntax : syntax, scroll : null, }; this.matches.push(match); // Global list of matches this.newMatches.push(match); // Only new matches } // ──────────────────────────────────── update(top, bottom) { this.start = Date.now(); // Get current editor const editor = moaiEditor.editor.current; // Get the starting and ending line var startline = 0; if (top !== null) startline = top.endline + 1; this.startline = startline; var endline = editor.watcher.lines.length-1; if (bottom !== null) endline = bottom.startline-1; // Remove matches in the changed area this.removeMatches(startline, endline, true); // Run matcher this.newMatches = []; this.matcher.onAjax (top, bottom, startline, endline); // Sort matches by start line this.matches.sort((a, b) => (a.startline > b.startline) ? 1 : -1); // Let the editor process the newly found matches var start = Date.now(); editor.onAjax(this.newMatches); // Calc scroll points of new matches for (let match of this.newMatches) { const top = editor.getLineRect(match.startline).top; const bottom = editor.getLineRect(match.endline).bottom; match.scroll = {top:top, bottom:bottom}; } // Show scrollpoints (for debug) this.showScrollPoints('onAjax'); const elapsed = Date.now() - start; this.measureFrameTime("🭎 MATCHES.ONAJAX (FRAME TIME)"); } // ──────────────────────────────────── onTextChanged(change) { // Get current editor const editor = moaiEditor.editor.current; // Remove matches in the changed area var keep = []; const start = change.num.keepfirst; const end = change.index.keeplast-1; this.removeMatches(start, end); // Shift the line-numbers and scroll-points of the matches (below the changed zone) var shift = null; for (let match of this.matches) if (match.startline >= change.index.keeplast) { // Shift line numbers match.startline += change.shift; match.endline += change.shift; // Shift the scroll positions if (match.scroll !== null) { if (shift === null) shift = editor.getLineRect(match.startline).top - match.scroll.top; match.scroll.top += shift; match.scroll.bottom += shift; } } // Show scrollpoints (for debug) this.showScrollPoints('onTextChanged'); } // ──────────────────────────────────── removeMatches(startline, endline, editor=false) { var keep = []; for (let match of this.matches) if (!this.collides(match, startline, endline)) keep.push(match); else if (editor) moaiEditor.editor.current.removeMatches(match.startline, match.endline); this.matches = keep; } // ──────────────────────────────────── collides(match, start, end) { if (match.startline >= start && match.startline <= end) return true; if (match.endline >= start && match.endline <= end) return true; if (start >= match.startline && start <= match.endline) return true; if (end >= match.startline && end <= match.endline) return true; return false; } // ──────────────────────────────────── find(handle) { for (let match of this.matches) if (match.handle == handle) return match; return null; } // ──────────────────────────────────── recalcScrollPoints(range=null) { /* @range can be: * null : to recalculate all scroll points (called when the editor size or font changes) * {from, to} : to recalculate scroll points in a range of lines (called when CodeMirror viewport changes) */ this.start = Date.now(); const editor = moaiEditor.editor.current; for (let match of this.matches) { if ( range !== null && !this.collides(match, range.from, range.to) ) continue; const top = editor.getLineRect(match.startline).top; const bottom = editor.getLineRect(match.endline).bottom; match.scroll = {top:top, bottom:bottom}; } } // ──────────────────────────────────── showScrollPoints(caller) { if (!this.settings.show.scrollpoints) return; const container = document.querySelector("#moaied__scrollpoints_overlay"); container.style.display = 'block'; container.innerHTML = ''; const editor = moaiEditor.editor.current; for (let match of this.matches) { const top = editor.getLineRect(match.startline).top; const bottom = editor.getLineRect(match.endline).bottom; var element = moaiEditor.createHTML('
'); element.style.top = match.scroll.top+'px'; element.style.height = (match.scroll.bottom - match.scroll.top)+'px'; container.appendChild(element); } } // ──────────────────────────────────── measureFrameTime (text) { requestAnimationFrame(() => { const elapsed = Date.now()-this.start; }); } }; // End Class