]> git.lizzy.rs Git - micro.git/blob - cmd/micro/cellview.go
Minor optimizations
[micro.git] / cmd / micro / cellview.go
1 package main
2
3 import (
4         "github.com/mattn/go-runewidth"
5         "github.com/zyedidia/tcell"
6 )
7
8 func min(a, b int) int {
9         if a <= b {
10                 return a
11         }
12         return b
13 }
14
15 func visualToCharPos(visualIndex int, lineN int, str string, buf *Buffer, tabsize int) (int, int, *tcell.Style) {
16         charPos := 0
17         var lineIdx int
18         var lastWidth int
19         var style *tcell.Style
20         var width int
21         var rw int
22         for i, c := range str {
23                 // width := StringWidth(str[:i], tabsize)
24
25                 if group, ok := buf.Match(lineN)[charPos]; ok {
26                         s := GetColor(group.String())
27                         style = &s
28                 }
29
30                 if width >= visualIndex {
31                         return charPos, visualIndex - lastWidth, style
32                 }
33
34                 if i != 0 {
35                         charPos++
36                         lineIdx += rw
37                 }
38                 lastWidth = width
39                 rw = 0
40                 if c == '\t' {
41                         rw = tabsize - (lineIdx % tabsize)
42                         width += rw
43                 } else {
44                         rw = runewidth.RuneWidth(c)
45                         width += rw
46                 }
47         }
48
49         return -1, -1, style
50 }
51
52 type Char struct {
53         visualLoc Loc
54         realLoc   Loc
55         char      rune
56         // The actual character that is drawn
57         // This is only different from char if it's for example hidden character
58         drawChar rune
59         style    tcell.Style
60         width    int
61 }
62
63 type CellView struct {
64         lines [][]*Char
65 }
66
67 func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
68         if width <= 0 {
69                 return
70         }
71
72         matchingBrace := Loc{-1, -1}
73         // bracePairs is defined in buffer.go
74         if buf.Settings["matchbrace"].(bool) {
75                 for _, bp := range bracePairs {
76                         r := buf.Cursor.RuneUnder(buf.Cursor.X)
77                         if r == bp[0] || r == bp[1] {
78                                 matchingBrace = buf.FindMatchingBrace(bp, buf.Cursor.Loc)
79                         }
80                 }
81         }
82
83         tabsize := int(buf.Settings["tabsize"].(float64))
84         softwrap := buf.Settings["softwrap"].(bool)
85         indentrunes := []rune(buf.Settings["indentchar"].(string))
86         // if empty indentchar settings, use space
87         if indentrunes == nil || len(indentrunes) == 0 {
88                 indentrunes = []rune{' '}
89         }
90         indentchar := indentrunes[0]
91
92         start := buf.Cursor.Y
93         if buf.Settings["syntax"].(bool) && buf.syntaxDef != nil {
94                 if start > 0 && buf.lines[start-1].rehighlight {
95                         buf.highlighter.ReHighlightLine(buf, start-1)
96                         buf.lines[start-1].rehighlight = false
97                 }
98
99                 buf.highlighter.ReHighlightStates(buf, start)
100
101                 buf.highlighter.HighlightMatches(buf, top, top+height)
102         }
103
104         c.lines = make([][]*Char, 0)
105
106         viewLine := 0
107         lineN := top
108
109         curStyle := defStyle
110         for viewLine < height {
111                 if lineN >= len(buf.lines) {
112                         break
113                 }
114
115                 lineStr := buf.Line(lineN)
116                 line := []rune(lineStr)
117
118                 colN, startOffset, startStyle := visualToCharPos(left, lineN, lineStr, buf, tabsize)
119                 if colN < 0 {
120                         colN = len(line)
121                 }
122                 viewCol := -startOffset
123                 if startStyle != nil {
124                         curStyle = *startStyle
125                 }
126
127                 // We'll either draw the length of the line, or the width of the screen
128                 // whichever is smaller
129                 lineLength := min(StringWidth(lineStr, tabsize), width)
130                 c.lines = append(c.lines, make([]*Char, lineLength))
131
132                 wrap := false
133                 // We only need to wrap if the length of the line is greater than the width of the terminal screen
134                 if softwrap && StringWidth(lineStr, tabsize) > width {
135                         wrap = true
136                         // We're going to draw the entire line now
137                         lineLength = StringWidth(lineStr, tabsize)
138                 }
139
140                 for viewCol < lineLength {
141                         if colN >= len(line) {
142                                 break
143                         }
144                         if group, ok := buf.Match(lineN)[colN]; ok {
145                                 curStyle = GetColor(group.String())
146                         }
147
148                         char := line[colN]
149
150                         if viewCol >= 0 {
151                                 st := curStyle
152                                 if colN == matchingBrace.X && lineN == matchingBrace.Y && !buf.Cursor.HasSelection() {
153                                         st = curStyle.Reverse(true)
154                                 }
155                                 if viewCol < len(c.lines[viewLine]) {
156                                         c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, st, 1}
157                                 }
158                         }
159                         if char == '\t' {
160                                 charWidth := tabsize - (viewCol+left)%tabsize
161                                 if viewCol >= 0 {
162                                         c.lines[viewLine][viewCol].drawChar = indentchar
163                                         c.lines[viewLine][viewCol].width = charWidth
164
165                                         indentStyle := curStyle
166                                         ch := buf.Settings["indentchar"].(string)
167                                         if group, ok := colorscheme["indent-char"]; ok && !IsStrWhitespace(ch) && ch != "" {
168                                                 indentStyle = group
169                                         }
170
171                                         c.lines[viewLine][viewCol].style = indentStyle
172                                 }
173
174                                 for i := 1; i < charWidth; i++ {
175                                         viewCol++
176                                         if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
177                                                 c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
178                                         }
179                                 }
180                                 viewCol++
181                         } else if runewidth.RuneWidth(char) > 1 {
182                                 charWidth := runewidth.RuneWidth(char)
183                                 if viewCol >= 0 {
184                                         c.lines[viewLine][viewCol].width = charWidth
185                                 }
186                                 for i := 1; i < charWidth; i++ {
187                                         viewCol++
188                                         if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
189                                                 c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
190                                         }
191                                 }
192                                 viewCol++
193                         } else {
194                                 viewCol++
195                         }
196                         colN++
197
198                         if wrap && viewCol >= width {
199                                 viewLine++
200
201                                 // If we go too far soft wrapping we have to cut off
202                                 if viewLine >= height {
203                                         break
204                                 }
205
206                                 nextLine := line[colN:]
207                                 lineLength := min(StringWidth(string(nextLine), tabsize), width)
208                                 c.lines = append(c.lines, make([]*Char, lineLength))
209
210                                 viewCol = 0
211                         }
212
213                 }
214                 if group, ok := buf.Match(lineN)[len(line)]; ok {
215                         curStyle = GetColor(group.String())
216                 }
217
218                 // newline
219                 viewLine++
220                 lineN++
221         }
222
223         for i := top; i < top+height; i++ {
224                 if i >= buf.NumLines {
225                         break
226                 }
227                 buf.SetMatch(i, nil)
228         }
229 }