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