]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/cellview.go
Update readme
[micro.git] / cmd / micro / cellview.go
index 87c026e1bb7f76dd3d4eda689fdad650253e7462..b0cb6523c3602312b9ba8470d7cae65e83e12ed6 100644 (file)
@@ -12,28 +12,52 @@ func min(a, b int) int {
        return b
 }
 
-func VisualToCharPos(visualIndex int, str string, tabsize int) int {
-       visualPos := 0
+func visualToCharPos(visualIndex int, lineN int, str string, buf *Buffer, tabsize int) (int, int, *tcell.Style) {
        charPos := 0
-       for _, c := range str {
-               width := StringWidth(string(c), tabsize)
+       var lineIdx int
+       var lastWidth int
+       var style *tcell.Style
+       var width int
+       var rw int
+       for i, c := range str {
+               // width := StringWidth(str[:i], tabsize)
+
+               if group, ok := buf.Match(lineN)[charPos]; ok {
+                       s := GetColor(group.String())
+                       style = &s
+               }
 
-               if visualPos+width > visualIndex {
-                       return charPos
+               if width >= visualIndex {
+                       return charPos, visualIndex - lastWidth, style
                }
 
-               visualPos += width
-               charPos++
+               if i != 0 {
+                       charPos++
+                       lineIdx += rw
+               }
+               lastWidth = width
+               rw = 0
+               if c == '\t' {
+                       rw = tabsize - (lineIdx % tabsize)
+                       width += rw
+               } else {
+                       rw = runewidth.RuneWidth(c)
+                       width += rw
+               }
        }
 
-       return 0
+       return -1, -1, style
 }
 
 type Char struct {
        visualLoc Loc
        realLoc   Loc
        char      rune
-       style     tcell.Style
+       // The actual character that is drawn
+       // This is only different from char if it's for example hidden character
+       drawChar rune
+       style    tcell.Style
+       width    int
 }
 
 type CellView struct {
@@ -41,30 +65,78 @@ type CellView struct {
 }
 
 func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
+       if width <= 0 {
+               return
+       }
+
+       matchingBrace := Loc{-1, -1}
+       // bracePairs is defined in buffer.go
+       if buf.Settings["matchbrace"].(bool) {
+               for _, bp := range bracePairs {
+                       curX := buf.Cursor.X
+                       curLoc := buf.Cursor.Loc
+                       if buf.Settings["matchbraceleft"].(bool) {
+                               if curX > 0 {
+                                       curX--
+                                       curLoc = curLoc.Move(-1, buf)
+                               }
+                       }
+
+                       r := buf.Cursor.RuneUnder(curX)
+                       if r == bp[0] || r == bp[1] {
+                               matchingBrace = buf.FindMatchingBrace(bp, curLoc)
+                       }
+               }
+       }
+
        tabsize := int(buf.Settings["tabsize"].(float64))
        softwrap := buf.Settings["softwrap"].(bool)
-       indentchar := []rune(buf.Settings["indentchar"].(string))[0]
+       indentrunes := []rune(buf.Settings["indentchar"].(string))
+       // if empty indentchar settings, use space
+       if indentrunes == nil || len(indentrunes) == 0 {
+               indentrunes = []rune{' '}
+       }
+       indentchar := indentrunes[0]
 
-       if len(c.lines) != height {
-               c.lines = make([][]*Char, height)
+       start := buf.Cursor.Y
+       if buf.Settings["syntax"].(bool) && buf.syntaxDef != nil {
+               if start > 0 && buf.lines[start-1].rehighlight {
+                       buf.highlighter.ReHighlightLine(buf, start-1)
+                       buf.lines[start-1].rehighlight = false
+               }
+
+               buf.highlighter.ReHighlightStates(buf, start)
+
+               buf.highlighter.HighlightMatches(buf, top, top+height)
        }
 
+       c.lines = make([][]*Char, 0)
+
        viewLine := 0
        lineN := top
 
+       curStyle := defStyle
        for viewLine < height {
-               lineStr := string(buf.lines[lineN])
+               if lineN >= len(buf.lines) {
+                       break
+               }
+
+               lineStr := buf.Line(lineN)
                line := []rune(lineStr)
 
-               colN := VisualToCharPos(left, lineStr, tabsize)
-               viewCol := 0
+               colN, startOffset, startStyle := visualToCharPos(left, lineN, lineStr, buf, tabsize)
+               if colN < 0 {
+                       colN = len(line)
+               }
+               viewCol := -startOffset
+               if startStyle != nil {
+                       curStyle = *startStyle
+               }
 
                // We'll either draw the length of the line, or the width of the screen
                // whichever is smaller
                lineLength := min(StringWidth(lineStr, tabsize), width)
-               if len(c.lines[viewLine]) != lineLength {
-                       c.lines[viewLine] = make([]*Char, lineLength)
-               }
+               c.lines = append(c.lines, make([]*Char, lineLength))
 
                wrap := false
                // We only need to wrap if the length of the line is greater than the width of the terminal screen
@@ -78,17 +150,56 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
                        if colN >= len(line) {
                                break
                        }
+                       if group, ok := buf.Match(lineN)[colN]; ok {
+                               curStyle = GetColor(group.String())
+                       }
+
                        char := line[colN]
 
+                       if viewCol >= 0 {
+                               st := curStyle
+                               if colN == matchingBrace.X && lineN == matchingBrace.Y && !buf.Cursor.HasSelection() {
+                                       st = curStyle.Reverse(true)
+                               }
+                               if viewCol < len(c.lines[viewLine]) {
+                                       c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, st, 1}
+                               }
+                       }
                        if char == '\t' {
-                               c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, indentchar, tcell.StyleDefault}
-                               // TODO: this always adds 4 spaces but it should really add just the remainder to the next tab location
-                               viewCol += tabsize
+                               charWidth := tabsize - (viewCol+left)%tabsize
+                               if viewCol >= 0 {
+                                       c.lines[viewLine][viewCol].drawChar = indentchar
+                                       c.lines[viewLine][viewCol].width = charWidth
+
+                                       indentStyle := curStyle
+                                       ch := buf.Settings["indentchar"].(string)
+                                       if group, ok := colorscheme["indent-char"]; ok && !IsStrWhitespace(ch) && ch != "" {
+                                               indentStyle = group
+                                       }
+
+                                       c.lines[viewLine][viewCol].style = indentStyle
+                               }
+
+                               for i := 1; i < charWidth; i++ {
+                                       viewCol++
+                                       if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
+                                               c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
+                                       }
+                               }
+                               viewCol++
                        } else if runewidth.RuneWidth(char) > 1 {
-                               c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, tcell.StyleDefault}
-                               viewCol += runewidth.RuneWidth(char)
+                               charWidth := runewidth.RuneWidth(char)
+                               if viewCol >= 0 {
+                                       c.lines[viewLine][viewCol].width = charWidth
+                               }
+                               for i := 1; i < charWidth; i++ {
+                                       viewCol++
+                                       if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
+                                               c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
+                                       }
+                               }
+                               viewCol++
                        } else {
-                               c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, tcell.StyleDefault}
                                viewCol++
                        }
                        colN++
@@ -96,23 +207,32 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
                        if wrap && viewCol >= width {
                                viewLine++
 
-                               nextLine := line[colN:]
-                               lineLength := min(StringWidth(string(nextLine), tabsize), width)
-                               if len(c.lines[viewLine]) != lineLength {
-                                       c.lines[viewLine] = make([]*Char, lineLength)
-                               }
-
-                               viewCol = 0
-
                                // If we go too far soft wrapping we have to cut off
                                if viewLine >= height {
                                        break
                                }
+
+                               nextLine := line[colN:]
+                               lineLength := min(StringWidth(string(nextLine), tabsize), width)
+                               c.lines = append(c.lines, make([]*Char, lineLength))
+
+                               viewCol = 0
                        }
+
+               }
+               if group, ok := buf.Match(lineN)[len(line)]; ok {
+                       curStyle = GetColor(group.String())
                }
 
                // newline
                viewLine++
                lineN++
        }
+
+       for i := top; i < top+height; i++ {
+               if i >= buf.NumLines {
+                       break
+               }
+               buf.SetMatch(i, nil)
+       }
 }