Skip to content

Commit 5330783

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 5330783

File tree

3 files changed

+154
-79
lines changed

3 files changed

+154
-79
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: 130 additions & 72 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 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,96 @@ 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 || isplaceholder {
525+
return r, style, bgoverridable
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, bgoverridable
533+
} else {
534+
return r, style.Reverse(true), bgoverridable
535+
}
536+
} else {
537+
return r, style.Underline(true), bgoverridable
538+
}
539+
}
540+
}
541+
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+
indentrunes = []rune(indenttabchars)
551+
} else {
552+
indentrunes = []rune(tabchars)
553+
}
554+
case ' ':
555+
if bloc.X%tabsize == 0 && bloc.X < leadingwsEnd && b.Settings["tabstospaces"].(bool) {
556+
indentrunes = []rune(indentspacechars)
557+
} else {
558+
indentrunes = []rune(spacechars)
559+
}
560+
}
561+
562+
var returnrune rune
563+
if showoffset < len(indentrunes) {
564+
returnrune = indentrunes[showoffset]
565+
} else {
566+
// use space if no showchars or after we showed showchars
567+
returnrune = ' '
568+
}
569+
570+
if s, ok := config.Colorscheme["indent-char"]; ok {
571+
fg, _, _ := s.Decompose()
572+
style = style.Foreground(fg)
573+
}
574+
575+
if b.Settings["hltaberrors"].(bool) && bloc.X < leadingwsEnd && !isplaceholder {
576+
if s, ok := config.Colorscheme["tab-error"]; ok {
577+
if b.Settings["tabstospaces"].(bool) && r == '\t' {
578+
fg, _, _ := s.Decompose()
579+
style = style.Background(fg)
580+
bgoverridable = false
581+
} else if !b.Settings["tabstospaces"].(bool) && r == ' ' {
582+
fg, _, _ := s.Decompose()
583+
style = style.Background(fg)
584+
bgoverridable = false
585+
}
586+
}
587+
}
588+
589+
if b.Settings["hltrailingws"].(bool) {
590+
if s, ok := config.Colorscheme["trailingws"]; ok {
591+
if bloc.X >= trailingwsStart && bloc.X < blineLen {
592+
hl := true
593+
for _, c := range cursors {
594+
if c.NewTrailingWsY == bloc.Y {
595+
hl = false
596+
break
597+
}
598+
}
599+
if hl {
600+
fg, _, _ := s.Decompose()
601+
style = style.Background(fg)
602+
bgoverridable = false
603+
}
604+
}
605+
}
606+
}
607+
608+
return returnrune, style, bgoverridable
609+
}
610+
611+
draw := func(r rune, combc []rune, style tcell.Style, highlight bool, showcursor bool, bgoverridable bool) {
498612
if nColsBeforeStart <= 0 && vloc.Y >= 0 {
499613
if highlight {
500614
if w.Buf.HighlightSearch && w.Buf.SearchMatch(bloc) {
@@ -510,36 +624,8 @@ func (w *BufWindow) displayBuffer() {
510624
// syntax or hlsearch highlighting with non-default background takes precedence
511625
// over cursor-line and color-column
512626
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-
}
627+
if !bgoverridable {
628+
dontOverrideBackground = true
543629
}
544630

545631
for _, c := range cursors {
@@ -571,40 +657,12 @@ func (w *BufWindow) displayBuffer() {
571657
}
572658
}
573659

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-
588660
if s, ok := config.Colorscheme["color-column"]; ok {
589661
if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground {
590662
fg, _, _ := s.Decompose()
591663
style = style.Background(fg)
592664
}
593665
}
594-
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-
}
607-
}
608666
}
609667

610668
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style)
@@ -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

runtime/help/options.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,8 @@ Here are the available options:
203203

204204
default value: `true`
205205

206-
* `indentchar`: sets the indentation character. This will not be inserted into
207-
files; it is only a visual indicator that whitespace is present. If set to a
208-
printing character, it functions as a subset of the "show invisibles"
209-
setting available in many other text editors. The color of this character is
210-
determined by the `indent-char` field in the current theme rather than the
211-
default text color.
206+
* `indentchar`: sets the character to be shown to display tab characters.
207+
This option is **deprecated**, use the `showchars` option instead.
212208

213209
default value: ` ` (space)
214210

@@ -386,6 +382,24 @@ Here are the available options:
386382

387383
default value: `2`
388384

385+
* `showchars`: sets what characters to be shown to display various invisible
386+
characters in the file. The characters shown will not be inserted into files.
387+
(This option overrides the `indentchar` option if that exists)
388+
This option is specified in the form of `key1=value1,key2=value2,...`.
389+
390+
Here are the list of keys:
391+
- `ispace`: indent space characters, for space at indent position before
392+
the first visible character
393+
- `itab`: indent tab characters, for tab before the first visible character
394+
- `space`: space characters, for space that is not displayed by `ispace`
395+
- `tab`: tab characters, for tab that is not displayed by `itab`
396+
An example of this option value could be `tab=>,space=.,itab=|>,ispace=|`
397+
398+
The color of the shown character is determined by the `indent-char`
399+
field in the current theme rather than the default text color.
400+
401+
default value: ``
402+
389403
* `smartpaste`: add leading whitespace when pasting multiple lines.
390404
This will attempt to preserve the current indentation level when pasting an
391405
unindented block.
@@ -577,6 +591,7 @@ so that you can see what the formatting should look like.
577591
"scrollbarchar": "|",
578592
"scrollmargin": 3,
579593
"scrollspeed": 2,
594+
"showchars": "",
580595
"smartpaste": true,
581596
"softwrap": false,
582597
"splitbottom": true,

0 commit comments

Comments
 (0)