diff --git a/internal/action/actions.go b/internal/action/actions.go index 6db8e822..c0334a6a 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -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.BufHeight()-1 { + v.StartLine = h.Scroll(end, -h.BufHeight()+1) } h.SetView(v) } @@ -117,7 +117,7 @@ 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.BufHeight()/2) h.SetView(v) h.ScrollAdjust() return true @@ -1251,22 +1251,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.BufHeight()+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.BufHeight()) return true } // PageDown scrolls the view down a page func (h *BufPane) PageDown() bool { - v := h.GetView() - h.ScrollDown(v.Height) + h.ScrollDown(h.BufHeight()) h.ScrollAdjust() return true } @@ -1276,7 +1274,7 @@ func (h *BufPane) SelectPageUp() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } - h.Cursor.UpN(h.GetView().Height) + h.Cursor.UpN(h.BufHeight()) h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true @@ -1287,7 +1285,7 @@ func (h *BufPane) SelectPageDown() bool { if !h.Cursor.HasSelection() { h.Cursor.OrigSelection[0] = h.Cursor.Loc } - h.Cursor.DownN(h.GetView().Height) + h.Cursor.DownN(h.BufHeight()) h.Cursor.SelectTo(h.Cursor.Loc) h.Relocate() return true @@ -1302,7 +1300,7 @@ func (h *BufPane) CursorPageUp() bool { h.Cursor.ResetSelection() h.Cursor.StoreVisualX() } - h.Cursor.UpN(h.GetView().Height) + h.Cursor.UpN(h.BufHeight()) h.Relocate() return true } @@ -1316,22 +1314,20 @@ func (h *BufPane) CursorPageDown() bool { h.Cursor.ResetSelection() h.Cursor.StoreVisualX() } - h.Cursor.DownN(h.GetView().Height) + h.Cursor.DownN(h.BufHeight()) 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.BufHeight() / 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.BufHeight() / 2) h.ScrollAdjust() return true } diff --git a/internal/display/bufwindow.go b/internal/display/bufwindow.go index 2fcf0ac5..3c1136b1 100644 --- a/internal/display/bufwindow.go +++ b/internal/display/bufwindow.go @@ -23,8 +23,12 @@ 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 @@ -64,6 +68,61 @@ func (w *BufWindow) IsActive() bool { return w.active } +// BufWidth returns the width of the actual buffer displayed in the window, +// which is usually less than the window width due to the gutter, ruler or scrollbar +func (w *BufWindow) BufWidth() int { + return w.bufWidth +} + +// BufHeight returns the height of the actual buffer displayed in the window, +// which is usually less than the window height due to the statusline +func (w *BufWindow) BufHeight() int { + return w.bufHeight +} + +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 +170,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)) @@ -162,20 +218,7 @@ func (w *BufWindow) Relocate() bool { func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc { b := w.Buf - 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-- - } - - // 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())) + maxWidth := w.gutterOffset + w.bufWidth tabsize := int(b.Settings["tabsize"].(float64)) softwrap := b.Settings["softwrap"].(bool) @@ -191,17 +234,8 @@ func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc { // 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 - } + for ; vloc.Y < w.bufHeight; vloc.Y++ { + vloc.X = w.gutterOffset line := b.LineBytes(bloc.Y) line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, w.StartCol, tabsize) @@ -251,12 +285,12 @@ func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc { totalwidth += width // If we reach the end of the window then we either stop or we wrap for softwrap - if vloc.X >= bufWidth { + if vloc.X >= maxWidth { if !softwrap { break } else { vloc.Y++ - if vloc.Y >= bufHeight { + if vloc.Y >= w.bufHeight { break } vloc.X = w.gutterOffset @@ -267,7 +301,7 @@ func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc { return bloc } - if bloc.Y+1 >= b.LinesNum() || vloc.Y+1 >= bufHeight { + if bloc.Y+1 >= b.LinesNum() || vloc.Y+1 >= w.bufHeight { return bloc } @@ -322,7 +356,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 +367,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 +414,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,10 +475,6 @@ 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) tabsize := util.IntOpt(b.Settings["tabsize"]) colorcolumn := util.IntOpt(b.Settings["colorcolumn"]) @@ -472,7 +493,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 +510,7 @@ func (w *BufWindow) displayBuffer() { } if vloc.Y >= 0 { - if hasMessage { + if w.hasMessage { w.drawGutter(&vloc, &bloc) } @@ -498,22 +519,12 @@ 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 @@ -633,16 +644,16 @@ func (w *BufWindow) displayBuffer() { totalwidth += width // If we reach the end of the window then we either stop or we wrap for softwrap - if vloc.X >= bufWidth { + if vloc.X >= maxWidth { if !softwrap { break } else { vloc.Y++ - if vloc.Y >= bufHeight { + if vloc.Y >= w.bufHeight { break } vloc.X = 0 - if hasMessage { + if w.hasMessage { w.drawGutter(&vloc, &bloc) } if b.Settings["diffgutter"].(bool) { @@ -651,7 +662,7 @@ func (w *BufWindow) displayBuffer() { // 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) + w.drawLineNum(lineNumStyle, true, &vloc, &bloc) } } } @@ -667,7 +678,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,7 +689,7 @@ 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) } @@ -692,18 +703,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 +727,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 +744,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 +752,8 @@ func (w *BufWindow) displayScrollBar() { // Display displays the buffer and the statusline func (w *BufWindow) Display() { + w.updateDisplayInfo() + w.displayStatusLine() w.displayScrollBar() w.displayBuffer() diff --git a/internal/display/infowindow.go b/internal/display/infowindow.go index 4cfbca32..530dce67 100644 --- a/internal/display/infowindow.go +++ b/internal/display/infowindow.go @@ -72,6 +72,9 @@ func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc { return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 0} } +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} } diff --git a/internal/display/softwrap.go b/internal/display/softwrap.go index 0f99b526..bbcc99e0 100644 --- a/internal/display/softwrap.go +++ b/internal/display/softwrap.go @@ -36,17 +36,13 @@ type SoftWrap interface { } 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-- - } - if width <= 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 / width + return x / w.bufWidth } func (w *BufWindow) getRowCount(line int) int { diff --git a/internal/display/window.go b/internal/display/window.go index eb71970f..eb2c09f4 100644 --- a/internal/display/window.go +++ b/internal/display/window.go @@ -33,4 +33,6 @@ type BWindow interface { Window SoftWrap SetBuffer(b *buffer.Buffer) + BufWidth() int + BufHeight() int }