Fossil

Changes On Branch diff-js-fetchqueue
Login

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

Changes In Branch diff-js-fetchqueue Excluding Merge-Ins

This is equivalent to a diff from 55a5b701 to b4dbdec8

2021-09-25
08:57
Possible solution to the problem of buttons randomly jumping away from under the mouse cursor. ... (Leaf check-in: b4dbdec8 user: florian tags: diff-js-fetchqueue)
2021-09-24
17:18
Added an optional widget to /chat which gives an overview of who is actively posting and enables filtering messages by users. ... (check-in: ce0d61bb user: stephan tags: trunk)
12:04
Experiment to implement a click-queue for the buttons dynamically loading diff context. See Forum Post c8919e12dd for comments and potential TODOs. ... (check-in: c714f251 user: florian tags: diff-js-fetchqueue)
2021-09-23
19:47
Earlier detection of unresolved deltas due to an incomplete clone. ... (check-in: 55a5b701 user: drh tags: trunk)
18:14
Improve the decision about when to stop doing HTTP round-trips while doing a clone so that the clone will continue as long as new content is being received and we have not yet seen the "clone_seqno 0" card. Proposed fix for the issue discussed in forum thread 60d48c2896. ... (check-in: ea5afad3 user: drh tags: trunk)

Changes to src/fossil.diff.js.

115
116
117
118
119
120
121

122
123
124
125
126
127
128

     The goal is to base these controls roughly on github's, a good
     example of which, for use as a model, is:

     https://github.com/msteveb/autosetup/commit/235925e914a52a542
  */
  const ChunkLoadControls = function(tr){

    this.e = {/*DOM elements*/
      tr: tr,
      table: tr.parentElement/*TBODY*/.parentElement
    };
    this.isSplit = this.e.table.classList.contains('splitdiff')/*else udiff*/;
    this.fileHash = this.e.table.dataset.lefthash;
    tr.$chunker = this /* keep GC from reaping this */;







>







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

     The goal is to base these controls roughly on github's, a good
     example of which, for use as a model, is:

     https://github.com/msteveb/autosetup/commit/235925e914a52a542
  */
  const ChunkLoadControls = function(tr){
    this.$fetchQueue = [];
    this.e = {/*DOM elements*/
      tr: tr,
      table: tr.parentElement/*TBODY*/.parentElement
    };
    this.isSplit = this.e.table.classList.contains('splitdiff')/*else udiff*/;
    this.fileHash = this.e.table.dataset.lefthash;
    tr.$chunker = this /* keep GC from reaping this */;
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
       this.pos.prev refers to the line numbers in the previous TR's chunk.
    */
    if(this.pos.prev && this.pos.next
       && ((this.pos.next.startLhs - this.pos.prev.endLhs)
           <= Diff.config.chunkLoadLines)){
      /* Place a single button to load the whole block, rather
         than separate up/down buttons. */
      btnDown = false;
      btnUp = this.createButton(this.FetchType.FillGap);
    }else{
      /* Figure out which chunk-load buttons to add... */
      if(this.pos.prev){
        btnDown = this.createButton(this.FetchType.PrevDown);
      }
      if(this.pos.next){







|







173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
       this.pos.prev refers to the line numbers in the previous TR's chunk.
    */
    if(this.pos.prev && this.pos.next
       && ((this.pos.next.startLhs - this.pos.prev.endLhs)
           <= Diff.config.chunkLoadLines)){
      /* Place a single button to load the whole block, rather
         than separate up/down buttons. */
      btnDown = this.createButton(this.FetchType.FillGap);
      btnUp = this.createButton(this.FetchType.FillGap);
    }else{
      /* Figure out which chunk-load buttons to add... */
      if(this.pos.prev){
        btnDown = this.createButton(this.FetchType.PrevDown);
      }
      if(this.pos.next){
207
208
209
210
211
212
213
214


215
216
217
218
219
220
221
      /** Append context to the bottom of the previous diff chunk. */
      PrevDown: 1,
      /** Fill a complete gap between the previous/next diff chunks
          or at the start of the next chunk or end of the previous
          chunks. */
      FillGap: 0,
      /** Prepend context to the start of the next diff chunk. */
      NextUp: -1


    },
    config: {
      /*
      glyphUp: '⇡', //'&#uarr;',
      glyphDown: '⇣' //'&#darr;'
      */
    },







|
>
>







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
      /** Append context to the bottom of the previous diff chunk. */
      PrevDown: 1,
      /** Fill a complete gap between the previous/next diff chunks
          or at the start of the next chunk or end of the previous
          chunks. */
      FillGap: 0,
      /** Prepend context to the start of the next diff chunk. */
      NextUp: -1,
      /** Process the next queued action. */
      ProcessQueue: 0x7fffffff
    },
    config: {
      /*
      glyphUp: '⇡', //'&#uarr;',
      glyphDown: '⇣' //'&#darr;'
      */
    },
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
      }
      return this;
    },

    /* Attempt to clean up resources and remove some circular references to
       that GC can do the right thing. */
    destroy: function(){

      D.remove(this.e.tr);
      delete this.e.tr.$chunker;
      delete this.e.tr;
      delete this.e;
      delete this.pos;
    },

    /**
       If the gap between this.pos.endLhs/startLhs is less than or equal to
       Diff.config.chunkLoadLines then this function replaces any up/down buttons
       with a gap-filler button, else it's a no-op. Returns this object.

       As a special case, do not apply this at the start or bottom
       of the diff, only between two diff chunks.
    */
    maybeReplaceButtons: function(){
      if(this.pos.next && this.pos.prev
         && (this.pos.endLhs - this.pos.startLhs <= Diff.config.chunkLoadLines)){
        D.clearElement(this.e.btnWrapper);

        D.append(this.e.btnWrapper, this.createButton(this.FetchType.FillGap));




      }
      return this;
    },

    /**
       Callack for /jchunk responses.
    */







>



















>
|
>
>
>
>







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
299
300
301
302
      }
      return this;
    },

    /* Attempt to clean up resources and remove some circular references to
       that GC can do the right thing. */
    destroy: function(){
      delete this.$fetchQueue;
      D.remove(this.e.tr);
      delete this.e.tr.$chunker;
      delete this.e.tr;
      delete this.e;
      delete this.pos;
    },

    /**
       If the gap between this.pos.endLhs/startLhs is less than or equal to
       Diff.config.chunkLoadLines then this function replaces any up/down buttons
       with a gap-filler button, else it's a no-op. Returns this object.

       As a special case, do not apply this at the start or bottom
       of the diff, only between two diff chunks.
    */
    maybeReplaceButtons: function(){
      if(this.pos.next && this.pos.prev
         && (this.pos.endLhs - this.pos.startLhs <= Diff.config.chunkLoadLines)){
        D.clearElement(this.e.btnWrapper);
        for( var i=0; i<2; i++ ){
          D.append(this.e.btnWrapper, this.createButton(this.FetchType.FillGap));
        }
        if( this.$fetchQueue && this.$fetchQueue.length>0 ){
          this.$fetchQueue = [this.FetchType.FillGap];
        }
      }
      return this;
    },

    /**
       Callack for /jchunk responses.
    */
499
500
501
502
503
504
505











506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523




524
525
526
527
528
529
530
       fetchTypes NextUp and PrevDown.

       This is an async operation. While it is in transit, any calls
       to this function will have no effect except (possibly) to emit
       a warning. Returns this object.
    */
    fetchChunk: function(fetchType){











      /* Forewarning, this is a bit confusing: when fetching the
         previous lines, we're doing so on behalf of the *next* diff
         chunk (this.pos.next), and vice versa. */
      if(this.$isFetching){
        return this.msg(true,"Cannot load chunk while a load is pending.");
      }
      if(fetchType===this.FetchType.NextUp && !this.pos.next
        || fetchType===this.FetchType.PrevDown && !this.pos.prev){
        console.error("Attempt to fetch diff lines but don't have any.");
        return this;
      }
      this.msg(false,"Fetching diff chunk...");
      const fOpt = {
        urlParams:{
          name: this.fileHash, from: 0, to: 0
        },
        aftersend: ()=>delete this.$isFetching,
        onload: (list)=>this.injectResponse(fetchType,up,list)




      };
      const up = fOpt.urlParams;
      if(fetchType===this.FetchType.FillGap){
        /* Easiest case: filling a whole gap. */
        up.from = this.pos.startLhs;
        up.to = this.pos.endLhs;
      }else if(this.FetchType.PrevDown===fetchType){







>
>
>
>
>
>
>
>
>
>
>



<
<
<





|




|
|
>
>
>
>







508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528



529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
       fetchTypes NextUp and PrevDown.

       This is an async operation. While it is in transit, any calls
       to this function will have no effect except (possibly) to emit
       a warning. Returns this object.
    */
    fetchChunk: function(fetchType){
      if( !this.$fetchQueue ) return this;  // HACKHACK: are we destroyed?
      if( fetchType==this.FetchType.ProcessQueue ){
        if( this.$fetchQueue.length==0 ) return this;
        //console.log('fetchChunk: processing queue ...');
      }
      else{
        this.$fetchQueue.push(fetchType);
        if( this.$fetchQueue.length!=1 ) return this;
        //console.log('fetchChunk: processing user input ...');
      }
      fetchType = this.$fetchQueue[0];
      /* Forewarning, this is a bit confusing: when fetching the
         previous lines, we're doing so on behalf of the *next* diff
         chunk (this.pos.next), and vice versa. */



      if(fetchType===this.FetchType.NextUp && !this.pos.next
        || fetchType===this.FetchType.PrevDown && !this.pos.prev){
        console.error("Attempt to fetch diff lines but don't have any.");
        return this;
      }
      this.msg(false);
      const fOpt = {
        urlParams:{
          name: this.fileHash, from: 0, to: 0
        },
        onload: function(list){
          this.injectResponse(fetchType,up,list);
          if( !this.$fetchQueue || this.$fetchQueue.length==0 ) return;
          this.$fetchQueue.shift();
          setTimeout(this.fetchChunk.bind(this,this.FetchType.ProcessQueue));
        }.bind(this)
      };
      const up = fOpt.urlParams;
      if(fetchType===this.FetchType.FillGap){
        /* Easiest case: filling a whole gap. */
        up.from = this.pos.startLhs;
        up.to = this.pos.endLhs;
      }else if(this.FetchType.PrevDown===fetchType){
549
550
551
552
553
554
555
556
557

558


559
560
561
562
563
564
565
        up.to = this.pos.next.startLhs - 1;
        up.from = Math.max(1, up.to - Diff.config.chunkLoadLines + 1);
        if( this.pos.prev && this.pos.prev.endLhs >= up.from ){
          up.from = this.pos.prev.endLhs + 1;
          fetchType = this.FetchType.FillGap;
        }
      }
      this.$isFetching = true;
      //console.debug("fetchChunk(",fetchType,")",up);

      fOpt.onerror = (err)=>this.msg(true,err.message);


      Diff.fetchArtifactChunk(fOpt);
      return this;
    }
  };

  /**
     Adds context-loading buttons to one or more tables. The argument







<

>
|
>
>







570
571
572
573
574
575
576

577
578
579
580
581
582
583
584
585
586
587
588
        up.to = this.pos.next.startLhs - 1;
        up.from = Math.max(1, up.to - Diff.config.chunkLoadLines + 1);
        if( this.pos.prev && this.pos.prev.endLhs >= up.from ){
          up.from = this.pos.prev.endLhs + 1;
          fetchType = this.FetchType.FillGap;
        }
      }

      //console.debug("fetchChunk(",fetchType,")",up);
      fOpt.onerror = function(err){
        this.msg(true,err.message);
        this.$fetchQueue = [];
      }.bind(this);
      Diff.fetchArtifactChunk(fOpt);
      return this;
    }
  };

  /**
     Adds context-loading buttons to one or more tables. The argument