Add VLoc, VLocFromLoc and LocFromVLoc

VLoc allows any location in the buffer to be represented as a visual
location in the linewrapped buffer. In particular, this is useful
for implementing moving cursor up and down within a wrapped line.
This commit is contained in:
Dmitry Maluka
2021-03-17 21:16:52 +01:00
parent 0487db8b99
commit 7a3d1e6e30
2 changed files with 108 additions and 20 deletions

View File

@@ -75,9 +75,11 @@ func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc {
func (i *InfoWindow) BufWidth() int { return i.Width }
func (i *InfoWindow) BufHeight() int { return 1 }
func (i *InfoWindow) Scroll(s SLoc, n int) SLoc { return s }
func (i *InfoWindow) Diff(s1, s2 SLoc) int { return 0 }
func (i *InfoWindow) SLocFromLoc(loc buffer.Loc) SLoc { return SLoc{0, 0} }
func (i *InfoWindow) Scroll(s SLoc, n int) SLoc { return s }
func (i *InfoWindow) Diff(s1, s2 SLoc) int { return 0 }
func (i *InfoWindow) SLocFromLoc(loc buffer.Loc) SLoc { return SLoc{0, 0} }
func (i *InfoWindow) VLocFromLoc(loc buffer.Loc) VLoc { return VLoc{SLoc{0, 0}, loc.X} }
func (i *InfoWindow) LocFromVLoc(vloc VLoc) buffer.Loc { return buffer.Loc{vloc.VisualX, 0} }
func (i *InfoWindow) Clear() {
for x := 0; x < i.Width; x++ {

View File

@@ -30,27 +30,36 @@ func (s SLoc) GreaterThan(b SLoc) bool {
return s.Line == b.Line && s.Row > b.Row
}
// VLoc represents a location in the buffer as a visual location in the
// linewrapped buffer.
type VLoc struct {
SLoc
VisualX int
}
type SoftWrap interface {
Scroll(s SLoc, n int) SLoc
Diff(s1, s2 SLoc) int
SLocFromLoc(loc buffer.Loc) SLoc
VLocFromLoc(loc buffer.Loc) VLoc
LocFromVLoc(vloc VLoc) buffer.Loc
}
func (w *BufWindow) getRow(loc buffer.Loc) int {
func (w *BufWindow) getVLocFromLoc(loc buffer.Loc) VLoc {
vloc := VLoc{SLoc: SLoc{loc.Y, 0}, VisualX: 0}
if loc.X <= 0 {
return 0
return vloc
}
if w.bufWidth <= 0 {
return 0
return vloc
}
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 {
@@ -60,7 +69,7 @@ func (w *BufWindow) getRow(loc buffer.Loc) int {
switch r {
case '\t':
ts := tabsize - (totalwidth % tabsize)
width = util.Min(ts, w.bufWidth-visualx)
width = util.Min(ts, w.bufWidth-vloc.VisualX)
totalwidth += ts
default:
width = runewidth.RuneWidth(r)
@@ -68,28 +77,81 @@ func (w *BufWindow) getRow(loc buffer.Loc) int {
}
// If a wide rune does not fit in the window
if visualx+width > w.bufWidth && visualx > 0 {
row++
visualx = 0
if vloc.VisualX+width > w.bufWidth && vloc.VisualX > 0 {
vloc.Row++
vloc.VisualX = 0
}
if x == loc.X {
return row
return vloc
}
x++
line = line[size:]
visualx += width
if visualx >= w.bufWidth {
row++
visualx = 0
vloc.VisualX += width
if vloc.VisualX >= w.bufWidth {
vloc.Row++
vloc.VisualX = 0
}
}
return row
return vloc
}
func (w *BufWindow) getLocFromVLoc(svloc VLoc) buffer.Loc {
loc := buffer.Loc{X: 0, Y: svloc.Line}
if w.bufWidth <= 0 {
return loc
}
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
line := w.Buf.LineBytes(svloc.Line)
vloc := VLoc{SLoc: SLoc{svloc.Line, 0}, VisualX: 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-vloc.VisualX)
totalwidth += ts
default:
width = runewidth.RuneWidth(r)
totalwidth += width
}
// If a wide rune does not fit in the window
if vloc.VisualX+width > w.bufWidth && vloc.VisualX > 0 {
if vloc.Row == svloc.Row {
return loc
}
vloc.Row++
vloc.VisualX = 0
}
vloc.VisualX += width
if vloc.Row == svloc.Row && vloc.VisualX > svloc.VisualX {
return loc
}
loc.X++
line = line[size:]
if vloc.VisualX >= w.bufWidth {
vloc.Row++
vloc.VisualX = 0
}
}
return loc
}
func (w *BufWindow) getRowCount(line int) int {
return w.getRow(buffer.Loc{X: util.CharacterCount(w.Buf.LineBytes(line)), Y: line}) + 1
eol := buffer.Loc{X: util.CharacterCount(w.Buf.LineBytes(line)), Y: line}
return w.getVLocFromLoc(eol).Row + 1
}
func (w *BufWindow) scrollUp(s SLoc, n int) SLoc {
@@ -184,5 +246,29 @@ func (w *BufWindow) SLocFromLoc(loc buffer.Loc) SLoc {
if !w.Buf.Settings["softwrap"].(bool) {
return SLoc{loc.Y, 0}
}
return SLoc{loc.Y, w.getRow(loc)}
return w.getVLocFromLoc(loc).SLoc
}
// VLocFromLoc takes a position in the buffer and returns the corresponding
// visual location in the linewrapped buffer.
func (w *BufWindow) VLocFromLoc(loc buffer.Loc) VLoc {
if !w.Buf.Settings["softwrap"].(bool) {
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
visualx := util.StringWidth(w.Buf.LineBytes(loc.Y), loc.X, tabsize)
return VLoc{SLoc{loc.Y, 0}, visualx}
}
return w.getVLocFromLoc(loc)
}
// LocFromVLoc takes a visual location in the linewrapped buffer and returns
// the position in the buffer corresponding to this visual location.
func (w *BufWindow) LocFromVLoc(vloc VLoc) buffer.Loc {
if !w.Buf.Settings["softwrap"].(bool) {
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
x := util.GetCharPosInLine(w.Buf.LineBytes(vloc.Line), vloc.VisualX, tabsize)
return buffer.Loc{x, vloc.Line}
}
return w.getLocFromVLoc(vloc)
}