Skip to content

Commit 0443ec3

Browse files
Adding showchars option and removing @ for wide rune in bufwindow
1 parent 98ff79d commit 0443ec3

File tree

3 files changed

+129
-64
lines changed

3 files changed

+129
-64
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: 107 additions & 57 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,6 +519,76 @@ func (w *BufWindow) displayBuffer() {
494519
}
495520
bloc.X = bslice
496521

522+
getRuneStyle := func(r rune, style tcell.Style, showoffset int, isplaceholder bool) (rune, tcell.Style) {
523+
if nColsBeforeStart > 0 || vloc.Y < 0 {
524+
return r, style
525+
}
526+
527+
if isplaceholder || (r != '\t' && r != ' ') {
528+
return r, style
529+
}
530+
531+
var indentrunes []rune
532+
switch r {
533+
case '\t':
534+
if bloc.X < leadingwsEnd && !b.Settings["tabstospaces"].(bool) {
535+
indentrunes = []rune(indenttabchars)
536+
} else {
537+
indentrunes = []rune(tabchars)
538+
}
539+
case ' ':
540+
if bloc.X%tabsize == 0 && bloc.X < leadingwsEnd && b.Settings["tabstospaces"].(bool) {
541+
indentrunes = []rune(indentspacechars)
542+
} else {
543+
indentrunes = []rune(spacechars)
544+
}
545+
}
546+
547+
if showoffset < len(indentrunes) {
548+
r = indentrunes[showoffset]
549+
} else {
550+
// use space if no showchars or after we showed showchars
551+
r = ' '
552+
}
553+
554+
if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' {
555+
fg, _, _ := s.Decompose()
556+
style = style.Foreground(fg)
557+
}
558+
559+
if b.Settings["hltaberrors"].(bool) && !isplaceholder {
560+
if s, ok := config.Colorscheme["tab-error"]; ok {
561+
if b.Settings["tabstospaces"].(bool) && r == '\t' {
562+
fg, _, _ := s.Decompose()
563+
style = style.Background(fg)
564+
} else if !b.Settings["tabstospaces"].(bool) && r == ' ' {
565+
fg, _, _ := s.Decompose()
566+
style = style.Background(fg)
567+
}
568+
}
569+
}
570+
571+
if b.Settings["hltrailingws"].(bool) {
572+
if s, ok := config.Colorscheme["trailingws"]; ok {
573+
if bloc.X >= trailingwsStart && bloc.X < blineLen {
574+
hl := true
575+
for _, c := range cursors {
576+
if c.NewTrailingWsY == bloc.Y {
577+
hl = false
578+
break
579+
}
580+
}
581+
if hl {
582+
fg, _, _ := s.Decompose()
583+
style = style.Background(fg)
584+
}
585+
}
586+
}
587+
}
588+
589+
return r, style
590+
}
591+
497592
draw := func(r rune, combc []rune, style tcell.Style, highlight bool, showcursor bool) {
498593
if nColsBeforeStart <= 0 && vloc.Y >= 0 {
499594
if highlight {
@@ -511,37 +606,6 @@ func (w *BufWindow) displayBuffer() {
511606
// over cursor-line and color-column
512607
dontOverrideBackground := origBg != defBg
513608

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-
}
543-
}
544-
545609
for _, c := range cursors {
546610
if c.HasSelection() &&
547611
(bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
@@ -571,20 +635,6 @@ func (w *BufWindow) displayBuffer() {
571635
}
572636
}
573637

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-
588638
if s, ok := config.Colorscheme["color-column"]; ok {
589639
if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground {
590640
fg, _, _ := s.Decompose()
@@ -708,18 +758,17 @@ func (w *BufWindow) displayBuffer() {
708758
}
709759

710760
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)
761+
drawrune, drawstyle := getRuneStyle(r.r, r.style, 0, false)
762+
draw(drawrune, r.combc, drawstyle, true, true)
763+
764+
// Draw extra characters for tabs or wide runes
765+
for i := 1; i < r.width; i++ {
766+
if r.r == '\t' {
767+
drawrune, drawstyle = getRuneStyle('\t', r.style, i, false)
768+
} else {
769+
drawrune, drawstyle = getRuneStyle(' ', r.style, i, true)
722770
}
771+
draw(drawrune, nil, drawstyle, true, false)
723772
}
724773
bloc.X++
725774
}
@@ -764,7 +813,8 @@ func (w *BufWindow) displayBuffer() {
764813

765814
if vloc.X != maxWidth {
766815
// Display newline within a selection
767-
draw(' ', nil, config.DefStyle, true, true)
816+
drawrune, drawstyle := getRuneStyle(' ', config.DefStyle, 0, true)
817+
draw(drawrune, nil, drawstyle, true, true)
768818
}
769819

770820
bloc.X = w.StartCol

runtime/help/options.md

Lines changed: 19 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,22 @@ Here are the available options:
386382

387383
default value: `2`
388384

385+
* `showchars`: sets what characters to be shown to display various inivisible
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+
Here are the list of keys:
390+
The color of the shown character is determined by the `indent-char`
391+
field in the current theme rather than the default text color.
392+
- `ispace`: indent space characters, for space at indent position before
393+
the first visible character
394+
- `itab`: indent tab characters, for tab before the first visible character
395+
- `space`: space characters, for space that is not displayed by `ispace`
396+
- `tab`: tab characters, for tab that is not displayed by `itab`
397+
An example of this option value could be `tab=>,space=.,itab=|>,ispace=|`
398+
399+
default value: ``
400+
389401
* `smartpaste`: add leading whitespace when pasting multiple lines.
390402
This will attempt to preserve the current indentation level when pasting an
391403
unindented block.
@@ -577,6 +589,7 @@ so that you can see what the formatting should look like.
577589
"scrollbarchar": "|",
578590
"scrollmargin": 3,
579591
"scrollspeed": 2,
592+
"showchars": "",
580593
"smartpaste": true,
581594
"softwrap": false,
582595
"splitbottom": true,

0 commit comments

Comments
 (0)