]> git.lizzy.rs Git - micro.git/blobdiff - internal/display/bufwindow.go
Merge
[micro.git] / internal / display / bufwindow.go
index 85719689e0f8bfb0aade88299551c6ae3a0692ee..afc2663db81db005153a32c11395ad0cc3afdce5 100644 (file)
@@ -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()
 }