Skip to content

Commit 990cd5e

Browse files
Adding showchars option and removing @ for wide rune in bufwindow, ...
Adding showchars option and deprecating indentchar Removing @ and display ' ' for wide rune instead Small refactoring for the draw function in bufwindow
1 parent 98ff79d commit 990cd5e

File tree

3 files changed

+204
-117
lines changed

3 files changed

+204
-117
lines changed

internal/config/settings.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ var defaultCommonSettings = map[string]interface{}{
7070
"hltrailingws": false,
7171
"ignorecase": true,
7272
"incsearch": true,
73-
"indentchar": " ",
73+
"indentchar": " ", // Deprecated
7474
"keepautoindent": false,
7575
"matchbrace": true,
7676
"matchbraceleft": true,
@@ -88,6 +88,7 @@ var defaultCommonSettings = map[string]interface{}{
8888
"scrollbar": false,
8989
"scrollmargin": float64(3),
9090
"scrollspeed": float64(2),
91+
"showchars": "",
9192
"smartpaste": true,
9293
"softwrap": false,
9394
"splitbottom": true,
@@ -210,6 +211,7 @@ func validateParsedSettings() error {
210211
}
211212
continue
212213
}
214+
213215
if _, ok := defaults[k]; ok {
214216
if e := verifySetting(k, v, defaults[k]); e != nil {
215217
err = e

internal/display/bufwindow.go

Lines changed: 180 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package display
22

33
import (
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,178 @@ 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
504-
}
505-
}
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 || isplaceholder {
525+
return r, style, bgoverridable
526+
}
506527

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-
}
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, bgoverridable
533+
} else {
534+
return r, style.Reverse(true), bgoverridable
523535
}
536+
} else {
537+
return r, style.Underline(true), bgoverridable
524538
}
539+
}
540+
}
525541

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-
}
542+
if r != '\t' && r != ' ' {
543+
return r, style, bgoverridable
544+
}
545+
546+
var indentrunes []rune
547+
switch r {
548+
case '\t':
549+
if bloc.X < leadingwsEnd && !b.Settings["tabstospaces"].(bool) {
550+
if indenttabchars != "" {
551+
indentrunes = []rune(indenttabchars)
552+
} else {
553+
indentrunes = []rune(tabchars)
554+
}
555+
} else {
556+
indentrunes = []rune(tabchars)
557+
}
558+
case ' ':
559+
if bloc.X%tabsize == 0 && bloc.X < leadingwsEnd && b.Settings["tabstospaces"].(bool) {
560+
if indentspacechars != "" {
561+
indentrunes = []rune(indentspacechars)
562+
} else {
563+
indentrunes = []rune(spacechars)
543564
}
565+
} else {
566+
indentrunes = []rune(spacechars)
567+
}
568+
}
569+
570+
var returnrune rune
571+
if showoffset < len(indentrunes) {
572+
returnrune = indentrunes[showoffset]
573+
} else {
574+
// use space if no showchars or after we showed showchars
575+
returnrune = ' '
576+
}
544577

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)
578+
if s, ok := config.Colorscheme["indent-char"]; ok {
579+
fg, _, _ := s.Decompose()
580+
style = style.Foreground(fg)
581+
}
551582

552-
if s, ok := config.Colorscheme["selection"]; ok {
553-
style = s
554-
}
555-
}
583+
if b.Settings["hltaberrors"].(bool) && bloc.X < leadingwsEnd && !isplaceholder {
584+
if s, ok := config.Colorscheme["tab-error"]; ok {
585+
if b.Settings["tabstospaces"].(bool) && r == '\t' {
586+
fg, _, _ := s.Decompose()
587+
style = style.Background(fg)
588+
bgoverridable = false
589+
} else if !b.Settings["tabstospaces"].(bool) && r == ' ' {
590+
fg, _, _ := s.Decompose()
591+
style = style.Background(fg)
592+
bgoverridable = false
593+
}
594+
}
595+
}
556596

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)
597+
if b.Settings["hltrailingws"].(bool) {
598+
if s, ok := config.Colorscheme["trailingws"]; ok {
599+
if bloc.X >= trailingwsStart && bloc.X < blineLen {
600+
hl := true
601+
for _, c := range cursors {
602+
if c.NewTrailingWsY == bloc.Y {
603+
hl = false
604+
break
562605
}
563606
}
607+
if hl {
608+
fg, _, _ := s.Decompose()
609+
style = style.Background(fg)
610+
bgoverridable = false
611+
}
564612
}
613+
}
614+
}
565615

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-
}
616+
return returnrune, style, bgoverridable
617+
}
618+
619+
draw := func(r rune, combc []rune, style tcell.Style, highlight bool, showcursor bool, bgoverridable bool) {
620+
defer func() {
621+
if nColsBeforeStart <= 0 {
622+
vloc.X++
623+
}
624+
nColsBeforeStart--
625+
}()
626+
627+
if nColsBeforeStart > 0 || vloc.Y < 0 {
628+
return
629+
}
630+
631+
if highlight {
632+
if w.Buf.HighlightSearch && w.Buf.SearchMatch(bloc) {
633+
style = config.DefStyle.Reverse(true)
634+
if s, ok := config.Colorscheme["hlsearch"]; ok {
635+
style = s
572636
}
637+
}
573638

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-
}
639+
_, origBg, _ := style.Decompose()
640+
_, defBg, _ := config.DefStyle.Decompose()
580641

581-
r = indentrunes[0]
582-
if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' {
583-
fg, _, _ := s.Decompose()
584-
style = style.Foreground(fg)
642+
// syntax or hlsearch highlighting with non-default background takes precedence
643+
// over cursor-line and color-column
644+
if bgoverridable {
645+
bgoverridable = origBg == defBg
646+
}
647+
648+
for _, c := range cursors {
649+
if c.HasSelection() &&
650+
(bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
651+
bloc.LessThan(c.CurSelection[0]) && bloc.GreaterEqual(c.CurSelection[1])) {
652+
// The current character is selected
653+
style = config.DefStyle.Reverse(true)
654+
655+
if s, ok := config.Colorscheme["selection"]; ok {
656+
style = s
585657
}
586658
}
587659

588-
if s, ok := config.Colorscheme["color-column"]; ok {
589-
if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground {
660+
if b.Settings["cursorline"].(bool) && w.active && bgoverridable &&
661+
!c.HasSelection() && c.Y == bloc.Y {
662+
if s, ok := config.Colorscheme["cursor-line"]; ok {
590663
fg, _, _ := s.Decompose()
591664
style = style.Background(fg)
592665
}
593666
}
667+
}
594668

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-
}
669+
for _, m := range b.Messages {
670+
if bloc.GreaterEqual(m.Start) && bloc.LessThan(m.End) ||
671+
bloc.LessThan(m.End) && bloc.GreaterEqual(m.Start) {
672+
style = style.Underline(true)
673+
break
607674
}
608675
}
609676

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-
}
677+
if s, ok := config.Colorscheme["color-column"]; ok {
678+
if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && bgoverridable {
679+
fg, _, _ := s.Decompose()
680+
style = style.Background(fg)
617681
}
618682
}
619683
}
620-
if nColsBeforeStart <= 0 {
621-
vloc.X++
684+
685+
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style)
686+
687+
if showcursor {
688+
for _, c := range cursors {
689+
if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() {
690+
w.showCursor(w.X+vloc.X, w.Y+vloc.Y, c.Num == 0)
691+
}
692+
}
622693
}
623-
nColsBeforeStart--
624694
}
625695

626696
wrap := func() {
@@ -692,7 +762,7 @@ func (w *BufWindow) displayBuffer() {
692762
// If a word (or just a wide rune) does not fit in the window
693763
if vloc.X+wordwidth > maxWidth && vloc.X > w.gutterOffset {
694764
for vloc.X < maxWidth {
695-
draw(' ', nil, config.DefStyle, false, false)
765+
draw(' ', nil, config.DefStyle, false, false, true)
696766
}
697767

698768
// We either stop or we wrap to draw the word in the next line
@@ -708,18 +778,17 @@ func (w *BufWindow) displayBuffer() {
708778
}
709779

710780
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)
781+
drawrune, drawstyle, bgoverridable := getRuneStyle(r.r, r.style, 0, false)
782+
draw(drawrune, r.combc, drawstyle, true, true, bgoverridable)
783+
784+
// Draw extra characters for tabs or wide runes
785+
for i := 1; i < r.width; i++ {
786+
if r.r == '\t' {
787+
drawrune, drawstyle, bgoverridable = getRuneStyle('\t', r.style, i, false)
788+
} else {
789+
drawrune, drawstyle, bgoverridable = getRuneStyle(' ', r.style, i, true)
722790
}
791+
draw(drawrune, nil, drawstyle, true, false, bgoverridable)
723792
}
724793
bloc.X++
725794
}
@@ -764,7 +833,8 @@ func (w *BufWindow) displayBuffer() {
764833

765834
if vloc.X != maxWidth {
766835
// Display newline within a selection
767-
draw(' ', nil, config.DefStyle, true, true)
836+
drawrune, drawstyle, bgoverridable := getRuneStyle(' ', config.DefStyle, 0, true)
837+
draw(drawrune, nil, drawstyle, true, true, bgoverridable)
768838
}
769839

770840
bloc.X = w.StartCol

0 commit comments

Comments
 (0)