]> git.lizzy.rs Git - micro.git/blob - cmd/micro/cellview.go
Merge pull request #1242 from dwwmmn/dwwmmn-erl
[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                         curX := buf.Cursor.X
77                         curLoc := buf.Cursor.Loc
78                         if buf.Settings["matchbraceleft"].(bool) {
79                                 if curX > 0 {
80                                         curX--
81                                         curLoc = curLoc.Move(-1, buf)
82                                 }
83                         }
84
85                         r := buf.Cursor.RuneUnder(curX)
86                         if r == bp[0] || r == bp[1] {
87                                 matchingBrace = buf.FindMatchingBrace(bp, curLoc)
88                         }
89                 }
90         }
91
92         tabsize := int(buf.Settings["tabsize"].(float64))
93         softwrap := buf.Settings["softwrap"].(bool)
94         indentrunes := []rune(buf.Settings["indentchar"].(string))
95         // if empty indentchar settings, use space
96         if indentrunes == nil || len(indentrunes) == 0 {
97                 indentrunes = []rune{' '}
98         }
99         indentchar := indentrunes[0]
100
101         start := buf.Cursor.Y
102         if buf.Settings["syntax"].(bool) && buf.syntaxDef != nil {
103                 if start > 0 && buf.lines[start-1].rehighlight {
104                         buf.highlighter.ReHighlightLine(buf, start-1)
105                         buf.lines[start-1].rehighlight = false
106                 }
107
108                 buf.highlighter.ReHighlightStates(buf, start)
109
110                 buf.highlighter.HighlightMatches(buf, top, top+height)
111         }
112
113         c.lines = make([][]*Char, 0)
114
115         viewLine := 0
116         lineN := top
117
118         curStyle := defStyle
119         for viewLine < height {
120                 if lineN >= len(buf.lines) {
121                         break
122                 }
123
124                 lineStr := buf.Line(lineN)
125                 line := []rune(lineStr)
126
127                 colN, startOffset, startStyle := visualToCharPos(left, lineN, lineStr, buf, tabsize)
128                 if colN < 0 {
129                         colN = len(line)
130                 }
131                 viewCol := -startOffset
132                 if startStyle != nil {
133                         curStyle = *startStyle
134                 }
135
136                 // We'll either draw the length of the line, or the width of the screen
137                 // whichever is smaller
138                 lineLength := min(StringWidth(lineStr, tabsize), width)
139                 c.lines = append(c.lines, make([]*Char, lineLength))
140
141                 wrap := false
142                 // We only need to wrap if the length of the line is greater than the width of the terminal screen
143                 if softwrap && StringWidth(lineStr, tabsize) > width {
144                         wrap = true
145                         // We're going to draw the entire line now
146                         lineLength = StringWidth(lineStr, tabsize)
147                 }
148
149                 for viewCol < lineLength {
150                         if colN >= len(line) {
151                                 break
152                         }
153                         if group, ok := buf.Match(lineN)[colN]; ok {
154                                 curStyle = GetColor(group.String())
155                         }
156
157                         char := line[colN]
158
159                         if viewCol >= 0 {
160                                 st := curStyle
161                                 if colN == matchingBrace.X && lineN == matchingBrace.Y && !buf.Cursor.HasSelection() {
162                                         st = curStyle.Reverse(true)
163                                 }
164                                 if viewCol < len(c.lines[viewLine]) {
165                                         c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, st, 1}
166                                 }
167                         }
168                         if char == '\t' {
169                                 charWidth := tabsize - (viewCol+left)%tabsize
170                                 if viewCol >= 0 {
171                                         c.lines[viewLine][viewCol].drawChar = indentchar
172                                         c.lines[viewLine][viewCol].width = charWidth
173
174                                         indentStyle := curStyle
175                                         ch := buf.Settings["indentchar"].(string)
176                                         if group, ok := colorscheme["indent-char"]; ok && !IsStrWhitespace(ch) && ch != "" {
177                                                 indentStyle = group
178                                         }
179
180                                         c.lines[viewLine][viewCol].style = indentStyle
181                                 }
182
183                                 for i := 1; i < charWidth; i++ {
184                                         viewCol++
185                                         if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
186                                                 c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
187                                         }
188                                 }
189                                 viewCol++
190                         } else if runewidth.RuneWidth(char) > 1 {
191                                 charWidth := runewidth.RuneWidth(char)
192                                 if viewCol >= 0 {
193                                         c.lines[viewLine][viewCol].width = charWidth
194                                 }
195                                 for i := 1; i < charWidth; i++ {
196                                         viewCol++
197                                         if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
198                                                 c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
199                                         }
200                                 }
201                                 viewCol++
202                         } else {
203                                 viewCol++
204                         }
205                         colN++
206
207                         if wrap && viewCol >= width {
208                                 viewLine++
209
210                                 // If we go too far soft wrapping we have to cut off
211                                 if viewLine >= height {
212                                         break
213                                 }
214
215                                 nextLine := line[colN:]
216                                 lineLength := min(StringWidth(string(nextLine), tabsize), width)
217                                 c.lines = append(c.lines, make([]*Char, lineLength))
218
219                                 viewCol = 0
220                         }
221
222                 }
223                 if group, ok := buf.Match(lineN)[len(line)]; ok {
224                         curStyle = GetColor(group.String())
225                 }
226
227                 // newline
228                 viewLine++
229                 lineN++
230         }
231
232         for i := top; i < top+height; i++ {
233                 if i >= buf.NumLines {
234                         break
235                 }
236                 buf.SetMatch(i, nil)
237         }
238 }