@@ -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 key1=val1,key2=val2,...
456+ spacechars := " "
457+ tabchars := b .Settings ["indentchar" ].(string )
458+ var indentspacechars string
459+ var indenttabchars 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,133 +519,171 @@ func (w *BufWindow) displayBuffer() {
494519 }
495520 bloc .X = bslice
496521
497- draw := func (r rune , combc []rune , style tcell.Style , highlight bool , showcursor bool ) {
498- if nColsBeforeStart <= 0 && vloc .Y >= 0 {
499- if highlight {
500- if w .Buf .HighlightSearch && w .Buf .SearchMatch (bloc ) {
501- style = config .DefStyle .Reverse (true )
502- if s , ok := config .Colorscheme ["hlsearch" ]; ok {
503- style = s
522+ // returns the rune to be drawn, style of it and if the bg should be preserved
523+ getRuneStyle := func (r rune , style tcell.Style , showoffset int , linex int , isplaceholder bool ) (rune , tcell.Style , bool ) {
524+ if nColsBeforeStart > 0 || vloc .Y < 0 || isplaceholder {
525+ return r , style , false
526+ }
527+
528+ for _ , mb := range matchingBraces {
529+ if mb .X == bloc .X && mb .Y == bloc .Y {
530+ if b .Settings ["matchbracestyle" ].(string ) == "highlight" {
531+ if s , ok := config .Colorscheme ["match-brace" ]; ok {
532+ return r , s , false
533+ } else {
534+ return r , style .Reverse (true ), false
504535 }
536+ } else {
537+ return r , style .Underline (true ), false
505538 }
539+ }
540+ }
506541
507- _ , origBg , _ := style .Decompose ()
508- _ , defBg , _ := config .DefStyle .Decompose ()
509-
510- // syntax or hlsearch highlighting with non-default background takes precedence
511- // over cursor-line and color-column
512- 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- }
542+ if r != '\t' && r != ' ' {
543+ return r , style , false
544+ }
545+
546+ var indentrunes []rune
547+ switch r {
548+ case '\t' :
549+ if bloc .X < leadingwsEnd && indenttabchars != "" {
550+ indentrunes = []rune (indenttabchars )
551+ } else {
552+ indentrunes = []rune (tabchars )
553+ }
554+ case ' ' :
555+ if linex % tabsize == 0 && bloc .X < leadingwsEnd && indentspacechars != "" {
556+ indentrunes = []rune (indentspacechars )
557+ } else {
558+ indentrunes = []rune (spacechars )
559+ }
560+ }
561+
562+ var drawrune rune
563+ if showoffset < len (indentrunes ) {
564+ drawrune = indentrunes [showoffset ]
565+ } else {
566+ // use space if no showchars or after we showed showchars
567+ drawrune = ' '
568+ }
569+
570+ if s , ok := config .Colorscheme ["indent-char" ]; ok {
571+ fg , _ , _ := s .Decompose ()
572+ style = style .Foreground (fg )
573+ }
574+
575+ preservebg := false
576+ if b .Settings ["hltaberrors" ].(bool ) && bloc .X < leadingwsEnd {
577+ if s , ok := config .Colorscheme ["tab-error" ]; ok {
578+ if b .Settings ["tabstospaces" ].(bool ) && r == '\t' {
579+ fg , _ , _ := s .Decompose ()
580+ style = style .Background (fg )
581+ preservebg = true
582+ } else if ! b .Settings ["tabstospaces" ].(bool ) && r == ' ' {
583+ fg , _ , _ := s .Decompose ()
584+ style = style .Background (fg )
585+ preservebg = true
524586 }
587+ }
588+ }
525589
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- }
590+ if b .Settings ["hltrailingws" ].(bool ) {
591+ if s , ok := config .Colorscheme ["trailingws" ]; ok {
592+ if bloc .X >= trailingwsStart && bloc .X < blineLen {
593+ hl := true
594+ for _ , c := range cursors {
595+ if c .NewTrailingWsY == bloc .Y {
596+ hl = false
597+ break
541598 }
542599 }
600+ if hl {
601+ fg , _ , _ := s .Decompose ()
602+ style = style .Background (fg )
603+ preservebg = true
604+ }
543605 }
606+ }
607+ }
544608
545- for _ , c := range cursors {
546- if c .HasSelection () &&
547- (bloc .GreaterEqual (c .CurSelection [0 ]) && bloc .LessThan (c .CurSelection [1 ]) ||
548- bloc .LessThan (c .CurSelection [0 ]) && bloc .GreaterEqual (c .CurSelection [1 ])) {
549- // The current character is selected
550- style = config .DefStyle .Reverse (true )
609+ return drawrune , style , preservebg
610+ }
551611
552- if s , ok := config .Colorscheme ["selection" ]; ok {
553- style = s
554- }
555- }
612+ draw := func (r rune , combc []rune , style tcell.Style , highlight bool , showcursor bool , preservebg bool ) {
613+ defer func () {
614+ if nColsBeforeStart <= 0 {
615+ vloc .X ++
616+ }
617+ nColsBeforeStart --
618+ }()
556619
557- if b .Settings ["cursorline" ].(bool ) && w .active && ! dontOverrideBackground &&
558- ! c .HasSelection () && c .Y == bloc .Y {
559- if s , ok := config .Colorscheme ["cursor-line" ]; ok {
560- fg , _ , _ := s .Decompose ()
561- style = style .Background (fg )
562- }
563- }
564- }
620+ if nColsBeforeStart > 0 || vloc .Y < 0 {
621+ return
622+ }
565623
566- for _ , m := range b .Messages {
567- if bloc .GreaterEqual (m .Start ) && bloc .LessThan (m .End ) ||
568- bloc .LessThan (m .End ) && bloc .GreaterEqual (m .Start ) {
569- style = style .Underline (true )
570- break
571- }
624+ if highlight {
625+ if w .Buf .HighlightSearch && w .Buf .SearchMatch (bloc ) {
626+ style = config .DefStyle .Reverse (true )
627+ if s , ok := config .Colorscheme ["hlsearch" ]; ok {
628+ style = s
572629 }
630+ }
573631
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- }
632+ _ , origBg , _ := style .Decompose ()
633+ _ , defBg , _ := config .DefStyle .Decompose ()
580634
581- r = indentrunes [0 ]
582- if s , ok := config .Colorscheme ["indent-char" ]; ok && r != ' ' {
583- fg , _ , _ := s .Decompose ()
584- style = style .Foreground (fg )
635+ // syntax or hlsearch highlighting with non-default background takes precedence
636+ // over cursor-line and color-column
637+ if ! preservebg && origBg != defBg {
638+ preservebg = true
639+ }
640+
641+ for _ , c := range cursors {
642+ if c .HasSelection () &&
643+ (bloc .GreaterEqual (c .CurSelection [0 ]) && bloc .LessThan (c .CurSelection [1 ]) ||
644+ bloc .LessThan (c .CurSelection [0 ]) && bloc .GreaterEqual (c .CurSelection [1 ])) {
645+ // The current character is selected
646+ style = config .DefStyle .Reverse (true )
647+
648+ if s , ok := config .Colorscheme ["selection" ]; ok {
649+ style = s
585650 }
586651 }
587652
588- if s , ok := config .Colorscheme ["color-column" ]; ok {
589- if colorcolumn != 0 && vloc .X - w .gutterOffset + w .StartCol == colorcolumn && ! dontOverrideBackground {
653+ if b .Settings ["cursorline" ].(bool ) && w .active && ! preservebg &&
654+ ! c .HasSelection () && c .Y == bloc .Y {
655+ if s , ok := config .Colorscheme ["cursor-line" ]; ok {
590656 fg , _ , _ := s .Decompose ()
591657 style = style .Background (fg )
592658 }
593659 }
660+ }
594661
595- for _ , mb := range matchingBraces {
596- if mb .X == bloc .X && mb .Y == bloc .Y {
597- if b .Settings ["matchbracestyle" ].(string ) == "highlight" {
598- if s , ok := config .Colorscheme ["match-brace" ]; ok {
599- style = s
600- } else {
601- style = style .Reverse (true )
602- }
603- } else {
604- style = style .Underline (true )
605- }
606- }
662+ for _ , m := range b .Messages {
663+ if bloc .GreaterEqual (m .Start ) && bloc .LessThan (m .End ) ||
664+ bloc .LessThan (m .End ) && bloc .GreaterEqual (m .Start ) {
665+ style = style .Underline (true )
666+ break
607667 }
608668 }
609669
610- screen .SetContent (w .X + vloc .X , w .Y + vloc .Y , r , combc , style )
611-
612- if showcursor {
613- for _ , c := range cursors {
614- if c .X == bloc .X && c .Y == bloc .Y && ! c .HasSelection () {
615- w .showCursor (w .X + vloc .X , w .Y + vloc .Y , c .Num == 0 )
616- }
670+ if s , ok := config .Colorscheme ["color-column" ]; ok {
671+ if colorcolumn != 0 && vloc .X - w .gutterOffset + w .StartCol == colorcolumn && ! preservebg {
672+ fg , _ , _ := s .Decompose ()
673+ style = style .Background (fg )
617674 }
618675 }
619676 }
620- if nColsBeforeStart <= 0 {
621- vloc .X ++
677+
678+ screen .SetContent (w .X + vloc .X , w .Y + vloc .Y , r , combc , style )
679+
680+ if showcursor {
681+ for _ , c := range cursors {
682+ if c .X == bloc .X && c .Y == bloc .Y && ! c .HasSelection () {
683+ w .showCursor (w .X + vloc .X , w .Y + vloc .Y , c .Num == 0 )
684+ }
685+ }
622686 }
623- nColsBeforeStart --
624687 }
625688
626689 wrap := func () {
@@ -668,6 +731,7 @@ func (w *BufWindow) displayBuffer() {
668731
669732 width := 0
670733
734+ linex := totalwidth
671735 switch r {
672736 case '\t' :
673737 ts := tabsize - (totalwidth % tabsize )
@@ -692,7 +756,7 @@ func (w *BufWindow) displayBuffer() {
692756 // If a word (or just a wide rune) does not fit in the window
693757 if vloc .X + wordwidth > maxWidth && vloc .X > w .gutterOffset {
694758 for vloc .X < maxWidth {
695- draw (' ' , nil , config .DefStyle , false , false )
759+ draw (' ' , nil , config .DefStyle , false , false , true )
696760 }
697761
698762 // We either stop or we wrap to draw the word in the next line
@@ -708,18 +772,17 @@ func (w *BufWindow) displayBuffer() {
708772 }
709773
710774 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 )
775+ drawrune , drawstyle , preservebg := getRuneStyle (r .r , r .style , 0 , linex , false )
776+ draw (drawrune , r .combc , drawstyle , true , true , preservebg )
777+
778+ // Draw extra characters for tabs or wide runes
779+ for i := 1 ; i < r .width ; i ++ {
780+ if r .r == '\t' {
781+ drawrune , drawstyle , preservebg = getRuneStyle ('\t' , r .style , i , linex + i , false )
782+ } else {
783+ drawrune , drawstyle , preservebg = getRuneStyle (' ' , r .style , i , linex + i , true )
722784 }
785+ draw (drawrune , nil , drawstyle , true , false , preservebg )
723786 }
724787 bloc .X ++
725788 }
@@ -764,7 +827,8 @@ func (w *BufWindow) displayBuffer() {
764827
765828 if vloc .X != maxWidth {
766829 // Display newline within a selection
767- draw (' ' , nil , config .DefStyle , true , true )
830+ drawrune , drawstyle , preservebg := getRuneStyle (' ' , config .DefStyle , 0 , totalwidth , true )
831+ draw (drawrune , nil , drawstyle , true , true , preservebg )
768832 }
769833
770834 bloc .X = w .StartCol
0 commit comments