Fossil SCM
When filling a whole gap with loaded jchunk lines, merge the previous and following TR elements together with the new content, providing a seamless fill, eliminating the extraneous scrollbars. This means we cannot style the newly-loaded chunk differently (like github does), but it looks much, much nicer than before. Partial-chunk loads are still pending.
Commit
11a981ead041010ad59befd46db13f2e4ad5311048302117a0db7fbbde3cdc5b
Parent
da8a0f82b5d5638…
2 files changed
-3
+78
-22
-3
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -545,13 +545,10 @@ | ||
| 545 | 545 | vertical-align: top; |
| 546 | 546 | } |
| 547 | 547 | table.diff pre { |
| 548 | 548 | margin: 0 0 0 0; |
| 549 | 549 | } |
| 550 | -table.diff tr.fetched { | |
| 551 | - opacity: 0.7; | |
| 552 | -} | |
| 553 | 550 | tr.diffskip.jchunk { |
| 554 | 551 | /* jchunk gets added from JS to diffskip rows when they are |
| 555 | 552 | plugged into the /jchunk route and removed after that data |
| 556 | 553 | is fetched. */ |
| 557 | 554 | background-color: aliceblue; |
| 558 | 555 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -545,13 +545,10 @@ | |
| 545 | vertical-align: top; |
| 546 | } |
| 547 | table.diff pre { |
| 548 | margin: 0 0 0 0; |
| 549 | } |
| 550 | table.diff tr.fetched { |
| 551 | opacity: 0.7; |
| 552 | } |
| 553 | tr.diffskip.jchunk { |
| 554 | /* jchunk gets added from JS to diffskip rows when they are |
| 555 | plugged into the /jchunk route and removed after that data |
| 556 | is fetched. */ |
| 557 | background-color: aliceblue; |
| 558 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -545,13 +545,10 @@ | |
| 545 | vertical-align: top; |
| 546 | } |
| 547 | table.diff pre { |
| 548 | margin: 0 0 0 0; |
| 549 | } |
| 550 | tr.diffskip.jchunk { |
| 551 | /* jchunk gets added from JS to diffskip rows when they are |
| 552 | plugged into the /jchunk route and removed after that data |
| 553 | is fetched. */ |
| 554 | background-color: aliceblue; |
| 555 |
+78
-22
| --- src/fossil.diff.js | ||
| +++ src/fossil.diff.js | ||
| @@ -344,27 +344,28 @@ | ||
| 344 | 344 | so). Returns an object containing the TR element and various TD |
| 345 | 345 | elements which will likely be needed by the routine which |
| 346 | 346 | called this. See this code for details. |
| 347 | 347 | */ |
| 348 | 348 | newTR: function(){ |
| 349 | - const tr = D.addClass(D.tr(),'fetched'), rc = { | |
| 349 | + const tr = D.tr(), rc = { | |
| 350 | 350 | tr, |
| 351 | 351 | preLnL: D.pre(), |
| 352 | - preLnR: D.pre() | |
| 352 | + preLnR: D.pre(), | |
| 353 | + preSep: D.pre() | |
| 353 | 354 | }; |
| 354 | 355 | if(this.isSplit){ |
| 355 | 356 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnl' ), rc.preLnL); |
| 356 | 357 | rc.preTxtL = D.pre(); |
| 357 | 358 | D.append(D.addClass( D.td(tr), 'difftxt', 'difftxtl' ), rc.preTxtL); |
| 358 | - D.addClass( D.td(tr), 'diffsep' ); | |
| 359 | + D.append(D.addClass( D.td(tr), 'diffsep' ), rc.preSep); | |
| 359 | 360 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnr' ), rc.preLnR); |
| 360 | 361 | rc.preTxtR = D.pre(); |
| 361 | 362 | D.append(D.addClass( D.td(tr), 'difftxt', 'difftxtr' ), rc.preTxtR); |
| 362 | 363 | }else{ |
| 363 | 364 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnl' ), rc.preLnL); |
| 364 | 365 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnr' ), rc.preLnR); |
| 365 | - D.addClass( D.td(tr), 'diffsep' ); | |
| 366 | + D.append(D.addClass( D.td(tr), 'diffsep' ), rc.preSep); | |
| 366 | 367 | rc.preTxtU = D.pre(); |
| 367 | 368 | D.append(D.addClass( D.td(tr), 'difftxt', 'difftxtu' ), rc.preTxtU); |
| 368 | 369 | } |
| 369 | 370 | return rc; |
| 370 | 371 | }, |
| @@ -371,24 +372,65 @@ | ||
| 371 | 372 | |
| 372 | 373 | injectResponse: function(direction/*as for fetchChunk()*/, |
| 373 | 374 | urlParam/*from fetchChunk()*/, |
| 374 | 375 | lines/*response lines*/){ |
| 375 | 376 | console.debug("Loading line range ",urlParam.from,"-",urlParam.to); |
| 376 | - const row = this.newTR(); | |
| 377 | - const lineno = []; | |
| 378 | - let i; | |
| 379 | - for( i = urlParam.from; i <= urlParam.to; ++i ){ | |
| 380 | - /* TODO: space-pad numbers, but we don't know the proper length from here. */ | |
| 381 | - lineno.push(i); | |
| 382 | - } | |
| 383 | - row.preLnL.innerText = lineno.join('\n')+'\n'; | |
| 384 | - if(row.preTxtU){//unified diff | |
| 385 | - row.preTxtU.innerText = lines.join('\n')+'\n'; | |
| 386 | - }else{//split diff | |
| 387 | - const code = lines.join('\n')+'\n'; | |
| 388 | - row.preTxtL.innerText = code; | |
| 389 | - row.preTxtR.innerText = code; | |
| 377 | + const lineno = [], | |
| 378 | + trPrev = this.e.tr.previousElementSibling, | |
| 379 | + trNext = this.e.tr.nextElementSibling, | |
| 380 | + doAppend = !!trPrev /* true to append to previous TR, else prepend to NEXT TR */; | |
| 381 | + const tr = trPrev || trNext; | |
| 382 | + if(0!==direction){ | |
| 383 | + console.error("this case is not yet handled",arguments); | |
| 384 | + return this; | |
| 385 | + } | |
| 386 | + const joinTr = (0===direction && trPrev && trNext); | |
| 387 | + let i, td; | |
| 388 | + if(1){ // LHS line numbers... | |
| 389 | + const selector = '.difflnl > pre'; | |
| 390 | + td = tr.querySelector(selector); | |
| 391 | + for( i = urlParam.from; i <= urlParam.to; ++i ){ | |
| 392 | + lineno.push(i); | |
| 393 | + } | |
| 394 | + const lineNoTxt = lineno.join('\n')+'\n'; | |
| 395 | + const content = []; | |
| 396 | + if(doAppend) content.push(td.innerHTML, lineNoTxt); | |
| 397 | + else content.push(lineNoTxt, td.innerHTML); | |
| 398 | + if(joinTr){ | |
| 399 | + content.push(trNext.querySelector(selector).innerHTML); | |
| 400 | + } | |
| 401 | + td.innerHTML = content.join(''); | |
| 402 | + } | |
| 403 | + | |
| 404 | + if(1){// code block... | |
| 405 | + const selector = '.difftxt > pre'; | |
| 406 | + td = tr.querySelectorAll(selector); | |
| 407 | + const code = D.append(D.div(),lines.join('\n')+'\n').innerText; | |
| 408 | + let joinNdx = 0; | |
| 409 | + td.forEach(function(e){ | |
| 410 | + const content = []; | |
| 411 | + if(doAppend) content.push(e.innerHTML, code); | |
| 412 | + else content.push(code, e.innerHTML); | |
| 413 | + if(joinTr){ | |
| 414 | + content.push(trNext.querySelectorAll(selector)[joinNdx++].innerHTML) | |
| 415 | + } | |
| 416 | + e.innerHTML = content.join(''); | |
| 417 | + }); | |
| 418 | + } | |
| 419 | + if(1){ | |
| 420 | + // Add blank lines in (.diffsep>pre) | |
| 421 | + const selector = '.diffsep > pre'; | |
| 422 | + td = tr.querySelector(selector); | |
| 423 | + for(i = 0; i < lineno.length; ++i) lineno[i] = ''; | |
| 424 | + const blanks = lineno.join('\n')+'\n'; | |
| 425 | + const content = []; | |
| 426 | + if(doAppend) content.push(td.innerHTML, blanks); | |
| 427 | + else content.push(blanks, td.innerHTML); | |
| 428 | + if(joinTr){ | |
| 429 | + content.push(trNext.querySelector(selector).innerHTML); | |
| 430 | + } | |
| 431 | + td.innerHTML = content.join(''); | |
| 390 | 432 | } |
| 391 | 433 | if(0===direction){ |
| 392 | 434 | /* Closing the whole gap between two chunks or a whole gap |
| 393 | 435 | at the start or end of a diff. */ |
| 394 | 436 | let startLnR = this.pos.prev |
| @@ -398,22 +440,36 @@ | ||
| 398 | 440 | lineno.length = 0; |
| 399 | 441 | for( i = startLnR; i < startLnR + lines.length; ++i ){ |
| 400 | 442 | /* TODO? space-pad numbers, but we don't know the proper length from here. */ |
| 401 | 443 | lineno.push(i); |
| 402 | 444 | } |
| 403 | - row.preLnR.innerText = lineno.join('\n')+'\n'; | |
| 404 | - this.e.tr.parentNode.insertBefore(row.tr, this.e.tr); | |
| 405 | - Diff.initTableDiff(this.e.table/*fix scrolling*/).checkTableWidth(true); | |
| 445 | + const selector = '.difflnr > pre'; | |
| 446 | + td = tr.querySelector(selector); | |
| 447 | + const lineNoTxt = lineno.join('\n')+'\n'; | |
| 448 | + const content = []; | |
| 449 | + if(doAppend) content.push(td.innerHTML, lineNoTxt); | |
| 450 | + else content.push(lineNoTxt, td.innerHTML); | |
| 451 | + if(joinTr){ | |
| 452 | + content.push(trNext.querySelector(selector).innerHTML); | |
| 453 | + } | |
| 454 | + td.innerHTML = content.join(''); | |
| 455 | + if(joinTr){ | |
| 456 | + D.remove(joinTr); | |
| 457 | + } | |
| 458 | + Diff.checkTableWidth(true); | |
| 406 | 459 | this.destroy(); |
| 407 | 460 | return this; |
| 408 | 461 | }else{ |
| 409 | 462 | console.debug("TODO: handle load of partial next/prev"); |
| 410 | 463 | this.updatePosDebug(); |
| 411 | 464 | } |
| 412 | 465 | }, |
| 413 | 466 | |
| 414 | - fetchChunk: function(direction/*-1=prev, 1=next, 0=both*/){ | |
| 467 | + fetchChunk: function(direction/*-1=down from prev chunk, | |
| 468 | + 1=up from next chunk, | |
| 469 | + 0=full-gap filler for any neighoring | |
| 470 | + chunk(s)*/){ | |
| 415 | 471 | /* Forewarning, this is a bit confusing: when fetching the |
| 416 | 472 | previous lines, we're doing so on behalf of the *next* diff |
| 417 | 473 | chunk (this.pos.next), and vice versa. */ |
| 418 | 474 | if(this.$isFetching){ |
| 419 | 475 | console.debug("Cannot load chunk while a load is pending."); |
| 420 | 476 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -344,27 +344,28 @@ | |
| 344 | so). Returns an object containing the TR element and various TD |
| 345 | elements which will likely be needed by the routine which |
| 346 | called this. See this code for details. |
| 347 | */ |
| 348 | newTR: function(){ |
| 349 | const tr = D.addClass(D.tr(),'fetched'), rc = { |
| 350 | tr, |
| 351 | preLnL: D.pre(), |
| 352 | preLnR: D.pre() |
| 353 | }; |
| 354 | if(this.isSplit){ |
| 355 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnl' ), rc.preLnL); |
| 356 | rc.preTxtL = D.pre(); |
| 357 | D.append(D.addClass( D.td(tr), 'difftxt', 'difftxtl' ), rc.preTxtL); |
| 358 | D.addClass( D.td(tr), 'diffsep' ); |
| 359 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnr' ), rc.preLnR); |
| 360 | rc.preTxtR = D.pre(); |
| 361 | D.append(D.addClass( D.td(tr), 'difftxt', 'difftxtr' ), rc.preTxtR); |
| 362 | }else{ |
| 363 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnl' ), rc.preLnL); |
| 364 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnr' ), rc.preLnR); |
| 365 | D.addClass( D.td(tr), 'diffsep' ); |
| 366 | rc.preTxtU = D.pre(); |
| 367 | D.append(D.addClass( D.td(tr), 'difftxt', 'difftxtu' ), rc.preTxtU); |
| 368 | } |
| 369 | return rc; |
| 370 | }, |
| @@ -371,24 +372,65 @@ | |
| 371 | |
| 372 | injectResponse: function(direction/*as for fetchChunk()*/, |
| 373 | urlParam/*from fetchChunk()*/, |
| 374 | lines/*response lines*/){ |
| 375 | console.debug("Loading line range ",urlParam.from,"-",urlParam.to); |
| 376 | const row = this.newTR(); |
| 377 | const lineno = []; |
| 378 | let i; |
| 379 | for( i = urlParam.from; i <= urlParam.to; ++i ){ |
| 380 | /* TODO: space-pad numbers, but we don't know the proper length from here. */ |
| 381 | lineno.push(i); |
| 382 | } |
| 383 | row.preLnL.innerText = lineno.join('\n')+'\n'; |
| 384 | if(row.preTxtU){//unified diff |
| 385 | row.preTxtU.innerText = lines.join('\n')+'\n'; |
| 386 | }else{//split diff |
| 387 | const code = lines.join('\n')+'\n'; |
| 388 | row.preTxtL.innerText = code; |
| 389 | row.preTxtR.innerText = code; |
| 390 | } |
| 391 | if(0===direction){ |
| 392 | /* Closing the whole gap between two chunks or a whole gap |
| 393 | at the start or end of a diff. */ |
| 394 | let startLnR = this.pos.prev |
| @@ -398,22 +440,36 @@ | |
| 398 | lineno.length = 0; |
| 399 | for( i = startLnR; i < startLnR + lines.length; ++i ){ |
| 400 | /* TODO? space-pad numbers, but we don't know the proper length from here. */ |
| 401 | lineno.push(i); |
| 402 | } |
| 403 | row.preLnR.innerText = lineno.join('\n')+'\n'; |
| 404 | this.e.tr.parentNode.insertBefore(row.tr, this.e.tr); |
| 405 | Diff.initTableDiff(this.e.table/*fix scrolling*/).checkTableWidth(true); |
| 406 | this.destroy(); |
| 407 | return this; |
| 408 | }else{ |
| 409 | console.debug("TODO: handle load of partial next/prev"); |
| 410 | this.updatePosDebug(); |
| 411 | } |
| 412 | }, |
| 413 | |
| 414 | fetchChunk: function(direction/*-1=prev, 1=next, 0=both*/){ |
| 415 | /* Forewarning, this is a bit confusing: when fetching the |
| 416 | previous lines, we're doing so on behalf of the *next* diff |
| 417 | chunk (this.pos.next), and vice versa. */ |
| 418 | if(this.$isFetching){ |
| 419 | console.debug("Cannot load chunk while a load is pending."); |
| 420 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -344,27 +344,28 @@ | |
| 344 | so). Returns an object containing the TR element and various TD |
| 345 | elements which will likely be needed by the routine which |
| 346 | called this. See this code for details. |
| 347 | */ |
| 348 | newTR: function(){ |
| 349 | const tr = D.tr(), rc = { |
| 350 | tr, |
| 351 | preLnL: D.pre(), |
| 352 | preLnR: D.pre(), |
| 353 | preSep: D.pre() |
| 354 | }; |
| 355 | if(this.isSplit){ |
| 356 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnl' ), rc.preLnL); |
| 357 | rc.preTxtL = D.pre(); |
| 358 | D.append(D.addClass( D.td(tr), 'difftxt', 'difftxtl' ), rc.preTxtL); |
| 359 | D.append(D.addClass( D.td(tr), 'diffsep' ), rc.preSep); |
| 360 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnr' ), rc.preLnR); |
| 361 | rc.preTxtR = D.pre(); |
| 362 | D.append(D.addClass( D.td(tr), 'difftxt', 'difftxtr' ), rc.preTxtR); |
| 363 | }else{ |
| 364 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnl' ), rc.preLnL); |
| 365 | D.append(D.addClass( D.td(tr), 'diffln', 'difflnr' ), rc.preLnR); |
| 366 | D.append(D.addClass( D.td(tr), 'diffsep' ), rc.preSep); |
| 367 | rc.preTxtU = D.pre(); |
| 368 | D.append(D.addClass( D.td(tr), 'difftxt', 'difftxtu' ), rc.preTxtU); |
| 369 | } |
| 370 | return rc; |
| 371 | }, |
| @@ -371,24 +372,65 @@ | |
| 372 | |
| 373 | injectResponse: function(direction/*as for fetchChunk()*/, |
| 374 | urlParam/*from fetchChunk()*/, |
| 375 | lines/*response lines*/){ |
| 376 | console.debug("Loading line range ",urlParam.from,"-",urlParam.to); |
| 377 | const lineno = [], |
| 378 | trPrev = this.e.tr.previousElementSibling, |
| 379 | trNext = this.e.tr.nextElementSibling, |
| 380 | doAppend = !!trPrev /* true to append to previous TR, else prepend to NEXT TR */; |
| 381 | const tr = trPrev || trNext; |
| 382 | if(0!==direction){ |
| 383 | console.error("this case is not yet handled",arguments); |
| 384 | return this; |
| 385 | } |
| 386 | const joinTr = (0===direction && trPrev && trNext); |
| 387 | let i, td; |
| 388 | if(1){ // LHS line numbers... |
| 389 | const selector = '.difflnl > pre'; |
| 390 | td = tr.querySelector(selector); |
| 391 | for( i = urlParam.from; i <= urlParam.to; ++i ){ |
| 392 | lineno.push(i); |
| 393 | } |
| 394 | const lineNoTxt = lineno.join('\n')+'\n'; |
| 395 | const content = []; |
| 396 | if(doAppend) content.push(td.innerHTML, lineNoTxt); |
| 397 | else content.push(lineNoTxt, td.innerHTML); |
| 398 | if(joinTr){ |
| 399 | content.push(trNext.querySelector(selector).innerHTML); |
| 400 | } |
| 401 | td.innerHTML = content.join(''); |
| 402 | } |
| 403 | |
| 404 | if(1){// code block... |
| 405 | const selector = '.difftxt > pre'; |
| 406 | td = tr.querySelectorAll(selector); |
| 407 | const code = D.append(D.div(),lines.join('\n')+'\n').innerText; |
| 408 | let joinNdx = 0; |
| 409 | td.forEach(function(e){ |
| 410 | const content = []; |
| 411 | if(doAppend) content.push(e.innerHTML, code); |
| 412 | else content.push(code, e.innerHTML); |
| 413 | if(joinTr){ |
| 414 | content.push(trNext.querySelectorAll(selector)[joinNdx++].innerHTML) |
| 415 | } |
| 416 | e.innerHTML = content.join(''); |
| 417 | }); |
| 418 | } |
| 419 | if(1){ |
| 420 | // Add blank lines in (.diffsep>pre) |
| 421 | const selector = '.diffsep > pre'; |
| 422 | td = tr.querySelector(selector); |
| 423 | for(i = 0; i < lineno.length; ++i) lineno[i] = ''; |
| 424 | const blanks = lineno.join('\n')+'\n'; |
| 425 | const content = []; |
| 426 | if(doAppend) content.push(td.innerHTML, blanks); |
| 427 | else content.push(blanks, td.innerHTML); |
| 428 | if(joinTr){ |
| 429 | content.push(trNext.querySelector(selector).innerHTML); |
| 430 | } |
| 431 | td.innerHTML = content.join(''); |
| 432 | } |
| 433 | if(0===direction){ |
| 434 | /* Closing the whole gap between two chunks or a whole gap |
| 435 | at the start or end of a diff. */ |
| 436 | let startLnR = this.pos.prev |
| @@ -398,22 +440,36 @@ | |
| 440 | lineno.length = 0; |
| 441 | for( i = startLnR; i < startLnR + lines.length; ++i ){ |
| 442 | /* TODO? space-pad numbers, but we don't know the proper length from here. */ |
| 443 | lineno.push(i); |
| 444 | } |
| 445 | const selector = '.difflnr > pre'; |
| 446 | td = tr.querySelector(selector); |
| 447 | const lineNoTxt = lineno.join('\n')+'\n'; |
| 448 | const content = []; |
| 449 | if(doAppend) content.push(td.innerHTML, lineNoTxt); |
| 450 | else content.push(lineNoTxt, td.innerHTML); |
| 451 | if(joinTr){ |
| 452 | content.push(trNext.querySelector(selector).innerHTML); |
| 453 | } |
| 454 | td.innerHTML = content.join(''); |
| 455 | if(joinTr){ |
| 456 | D.remove(joinTr); |
| 457 | } |
| 458 | Diff.checkTableWidth(true); |
| 459 | this.destroy(); |
| 460 | return this; |
| 461 | }else{ |
| 462 | console.debug("TODO: handle load of partial next/prev"); |
| 463 | this.updatePosDebug(); |
| 464 | } |
| 465 | }, |
| 466 | |
| 467 | fetchChunk: function(direction/*-1=down from prev chunk, |
| 468 | 1=up from next chunk, |
| 469 | 0=full-gap filler for any neighoring |
| 470 | chunk(s)*/){ |
| 471 | /* Forewarning, this is a bit confusing: when fetching the |
| 472 | previous lines, we're doing so on behalf of the *next* diff |
| 473 | chunk (this.pos.next), and vice versa. */ |
| 474 | if(this.$isFetching){ |
| 475 | console.debug("Cannot load chunk while a load is pending."); |
| 476 |