mirror of
https://github.com/zyedidia/micro.git
synced 2025-06-18 14:55:38 -04:00
Merge pull request #2076 from dmaluka/softwrap-improvement2
Softwrap improvements
This commit is contained in:
commit
346c10f20e
@ -36,8 +36,8 @@ func (h *BufPane) ScrollDown(n int) {
|
||||
func (h *BufPane) ScrollAdjust() {
|
||||
v := h.GetView()
|
||||
end := h.SLocFromLoc(h.Buf.End())
|
||||
if h.Diff(v.StartLine, end) < v.Height-1 {
|
||||
v.StartLine = h.Scroll(end, -v.Height+1)
|
||||
if h.Diff(v.StartLine, end) < h.BufView().Height-1 {
|
||||
v.StartLine = h.Scroll(end, -h.BufView().Height+1)
|
||||
}
|
||||
h.SetView(v)
|
||||
}
|
||||
@ -117,16 +117,55 @@ func (h *BufPane) ScrollDownAction() bool {
|
||||
// Center centers the view on the cursor
|
||||
func (h *BufPane) Center() bool {
|
||||
v := h.GetView()
|
||||
v.StartLine = h.Scroll(h.SLocFromLoc(h.Cursor.Loc), -v.Height/2)
|
||||
v.StartLine = h.Scroll(h.SLocFromLoc(h.Cursor.Loc), -h.BufView().Height/2)
|
||||
h.SetView(v)
|
||||
h.ScrollAdjust()
|
||||
return true
|
||||
}
|
||||
|
||||
// MoveCursorUp is not an action
|
||||
func (h *BufPane) MoveCursorUp(n int) {
|
||||
if !h.Buf.Settings["softwrap"].(bool) {
|
||||
h.Cursor.UpN(n)
|
||||
} else {
|
||||
vloc := h.VLocFromLoc(h.Cursor.Loc)
|
||||
sloc := h.Scroll(vloc.SLoc, -n)
|
||||
if sloc == vloc.SLoc {
|
||||
// we are at the beginning of buffer
|
||||
h.Cursor.Loc = h.Buf.Start()
|
||||
h.Cursor.LastVisualX = 0
|
||||
} else {
|
||||
vloc.SLoc = sloc
|
||||
vloc.VisualX = h.Cursor.LastVisualX
|
||||
h.Cursor.Loc = h.LocFromVLoc(vloc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MoveCursorDown is not an action
|
||||
func (h *BufPane) MoveCursorDown(n int) {
|
||||
if !h.Buf.Settings["softwrap"].(bool) {
|
||||
h.Cursor.DownN(n)
|
||||
} else {
|
||||
vloc := h.VLocFromLoc(h.Cursor.Loc)
|
||||
sloc := h.Scroll(vloc.SLoc, n)
|
||||
if sloc == vloc.SLoc {
|
||||
// we are at the end of buffer
|
||||
h.Cursor.Loc = h.Buf.End()
|
||||
vloc = h.VLocFromLoc(h.Cursor.Loc)
|
||||
h.Cursor.LastVisualX = vloc.VisualX
|
||||
} else {
|
||||
vloc.SLoc = sloc
|
||||
vloc.VisualX = h.Cursor.LastVisualX
|
||||
h.Cursor.Loc = h.LocFromVLoc(vloc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CursorUp moves the cursor up
|
||||
func (h *BufPane) CursorUp() bool {
|
||||
h.Cursor.Deselect(true)
|
||||
h.Cursor.Up()
|
||||
h.MoveCursorUp(1)
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
@ -134,7 +173,7 @@ func (h *BufPane) CursorUp() bool {
|
||||
// CursorDown moves the cursor down
|
||||
func (h *BufPane) CursorDown() bool {
|
||||
h.Cursor.Deselect(true)
|
||||
h.Cursor.Down()
|
||||
h.MoveCursorDown(1)
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
@ -212,7 +251,7 @@ func (h *BufPane) SelectUp() bool {
|
||||
if !h.Cursor.HasSelection() {
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
}
|
||||
h.Cursor.Up()
|
||||
h.MoveCursorUp(1)
|
||||
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||
h.Relocate()
|
||||
return true
|
||||
@ -223,7 +262,7 @@ func (h *BufPane) SelectDown() bool {
|
||||
if !h.Cursor.HasSelection() {
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
}
|
||||
h.Cursor.Down()
|
||||
h.MoveCursorDown(1)
|
||||
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||
h.Relocate()
|
||||
return true
|
||||
@ -1251,22 +1290,20 @@ func (h *BufPane) Start() bool {
|
||||
// End moves the viewport to the end of the buffer
|
||||
func (h *BufPane) End() bool {
|
||||
v := h.GetView()
|
||||
v.StartLine = h.Scroll(h.SLocFromLoc(h.Buf.End()), -v.Height+1)
|
||||
v.StartLine = h.Scroll(h.SLocFromLoc(h.Buf.End()), -h.BufView().Height+1)
|
||||
h.SetView(v)
|
||||
return true
|
||||
}
|
||||
|
||||
// PageUp scrolls the view up a page
|
||||
func (h *BufPane) PageUp() bool {
|
||||
v := h.GetView()
|
||||
h.ScrollUp(v.Height)
|
||||
h.ScrollUp(h.BufView().Height)
|
||||
return true
|
||||
}
|
||||
|
||||
// PageDown scrolls the view down a page
|
||||
func (h *BufPane) PageDown() bool {
|
||||
v := h.GetView()
|
||||
h.ScrollDown(v.Height)
|
||||
h.ScrollDown(h.BufView().Height)
|
||||
h.ScrollAdjust()
|
||||
return true
|
||||
}
|
||||
@ -1276,7 +1313,7 @@ func (h *BufPane) SelectPageUp() bool {
|
||||
if !h.Cursor.HasSelection() {
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
}
|
||||
h.Cursor.UpN(h.GetView().Height)
|
||||
h.MoveCursorUp(h.BufView().Height)
|
||||
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||
h.Relocate()
|
||||
return true
|
||||
@ -1287,7 +1324,7 @@ func (h *BufPane) SelectPageDown() bool {
|
||||
if !h.Cursor.HasSelection() {
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
}
|
||||
h.Cursor.DownN(h.GetView().Height)
|
||||
h.MoveCursorDown(h.BufView().Height)
|
||||
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||
h.Relocate()
|
||||
return true
|
||||
@ -1302,7 +1339,7 @@ func (h *BufPane) CursorPageUp() bool {
|
||||
h.Cursor.ResetSelection()
|
||||
h.Cursor.StoreVisualX()
|
||||
}
|
||||
h.Cursor.UpN(h.GetView().Height)
|
||||
h.MoveCursorUp(h.BufView().Height)
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
@ -1316,22 +1353,20 @@ func (h *BufPane) CursorPageDown() bool {
|
||||
h.Cursor.ResetSelection()
|
||||
h.Cursor.StoreVisualX()
|
||||
}
|
||||
h.Cursor.DownN(h.GetView().Height)
|
||||
h.MoveCursorDown(h.BufView().Height)
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
|
||||
// HalfPageUp scrolls the view up half a page
|
||||
func (h *BufPane) HalfPageUp() bool {
|
||||
v := h.GetView()
|
||||
h.ScrollUp(v.Height / 2)
|
||||
h.ScrollUp(h.BufView().Height / 2)
|
||||
return true
|
||||
}
|
||||
|
||||
// HalfPageDown scrolls the view down half a page
|
||||
func (h *BufPane) HalfPageDown() bool {
|
||||
v := h.GetView()
|
||||
h.ScrollDown(v.Height / 2)
|
||||
h.ScrollDown(h.BufView().Height / 2)
|
||||
h.ScrollAdjust()
|
||||
return true
|
||||
}
|
||||
|
@ -189,6 +189,19 @@ type Buffer struct {
|
||||
cursors []*Cursor
|
||||
curCursor int
|
||||
StartCursor Loc
|
||||
|
||||
// OptionCallback is called after a buffer option value is changed.
|
||||
// The display module registers its OptionCallback to ensure the buffer window
|
||||
// is properly updated when needed. This is a workaround for the fact that
|
||||
// the buffer module cannot directly call the display's API (it would mean
|
||||
// a circular dependency between packages).
|
||||
OptionCallback func(option string, nativeValue interface{})
|
||||
|
||||
// The display module registers its own GetVisualX function for getting
|
||||
// the correct visual x location of a cursor when softwrap is used.
|
||||
// This is hacky. Maybe it would be better to move all the visual x logic
|
||||
// from buffer to display, but it would require rewriting a lot of code.
|
||||
GetVisualX func(loc Loc) int
|
||||
}
|
||||
|
||||
// NewBufferFromFileAtLoc opens a new buffer with a given cursor location
|
||||
|
@ -67,6 +67,10 @@ func (c *Cursor) GotoLoc(l Loc) {
|
||||
|
||||
// GetVisualX returns the x value of the cursor in visual spaces
|
||||
func (c *Cursor) GetVisualX() int {
|
||||
if c.buf.GetVisualX != nil {
|
||||
return c.buf.GetVisualX(c.Loc)
|
||||
}
|
||||
|
||||
if c.X <= 0 {
|
||||
c.X = 0
|
||||
return 0
|
||||
|
@ -41,6 +41,10 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error {
|
||||
b.Type.Readonly = nativeValue.(bool)
|
||||
}
|
||||
|
||||
if b.OptionCallback != nil {
|
||||
b.OptionCallback(option, nativeValue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -297,6 +297,7 @@ var defaultCommonSettings = map[string]interface{}{
|
||||
"tabsize": float64(4),
|
||||
"tabstospaces": false,
|
||||
"useprimary": true,
|
||||
"wordwrap": false,
|
||||
}
|
||||
|
||||
func GetInfoBarOffset() int {
|
||||
|
@ -23,15 +23,20 @@ type BufWindow struct {
|
||||
|
||||
sline *StatusLine
|
||||
|
||||
gutterOffset int
|
||||
drawStatus bool
|
||||
bufWidth int
|
||||
bufHeight int
|
||||
gutterOffset int
|
||||
hasMessage bool
|
||||
maxLineNumLength int
|
||||
drawDivider bool
|
||||
}
|
||||
|
||||
// NewBufWindow creates a new window at a location in the screen with a width and height
|
||||
func NewBufWindow(x, y, width, height int, buf *buffer.Buffer) *BufWindow {
|
||||
w := new(BufWindow)
|
||||
w.View = new(View)
|
||||
w.X, w.Y, w.Width, w.Height, w.Buf = x, y, width, height, buf
|
||||
w.X, w.Y, w.Width, w.Height = x, y, width, height
|
||||
w.SetBuffer(buf)
|
||||
w.active = true
|
||||
|
||||
w.sline = NewStatusLine(w)
|
||||
@ -41,6 +46,23 @@ func NewBufWindow(x, y, width, height int, buf *buffer.Buffer) *BufWindow {
|
||||
|
||||
func (w *BufWindow) SetBuffer(b *buffer.Buffer) {
|
||||
w.Buf = b
|
||||
b.OptionCallback = func(option string, nativeValue interface{}) {
|
||||
if option == "softwrap" {
|
||||
if nativeValue.(bool) {
|
||||
w.StartCol = 0
|
||||
} else {
|
||||
w.StartLine.Row = 0
|
||||
}
|
||||
w.Relocate()
|
||||
|
||||
for _, c := range w.Buf.GetCursors() {
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
}
|
||||
}
|
||||
}
|
||||
b.GetVisualX = func(loc buffer.Loc) int {
|
||||
return w.VLocFromLoc(loc).VisualX
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) GetView() *View {
|
||||
@ -53,7 +75,15 @@ func (w *BufWindow) SetView(view *View) {
|
||||
|
||||
func (w *BufWindow) Resize(width, height int) {
|
||||
w.Width, w.Height = width, height
|
||||
w.updateDisplayInfo()
|
||||
|
||||
w.Relocate()
|
||||
|
||||
if w.Buf.Settings["softwrap"].(bool) {
|
||||
for _, c := range w.Buf.GetCursors() {
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) SetActive(b bool) {
|
||||
@ -64,6 +94,63 @@ func (w *BufWindow) IsActive() bool {
|
||||
return w.active
|
||||
}
|
||||
|
||||
// BufView returns the width, height and x,y location of the actual buffer.
|
||||
// It is not exactly the same as the whole window which also contains gutter,
|
||||
// ruler, scrollbar and statusline.
|
||||
func (w *BufWindow) BufView() View {
|
||||
return View{
|
||||
X: w.X + w.gutterOffset,
|
||||
Y: w.Y,
|
||||
Width: w.bufWidth,
|
||||
Height: w.bufHeight,
|
||||
StartLine: w.StartLine,
|
||||
StartCol: w.StartCol,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) updateDisplayInfo() {
|
||||
b := w.Buf
|
||||
|
||||
w.drawDivider = false
|
||||
if !b.Settings["statusline"].(bool) {
|
||||
_, h := screen.Screen.Size()
|
||||
infoY := h
|
||||
if config.GetGlobalOption("infobar").(bool) {
|
||||
infoY--
|
||||
}
|
||||
if w.Y+w.Height != infoY {
|
||||
w.drawDivider = true
|
||||
}
|
||||
}
|
||||
|
||||
w.bufHeight = w.Height
|
||||
if b.Settings["statusline"].(bool) || w.drawDivider {
|
||||
w.bufHeight--
|
||||
}
|
||||
|
||||
w.hasMessage = len(b.Messages) > 0
|
||||
|
||||
// We need to know the string length of the largest line number
|
||||
// so we can pad appropriately when displaying line numbers
|
||||
w.maxLineNumLength = len(strconv.Itoa(b.LinesNum()))
|
||||
|
||||
w.gutterOffset = 0
|
||||
if w.hasMessage {
|
||||
w.gutterOffset += 2
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
w.gutterOffset++
|
||||
}
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.gutterOffset += w.maxLineNumLength + 1
|
||||
}
|
||||
|
||||
w.bufWidth = w.Width - w.gutterOffset
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
w.bufWidth--
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style) {
|
||||
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
||||
width := 0
|
||||
@ -111,10 +198,7 @@ func (w *BufWindow) Clear() {
|
||||
// Returns true if the window location is moved
|
||||
func (w *BufWindow) Relocate() bool {
|
||||
b := w.Buf
|
||||
height := w.Height
|
||||
if w.drawStatus {
|
||||
height--
|
||||
}
|
||||
height := w.bufHeight
|
||||
ret := false
|
||||
activeC := w.Buf.GetActiveCursor()
|
||||
scrollmargin := int(b.Settings["scrollmargin"].(float64))
|
||||
@ -141,12 +225,17 @@ func (w *BufWindow) Relocate() bool {
|
||||
// horizontal relocation (scrolling)
|
||||
if !b.Settings["softwrap"].(bool) {
|
||||
cx := activeC.GetVisualX()
|
||||
rw := runewidth.RuneWidth(activeC.RuneUnder(activeC.X))
|
||||
if rw == 0 {
|
||||
rw = 1 // tab or newline
|
||||
}
|
||||
|
||||
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
|
||||
if cx+w.gutterOffset+rw > w.StartCol+w.Width {
|
||||
w.StartCol = cx - w.Width + w.gutterOffset + rw
|
||||
ret = true
|
||||
}
|
||||
}
|
||||
@ -155,127 +244,18 @@ func (w *BufWindow) Relocate() bool {
|
||||
|
||||
// LocFromVisual takes a visual location (x and y position) and returns the
|
||||
// position in the buffer corresponding to the visual location
|
||||
// Computing the buffer location requires essentially drawing the entire screen
|
||||
// to account for complications like softwrap, wide characters, and horizontal scrolling
|
||||
// If the requested position does not correspond to a buffer location it returns
|
||||
// the nearest position
|
||||
func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc {
|
||||
b := w.Buf
|
||||
|
||||
hasMessage := len(b.Messages) > 0
|
||||
bufHeight := w.Height
|
||||
if w.drawStatus {
|
||||
bufHeight--
|
||||
vx := svloc.X - w.X - w.gutterOffset
|
||||
if vx < 0 {
|
||||
vx = 0
|
||||
}
|
||||
|
||||
bufWidth := w.Width
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
bufWidth--
|
||||
vloc := VLoc{
|
||||
SLoc: w.Scroll(w.StartLine, svloc.Y-w.Y),
|
||||
VisualX: vx + w.StartCol,
|
||||
}
|
||||
|
||||
// We need to know the string length of the largest line number
|
||||
// so we can pad appropriately when displaying line numbers
|
||||
maxLineNumLength := len(strconv.Itoa(b.LinesNum()))
|
||||
|
||||
tabsize := int(b.Settings["tabsize"].(float64))
|
||||
softwrap := b.Settings["softwrap"].(bool)
|
||||
|
||||
// this represents the current draw position
|
||||
// within the current window
|
||||
vloc := buffer.Loc{X: 0, Y: 0}
|
||||
if softwrap {
|
||||
// the start line may be partially out of the current window
|
||||
vloc.Y = -w.StartLine.Row
|
||||
}
|
||||
|
||||
// this represents the current draw position in the buffer (char positions)
|
||||
bloc := buffer.Loc{X: -1, Y: w.StartLine.Line}
|
||||
|
||||
for ; vloc.Y < bufHeight; vloc.Y++ {
|
||||
vloc.X = 0
|
||||
if hasMessage {
|
||||
vloc.X += 2
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
vloc.X++
|
||||
}
|
||||
if b.Settings["ruler"].(bool) {
|
||||
vloc.X += maxLineNumLength + 1
|
||||
}
|
||||
|
||||
line := b.LineBytes(bloc.Y)
|
||||
line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, w.StartCol, tabsize)
|
||||
bloc.X = bslice
|
||||
|
||||
draw := func() {
|
||||
if nColsBeforeStart <= 0 {
|
||||
vloc.X++
|
||||
}
|
||||
nColsBeforeStart--
|
||||
}
|
||||
|
||||
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
|
||||
default:
|
||||
width = runewidth.RuneWidth(r)
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
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 >= bufWidth {
|
||||
if !softwrap {
|
||||
break
|
||||
} else {
|
||||
vloc.Y++
|
||||
if vloc.Y >= bufHeight {
|
||||
break
|
||||
}
|
||||
vloc.X = w.gutterOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
if vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
|
||||
if bloc.Y+1 >= b.LinesNum() || vloc.Y+1 >= bufHeight {
|
||||
return bloc
|
||||
}
|
||||
|
||||
bloc.X = w.StartCol
|
||||
bloc.Y++
|
||||
}
|
||||
|
||||
return buffer.Loc{}
|
||||
return w.LocFromVLoc(vloc)
|
||||
}
|
||||
|
||||
func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||
@ -322,7 +302,7 @@ func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool
|
||||
vloc.X++
|
||||
}
|
||||
|
||||
func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxLineNumLength int, vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||
func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||
cursorLine := w.Buf.GetActiveCursor().Loc.Y
|
||||
var lineInt int
|
||||
if w.Buf.Settings["relativeruler"] == false || cursorLine == bloc.Y {
|
||||
@ -333,7 +313,7 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxL
|
||||
lineNum := strconv.Itoa(util.Abs(lineInt))
|
||||
|
||||
// Write the spaces before the line number if necessary
|
||||
for i := 0; i < maxLineNumLength-len(lineNum); i++ {
|
||||
for i := 0; i < w.maxLineNumLength-len(lineNum); i++ {
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||
vloc.X++
|
||||
}
|
||||
@ -380,16 +360,7 @@ func (w *BufWindow) displayBuffer() {
|
||||
return
|
||||
}
|
||||
|
||||
hasMessage := len(b.Messages) > 0
|
||||
bufHeight := w.Height
|
||||
if w.drawStatus {
|
||||
bufHeight--
|
||||
}
|
||||
|
||||
bufWidth := w.Width
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
bufWidth--
|
||||
}
|
||||
maxWidth := w.gutterOffset + w.bufWidth
|
||||
|
||||
if b.ModifiedThisFrame {
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
@ -450,11 +421,9 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
}
|
||||
|
||||
// We need to know the string length of the largest line number
|
||||
// so we can pad appropriately when displaying line numbers
|
||||
maxLineNumLength := len(strconv.Itoa(b.LinesNum()))
|
||||
|
||||
softwrap := b.Settings["softwrap"].(bool)
|
||||
wordwrap := softwrap && b.Settings["wordwrap"].(bool)
|
||||
|
||||
tabsize := util.IntOpt(b.Settings["tabsize"])
|
||||
colorcolumn := util.IntOpt(b.Settings["colorcolumn"])
|
||||
|
||||
@ -472,7 +441,7 @@ func (w *BufWindow) displayBuffer() {
|
||||
cursors := b.GetCursors()
|
||||
|
||||
curStyle := config.DefStyle
|
||||
for ; vloc.Y < bufHeight; vloc.Y++ {
|
||||
for ; vloc.Y < w.bufHeight; vloc.Y++ {
|
||||
vloc.X = 0
|
||||
|
||||
currentLine := false
|
||||
@ -489,7 +458,7 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
|
||||
if vloc.Y >= 0 {
|
||||
if hasMessage {
|
||||
if w.hasMessage {
|
||||
w.drawGutter(&vloc, &bloc)
|
||||
}
|
||||
|
||||
@ -498,90 +467,82 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.drawLineNum(s, false, maxLineNumLength, &vloc, &bloc)
|
||||
w.drawLineNum(s, false, &vloc, &bloc)
|
||||
}
|
||||
} else {
|
||||
if hasMessage {
|
||||
vloc.X += 2
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
vloc.X++
|
||||
}
|
||||
if b.Settings["ruler"].(bool) {
|
||||
vloc.X += maxLineNumLength + 1
|
||||
}
|
||||
vloc.X = w.gutterOffset
|
||||
}
|
||||
|
||||
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, combc []rune, style tcell.Style, showcursor bool) {
|
||||
draw := func(r rune, combc []rune, style tcell.Style, highlight bool, showcursor bool) {
|
||||
if nColsBeforeStart <= 0 && vloc.Y >= 0 {
|
||||
_, origBg, _ := style.Decompose()
|
||||
_, defBg, _ := config.DefStyle.Decompose()
|
||||
if highlight {
|
||||
_, origBg, _ := style.Decompose()
|
||||
_, defBg, _ := config.DefStyle.Decompose()
|
||||
|
||||
// syntax highlighting with non-default background takes precedence
|
||||
// over cursor-line and color-column
|
||||
dontOverrideBackground := origBg != defBg
|
||||
// syntax highlighting with non-default background takes precedence
|
||||
// over cursor-line and color-column
|
||||
dontOverrideBackground := origBg != defBg
|
||||
|
||||
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)
|
||||
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["selection"]; ok {
|
||||
style = s
|
||||
}
|
||||
}
|
||||
|
||||
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 b.Settings["cursorline"].(bool) && w.active && !dontOverrideBackground &&
|
||||
!c.HasSelection() && c.Y == bloc.Y {
|
||||
if s, ok := config.Colorscheme["cursor-line"]; ok {
|
||||
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 r == '\t' {
|
||||
indentrunes := []rune(b.Settings["indentchar"].(string))
|
||||
// if empty indentchar settings, use space
|
||||
if len(indentrunes) == 0 {
|
||||
indentrunes = []rune{' '}
|
||||
}
|
||||
|
||||
r = indentrunes[0]
|
||||
if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Foreground(fg)
|
||||
}
|
||||
}
|
||||
|
||||
if s, ok := config.Colorscheme["color-column"]; ok {
|
||||
if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Background(fg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 r == '\t' {
|
||||
indentrunes := []rune(b.Settings["indentchar"].(string))
|
||||
// if empty indentchar settings, use space
|
||||
if len(indentrunes) == 0 {
|
||||
indentrunes = []rune{' '}
|
||||
}
|
||||
|
||||
r = indentrunes[0]
|
||||
if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Foreground(fg)
|
||||
}
|
||||
}
|
||||
|
||||
if s, ok := config.Colorscheme["color-column"]; ok {
|
||||
if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Background(fg)
|
||||
}
|
||||
}
|
||||
|
||||
for _, mb := range matchingBraces {
|
||||
if mb.X == bloc.X && mb.Y == bloc.Y {
|
||||
style = style.Underline(true)
|
||||
for _, mb := range matchingBraces {
|
||||
if mb.X == bloc.X && mb.Y == bloc.Y {
|
||||
style = style.Underline(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -601,59 +562,116 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
type glyph struct {
|
||||
r rune
|
||||
combc []rune
|
||||
style tcell.Style
|
||||
width int
|
||||
}
|
||||
|
||||
var word []glyph
|
||||
if wordwrap {
|
||||
word = make([]glyph, 0, w.bufWidth)
|
||||
} else {
|
||||
word = make([]glyph, 0, 1)
|
||||
}
|
||||
wordwidth := 0
|
||||
|
||||
totalwidth := w.StartCol - nColsBeforeStart
|
||||
for len(line) > 0 {
|
||||
r, combc, size := util.DecodeCharacter(line)
|
||||
line = line[size:]
|
||||
|
||||
curStyle, _ = w.getStyle(curStyle, bloc)
|
||||
|
||||
draw(r, combc, curStyle, true)
|
||||
loc := buffer.Loc{X: bloc.X + len(word), Y: bloc.Y}
|
||||
curStyle, _ = w.getStyle(curStyle, loc)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
|
||||
if width > 1 {
|
||||
for i := 1; i < width; i++ {
|
||||
draw(char, nil, curStyle, false)
|
||||
word = append(word, glyph{r, combc, curStyle, width})
|
||||
wordwidth += width
|
||||
|
||||
// Collect a complete word to know its width.
|
||||
// If wordwrap is off, every single character is a complete "word".
|
||||
if wordwrap {
|
||||
if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth {
|
||||
continue
|
||||
}
|
||||
}
|
||||
bloc.X++
|
||||
line = line[size:]
|
||||
|
||||
totalwidth += width
|
||||
// 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)
|
||||
}
|
||||
|
||||
// If we reach the end of the window then we either stop or we wrap for softwrap
|
||||
if vloc.X >= bufWidth {
|
||||
// We either stop or we wrap to draw the word in the next line
|
||||
if !softwrap {
|
||||
break
|
||||
} else {
|
||||
vloc.Y++
|
||||
if vloc.Y >= bufHeight {
|
||||
if vloc.Y >= w.bufHeight {
|
||||
break
|
||||
}
|
||||
vloc.X = 0
|
||||
if hasMessage {
|
||||
w.drawGutter(&vloc, &bloc)
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
w.drawDiffGutter(lineNumStyle, true, &vloc, &bloc)
|
||||
wrap()
|
||||
}
|
||||
}
|
||||
|
||||
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 = '@'
|
||||
}
|
||||
|
||||
// This will draw an empty line number because the current line is wrapped
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.drawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc)
|
||||
for i := 1; i < r.width; i++ {
|
||||
draw(char, nil, r.style, true, false)
|
||||
}
|
||||
}
|
||||
bloc.X++
|
||||
}
|
||||
|
||||
word = word[:0]
|
||||
wordwidth = 0
|
||||
|
||||
// If we reach the end of the window then we either stop or we wrap for softwrap
|
||||
if vloc.X >= maxWidth {
|
||||
if !softwrap {
|
||||
break
|
||||
} else {
|
||||
vloc.Y++
|
||||
if vloc.Y >= w.bufHeight {
|
||||
break
|
||||
}
|
||||
wrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -667,7 +685,7 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := vloc.X; i < bufWidth; i++ {
|
||||
for i := vloc.X; i < maxWidth; i++ {
|
||||
curStyle := style
|
||||
if s, ok := config.Colorscheme["color-column"]; ok {
|
||||
if colorcolumn != 0 && i-w.gutterOffset+w.StartCol == colorcolumn {
|
||||
@ -678,9 +696,9 @@ func (w *BufWindow) displayBuffer() {
|
||||
screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle)
|
||||
}
|
||||
|
||||
if vloc.X != bufWidth {
|
||||
if vloc.X != maxWidth {
|
||||
// Display newline within a selection
|
||||
draw(' ', nil, config.DefStyle, true)
|
||||
draw(' ', nil, config.DefStyle, true, true)
|
||||
}
|
||||
|
||||
bloc.X = w.StartCol
|
||||
@ -692,18 +710,9 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
|
||||
func (w *BufWindow) displayStatusLine() {
|
||||
_, h := screen.Screen.Size()
|
||||
infoY := h
|
||||
if config.GetGlobalOption("infobar").(bool) {
|
||||
infoY--
|
||||
}
|
||||
|
||||
if w.Buf.Settings["statusline"].(bool) {
|
||||
w.drawStatus = true
|
||||
w.sline.Display()
|
||||
} else if w.Y+w.Height != infoY {
|
||||
w.drawStatus = true
|
||||
|
||||
} else if w.drawDivider {
|
||||
divchars := config.GetGlobalOption("divchars").(string)
|
||||
if util.CharacterCountInString(divchars) != 2 {
|
||||
divchars = "|-"
|
||||
@ -725,18 +734,12 @@ func (w *BufWindow) displayStatusLine() {
|
||||
for x := w.X; x < w.X+w.Width; x++ {
|
||||
screen.SetContent(x, w.Y+w.Height-1, divchar, combc, dividerStyle)
|
||||
}
|
||||
} else {
|
||||
w.drawStatus = false
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) displayScrollBar() {
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
scrollX := w.X + w.Width - 1
|
||||
bufHeight := w.Height
|
||||
if w.drawStatus {
|
||||
bufHeight--
|
||||
}
|
||||
barsize := int(float64(w.Height) / float64(w.Buf.LinesNum()) * float64(w.Height))
|
||||
if barsize < 1 {
|
||||
barsize = 1
|
||||
@ -748,7 +751,7 @@ func (w *BufWindow) displayScrollBar() {
|
||||
scrollBarStyle = style
|
||||
}
|
||||
|
||||
for y := barstart; y < util.Min(barstart+barsize, w.Y+bufHeight); y++ {
|
||||
for y := barstart; y < util.Min(barstart+barsize, w.Y+w.bufHeight); y++ {
|
||||
screen.SetContent(scrollX, y, '|', nil, scrollBarStyle)
|
||||
}
|
||||
}
|
||||
@ -756,6 +759,8 @@ func (w *BufWindow) displayScrollBar() {
|
||||
|
||||
// Display displays the buffer and the statusline
|
||||
func (w *BufWindow) Display() {
|
||||
w.updateDisplayInfo()
|
||||
|
||||
w.displayStatusLine()
|
||||
w.displayScrollBar()
|
||||
w.displayBuffer()
|
||||
|
@ -72,9 +72,22 @@ func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc {
|
||||
return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 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) BufView() View {
|
||||
return View{
|
||||
X: 0,
|
||||
Y: i.Y,
|
||||
Width: i.Width,
|
||||
Height: 1,
|
||||
StartLine: SLoc{0, 0},
|
||||
StartCol: 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++ {
|
||||
|
@ -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"
|
||||
)
|
||||
@ -29,28 +30,180 @@ 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 {
|
||||
width := w.Width - w.gutterOffset
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
width--
|
||||
func (w *BufWindow) getVLocFromLoc(loc buffer.Loc) VLoc {
|
||||
vloc := VLoc{SLoc: SLoc{loc.Y, 0}, VisualX: 0}
|
||||
|
||||
if loc.X <= 0 {
|
||||
return vloc
|
||||
}
|
||||
if width <= 0 {
|
||||
return 0
|
||||
|
||||
if w.bufWidth <= 0 {
|
||||
return vloc
|
||||
}
|
||||
// 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 / width
|
||||
|
||||
wordwrap := w.Buf.Settings["wordwrap"].(bool)
|
||||
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
||||
|
||||
line := w.Buf.LineBytes(loc.Y)
|
||||
x := 0
|
||||
totalwidth := 0
|
||||
|
||||
wordwidth := 0
|
||||
wordoffset := 0
|
||||
|
||||
for len(line) > 0 {
|
||||
r, _, size := util.DecodeCharacter(line)
|
||||
line = line[size:]
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
wordwidth += width
|
||||
|
||||
// Collect a complete word to know its width.
|
||||
// If wordwrap is off, every single character is a complete "word".
|
||||
if wordwrap {
|
||||
if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth {
|
||||
if x < loc.X {
|
||||
wordoffset += width
|
||||
x++
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If a word (or just a wide rune) does not fit in the window
|
||||
if vloc.VisualX+wordwidth > w.bufWidth && vloc.VisualX > 0 {
|
||||
vloc.Row++
|
||||
vloc.VisualX = 0
|
||||
}
|
||||
|
||||
if x == loc.X {
|
||||
vloc.VisualX += wordoffset
|
||||
return vloc
|
||||
}
|
||||
x++
|
||||
|
||||
vloc.VisualX += wordwidth
|
||||
|
||||
wordwidth = 0
|
||||
wordoffset = 0
|
||||
|
||||
if vloc.VisualX >= w.bufWidth {
|
||||
vloc.Row++
|
||||
vloc.VisualX = 0
|
||||
}
|
||||
}
|
||||
return vloc
|
||||
}
|
||||
|
||||
func (w *BufWindow) getLocFromVLoc(svloc VLoc) buffer.Loc {
|
||||
loc := buffer.Loc{X: 0, Y: svloc.Line}
|
||||
|
||||
if w.bufWidth <= 0 {
|
||||
return loc
|
||||
}
|
||||
|
||||
wordwrap := w.Buf.Settings["wordwrap"].(bool)
|
||||
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
||||
|
||||
line := w.Buf.LineBytes(svloc.Line)
|
||||
vloc := VLoc{SLoc: SLoc{svloc.Line, 0}, VisualX: 0}
|
||||
|
||||
totalwidth := 0
|
||||
|
||||
var widths []int
|
||||
if wordwrap {
|
||||
widths = make([]int, 0, w.bufWidth)
|
||||
} else {
|
||||
widths = make([]int, 0, 1)
|
||||
}
|
||||
wordwidth := 0
|
||||
|
||||
for len(line) > 0 {
|
||||
r, _, size := util.DecodeCharacter(line)
|
||||
line = line[size:]
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
widths = append(widths, width)
|
||||
wordwidth += width
|
||||
|
||||
// Collect a complete word to know its width.
|
||||
// If wordwrap is off, every single character is a complete "word".
|
||||
if wordwrap {
|
||||
if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If a word (or just a wide rune) does not fit in the window
|
||||
if vloc.VisualX+wordwidth > w.bufWidth && vloc.VisualX > 0 {
|
||||
if vloc.Row == svloc.Row {
|
||||
if wordwrap {
|
||||
// it's a word, not a wide rune
|
||||
loc.X--
|
||||
}
|
||||
return loc
|
||||
}
|
||||
vloc.Row++
|
||||
vloc.VisualX = 0
|
||||
}
|
||||
|
||||
for i := range widths {
|
||||
vloc.VisualX += widths[i]
|
||||
if vloc.Row == svloc.Row && vloc.VisualX > svloc.VisualX {
|
||||
return loc
|
||||
}
|
||||
loc.X++
|
||||
}
|
||||
|
||||
widths = widths[:0]
|
||||
wordwidth = 0
|
||||
|
||||
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 {
|
||||
@ -145,5 +298,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)
|
||||
}
|
||||
|
@ -33,4 +33,5 @@ type BWindow interface {
|
||||
Window
|
||||
SoftWrap
|
||||
SetBuffer(b *buffer.Buffer)
|
||||
BufView() View
|
||||
}
|
||||
|
@ -365,6 +365,11 @@ Here are the available options:
|
||||
|
||||
default value: `true`
|
||||
|
||||
* `wordwrap`: wrap long lines by words, i.e. break at spaces. This option
|
||||
only does anything if `softwrap` is on.
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `xterm`: micro will assume that the terminal it is running in conforms to
|
||||
`xterm-256color` regardless of what the `$TERM` variable actually contains.
|
||||
Enabling this option may cause unwanted effects if your terminal in fact
|
||||
|
Loading…
Reference in New Issue
Block a user