@@ -2,6 +2,7 @@ package display
22
33import (
44 "strconv"
5+ "strings"
56
67 runewidth "github.com/mattn/go-runewidth"
78 "github.com/micro-editor/tcell/v2"
@@ -450,6 +451,30 @@ func (w *BufWindow) displayBuffer() {
450451 cursors := b .GetCursors ()
451452
452453 curStyle := config .DefStyle
454+
455+ // Parse showchars which is in the format of option1=val1,option2=val2,...
456+ spacechars := " "
457+ tabchars := b .Settings ["indentchar" ].(string )
458+ indentspacechars := " "
459+ indenttabchars := b .Settings ["indentchar" ].(string )
460+ for _ , entry := range strings .Split (b .Settings ["showchars" ].(string ), "," ) {
461+ split := strings .SplitN (entry , "=" , 2 )
462+ if len (split ) < 2 {
463+ continue
464+ }
465+ key , val := split [0 ], split [1 ]
466+ switch key {
467+ case "space" :
468+ spacechars = val
469+ case "tab" :
470+ tabchars = val
471+ case "ispace" :
472+ indentspacechars = val
473+ case "itab" :
474+ indenttabchars = val
475+ }
476+ }
477+
453478 for ; vloc .Y < w .bufHeight ; vloc .Y ++ {
454479 vloc .X = 0
455480
@@ -494,7 +519,82 @@ func (w *BufWindow) displayBuffer() {
494519 }
495520 bloc .X = bslice
496521
497- draw := func (r rune , combc []rune , style tcell.Style , highlight bool , showcursor bool ) {
522+ getRuneStyle := func (r rune , style tcell.Style , showoffset int , isplaceholder bool ) (rune , tcell.Style , bool ) {
523+ bgoverridable := true
524+ if nColsBeforeStart > 0 || vloc .Y < 0 {
525+ return r , style , bgoverridable
526+ }
527+
528+ if isplaceholder || (r != '\t' && r != ' ' ) {
529+ return r , style , bgoverridable
530+ }
531+
532+ var indentrunes []rune
533+ switch r {
534+ case '\t' :
535+ if bloc .X < leadingwsEnd && ! b .Settings ["tabstospaces" ].(bool ) {
536+ indentrunes = []rune (indenttabchars )
537+ } else {
538+ indentrunes = []rune (tabchars )
539+ }
540+ case ' ' :
541+ if bloc .X % tabsize == 0 && bloc .X < leadingwsEnd && b .Settings ["tabstospaces" ].(bool ) {
542+ indentrunes = []rune (indentspacechars )
543+ } else {
544+ indentrunes = []rune (spacechars )
545+ }
546+ }
547+
548+ var returnrune rune
549+ if showoffset < len (indentrunes ) {
550+ returnrune = indentrunes [showoffset ]
551+ } else {
552+ // use space if no showchars or after we showed showchars
553+ returnrune = ' '
554+ }
555+
556+ if s , ok := config .Colorscheme ["indent-char" ]; ok {
557+ fg , _ , _ := s .Decompose ()
558+ style = style .Foreground (fg )
559+ }
560+
561+ if b .Settings ["hltaberrors" ].(bool ) && bloc .X < leadingwsEnd && ! isplaceholder {
562+ if s , ok := config .Colorscheme ["tab-error" ]; ok {
563+ if b .Settings ["tabstospaces" ].(bool ) && r == '\t' {
564+ fg , _ , _ := s .Decompose ()
565+ style = style .Background (fg )
566+ bgoverridable = false
567+ } else if ! b .Settings ["tabstospaces" ].(bool ) && r == ' ' {
568+ fg , _ , _ := s .Decompose ()
569+ style = style .Background (fg )
570+ bgoverridable = false
571+ }
572+ }
573+ }
574+
575+ if b .Settings ["hltrailingws" ].(bool ) {
576+ if s , ok := config .Colorscheme ["trailingws" ]; ok {
577+ if bloc .X >= trailingwsStart && bloc .X < blineLen {
578+ hl := true
579+ for _ , c := range cursors {
580+ if c .NewTrailingWsY == bloc .Y {
581+ hl = false
582+ break
583+ }
584+ }
585+ if hl {
586+ fg , _ , _ := s .Decompose ()
587+ style = style .Background (fg )
588+ bgoverridable = false
589+ }
590+ }
591+ }
592+ }
593+
594+ return returnrune , style , bgoverridable
595+ }
596+
597+ draw := func (r rune , combc []rune , style tcell.Style , highlight bool , showcursor bool , bgoverridable bool ) {
498598 if nColsBeforeStart <= 0 && vloc .Y >= 0 {
499599 if highlight {
500600 if w .Buf .HighlightSearch && w .Buf .SearchMatch (bloc ) {
@@ -510,36 +610,8 @@ func (w *BufWindow) displayBuffer() {
510610 // syntax or hlsearch highlighting with non-default background takes precedence
511611 // over cursor-line and color-column
512612 dontOverrideBackground := origBg != defBg
513-
514- if b .Settings ["hltaberrors" ].(bool ) {
515- if s , ok := config .Colorscheme ["tab-error" ]; ok {
516- isTab := (r == '\t' ) || (r == ' ' && ! showcursor )
517- if (b .Settings ["tabstospaces" ].(bool ) && isTab ) ||
518- (! b .Settings ["tabstospaces" ].(bool ) && bloc .X < leadingwsEnd && r == ' ' && ! isTab ) {
519- fg , _ , _ := s .Decompose ()
520- style = style .Background (fg )
521- dontOverrideBackground = true
522- }
523- }
524- }
525-
526- if b .Settings ["hltrailingws" ].(bool ) {
527- if s , ok := config .Colorscheme ["trailingws" ]; ok {
528- if bloc .X >= trailingwsStart && bloc .X < blineLen {
529- hl := true
530- for _ , c := range cursors {
531- if c .NewTrailingWsY == bloc .Y {
532- hl = false
533- break
534- }
535- }
536- if hl {
537- fg , _ , _ := s .Decompose ()
538- style = style .Background (fg )
539- dontOverrideBackground = true
540- }
541- }
542- }
613+ if ! bgoverridable {
614+ dontOverrideBackground = true
543615 }
544616
545617 for _ , c := range cursors {
@@ -571,20 +643,6 @@ func (w *BufWindow) displayBuffer() {
571643 }
572644 }
573645
574- if r == '\t' {
575- indentrunes := []rune (b .Settings ["indentchar" ].(string ))
576- // if empty indentchar settings, use space
577- if len (indentrunes ) == 0 {
578- indentrunes = []rune {' ' }
579- }
580-
581- r = indentrunes [0 ]
582- if s , ok := config .Colorscheme ["indent-char" ]; ok && r != ' ' {
583- fg , _ , _ := s .Decompose ()
584- style = style .Foreground (fg )
585- }
586- }
587-
588646 if s , ok := config .Colorscheme ["color-column" ]; ok {
589647 if colorcolumn != 0 && vloc .X - w .gutterOffset + w .StartCol == colorcolumn && ! dontOverrideBackground {
590648 fg , _ , _ := s .Decompose ()
@@ -692,7 +750,7 @@ func (w *BufWindow) displayBuffer() {
692750 // If a word (or just a wide rune) does not fit in the window
693751 if vloc .X + wordwidth > maxWidth && vloc .X > w .gutterOffset {
694752 for vloc .X < maxWidth {
695- draw (' ' , nil , config .DefStyle , false , false )
753+ draw (' ' , nil , config .DefStyle , false , false , true )
696754 }
697755
698756 // We either stop or we wrap to draw the word in the next line
@@ -708,18 +766,17 @@ func (w *BufWindow) displayBuffer() {
708766 }
709767
710768 for _ , r := range word {
711- draw (r .r , r .combc , r .style , true , true )
712-
713- // Draw any extra characters either spaces for tabs or @ for incomplete wide runes
714- if r .width > 1 {
715- char := ' '
716- if r .r != '\t' {
717- char = '@'
718- }
719-
720- for i := 1 ; i < r .width ; i ++ {
721- draw (char , nil , r .style , true , false )
769+ drawrune , drawstyle , bgoverridable := getRuneStyle (r .r , r .style , 0 , false )
770+ draw (drawrune , r .combc , drawstyle , true , true , bgoverridable )
771+
772+ // Draw extra characters for tabs or wide runes
773+ for i := 1 ; i < r .width ; i ++ {
774+ if r .r == '\t' {
775+ drawrune , drawstyle , bgoverridable = getRuneStyle ('\t' , r .style , i , false )
776+ } else {
777+ drawrune , drawstyle , bgoverridable = getRuneStyle (' ' , r .style , i , true )
722778 }
779+ draw (drawrune , nil , drawstyle , true , false , bgoverridable )
723780 }
724781 bloc .X ++
725782 }
@@ -764,7 +821,8 @@ func (w *BufWindow) displayBuffer() {
764821
765822 if vloc .X != maxWidth {
766823 // Display newline within a selection
767- draw (' ' , nil , config .DefStyle , true , true )
824+ drawrune , drawstyle , bgoverridable := getRuneStyle (' ' , config .DefStyle , 0 , true )
825+ draw (drawrune , nil , drawstyle , true , true , bgoverridable )
768826 }
769827
770828 bloc .X = w .StartCol
0 commit comments