Fossil

Check-in [cedcd3585b]
Login

Check-in [cedcd3585b]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Refactored tr.diffsplit to hold enough information to allow partial chunk loads in either direction and to know where the next/previous chunks (if any) start/end. Actual loading is currently disabled, pending addition of controls which make use of this new state.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | diff-js-refactoring
Files: files | file ages | folders
SHA3-256: cedcd3585b4cd8bd9997bca5de7e1f94541d7f5686f95715b0251f8a06a55376
User & Date: stephan 2021-09-09 15:06:55.082
Context
2021-09-09
18:28
Got jchunk loader buttons in place but they're currently non-functional. ... (check-in: 365ef58b8c user: stephan tags: diff-js-refactoring)
15:06
Refactored tr.diffsplit to hold enough information to allow partial chunk loads in either direction and to know where the next/previous chunks (if any) start/end. Actual loading is currently disabled, pending addition of controls which make use of this new state. ... (check-in: cedcd3585b user: stephan tags: diff-js-refactoring)
15:01
Fixed left/right arrow key scrolling in diff.js. ... (check-in: 566b7f1165 user: stephan tags: diff-js-refactoring)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/default.css.
547
548
549
550
551
552
553














554
555
556
557
558
559
560
}
tr.diffskip.jchunk:hover {
  /* jchunk gets added from JS to diffskip rows when they are
     plugged into the /jchunk route and removed after that data
     is fetched. */
  background-color: rgba(127,127,127,0.5);
  cursor: pointer;














}
td.diffln {
  width: 1px;
  text-align: right;
  padding: 0 1em 0 0;
}
td.difflne {







>
>
>
>
>
>
>
>
>
>
>
>
>
>







547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
}
tr.diffskip.jchunk:hover {
  /* jchunk gets added from JS to diffskip rows when they are
     plugged into the /jchunk route and removed after that data
     is fetched. */
  background-color: rgba(127,127,127,0.5);
  cursor: pointer;
}
tr.diffskip > td.chunkctrl {
  text-align: left;
  font-family: monospace;
  /* Border is only for visibility during development. Remove it when done. */
  border-width: 1px;
  border-style: dotted;
}
tr.diffskip > td.chunkctrl > .button {
  min-width: 1.5em;
  min-height: 1.5em;
  max-width: 1.5em;
  max-height: 1.5em;
  text-align: center;
}
td.diffln {
  width: 1px;
  text-align: right;
  padding: 0 1em 0 0;
}
td.difflne {
Changes to src/fossil.diff.js.
78
79
80
81
82
83
84
























85
86
87
88
89
90
91
    if(!fetchOpt.beforesend) fetchOpt.beforesend = Diff.config.chunkFetch.beforesend;
    if(!fetchOpt.aftersend) fetchOpt.aftersend = Diff.config.chunkFetch.aftersend;
    if(!fetchOpt.onerror) fetchOpt.onerror = Diff.config.chunkFetch.onerror;
    fetchOpt.responseType = 'json';
    return F.fetch('jchunk', fetchOpt);
  };

























  /**
     Fetches /jchunk for the given TR element then replaces the TR's
     contents with data from the result of that request.
  */
  const fetchTrChunk = function(tr){
    if(tr.dataset.xfer /* already being fetched */) return;
    const table = tr.parentElement.parentElement;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
    if(!fetchOpt.beforesend) fetchOpt.beforesend = Diff.config.chunkFetch.beforesend;
    if(!fetchOpt.aftersend) fetchOpt.aftersend = Diff.config.chunkFetch.aftersend;
    if(!fetchOpt.onerror) fetchOpt.onerror = Diff.config.chunkFetch.onerror;
    fetchOpt.responseType = 'json';
    return F.fetch('jchunk', fetchOpt);
  };


  /**
     Extracts either the starting or ending line number from a
     line-numer column in the given tr. isSplit must be true if tr
     represents a split diff, else false. Expects its tr to be valid:
     GIGO applies.  Returns the starting line number if getStart, else
     the ending line number. Returns the line number from the LHS file
     if getLHS is true, else the RHS.
  */
  const extractLineNo = function f(getLHS, getStart, tr, isSplit){
    if(!f.rx){
      f.rx = {
        start: /^\s*(\d+)/,
        end: /(\d+)\n?$/
      }
    }
    const td = tr.querySelector('td:nth-child('+(
      /* TD element with the line numbers */
      getLHS ? 1 : (isSplit ? 4 : 2)
    )+')');
    const m = f.rx[getStart ? 'start' : 'end'].exec(td.innerText);
    return m ? +m[1] : undefined/*"shouldn't happen"*/;
  };

  /**
     Fetches /jchunk for the given TR element then replaces the TR's
     contents with data from the result of that request.
  */
  const fetchTrChunk = function(tr){
    if(tr.dataset.xfer /* already being fetched */) return;
    const table = tr.parentElement.parentElement;
145
146
147
148
149
150
151

152
153

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197




198



199






200





201













202
203
















204
205
206
207
208
209
210
211
212
213
214
215
216
















217

218
219
220


221
222
223
224
225
226
227
          D.append(cols[3], preCode[0]);
        }
        let lineno = [], i;
        for( i = lineFrom; i <= lineTo; ++i ){
          lineno.push(i);
        }
        preLines[0].append(lineno.join('\n')+'\n');

        const code = result.join('\n')+'\n';
        preCode.forEach((e)=>e.innerText = code);

        //console.debug("Updated TR",tr);
        Diff.initTableDiff(table).checkTableWidth(true);
        /*
          At this point we need to:

          - Read the previous TR, if any, to get the preceeding LHS/RHS
          line numbers so that we know where to start counting.

          - If there is no previous TR, we're at the top and we
          instead need to get the LHS/RHS line numbers from the
          following TR's children.

          - D.clearElement(tr) and insert columns appropriate for the
          parent table's diff type.

          We can fish the line numbers out of the PRE columns with something
          like this inefficient but effective hack:

          theElement.innerText.split(/\n+/)

          (need /\n+/ instead of '\n' b/c of INS/DEL elements)

          Noting that the result array will end with an empty element
          due to the trailing \n character, so a call to pop() will be
          needed.

          SBS diff col layout:
            <td.diffln.difflnl><pre>...LHS line numbers...</pre></td>
            <td.difftxt.difftxtl><pre>...code lines...</pre></td>
            <td.diffsep>empty for this case (common lines)</td>
            <td.diffln.difflnr><pre>...RHS line numbers...</pre></td>
            <td.difftxt.difftxtr><pre>...dupe of col 2</pre></td>

          Unified diff col layout:
            <td.diffln.difflnl><pre>LHS line numbers</pre></td>
            <td.diffln.difflnr><pre>RHS line numbers</pre></td>
            <td.diffsep>empty in this case (common lines)</td>
            <td.difftxt.difftxtu><pre>code line</pre></td>

          C-side TODOs:

          - If we have that data readily available, it would be a big
          help (simplify our line calculations) if we stored the line
          number ranges in the (td.diffln pre) elements as




          data-startln and data-endln.



         */






      }





    });













  };
  
















  Diff.addDiffSkipHandlers = function(){
    const tables = document.querySelectorAll('table.diff[data-lefthash]');
    if(!tables.length) return F;
    const addDiffSkipToTr = function f(tr){
      D.addClass(tr, 'jchunk');
      if(!f._handler){
        f._handler = function ff(event){
          const e = this;
          e.removeEventListener('click',ff);
          D.removeClass(e, 'jchunk', 'diffskip');
          fetchTrChunk(e);
        };
      }
















      tr.addEventListener('click', f._handler, false);

    };
    tables.forEach(function(t){
      t.querySelectorAll('tr.diffskip[data-startln]').forEach(addDiffSkipToTr);


    });
  };

  Diff.addDiffSkipHandlers();
});

/**







>
|
|
>



<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















|
>
>
>
>
|
>
>
>
|
>
>
>
>
>
>
|
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>

|
|
>
>







169
170
171
172
173
174
175
176
177
178
179
180
181
182

183




















184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
          D.append(cols[3], preCode[0]);
        }
        let lineno = [], i;
        for( i = lineFrom; i <= lineTo; ++i ){
          lineno.push(i);
        }
        preLines[0].append(lineno.join('\n')+'\n');
        if(1){
          const code = result.join('\n')+'\n';
          preCode.forEach((e)=>e.innerText = code);
        }
        //console.debug("Updated TR",tr);
        Diff.initTableDiff(table).checkTableWidth(true);
        /*

          Reminders to self during development:





















          SBS diff col layout:
            <td.diffln.difflnl><pre>...LHS line numbers...</pre></td>
            <td.difftxt.difftxtl><pre>...code lines...</pre></td>
            <td.diffsep>empty for this case (common lines)</td>
            <td.diffln.difflnr><pre>...RHS line numbers...</pre></td>
            <td.difftxt.difftxtr><pre>...dupe of col 2</pre></td>

          Unified diff col layout:
            <td.diffln.difflnl><pre>LHS line numbers</pre></td>
            <td.diffln.difflnr><pre>RHS line numbers</pre></td>
            <td.diffsep>empty in this case (common lines)</td>
            <td.difftxt.difftxtu><pre>code line</pre></td>

          C-side TODOs:

          - If we have that data readily available, it would be a big
          help (simplify our line calculations) if we stored the line
          number ranges in all elements which have that state handy.
         */
      }
    });
  };
  
  /**
     Installs chunk-loading controls into TR element tr. isSplit is true
     if the parent table is a split diff, else false.)
   */
  Diff.ChunkLoadControls = function(isSplit, tr){
    this.isSplit = isSplit;
    this.e = {/*DOM elements*/};
    this.pos = {
      start: +tr.dataset.startln,
      end: +tr.dataset.endln
    };
    this.e.tr = tr;
    D.clearElement(tr);
    this.e.td = D.addClass(
      D.attr(D.td(tr), 'colspan', isSplit ? 5 : 4),
      'chunkctrl'
    );
    /**
       Depending on various factors, we need one of:

       - A single button to load all lines then remove this control

       - A single button to load the initial chunk

       - Two buttons: one to load upwards, one to load downwards
    */
    if(tr.nextElementSibling){
      this.pos.next = {
        startLhs: extractLineNo(true, true, tr.nextElementSibling, isSplit),
        startRhs: extractLineNo(false, true, tr.nextElementSibling, isSplit)
      };
    }
    if(tr.previousElementSibling){
      this.pos.prev = {
        endLhs: extractLineNo(true, false, tr.previousElementSibling, isSplit),
        endRhs: extractLineNo(false, false, tr.previousElementSibling, isSplit)
      };
    }
    D.append(this.e.td,"Controls pending: ",JSON.stringify(this.pos));
  };

  Diff.ChunkLoadControls.prototype = {
    config: {
      glyphUp: '&#uarr;',
      glyphDown: '&#darr;'
    }
  };

  Diff.addDiffSkipHandlers = function(){
    const tables = document.querySelectorAll('table.diff[data-lefthash]');
    if(!tables.length) return F;
    const addDiffSkipToTr = function f(isSplit, tr){
      D.addClass(tr, 'jchunk');
      if(!f._handler){
        f._handler = function ff(event){
          const e = this;
          e.removeEventListener('click',ff);
          D.removeClass(e, 'jchunk', 'diffskip');
          fetchTrChunk(e);
        };
      }
      /* TODO:

         Depending on tr.dataset.{startln,endln}, install one or two
         controls for loading the next diff chunk. For both types of
         diff, put the control(s) into tr->td[0], delete tr->td[1],
         give tr->td[0] a colspan of 2. Change the click handler to
         address those controls, instead of the TR element, for
         purposes of figuring out which lines to fetch. Use a helper
         class to encapsulate the activation and updates of the
         controls (e.g. removing controls which are no longer relevant
         once a chunk is fully loaded).

         Good example from github to use as a model:

         https://github.com/msteveb/autosetup/commit/235925e914a52a542
      */
      //tr.addEventListener('click', f._handler, false);
      new Diff.ChunkLoadControls(isSplit, tr);
    };
    tables.forEach(function(table){
      table.querySelectorAll('tr.diffskip[data-startln]').forEach(function(tr){
        addDiffSkipToTr(table.classList.contains('splitdiff')/*else udiff*/, tr);
      });
    });
  };

  Diff.addDiffSkipHandlers();
});

/**
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
    return this;
  };

  const scrollLeft = function(event){
    //console.debug("scrollLeft",this,event);
    const table = this.parentElement/*TD*/.parentElement/*TR*/.
      parentElement/*TBODY*/.parentElement/*TABLE*/;
    table.$txtPres.forEach((e)=>e.scrollLeft = this.scrollLeft);
    return false;
  };
  Diff.initTableDiff = function f(diff){
    if(!diff){
      let i, diffs = document.querySelectorAll('table.splitdiff');
      for(i=0; i<diffs.length; ++i){
        f.call(this, diffs[i]);







|







341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
    return this;
  };

  const scrollLeft = function(event){
    //console.debug("scrollLeft",this,event);
    const table = this.parentElement/*TD*/.parentElement/*TR*/.
      parentElement/*TBODY*/.parentElement/*TABLE*/;
    table.$txtPres.forEach((e)=>(e===this) ? 1 : (e.scrollLeft = this.scrollLeft));
    return false;
  };
  Diff.initTableDiff = function f(diff){
    if(!diff){
      let i, diffs = document.querySelectorAll('table.splitdiff');
      for(i=0; i<diffs.length; ++i){
        f.call(this, diffs[i]);
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
    diff.tabIndex = 0;
    if(!diff.classList.contains('scroller')){
      D.addClass(diff, 'scroller');
      diff.addEventListener('keydown', function(e){
        e = e || event;
        const len = {37: -SCROLL_LEN, 39: SCROLL_LEN}[e.keyCode];
        if( !len ) return;
        diff.$txtCols[0].scrollLeft += len;
        return false;
      }, false);
    }
    return this;
  }
  window.fossil.page.tweakSbsDiffs = function(){
    document.querySelectorAll('table.splitdiff').forEach((e)=>Diff.initTableDiff);
  };
  Diff.initTableDiff().checkTableWidth();
  window.addEventListener('resize', ()=>Diff.checkTableWidth());
}, false);








|












375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
    diff.tabIndex = 0;
    if(!diff.classList.contains('scroller')){
      D.addClass(diff, 'scroller');
      diff.addEventListener('keydown', function(e){
        e = e || event;
        const len = {37: -SCROLL_LEN, 39: SCROLL_LEN}[e.keyCode];
        if( !len ) return;
        this.$txtPres[0].scrollLeft += len;
        return false;
      }, false);
    }
    return this;
  }
  window.fossil.page.tweakSbsDiffs = function(){
    document.querySelectorAll('table.splitdiff').forEach((e)=>Diff.initTableDiff);
  };
  Diff.initTableDiff().checkTableWidth();
  window.addEventListener('resize', ()=>Diff.checkTableWidth());
}, false);