thunderbird-patch-review

Easy-to-use patch review interface for Thunderbird

Contribute: ~marcc/thunderbird-review-plugin@lists.sr.ht

git clone git://mccd.space/thunderbird-patch-review

intraline.js (2653B)

      1 // Intra-line ("word level") diff highlighting: within a replacement block —
      2 // a run of deleted lines followed by a run of added lines — pair the lines
      3 // index-wise and mark the span that actually changed, computed as everything
      4 // between the common prefix and common suffix of the pair.
      5 
      6 /**
      7  * Changed spans for one del/add line pair, as ranges over the line text
      8  * (without the +/- prefix). Returns null when the lines share too little
      9  * (under ~25% of the longer line) for a partial highlight to mean anything.
     10  * A side whose range is empty (pure insertion/removal) should not be
     11  * highlighted; computeIntraline already filters those out.
     12  */
     13 export function pairRanges(delText, addText) {
     14   const max = Math.min(delText.length, addText.length);
     15   let prefix = 0;
     16   while (prefix < max && delText[prefix] === addText[prefix]) {
     17     prefix++;
     18   }
     19   let suffix = 0;
     20   while (
     21     suffix < max - prefix &&
     22     delText[delText.length - 1 - suffix] === addText[addText.length - 1 - suffix]
     23   ) {
     24     suffix++;
     25   }
     26   if ((prefix + suffix) * 4 < Math.max(delText.length, addText.length)) {
     27     return null;
     28   }
     29   return {
     30     del: [prefix, delText.length - suffix],
     31     add: [prefix, addText.length - suffix],
     32   };
     33 }
     34 
     35 const isNoNewlineMarker = (line) => line.origin === "ctx" && line.raw.startsWith("\\");
     36 
     37 /**
     38  * @param {Array<{origin: string, text: string, raw: string}>} lines hunk lines
     39  * @returns {Array<{start: number, end: number}|null>} per-line changed range
     40  *          over line.text, null where nothing (or everything) changed
     41  */
     42 export function computeIntraline(lines) {
     43   const result = new Array(lines.length).fill(null);
     44 
     45   let i = 0;
     46   while (i < lines.length) {
     47     if (lines[i].origin !== "del") {
     48       i++;
     49       continue;
     50     }
     51     const dels = [];
     52     while (i < lines.length && lines[i].origin === "del") {
     53       dels.push(i);
     54       i++;
     55     }
     56     // A "\ No newline at end of file" marker may sit between the runs.
     57     let j = i;
     58     while (j < lines.length && isNoNewlineMarker(lines[j])) {
     59       j++;
     60     }
     61     const adds = [];
     62     while (j < lines.length && lines[j].origin === "add") {
     63       adds.push(j);
     64       j++;
     65     }
     66 
     67     const pairs = Math.min(dels.length, adds.length);
     68     for (let k = 0; k < pairs; k++) {
     69       const ranges = pairRanges(lines[dels[k]].text, lines[adds[k]].text);
     70       if (!ranges) {
     71         continue;
     72       }
     73       if (ranges.del[1] > ranges.del[0]) {
     74         result[dels[k]] = { start: ranges.del[0], end: ranges.del[1] };
     75       }
     76       if (ranges.add[1] > ranges.add[0]) {
     77         result[adds[k]] = { start: ranges.add[0], end: ranges.add[1] };
     78       }
     79     }
     80   }
     81 
     82   return result;
     83 }