Fix displaying incomplete tab or wide rune at the right edge of window
Fix displaying tabs and wide runes which don't fit in the window. Don't overwrite the vertical divider and the adjacent window. - For tabs: display only as many of the tab's spaces as fit in the window. - For wide runes: if a rune doesn't fit, don't display it in this line at all. If softwrap is on, display this rune in the next line. Fixes #1979
This commit is contained in:
parent
a1651aec2f
commit
cd7ab640c5
2 changed files with 114 additions and 35 deletions
|
@ -261,40 +261,54 @@ func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc {
|
|||
|
||||
totalwidth := w.StartCol - nColsBeforeStart
|
||||
|
||||
if svloc.X <= vloc.X+w.X && vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
for len(line) > 0 {
|
||||
if vloc.X+w.X == svloc.X && vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
|
||||
r, _, size := util.DecodeCharacter(line)
|
||||
draw()
|
||||
|
||||
width := 0
|
||||
|
||||
switch r {
|
||||
case '\t':
|
||||
ts := tabsize - (totalwidth % tabsize)
|
||||
width = ts
|
||||
width = util.Min(ts, maxWidth-vloc.X)
|
||||
totalwidth += ts
|
||||
default:
|
||||
width = runewidth.RuneWidth(r)
|
||||
totalwidth += width
|
||||
}
|
||||
|
||||
// If a wide rune does not fit in the window
|
||||
if vloc.X+width > maxWidth && vloc.X > w.gutterOffset {
|
||||
if vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
|
||||
// We either stop or we wrap to draw the rune in the next line
|
||||
if !softwrap {
|
||||
break
|
||||
} else {
|
||||
vloc.Y++
|
||||
if vloc.Y >= w.bufHeight {
|
||||
break
|
||||
}
|
||||
vloc.X = w.gutterOffset
|
||||
}
|
||||
}
|
||||
|
||||
draw()
|
||||
|
||||
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
|
||||
if width > 1 {
|
||||
for i := 1; i < width; i++ {
|
||||
if vloc.X+w.X == svloc.X && vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
draw()
|
||||
}
|
||||
}
|
||||
|
||||
if svloc.X < vloc.X+w.X && vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
bloc.X++
|
||||
line = line[size:]
|
||||
|
||||
totalwidth += width
|
||||
|
||||
// If we reach the end of the window then we either stop or we wrap for softwrap
|
||||
if vloc.X >= maxWidth {
|
||||
if !softwrap {
|
||||
|
@ -623,26 +637,61 @@ func (w *BufWindow) displayBuffer() {
|
|||
nColsBeforeStart--
|
||||
}
|
||||
|
||||
wrap := func() {
|
||||
vloc.X = 0
|
||||
if w.hasMessage {
|
||||
w.drawGutter(&vloc, &bloc)
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
w.drawDiffGutter(lineNumStyle, true, &vloc, &bloc)
|
||||
}
|
||||
|
||||
// This will draw an empty line number because the current line is wrapped
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.drawLineNum(lineNumStyle, true, &vloc, &bloc)
|
||||
}
|
||||
}
|
||||
|
||||
totalwidth := w.StartCol - nColsBeforeStart
|
||||
for len(line) > 0 {
|
||||
r, combc, size := util.DecodeCharacter(line)
|
||||
|
||||
curStyle, _ = w.getStyle(curStyle, bloc)
|
||||
|
||||
draw(r, combc, curStyle, true)
|
||||
|
||||
width := 0
|
||||
|
||||
char := ' '
|
||||
switch r {
|
||||
case '\t':
|
||||
ts := tabsize - (totalwidth % tabsize)
|
||||
width = ts
|
||||
width = util.Min(ts, maxWidth-vloc.X)
|
||||
totalwidth += ts
|
||||
default:
|
||||
width = runewidth.RuneWidth(r)
|
||||
char = '@'
|
||||
totalwidth += width
|
||||
}
|
||||
|
||||
// If a wide rune does not fit in the window
|
||||
if vloc.X+width > maxWidth && vloc.X > w.gutterOffset {
|
||||
for vloc.X < maxWidth {
|
||||
draw(' ', nil, config.DefStyle, false)
|
||||
}
|
||||
|
||||
// We either stop or we wrap to draw the rune in the next line
|
||||
if !softwrap {
|
||||
break
|
||||
} else {
|
||||
vloc.Y++
|
||||
if vloc.Y >= w.bufHeight {
|
||||
break
|
||||
}
|
||||
wrap()
|
||||
}
|
||||
}
|
||||
|
||||
draw(r, combc, curStyle, true)
|
||||
|
||||
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
|
||||
if width > 1 {
|
||||
for i := 1; i < width; i++ {
|
||||
|
@ -652,8 +701,6 @@ func (w *BufWindow) displayBuffer() {
|
|||
bloc.X++
|
||||
line = line[size:]
|
||||
|
||||
totalwidth += width
|
||||
|
||||
// If we reach the end of the window then we either stop or we wrap for softwrap
|
||||
if vloc.X >= maxWidth {
|
||||
if !softwrap {
|
||||
|
@ -663,18 +710,7 @@ func (w *BufWindow) displayBuffer() {
|
|||
if vloc.Y >= w.bufHeight {
|
||||
break
|
||||
}
|
||||
vloc.X = 0
|
||||
if w.hasMessage {
|
||||
w.drawGutter(&vloc, &bloc)
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
w.drawDiffGutter(lineNumStyle, true, &vloc, &bloc)
|
||||
}
|
||||
|
||||
// This will draw an empty line number because the current line is wrapped
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.drawLineNum(lineNumStyle, true, &vloc, &bloc)
|
||||
}
|
||||
wrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
runewidth "github.com/mattn/go-runewidth"
|
||||
"github.com/zyedidia/micro/v2/internal/buffer"
|
||||
"github.com/zyedidia/micro/v2/internal/util"
|
||||
)
|
||||
|
@ -36,13 +37,55 @@ type SoftWrap interface {
|
|||
}
|
||||
|
||||
func (w *BufWindow) getRow(loc buffer.Loc) int {
|
||||
if loc.X <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if w.bufWidth <= 0 {
|
||||
return 0
|
||||
}
|
||||
// TODO: this doesn't work quite correctly if there is an incomplete tab
|
||||
// or wide character at the end of a row. See also issue #1979
|
||||
x := util.StringWidth(w.Buf.LineBytes(loc.Y), loc.X, util.IntOpt(w.Buf.Settings["tabsize"]))
|
||||
return x / w.bufWidth
|
||||
|
||||
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
||||
|
||||
line := w.Buf.LineBytes(loc.Y)
|
||||
x := 0
|
||||
visualx := 0
|
||||
row := 0
|
||||
totalwidth := 0
|
||||
|
||||
for len(line) > 0 {
|
||||
r, _, size := util.DecodeCharacter(line)
|
||||
|
||||
width := 0
|
||||
switch r {
|
||||
case '\t':
|
||||
ts := tabsize - (totalwidth % tabsize)
|
||||
width = util.Min(ts, w.bufWidth-visualx)
|
||||
totalwidth += ts
|
||||
default:
|
||||
width = runewidth.RuneWidth(r)
|
||||
totalwidth += width
|
||||
}
|
||||
|
||||
// If a wide rune does not fit in the window
|
||||
if visualx+width > w.bufWidth && visualx > 0 {
|
||||
row++
|
||||
visualx = 0
|
||||
}
|
||||
|
||||
if x == loc.X {
|
||||
return row
|
||||
}
|
||||
x++
|
||||
line = line[size:]
|
||||
|
||||
visualx += width
|
||||
if visualx >= w.bufWidth {
|
||||
row++
|
||||
visualx = 0
|
||||
}
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
func (w *BufWindow) getRowCount(line int) int {
|
||||
|
|
Loading…
Reference in a new issue