Working horizontal scrolling

This commit is contained in:
Zachary Yedidia
2019-01-02 20:07:48 -05:00
parent 49e8c293ee
commit 8a0bd1f786
5 changed files with 122 additions and 65 deletions

View File

@@ -376,8 +376,8 @@ func DefaultBindings() map[string]string {
"Backspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft",
"Tab": "IndentSelection,InsertTab",
"Backtab": "OutdentSelection,OutdentLine",
"Tab": "InsertTab",
"Backtab": "OutdentLine",
"CtrlO": "OpenFile",
"CtrlS": "Save",
"CtrlF": "Find",

View File

@@ -3,7 +3,6 @@ package buffer
import (
"unicode/utf8"
runewidth "github.com/mattn/go-runewidth"
"github.com/zyedidia/clipboard"
"github.com/zyedidia/micro/cmd/micro/util"
)
@@ -89,33 +88,7 @@ func (c *Cursor) GetVisualX() int {
// 4 visual spaces)
func (c *Cursor) GetCharPosInLine(b []byte, visualPos int) int {
tabsize := int(c.buf.Settings["tabsize"].(float64))
// Scan rune by rune until we exceed the visual width that we are
// looking for. Then we can return the character position we have found
i := 0 // char pos
width := 0 // string visual width
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
b = b[size:]
switch r {
case '\t':
ts := tabsize - (width % tabsize)
width += ts
default:
width += runewidth.RuneWidth(r)
}
if width >= visualPos {
if width == visualPos {
i++
}
break
}
i++
}
return i
return util.GetCharPosInLine(b, visualPos, tabsize)
}
// Start moves the cursor to the start of the line it is on

View File

@@ -103,7 +103,8 @@ func (i *InfoWindow) displayBuffer() {
vlocX := utf8.RuneCountInString(i.Msg)
tabsize := 4
line, nColsBeforeStart := util.SliceVisualEnd(line, blocX, tabsize)
line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, blocX, tabsize)
blocX = bslice
draw := func(r rune, style tcell.Style) {
if nColsBeforeStart <= 0 {

View File

@@ -13,9 +13,13 @@ import (
)
type View struct {
X, Y int // X,Y location of the view
Width, Height int // Width and height of the view
StartLine, StartCol int // Start line and start column of the view (vertical/horizontal scroll)
X, Y int // X,Y location of the view
Width, Height int // Width and height of the view
// Start line and start column of the view (vertical/horizontal scroll)
// note that since the starting column of every line is different if the view
// is scrolled, StartCol is a visual index (will be the same for every line)
StartLine, StartCol int
}
type Window interface {
@@ -37,7 +41,8 @@ type BufWindow struct {
sline *StatusLine
lineHeight []int
lineHeight []int
gutterOffset int
}
// NewBufWindow creates a new window at a location in the screen with a width and height
@@ -60,6 +65,39 @@ func (v *View) SetView(view *View) {
v = view
}
func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style) {
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
width := 0
bloc := buffer.Loc{0, lineN}
b := w.Buf.LineBytes(lineN)
curStyle := config.DefStyle
var s *tcell.Style
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
curStyle, found := w.getStyle(curStyle, bloc, r)
if found {
s = &curStyle
}
w := 0
switch r {
case '\t':
ts := tabsize - (width % tabsize)
w = ts
default:
w = runewidth.RuneWidth(r)
}
if width+w > n {
return b, n - width, bloc.X, s
}
width += w
b = b[size:]
bloc.X++
}
return b, n - width, bloc.X, s
}
// Clear resets all cells in this window to the default style
func (w *BufWindow) Clear() {
for y := 0; y < w.Height; y++ {
@@ -117,18 +155,18 @@ func (w *BufWindow) Relocate() bool {
ret = true
}
// TODO: horizontal scroll
// if !b.Settings["softwrap"].(bool) {
// cx := activeC.GetVisualX()
// if cx < w.StartCol {
// w.StartCol = cx
// ret = true
// }
// if cx+v.lineNumOffset+1 > v.leftCol+v.Width {
// v.leftCol = cx - v.Width + v.lineNumOffset + 1
// ret = true
// }
// }
// horizontal relocation (scrolling)
if !b.Settings["softwrap"].(bool) {
cx := activeC.GetVisualX()
if cx < w.StartCol {
w.StartCol = cx
ret = true
}
if cx+w.gutterOffset+1 > w.StartCol+w.Width {
w.StartCol = cx - w.Width + w.gutterOffset + 1
ret = true
}
}
return ret
}
@@ -158,7 +196,7 @@ func (w *BufWindow) GetMouseLoc(svloc buffer.Loc) buffer.Loc {
vloc := buffer.Loc{X: 0, Y: 0}
// this represents the current draw position in the buffer (char positions)
bloc := buffer.Loc{X: w.StartCol, Y: w.StartLine}
bloc := buffer.Loc{X: -1, Y: w.StartLine}
for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ {
vloc.X = 0
@@ -166,12 +204,9 @@ func (w *BufWindow) GetMouseLoc(svloc buffer.Loc) buffer.Loc {
vloc.X += maxLineNumLength + 1
}
if svloc.X <= vloc.X && vloc.Y == svloc.Y {
return bloc
}
line := b.LineBytes(bloc.Y)
line, nColsBeforeStart := util.SliceVisualEnd(line, bloc.X, tabsize)
line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, w.StartCol, tabsize)
bloc.X = bslice
draw := func() {
if nColsBeforeStart <= 0 {
@@ -182,7 +217,11 @@ func (w *BufWindow) GetMouseLoc(svloc buffer.Loc) buffer.Loc {
w.lineHeight[vloc.Y] = bloc.Y
totalwidth := bloc.X - nColsBeforeStart
totalwidth := w.StartCol - nColsBeforeStart
if svloc.X <= vloc.X && vloc.Y == svloc.Y {
return bloc
}
for len(line) > 0 {
if vloc.X == svloc.X && vloc.Y == svloc.Y {
return bloc
@@ -269,12 +308,12 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxL
// getStyle returns the highlight style for the given character position
// If there is no change to the current highlight style it just returns that
func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc, r rune) tcell.Style {
func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc, r rune) (tcell.Style, bool) {
if group, ok := w.Buf.Match(bloc.Y)[bloc.X]; ok {
s := config.GetColor(group.String())
return s
return s, true
}
return style
return style, false
}
func (w *BufWindow) showCursor(x, y int, main bool) {
@@ -329,7 +368,7 @@ func (w *BufWindow) displayBuffer() {
vloc := buffer.Loc{X: 0, Y: 0}
// this represents the current draw position in the buffer (char positions)
bloc := buffer.Loc{X: w.StartCol, Y: w.StartLine}
bloc := buffer.Loc{X: -1, Y: w.StartLine}
activeC := b.GetActiveCursor()
@@ -344,8 +383,13 @@ func (w *BufWindow) displayBuffer() {
w.drawLineNum(s, false, maxLineNumLength, &vloc, &bloc)
}
line := b.LineBytes(bloc.Y)
line, nColsBeforeStart := util.SliceVisualEnd(line, bloc.X, tabsize)
w.gutterOffset = vloc.X
line, nColsBeforeStart, bslice, startStyle := w.getStartInfo(w.StartCol, bloc.Y)
if startStyle != nil {
curStyle = *startStyle
}
bloc.X = bslice
draw := func(r rune, style tcell.Style) {
if nColsBeforeStart <= 0 {
@@ -376,14 +420,14 @@ func (w *BufWindow) displayBuffer() {
w.lineHeight[vloc.Y] = bloc.Y
totalwidth := bloc.X - nColsBeforeStart
totalwidth := w.StartCol - nColsBeforeStart
for len(line) > 0 {
if activeC.X == bloc.X && activeC.Y == bloc.Y {
w.showCursor(vloc.X, vloc.Y, true)
}
r, size := utf8.DecodeRune(line)
curStyle = w.getStyle(curStyle, bloc, r)
curStyle, _ = w.getStyle(curStyle, bloc, r)
draw(r, curStyle)

View File

@@ -54,8 +54,10 @@ func SliceStart(slc []byte, index int) []byte {
// SliceVisualEnd will take a byte slice and slice off the start
// up to a given visual index. If the index is in the middle of a
// rune the number of visual columns into the rune will be returned
func SliceVisualEnd(b []byte, n, tabsize int) ([]byte, int) {
// It will also return the char pos of the first character of the slice
func SliceVisualEnd(b []byte, n, tabsize int) ([]byte, int, int) {
width := 0
i := 0
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
@@ -68,12 +70,13 @@ func SliceVisualEnd(b []byte, n, tabsize int) ([]byte, int) {
w = runewidth.RuneWidth(r)
}
if width+w > n {
return b, n - width
return b, n - width, i
}
width += w
b = b[size:]
i++
}
return b, width
return b, n - width, i
}
// Abs is a simple absolute value function for ints
@@ -87,6 +90,9 @@ func Abs(n int) int {
// StringWidth returns the visual width of a byte array indexed from 0 to n (rune index)
// with a given tabsize
func StringWidth(b []byte, n, tabsize int) int {
if n <= 0 {
return 0
}
i := 0
width := 0
for len(b) > 0 {
@@ -263,3 +269,36 @@ func GetLeadingWhitespace(b []byte) []byte {
func IntOpt(opt interface{}) int {
return int(opt.(float64))
}
// GetCharPosInLine gets the char position of a visual x y
// coordinate (this is necessary because tabs are 1 char but
// 4 visual spaces)
func GetCharPosInLine(b []byte, visualPos int, tabsize int) int {
// Scan rune by rune until we exceed the visual width that we are
// looking for. Then we can return the character position we have found
i := 0 // char pos
width := 0 // string visual width
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
b = b[size:]
switch r {
case '\t':
ts := tabsize - (width % tabsize)
width += ts
default:
width += runewidth.RuneWidth(r)
}
if width >= visualPos {
if width == visualPos {
i++
}
break
}
i++
}
return i
}