X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=internal%2Fdisplay%2Fbufwindow.go;h=afc2663db81db005153a32c11395ad0cc3afdce5;hb=9aaafe5dcf2c761b398e5caefc198dfb73521c27;hp=85719689e0f8bfb0aade88299551c6ae3a0692ee;hpb=9eed8bc2471bedf5bfae2739290d99a3db30f1fd;p=micro.git diff --git a/internal/display/bufwindow.go b/internal/display/bufwindow.go index 85719689..afc2663d 100644 --- a/internal/display/bufwindow.go +++ b/internal/display/bufwindow.go @@ -24,10 +24,8 @@ type BufWindow struct { sline *StatusLine - lineHeight []int - hasCalcHeight bool - gutterOffset int - drawStatus bool + gutterOffset int + drawStatus bool } // NewBufWindow creates a new window at a location in the screen with a width and height @@ -35,7 +33,6 @@ 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.lineHeight = make([]int, height) w.active = true w.sline = NewStatusLine(w) @@ -47,26 +44,27 @@ func (w *BufWindow) SetBuffer(b *buffer.Buffer) { w.Buf = b } -func (v *View) GetView() *View { - return v +func (w *BufWindow) GetView() *View { + return w.View } -func (v *View) SetView(view *View) { - v = view +func (w *BufWindow) SetView(view *View) { + w.View = view } func (w *BufWindow) Resize(width, height int) { w.Width, w.Height = width, height - w.lineHeight = make([]int, height) - w.hasCalcHeight = false - // This recalculates lineHeight - w.GetMouseLoc(buffer.Loc{width, height}) + w.Relocate() } func (w *BufWindow) SetActive(b bool) { w.active = b } +func (w *BufWindow) IsActive() bool { + return w.active +} + func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style) { tabsize := util.IntOpt(w.Buf.Settings["tabsize"]) width := 0 @@ -104,7 +102,7 @@ func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style) func (w *BufWindow) Clear() { for y := 0; y < w.Height; y++ { for x := 0; x < w.Width; x++ { - screen.Screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle) + screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle) } } } @@ -114,22 +112,17 @@ func (w *BufWindow) Clear() { // but if softwrap is enabled things get complicated since one buffer // line can take up multiple lines in the view func (w *BufWindow) Bottomline() int { - // b := w.Buf - - // TODO: possible non-softwrap optimization - // if !b.Settings["softwrap"].(bool) { - // return w.StartLine + w.Height - // } - - prev := 0 - for _, l := range w.lineHeight { - if l >= prev { - prev = l - } else { - break + if !w.Buf.Settings["softwrap"].(bool) { + h := w.StartLine + w.Height - 1 + if w.drawStatus { + h-- } + return h } - return prev + + l := w.LocFromVisual(buffer.Loc{0, w.Y + w.Height}) + + return l.Y } // Relocate moves the view window so that the cursor is in view @@ -143,7 +136,7 @@ func (w *BufWindow) Relocate() bool { if w.drawStatus { h-- } - if b.LinesNum() <= h || !w.hasCalcHeight { + if b.LinesNum() <= h { height = w.Height } ret := false @@ -180,21 +173,26 @@ func (w *BufWindow) Relocate() bool { return ret } -func (w *BufWindow) GetMouseLoc(svloc buffer.Loc) buffer.Loc { +// 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 - // TODO: possible non-softwrap optimization - // if !b.Settings["softwrap"].(bool) { - // l := b.LineBytes(svloc.Y) - // return buffer.Loc{b.GetActiveCursor().GetCharPosInLine(l, svloc.X), svloc.Y} - // } - 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())) @@ -266,7 +264,7 @@ func (w *BufWindow) GetMouseLoc(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 >= w.Width { + if vloc.X >= bufWidth { if !softwrap { break } else { @@ -276,7 +274,9 @@ func (w *BufWindow) GetMouseLoc(svloc buffer.Loc) buffer.Loc { } vloc.X = 0 // This will draw an empty line number because the current line is wrapped - vloc.X += maxLineNumLength + 1 + if b.Settings["ruler"].(bool) { + vloc.X += maxLineNumLength + 1 + } } } } @@ -284,11 +284,12 @@ func (w *BufWindow) GetMouseLoc(svloc buffer.Loc) buffer.Loc { return bloc } + if bloc.Y+1 >= b.LinesNum() || vloc.Y+1 >= bufHeight { + return bloc + } + bloc.X = w.StartCol bloc.Y++ - if bloc.Y >= b.LinesNum() { - break - } } return buffer.Loc{} @@ -304,9 +305,9 @@ func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) { break } } - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s) vloc.X++ - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s) vloc.X++ } @@ -315,21 +316,21 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxL // Write the spaces before the line number if necessary for i := 0; i < maxLineNumLength-len(lineNum); i++ { - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) vloc.X++ } // Write the actual line number for _, ch := range lineNum { if softwrapped { - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) } else { - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle) } vloc.X++ } // Write the extra space - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) vloc.X++ } @@ -346,10 +347,9 @@ func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc, r rune) (tcell. func (w *BufWindow) showCursor(x, y int, main bool) { if w.active { if main { - screen.Screen.ShowCursor(x, y) + screen.ShowCursor(x, y) } else { - r, _, _, _ := screen.Screen.GetContent(x, y) - screen.Screen.SetContent(x, y, r, nil, config.DefStyle.Reverse(true)) + screen.ShowFakeCursorMulti(x, y) } } } @@ -358,23 +358,56 @@ func (w *BufWindow) showCursor(x, y int, main bool) { func (w *BufWindow) displayBuffer() { b := w.Buf + if w.Height <= 0 || w.Width <= 0 { + return + } + hasMessage := len(b.Messages) > 0 bufHeight := w.Height if w.drawStatus { bufHeight-- } - w.hasCalcHeight = true - start := w.StartLine + bufWidth := w.Width + if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height { + bufWidth-- + } + if b.Settings["syntax"].(bool) && b.SyntaxDef != nil { - if start > 0 && b.Rehighlight(start-1) { - b.Highlighter.ReHighlightLine(b, start-1) - b.SetRehighlight(start-1, false) + for _, r := range b.Modifications { + final := -1 + for i := r.X; i <= r.Y; i++ { + final = util.Max(b.Highlighter.ReHighlightStates(b, i), final) + } + b.Highlighter.HighlightMatches(b, r.X, final+1) } + b.ClearModifications() + } - b.Highlighter.ReHighlightStates(b, start) - - b.Highlighter.HighlightMatches(b, w.StartLine, w.StartLine+bufHeight) + var matchingBraces []buffer.Loc + // bracePairs is defined in buffer.go + if b.Settings["matchbrace"].(bool) { + for _, bp := range buffer.BracePairs { + for _, c := range b.GetCursors() { + if c.HasSelection() { + continue + } + curX := c.X + curLoc := c.Loc + + r := c.RuneUnder(curX) + rl := c.RuneUnder(curX - 1) + if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] { + mb, left := b.FindMatchingBrace(bp, curLoc) + matchingBraces = append(matchingBraces, mb) + if !left { + matchingBraces = append(matchingBraces, curLoc) + } else { + matchingBraces = append(matchingBraces, curLoc.Move(-1, b)) + } + } + } + } } lineNumStyle := config.DefStyle @@ -383,7 +416,11 @@ func (w *BufWindow) displayBuffer() { } curNumStyle := config.DefStyle if style, ok := config.Colorscheme["current-line-number"]; ok { - curNumStyle = style + if !b.Settings["cursorline"].(bool) { + curNumStyle = lineNumStyle + } else { + curNumStyle = style + } } // We need to know the string length of the largest line number @@ -453,16 +490,25 @@ func (w *BufWindow) displayBuffer() { } } + 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' { - if s, ok := config.Colorscheme["indent-char"]; ok { - style = s + indentrunes := []rune(b.Settings["indentchar"].(string)) + // if empty indentchar settings, use space + if indentrunes == nil || len(indentrunes) == 0 { + indentrunes = []rune{' '} + } - indentrunes := []rune(b.Settings["indentchar"].(string)) - // if empty indentchar settings, use space - if indentrunes == nil || len(indentrunes) == 0 { - indentrunes = []rune{' '} - } - r = indentrunes[0] + r = indentrunes[0] + if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' { + fg, _, _ := s.Decompose() + style = style.Foreground(fg) } } @@ -473,7 +519,13 @@ func (w *BufWindow) displayBuffer() { } } - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style) + for _, mb := range matchingBraces { + if mb.X == bloc.X && mb.Y == bloc.Y { + style = style.Underline(true) + } + } + + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style) if showcursor { for _, c := range cursors { @@ -487,8 +539,6 @@ func (w *BufWindow) displayBuffer() { nColsBeforeStart-- } - w.lineHeight[vloc.Y] = bloc.Y - totalwidth := w.StartCol - nColsBeforeStart for len(line) > 0 { r, size := utf8.DecodeRune(line) @@ -520,7 +570,7 @@ 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 >= w.Width { + if vloc.X >= bufWidth { if !softwrap { break } else { @@ -529,9 +579,10 @@ func (w *BufWindow) displayBuffer() { break } vloc.X = 0 - w.lineHeight[vloc.Y] = bloc.Y // This will draw an empty line number because the current line is wrapped - w.drawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc) + if b.Settings["ruler"].(bool) { + w.drawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc) + } } } } @@ -546,7 +597,7 @@ func (w *BufWindow) displayBuffer() { } } } - for i := vloc.X; i < w.Width; i++ { + for i := vloc.X; i < bufWidth; i++ { curStyle := style if s, ok := config.Colorscheme["color-column"]; ok { if colorcolumn != 0 && i-w.gutterOffset == colorcolumn { @@ -554,13 +605,11 @@ func (w *BufWindow) displayBuffer() { curStyle = style.Background(fg) } } - screen.Screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle) + screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle) } - for _, c := range cursors { - if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() { - w.showCursor(w.X+vloc.X, w.Y+vloc.Y, c.Num == 0) - } + if vloc.X != bufWidth { + draw(' ', curStyle, true) } bloc.X = w.StartCol @@ -584,15 +633,34 @@ func (w *BufWindow) displayStatusLine() { } else if w.Y+w.Height != infoY { w.drawStatus = true for x := w.X; x < w.X+w.Width; x++ { - screen.Screen.SetContent(x, w.Y+w.Height-1, '-', nil, config.DefStyle.Reverse(true)) + screen.SetContent(x, w.Y+w.Height-1, '-', nil, config.DefStyle.Reverse(true)) } } 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 + } + barstart := w.Y + int(float64(w.StartLine)/float64(w.Buf.LinesNum())*float64(w.Height)) + for y := barstart; y < util.Min(barstart+barsize, w.Y+bufHeight); y++ { + screen.SetContent(scrollX, y, '|', nil, config.DefStyle.Reverse(true)) + } + } +} + // Display displays the buffer and the statusline func (w *BufWindow) Display() { w.displayStatusLine() + w.displayScrollBar() w.displayBuffer() }