@@ -63,8 +63,9 @@ class TextPlane extends React.Component {
6363 canvasHeight : this . props . height * window . devicePixelRatio ,
6464 scrollTop : this . props . scrollTop ,
6565 scrollLeft : this . props . scrollLeft ,
66- paddingLeft : this . props . paddingLeft || 0 ,
66+ paddingLeft : this . props . paddingLeft * window . devicePixelRatio || 0 ,
6767 firstVisibleRow : this . props . firstVisibleRow ,
68+ totalRowCount : this . props . totalRowCount ,
6869 lines : this . props . lines ,
6970 selections : this . props . selections ,
7071 showLocalCursors : this . props . showLocalCursors ,
@@ -78,6 +79,10 @@ class TextPlane extends React.Component {
7879 return this . renderer . measureLine ( line , column ) ;
7980 }
8081
82+ getGutterWidth ( totalRowCount ) {
83+ return this . renderer . getGutterWidth ( totalRowCount ) ;
84+ }
85+
8186 isReady ( ) {
8287 return this . renderer != null ;
8388 }
@@ -192,7 +197,12 @@ class Renderer {
192197 this . gl . STATIC_DRAW
193198 ) ;
194199
195- this . glyphInstances = new Float32Array ( MAX_INSTANCES * GLYPH_INSTANCE_SIZE ) ;
200+ this . lineGlyphInstances = new Float32Array (
201+ MAX_INSTANCES * GLYPH_INSTANCE_SIZE
202+ ) ;
203+ this . gutterGlyphInstances = new Float32Array (
204+ MAX_INSTANCES * GLYPH_INSTANCE_SIZE
205+ ) ;
196206 this . glyphInstancesBuffer = this . gl . createBuffer ( ) ;
197207
198208 this . selectionSolidInstances = new Float32Array (
@@ -201,6 +211,7 @@ class Renderer {
201211 this . cursorSolidInstances = new Float32Array (
202212 MAX_INSTANCES * SOLID_INSTANCE_SIZE
203213 ) ;
214+ this . opaqueRectangleInstances = new Float32Array ( 1 * SOLID_INSTANCE_SIZE ) ;
204215 this . solidInstancesBuffer = this . gl . createBuffer ( ) ;
205216 }
206217
@@ -359,6 +370,7 @@ class Renderer {
359370 scrollLeft,
360371 paddingLeft,
361372 firstVisibleRow,
373+ totalRowCount,
362374 lines,
363375 selections,
364376 showLocalCursors,
@@ -372,6 +384,7 @@ class Renderer {
372384
373385 const textColor = { r : 0 , g : 0 , b : 0 , a : 255 } ;
374386 const cursorWidth = 2 ;
387+ const gutterWidth = this . getGutterWidth ( totalRowCount ) ;
375388
376389 const xPositions = new Map ( ) ;
377390 for ( let i = 0 ; i < selections . length ; i ++ ) {
@@ -380,23 +393,29 @@ class Renderer {
380393 xPositions . set ( keyForPoint ( end ) , 0 ) ;
381394 }
382395
383- const glyphCount = this . populateGlyphInstances (
396+ const gutterGlyphCount = this . populateGutterGlyphInstances (
397+ scrollTop ,
398+ firstVisibleRow ,
399+ firstVisibleRow + lines . length ,
400+ totalRowCount ,
401+ textColor
402+ ) ;
403+ const lineGlyphCount = this . populateLineGlyphInstances (
384404 scrollTop ,
385405 firstVisibleRow ,
386- paddingLeft ,
406+ gutterWidth + paddingLeft ,
387407 lines ,
388408 selections ,
389409 textColor ,
390410 xPositions
391411 ) ;
392-
393412 const {
394413 selectionSolidCount,
395414 cursorSolidCount
396415 } = this . populateSelectionSolidInstances (
397416 scrollTop ,
398417 canvasWidth ,
399- paddingLeft ,
418+ gutterWidth + paddingLeft ,
400419 selections ,
401420 xPositions ,
402421 selectionColors ,
@@ -416,13 +435,69 @@ class Renderer {
416435 viewportScaleX ,
417436 viewportScaleY
418437 ) ;
419- this . drawText ( glyphCount , scrollLeft , viewportScaleX , viewportScaleY ) ;
438+ this . drawText (
439+ this . lineGlyphInstances ,
440+ lineGlyphCount ,
441+ scrollLeft ,
442+ viewportScaleX ,
443+ viewportScaleY
444+ ) ;
420445 this . drawCursors (
421446 cursorSolidCount ,
422447 scrollLeft ,
423448 viewportScaleX ,
424449 viewportScaleY
425450 ) ;
451+
452+ this . clearRectangle (
453+ 0 ,
454+ 0 ,
455+ gutterWidth ,
456+ canvasHeight ,
457+ viewportScaleX ,
458+ viewportScaleY
459+ ) ;
460+ this . drawText (
461+ this . gutterGlyphInstances ,
462+ gutterGlyphCount ,
463+ 0 ,
464+ viewportScaleX ,
465+ viewportScaleY
466+ ) ;
467+ }
468+
469+ clearRectangle ( x , y , width , height , viewportScaleX , viewportScaleY ) {
470+ this . gl . bindVertexArray ( this . solidVAO ) ;
471+ this . gl . disable ( this . gl . BLEND ) ;
472+ this . gl . useProgram ( this . solidProgram ) ;
473+ this . gl . uniform2f (
474+ this . solidViewportScaleLocation ,
475+ viewportScaleX ,
476+ viewportScaleY
477+ ) ;
478+ this . gl . uniform1f ( this . solidScrollLeftLocation , 0 ) ;
479+ this . gl . bindBuffer ( this . gl . ARRAY_BUFFER , this . solidInstancesBuffer ) ;
480+ this . updateSolidInstance (
481+ this . opaqueRectangleInstances ,
482+ 0 ,
483+ x ,
484+ y ,
485+ width ,
486+ height ,
487+ { r : 255 , g : 255 , b : 255 , a : 1 }
488+ ) ;
489+ this . gl . bufferData (
490+ this . gl . ARRAY_BUFFER ,
491+ this . opaqueRectangleInstances ,
492+ this . gl . STREAM_DRAW
493+ ) ;
494+ this . gl . drawElementsInstanced (
495+ this . gl . TRIANGLES ,
496+ 6 ,
497+ this . gl . UNSIGNED_BYTE ,
498+ 0 ,
499+ 1
500+ ) ;
426501 }
427502
428503 drawSelections (
@@ -461,7 +536,13 @@ class Renderer {
461536 ) ;
462537 }
463538
464- drawText ( glyphCount , scrollLeft , viewportScaleX , viewportScaleY ) {
539+ drawText (
540+ glyphInstances ,
541+ glyphCount ,
542+ scrollLeft ,
543+ viewportScaleX ,
544+ viewportScaleY
545+ ) {
465546 this . gl . bindVertexArray ( this . textBlendVAO ) ;
466547 this . gl . enable ( this . gl . BLEND ) ;
467548 this . gl . useProgram ( this . textBlendPass1Program ) ;
@@ -474,7 +555,7 @@ class Renderer {
474555 this . gl . bindBuffer ( this . gl . ARRAY_BUFFER , this . glyphInstancesBuffer ) ;
475556 this . gl . bufferData (
476557 this . gl . ARRAY_BUFFER ,
477- this . glyphInstances ,
558+ glyphInstances ,
478559 this . gl . STREAM_DRAW
479560 ) ;
480561 this . gl . blendFuncSeparate (
@@ -538,7 +619,50 @@ class Renderer {
538619 ) ;
539620 }
540621
541- populateGlyphInstances (
622+ populateGutterGlyphInstances (
623+ scrollTop ,
624+ firstVisibleRow ,
625+ lastVisibleRow ,
626+ totalRowCount ,
627+ textColor
628+ ) {
629+ const firstVisibleRowY = firstVisibleRow * this . style . computedLineHeight ;
630+ let glyphCount = 0 ;
631+ let y = Math . round ( ( firstVisibleRowY - scrollTop ) * this . style . dpiScale ) ;
632+
633+ for ( let row = firstVisibleRow ; row < lastVisibleRow ; row ++ ) {
634+ const text = ( row + 1 ) . toString ( ) ;
635+ let x = 0 ;
636+ for ( let i = 0 ; i < text . length ; i ++ ) {
637+ const char = text [ i ] ;
638+ const variantIndex =
639+ Math . round ( x * SUBPIXEL_DIVISOR ) % SUBPIXEL_DIVISOR ;
640+ const glyph = this . atlas . getGlyph ( char , variantIndex ) ;
641+ this . updateGlyphInstance (
642+ this . gutterGlyphInstances ,
643+ glyphCount ++ ,
644+ Math . round ( x - glyph . variantOffset ) ,
645+ y ,
646+ glyph ,
647+ textColor
648+ ) ;
649+
650+ x += glyph . subpixelWidth ;
651+ }
652+
653+ y += Math . round ( this . style . computedLineHeight * this . style . dpiScale ) ;
654+ }
655+
656+ if ( glyphCount > MAX_INSTANCES ) {
657+ console . error (
658+ `glyphCount of ${ glyphCount } exceeds MAX_INSTANCES of ${ MAX_INSTANCES } `
659+ ) ;
660+ }
661+
662+ return glyphCount ;
663+ }
664+
665+ populateLineGlyphInstances (
542666 scrollTop ,
543667 firstVisibleRow ,
544668 paddingLeft ,
@@ -576,6 +700,7 @@ class Renderer {
576700 const glyph = this . atlas . getGlyph ( char , variantIndex ) ;
577701
578702 this . updateGlyphInstance (
703+ this . lineGlyphInstances ,
579704 glyphCount ++ ,
580705 Math . round ( x - glyph . variantOffset ) ,
581706 y ,
@@ -599,25 +724,25 @@ class Renderer {
599724 return glyphCount ;
600725 }
601726
602- updateGlyphInstance ( i , x , y , glyph , color ) {
727+ updateGlyphInstance ( glyphInstances , i , x , y , glyph , color ) {
603728 const startOffset = 12 * i ;
604729 // targetOrigin
605- this . glyphInstances [ 0 + startOffset ] = x ;
606- this . glyphInstances [ 1 + startOffset ] = y ;
730+ glyphInstances [ 0 + startOffset ] = x ;
731+ glyphInstances [ 1 + startOffset ] = y ;
607732 // targetSize
608- this . glyphInstances [ 2 + startOffset ] = glyph . width ;
609- this . glyphInstances [ 3 + startOffset ] = glyph . height ;
733+ glyphInstances [ 2 + startOffset ] = glyph . width ;
734+ glyphInstances [ 3 + startOffset ] = glyph . height ;
610735 // textColorRGBA
611- this . glyphInstances [ 4 + startOffset ] = color . r ;
612- this . glyphInstances [ 5 + startOffset ] = color . g ;
613- this . glyphInstances [ 6 + startOffset ] = color . b ;
614- this . glyphInstances [ 7 + startOffset ] = color . a ;
736+ glyphInstances [ 4 + startOffset ] = color . r ;
737+ glyphInstances [ 5 + startOffset ] = color . g ;
738+ glyphInstances [ 6 + startOffset ] = color . b ;
739+ glyphInstances [ 7 + startOffset ] = color . a ;
615740 // atlasOrigin
616- this . glyphInstances [ 8 + startOffset ] = glyph . textureU ;
617- this . glyphInstances [ 9 + startOffset ] = glyph . textureV ;
741+ glyphInstances [ 8 + startOffset ] = glyph . textureU ;
742+ glyphInstances [ 9 + startOffset ] = glyph . textureV ;
618743 // atlasSize
619- this . glyphInstances [ 10 + startOffset ] = glyph . textureWidth ;
620- this . glyphInstances [ 11 + startOffset ] = glyph . textureHeight ;
744+ glyphInstances [ 10 + startOffset ] = glyph . textureWidth ;
745+ glyphInstances [ 11 + startOffset ] = glyph . textureHeight ;
621746 }
622747
623748 populateSelectionSolidInstances (
@@ -746,6 +871,11 @@ class Renderer {
746871 arrayBuffer [ 7 + startOffset ] = color . a ;
747872 }
748873
874+ getGutterWidth ( totalRowCount ) {
875+ const digitsCount = Math . floor ( Math . log10 ( totalRowCount ) ) + 1 ;
876+ return Math . ceil ( digitsCount * this . atlas . getGlyph ( "9" , 0 ) . subpixelWidth ) ;
877+ }
878+
749879 createProgram ( vertexShader , fragmentShader ) {
750880 const program = this . gl . createProgram ( ) ;
751881 this . gl . attachShader ( program , vertexShader ) ;
0 commit comments