@@ -6,80 +6,61 @@ import onPrFileLoad from '../libs/on-pr-file-load';
66import onNewComments from '../libs/on-new-comments' ;
77import getTextNodes from '../libs/get-text-nodes' ;
88
9+ // `splitText` is used before and after each whitespace group so a new whitespace-only text node is created. This new node is then wrapped in a <span>
910function showWhiteSpacesOn ( line : Element ) : void {
10- const textNodes = getTextNodes ( line ) ;
11+ for ( const textNode of getTextNodes ( line ) ) {
12+ // `textContent` reads must be cached #2737
13+ let text = textNode . textContent ! ;
1114
12- for ( const textNode of textNodes ) {
13- const textContent = textNode . textContent ! ;
14- if ( textContent . length === 0 || ! ( textContent . includes ( ' ' ) || textContent . includes ( '\t' ) ) ) {
15- continue ;
16- }
15+ // Loop goes in reverse otherwise `splitText`'s `index` parameter needs to keep track of the previous split
16+ for ( let i = text . length - 1 ; i >= 0 ; i -- ) {
17+ const thisCharacter = text [ i ] ;
1718
18- const fragment = document . createDocumentFragment ( ) ;
19+ // Exclude irrelevant characters
20+ if ( thisCharacter !== ' ' && thisCharacter !== '\t' ) {
21+ continue ;
22+ }
1923
20- let lastEncounteredCharType ;
21- let charType : 'space' | 'tab' | 'other' ;
22- let node ;
24+ if ( i < text . length - 1 ) {
25+ textNode . splitText ( i + 1 ) ;
26+ }
2327
24- for ( const char of textContent ) {
25- if ( char === ' ' ) {
26- charType = 'space' ;
27- } else if ( char === '\t' ) {
28- charType = 'tab' ;
29- } else {
30- charType = 'other' ;
28+ // Find the same character so they can be wrapped together
29+ while ( text [ i - 1 ] === thisCharacter ) {
30+ i -- ;
3131 }
3232
33- if ( node && lastEncounteredCharType === charType ) {
34- node . textContent += char ;
33+ textNode . splitText ( i ) ;
3534
36- if ( charType === 'space' ) {
37- node . dataset . rghSpaces += '·' ;
38- } else if ( charType === 'tab' ) {
39- node . dataset . rghTabs += '→' ;
40- }
41- } else {
42- if ( node ) {
43- fragment . append ( node ) ;
44- }
35+ // Update cached variable here because it just changed
36+ text = textNode . textContent ! ;
4537
46- if ( charType === 'space' ) {
47- node = < span className = "rgh-ws-char rgh-space-char" data-rgh-spaces = "·" > { char } </ span > ;
48- } else if ( charType === 'tab' ) {
49- node = < span className = "rgh-ws-char rgh-tab-char" data-rgh-tabs = "→" > { char } </ span > ;
50- } else {
51- node = < > { char } </ > ;
52- }
53- }
38+ const whitespace = textNode . nextSibling ! . textContent !
39+ . replace ( / / g, '·' )
40+ . replace ( / \t / g, '→' ) ;
5441
55- lastEncounteredCharType = charType ;
42+ textNode . after (
43+ < span data-rgh-whitespace = { whitespace } >
44+ { textNode . nextSibling }
45+ </ span >
46+ ) ;
5647 }
48+ }
49+ }
5750
58- if ( node ) {
59- fragment . append ( node ) ;
51+ const viewportObserver = new IntersectionObserver ( changes => {
52+ for ( const change of changes ) {
53+ if ( change . isIntersecting ) {
54+ showWhiteSpacesOn ( change . target ) ;
55+ viewportObserver . unobserve ( change . target ) ;
6056 }
61-
62- textNode . replaceWith ( fragment ) ;
6357 }
64- }
58+ } ) ;
6559
6660async function run ( ) : Promise < void > {
67- const tables = select . all ( [
68- 'table.js-file-line-container:not(.rgh-showing-whitespace)' , // Single blob file, and gist
69- '.file table.diff-table:not(.rgh-showing-whitespace)' , // Split and unified diffs
70- '.file table.d-table:not(.rgh-showing-whitespace)' // "Suggested changes" in PRs
71- ] . join ( ) ) ;
72-
73- for ( const table of tables ) {
74- table . classList . add ( 'rgh-showing-whitespace' ) ;
75-
76- for ( const [ i , line ] of select . all ( '.blob-code-inner' , table ) . entries ( ) ) {
77- showWhiteSpacesOn ( line ) ;
78-
79- if ( i % 100 === 0 ) {
80- await new Promise ( resolve => setTimeout ( resolve ) ) ; // eslint-disable-line no-await-in-loop
81- }
82- }
61+ for ( const line of select . all ( '.blob-code-inner:not(.rgh-observing-whitespace)' ) ) {
62+ line . classList . add ( 'rgh-observing-whitespace' ) ;
63+ viewportObserver . observe ( line ) ;
8364 }
8465}
8566
0 commit comments