-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Adding indenttabchar, indentspacechar and spacechar options #3760
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ package display | |
|
|
||
| import ( | ||
| "strconv" | ||
| "strings" | ||
|
|
||
| runewidth "github.com/mattn/go-runewidth" | ||
| "github.com/micro-editor/tcell/v2" | ||
|
|
@@ -450,6 +451,30 @@ func (w *BufWindow) displayBuffer() { | |
| cursors := b.GetCursors() | ||
|
|
||
| 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++ { | ||
| vloc.X = 0 | ||
|
|
||
|
|
@@ -494,133 +519,171 @@ func (w *BufWindow) displayBuffer() { | |
| } | ||
| bloc.X = bslice | ||
|
|
||
| draw := func(r rune, combc []rune, style tcell.Style, highlight bool, showcursor bool) { | ||
| if nColsBeforeStart <= 0 && vloc.Y >= 0 { | ||
| if highlight { | ||
| if w.Buf.HighlightSearch && w.Buf.SearchMatch(bloc) { | ||
| style = config.DefStyle.Reverse(true) | ||
| if s, ok := config.Colorscheme["hlsearch"]; ok { | ||
| style = s | ||
| // returns the rune to be drawn, style of it and if the bg should be preserved | ||
| getRuneStyle := func(r rune, style tcell.Style, showoffset int, linex int, isplaceholder bool) (rune, tcell.Style, bool) { | ||
| if nColsBeforeStart > 0 || vloc.Y < 0 || isplaceholder { | ||
| return r, style, false | ||
| } | ||
|
|
||
| for _, mb := range matchingBraces { | ||
|
dmaluka marked this conversation as resolved.
|
||
| if mb.X == bloc.X && mb.Y == bloc.Y { | ||
| if b.Settings["matchbracestyle"].(string) == "highlight" { | ||
| if s, ok := config.Colorscheme["match-brace"]; ok { | ||
| return r, s, false | ||
| } else { | ||
| return r, style.Reverse(true), false | ||
| } | ||
| } else { | ||
| return r, style.Underline(true), false | ||
| } | ||
| } | ||
| } | ||
|
|
||
| _, origBg, _ := style.Decompose() | ||
| _, defBg, _ := config.DefStyle.Decompose() | ||
|
|
||
| // syntax or hlsearch highlighting with non-default background takes precedence | ||
| // over cursor-line and color-column | ||
| dontOverrideBackground := origBg != defBg | ||
|
|
||
| if b.Settings["hltaberrors"].(bool) { | ||
| if s, ok := config.Colorscheme["tab-error"]; ok { | ||
| isTab := (r == '\t') || (r == ' ' && !showcursor) | ||
| if (b.Settings["tabstospaces"].(bool) && isTab) || | ||
| (!b.Settings["tabstospaces"].(bool) && bloc.X < leadingwsEnd && r == ' ' && !isTab) { | ||
| fg, _, _ := s.Decompose() | ||
| style = style.Background(fg) | ||
| dontOverrideBackground = true | ||
|
dmaluka marked this conversation as resolved.
|
||
| } | ||
| } | ||
| if r != '\t' && r != ' ' { | ||
| return r, style, false | ||
| } | ||
|
|
||
| var indentrunes []rune | ||
| switch r { | ||
| case '\t': | ||
| if bloc.X < leadingwsEnd && indenttabchars != "" { | ||
| indentrunes = []rune(indenttabchars) | ||
| } else { | ||
| indentrunes = []rune(tabchars) | ||
| } | ||
| case ' ': | ||
| if linex%tabsize == 0 && bloc.X < leadingwsEnd && indentspacechars != "" { | ||
| indentrunes = []rune(indentspacechars) | ||
| } else { | ||
| indentrunes = []rune(spacechars) | ||
| } | ||
| } | ||
|
|
||
| var drawrune rune | ||
| if showoffset < len(indentrunes) { | ||
| drawrune = indentrunes[showoffset] | ||
| } 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 | ||
| if b.Settings["hltaberrors"].(bool) && bloc.X < leadingwsEnd { | ||
| if s, ok := config.Colorscheme["tab-error"]; ok { | ||
| if b.Settings["tabstospaces"].(bool) && r == '\t' { | ||
| fg, _, _ := s.Decompose() | ||
| style = style.Background(fg) | ||
| preservebg = true | ||
| } else if !b.Settings["tabstospaces"].(bool) && r == ' ' { | ||
| fg, _, _ := s.Decompose() | ||
| style = style.Background(fg) | ||
| preservebg = true | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if b.Settings["hltrailingws"].(bool) { | ||
| if s, ok := config.Colorscheme["trailingws"]; ok { | ||
| if bloc.X >= trailingwsStart && bloc.X < blineLen { | ||
| hl := true | ||
| for _, c := range cursors { | ||
| if c.NewTrailingWsY == bloc.Y { | ||
| hl = false | ||
| break | ||
| } | ||
| } | ||
| if hl { | ||
| fg, _, _ := s.Decompose() | ||
| style = style.Background(fg) | ||
| dontOverrideBackground = true | ||
| } | ||
| if b.Settings["hltrailingws"].(bool) { | ||
| if s, ok := config.Colorscheme["trailingws"]; ok { | ||
| if bloc.X >= trailingwsStart && bloc.X < blineLen { | ||
| hl := true | ||
| for _, c := range cursors { | ||
| if c.NewTrailingWsY == bloc.Y { | ||
| hl = false | ||
| break | ||
| } | ||
| } | ||
| if hl { | ||
| fg, _, _ := s.Decompose() | ||
| style = style.Background(fg) | ||
| preservebg = true | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for _, c := range cursors { | ||
| if c.HasSelection() && | ||
| (bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) || | ||
| bloc.LessThan(c.CurSelection[0]) && bloc.GreaterEqual(c.CurSelection[1])) { | ||
| // The current character is selected | ||
| style = config.DefStyle.Reverse(true) | ||
| return drawrune, style, preservebg | ||
| } | ||
|
|
||
| if s, ok := config.Colorscheme["selection"]; ok { | ||
| style = s | ||
| } | ||
| } | ||
| draw := func(r rune, combc []rune, style tcell.Style, highlight bool, showcursor bool, preservebg bool) { | ||
| defer func() { | ||
|
dmaluka marked this conversation as resolved.
|
||
| if nColsBeforeStart <= 0 { | ||
| vloc.X++ | ||
| } | ||
| nColsBeforeStart-- | ||
| }() | ||
|
|
||
| if b.Settings["cursorline"].(bool) && w.active && !dontOverrideBackground && | ||
| !c.HasSelection() && c.Y == bloc.Y { | ||
| if s, ok := config.Colorscheme["cursor-line"]; ok { | ||
| fg, _, _ := s.Decompose() | ||
| style = style.Background(fg) | ||
| } | ||
| } | ||
| } | ||
| if nColsBeforeStart > 0 || vloc.Y < 0 { | ||
| return | ||
| } | ||
|
|
||
| for _, m := range b.Messages { | ||
| if bloc.GreaterEqual(m.Start) && bloc.LessThan(m.End) || | ||
| bloc.LessThan(m.End) && bloc.GreaterEqual(m.Start) { | ||
| style = style.Underline(true) | ||
| break | ||
| } | ||
| if highlight { | ||
| if w.Buf.HighlightSearch && w.Buf.SearchMatch(bloc) { | ||
| style = config.DefStyle.Reverse(true) | ||
| if s, ok := config.Colorscheme["hlsearch"]; ok { | ||
| style = s | ||
| } | ||
| } | ||
|
|
||
| if r == '\t' { | ||
| indentrunes := []rune(b.Settings["indentchar"].(string)) | ||
| // if empty indentchar settings, use space | ||
| if len(indentrunes) == 0 { | ||
| indentrunes = []rune{' '} | ||
| } | ||
| _, origBg, _ := style.Decompose() | ||
| _, defBg, _ := config.DefStyle.Decompose() | ||
|
|
||
| r = indentrunes[0] | ||
| if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' { | ||
| fg, _, _ := s.Decompose() | ||
| style = style.Foreground(fg) | ||
| // syntax or hlsearch highlighting with non-default background takes precedence | ||
| // over cursor-line and color-column | ||
| if !preservebg && origBg != defBg { | ||
| preservebg = true | ||
| } | ||
|
|
||
| for _, c := range cursors { | ||
| if c.HasSelection() && | ||
| (bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) || | ||
| bloc.LessThan(c.CurSelection[0]) && bloc.GreaterEqual(c.CurSelection[1])) { | ||
| // The current character is selected | ||
| style = config.DefStyle.Reverse(true) | ||
|
|
||
| if s, ok := config.Colorscheme["selection"]; ok { | ||
| style = s | ||
| } | ||
| } | ||
|
|
||
| if s, ok := config.Colorscheme["color-column"]; ok { | ||
| if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground { | ||
| if b.Settings["cursorline"].(bool) && w.active && !preservebg && | ||
| !c.HasSelection() && c.Y == bloc.Y { | ||
| if s, ok := config.Colorscheme["cursor-line"]; ok { | ||
| fg, _, _ := s.Decompose() | ||
| style = style.Background(fg) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for _, mb := range matchingBraces { | ||
| if mb.X == bloc.X && mb.Y == bloc.Y { | ||
| if b.Settings["matchbracestyle"].(string) == "highlight" { | ||
| if s, ok := config.Colorscheme["match-brace"]; ok { | ||
| style = s | ||
| } else { | ||
| style = style.Reverse(true) | ||
| } | ||
| } else { | ||
| style = style.Underline(true) | ||
| } | ||
| } | ||
| for _, m := range b.Messages { | ||
| if bloc.GreaterEqual(m.Start) && bloc.LessThan(m.End) || | ||
| bloc.LessThan(m.End) && bloc.GreaterEqual(m.Start) { | ||
| style = style.Underline(true) | ||
| break | ||
| } | ||
| } | ||
|
|
||
| screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style) | ||
|
|
||
| if showcursor { | ||
| for _, c := range cursors { | ||
| if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() { | ||
| w.showCursor(w.X+vloc.X, w.Y+vloc.Y, c.Num == 0) | ||
| } | ||
| if s, ok := config.Colorscheme["color-column"]; ok { | ||
| if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !preservebg { | ||
| fg, _, _ := s.Decompose() | ||
| style = style.Background(fg) | ||
| } | ||
| } | ||
| } | ||
| if nColsBeforeStart <= 0 { | ||
| vloc.X++ | ||
|
|
||
| screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style) | ||
|
|
||
| if showcursor { | ||
| for _, c := range cursors { | ||
| if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() { | ||
| w.showCursor(w.X+vloc.X, w.Y+vloc.Y, c.Num == 0) | ||
| } | ||
| } | ||
| } | ||
| nColsBeforeStart-- | ||
| } | ||
|
|
||
| wrap := func() { | ||
|
|
@@ -668,6 +731,7 @@ func (w *BufWindow) displayBuffer() { | |
|
|
||
| width := 0 | ||
|
|
||
| linex := totalwidth | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't use |
||
| switch r { | ||
| case '\t': | ||
| ts := tabsize - (totalwidth % tabsize) | ||
|
|
@@ -692,7 +756,7 @@ func (w *BufWindow) displayBuffer() { | |
| // If a word (or just a wide rune) does not fit in the window | ||
| if vloc.X+wordwidth > maxWidth && vloc.X > w.gutterOffset { | ||
| for vloc.X < maxWidth { | ||
| draw(' ', nil, config.DefStyle, false, false) | ||
| draw(' ', nil, config.DefStyle, false, false, true) | ||
| } | ||
|
|
||
| // We either stop or we wrap to draw the word in the next line | ||
|
|
@@ -708,18 +772,17 @@ func (w *BufWindow) displayBuffer() { | |
| } | ||
|
|
||
| for _, r := range word { | ||
| draw(r.r, r.combc, r.style, true, true) | ||
|
|
||
| // Draw any extra characters either spaces for tabs or @ for incomplete wide runes | ||
| if r.width > 1 { | ||
| char := ' ' | ||
| if r.r != '\t' { | ||
| char = '@' | ||
| } | ||
|
|
||
| for i := 1; i < r.width; i++ { | ||
| draw(char, nil, r.style, true, false) | ||
| drawrune, drawstyle, preservebg := getRuneStyle(r.r, r.style, 0, linex, false) | ||
| draw(drawrune, r.combc, drawstyle, true, true, preservebg) | ||
|
|
||
| // Draw extra characters for tabs or wide runes | ||
| for i := 1; i < r.width; i++ { | ||
| if r.r == '\t' { | ||
| drawrune, drawstyle, preservebg = getRuneStyle('\t', r.style, i, linex+i, false) | ||
| } else { | ||
| drawrune, drawstyle, preservebg = getRuneStyle(' ', r.style, i, linex+i, true) | ||
| } | ||
| draw(drawrune, nil, drawstyle, true, false, preservebg) | ||
| } | ||
| bloc.X++ | ||
| } | ||
|
|
@@ -764,7 +827,8 @@ func (w *BufWindow) displayBuffer() { | |
|
|
||
| if vloc.X != maxWidth { | ||
| // Display newline within a selection | ||
| draw(' ', nil, config.DefStyle, true, true) | ||
| drawrune, drawstyle, preservebg := getRuneStyle(' ', config.DefStyle, 0, totalwidth, true) | ||
| draw(drawrune, nil, drawstyle, true, true, preservebg) | ||
| } | ||
|
|
||
| bloc.X = w.StartCol | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of this comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point of the comment is to describe the return values, otherwise you have no idea what it returns unless you read the rest of the impl/call sites.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what we do - look at the call sites to see how the function is actually used and what for. If we are not interested in the call sites, why would we be interested in the function itself?
If the problem is just that we don't see the names of the return values here in the function declaration, we could use named return values? Or the problem is that we don't want to use them since the line would become enormously long (which it already is)?
Anyway, I'm ok with keeping it as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But again, why are you doing that in this commit "Simplifying draw to be less nested" which is supposed to be just about simplifying
draw()to be less nested? This comment should added in the first commit, wheregetRuneStyle()itself is added?And same about renaming
bgoverridableandreturnrune- why do that in this commit, and why do that at all? If we agree they should bepreservebganddrawrune, we should name thempreservebganddrawrunefrom the beginning, in the first commit?I genuinely don't get it.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine, just merged to the wrong commit, have moved it to the first one. Should be good now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, now looks good.
Except that, the commit message of the 2nd commit includes an outdated sentence?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, missed that. Have removed it.