Adding showchars option

This commit is contained in:
Neko Box Coder
2025-07-13 20:05:39 +01:00
parent 532c315f79
commit 1ef6459846
3 changed files with 81 additions and 23 deletions

View File

@@ -70,7 +70,7 @@ var defaultCommonSettings = map[string]interface{}{
"hltrailingws": false, "hltrailingws": false,
"ignorecase": true, "ignorecase": true,
"incsearch": true, "incsearch": true,
"indentchar": " ", "indentchar": " ", // Deprecated
"keepautoindent": false, "keepautoindent": false,
"matchbrace": true, "matchbrace": true,
"matchbraceleft": true, "matchbraceleft": true,
@@ -88,6 +88,7 @@ var defaultCommonSettings = map[string]interface{}{
"scrollbar": false, "scrollbar": false,
"scrollmargin": float64(3), "scrollmargin": float64(3),
"scrollspeed": float64(2), "scrollspeed": float64(2),
"showchars": "",
"smartpaste": true, "smartpaste": true,
"softwrap": false, "softwrap": false,
"splitbottom": true, "splitbottom": true,
@@ -210,6 +211,7 @@ func validateParsedSettings() error {
} }
continue continue
} }
if _, ok := defaults[k]; ok { if _, ok := defaults[k]; ok {
if e := verifySetting(k, v, defaults[k]); e != nil { if e := verifySetting(k, v, defaults[k]); e != nil {
err = e err = e

View File

@@ -2,6 +2,7 @@ package display
import ( import (
"strconv" "strconv"
"strings"
runewidth "github.com/mattn/go-runewidth" runewidth "github.com/mattn/go-runewidth"
"github.com/micro-editor/tcell/v2" "github.com/micro-editor/tcell/v2"
@@ -450,6 +451,30 @@ func (w *BufWindow) displayBuffer() {
cursors := b.GetCursors() cursors := b.GetCursors()
curStyle := config.DefStyle curStyle := config.DefStyle
// Parse showchars which is in the format of key1=val1,key2=val2,...
spacechars := " "
tabchars := b.Settings["indentchar"].(string)
var indentspacechars string
var indenttabchars string
for _, entry := range strings.Split(b.Settings["showchars"].(string), ",") {
split := strings.SplitN(entry, "=", 2)
if len(split) < 2 {
continue
}
key, val := split[0], split[1]
switch key {
case "space":
spacechars = val
case "tab":
tabchars = val
case "ispace":
indentspacechars = val
case "itab":
indenttabchars = val
}
}
for ; vloc.Y < w.bufHeight; vloc.Y++ { for ; vloc.Y < w.bufHeight; vloc.Y++ {
vloc.X = 0 vloc.X = 0
@@ -495,7 +520,7 @@ func (w *BufWindow) displayBuffer() {
bloc.X = bslice bloc.X = bslice
// returns the rune to be drawn, style of it and if the bg should be preserved // returns the rune to be drawn, style of it and if the bg should be preserved
getRuneStyle := func(r rune, style tcell.Style, isplaceholder bool) (rune, tcell.Style, bool) { getRuneStyle := func(r rune, style tcell.Style, showoffset int, linex int, isplaceholder bool) (rune, tcell.Style, bool) {
if nColsBeforeStart > 0 || vloc.Y < 0 || isplaceholder { if nColsBeforeStart > 0 || vloc.Y < 0 || isplaceholder {
return r, style, false return r, style, false
} }
@@ -518,19 +543,33 @@ func (w *BufWindow) displayBuffer() {
return r, style, false return r, style, false
} }
var drawrune rune var indentrunes []rune
if r == '\t' { switch r {
indentrunes := []rune(b.Settings["indentchar"].(string)) case '\t':
// if empty indentchar settings, use space if bloc.X < leadingwsEnd && indenttabchars != "" {
if len(indentrunes) == 0 { indentrunes = []rune(indenttabchars)
indentrunes = []rune{' '} } else {
indentrunes = []rune(tabchars)
} }
case ' ':
if linex%tabsize == 0 && bloc.X < leadingwsEnd && indentspacechars != "" {
indentrunes = []rune(indentspacechars)
} else {
indentrunes = []rune(spacechars)
}
}
drawrune = indentrunes[0] var drawrune rune
if s, ok := config.Colorscheme["indent-char"]; ok { if showoffset < len(indentrunes) {
fg, _, _ := s.Decompose() drawrune = indentrunes[showoffset]
style = style.Foreground(fg) } else {
} // use space if no showchars or after we showed showchars
drawrune = ' '
}
if s, ok := config.Colorscheme["indent-char"]; ok {
fg, _, _ := s.Decompose()
style = style.Foreground(fg)
} }
preservebg := false preservebg := false
@@ -692,6 +731,7 @@ func (w *BufWindow) displayBuffer() {
width := 0 width := 0
linex := totalwidth
switch r { switch r {
case '\t': case '\t':
ts := tabsize - (totalwidth % tabsize) ts := tabsize - (totalwidth % tabsize)
@@ -732,15 +772,15 @@ func (w *BufWindow) displayBuffer() {
} }
for _, r := range word { for _, r := range word {
drawrune, drawstyle, preservebg := getRuneStyle(r.r, r.style, false) drawrune, drawstyle, preservebg := getRuneStyle(r.r, r.style, 0, linex, false)
draw(drawrune, r.combc, drawstyle, true, true, preservebg) draw(drawrune, r.combc, drawstyle, true, true, preservebg)
// Draw extra characters for tabs or wide runes // Draw extra characters for tabs or wide runes
for i := 1; i < r.width; i++ { for i := 1; i < r.width; i++ {
if r.r == '\t' { if r.r == '\t' {
drawrune, drawstyle, preservebg = getRuneStyle('\t', r.style, false) drawrune, drawstyle, preservebg = getRuneStyle('\t', r.style, i, linex+i, false)
} else { } else {
drawrune, drawstyle, preservebg = getRuneStyle(' ', r.style, true) drawrune, drawstyle, preservebg = getRuneStyle(' ', r.style, i, linex+i, true)
} }
draw(drawrune, nil, drawstyle, true, false, preservebg) draw(drawrune, nil, drawstyle, true, false, preservebg)
} }
@@ -787,7 +827,7 @@ func (w *BufWindow) displayBuffer() {
if vloc.X != maxWidth { if vloc.X != maxWidth {
// Display newline within a selection // Display newline within a selection
drawrune, drawstyle, preservebg := getRuneStyle(' ', config.DefStyle, true) drawrune, drawstyle, preservebg := getRuneStyle(' ', config.DefStyle, 0, totalwidth, true)
draw(drawrune, nil, drawstyle, true, true, preservebg) draw(drawrune, nil, drawstyle, true, true, preservebg)
} }

View File

@@ -203,12 +203,8 @@ Here are the available options:
default value: `true` default value: `true`
* `indentchar`: sets the indentation character. This will not be inserted into * `indentchar`: sets the character to be shown to display tab characters.
files; it is only a visual indicator that whitespace is present. If set to a This option is **deprecated**, use the `tab` key in `showchars` option instead.
printing character, it functions as a subset of the "show invisibles"
setting available in many other text editors. The color of this character is
determined by the `indent-char` field in the current theme rather than the
default text color.
default value: ` ` (space) default value: ` ` (space)
@@ -386,6 +382,25 @@ Here are the available options:
default value: `2` default value: `2`
* `showchars`: sets what characters to be shown to display various invisible
characters in the file. The characters shown will not be inserted into files.
This option is specified in the form of `key1=value1,key2=value2,...`.
Here are the list of keys:
- `space`: space characters
- `tab`: tab characters. If set, overrides the `indentchar` option.
- `ispace`: space characters at indent position before the first visible
character in a line. If this is not set, `space` will be shown
instead.
- `itab`: tab characters before the first visible character in a line.
If this is not set, `tab` will be shown instead.
An example of this option value could be `tab=>,space=.,itab=|>,ispace=|`
The color of the shown character is determined by the `indent-char`
field in the current theme rather than the default text color.
default value: ``
* `smartpaste`: add leading whitespace when pasting multiple lines. * `smartpaste`: add leading whitespace when pasting multiple lines.
This will attempt to preserve the current indentation level when pasting an This will attempt to preserve the current indentation level when pasting an
unindented block. unindented block.
@@ -577,6 +592,7 @@ so that you can see what the formatting should look like.
"scrollbarchar": "|", "scrollbarchar": "|",
"scrollmargin": 3, "scrollmargin": 3,
"scrollspeed": 2, "scrollspeed": 2,
"showchars": "",
"smartpaste": true, "smartpaste": true,
"softwrap": false, "softwrap": false,
"splitbottom": true, "splitbottom": true,