4 "github.com/mattn/go-runewidth"
5 "github.com/zyedidia/tcell"
8 func min(a, b int) int {
15 func visualToCharPos(visualIndex int, lineN int, str string, buf *Buffer, tabsize int) (int, int, *tcell.Style) {
19 var style *tcell.Style
22 for i, c := range str {
23 // width := StringWidth(str[:i], tabsize)
25 if group, ok := buf.Match(lineN)[charPos]; ok {
26 s := GetColor(group.String())
30 if width >= visualIndex {
31 return charPos, visualIndex - lastWidth, style
41 rw = tabsize - (lineIdx % tabsize)
44 rw = runewidth.RuneWidth(c)
56 // The actual character that is drawn
57 // This is only different from char if it's for example hidden character
63 type CellView struct {
67 func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
72 matchingBrace := Loc{-1, -1}
73 // bracePairs is defined in buffer.go
74 if buf.Settings["matchbrace"].(bool) {
75 for _, bp := range bracePairs {
77 curLoc := buf.Cursor.Loc
78 if buf.Settings["matchbraceleft"].(bool) {
81 curLoc = curLoc.Move(-1, buf)
85 r := buf.Cursor.RuneUnder(curX)
86 if r == bp[0] || r == bp[1] {
87 matchingBrace = buf.FindMatchingBrace(bp, curLoc)
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{' '}
99 indentchar := indentrunes[0]
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
108 buf.highlighter.ReHighlightStates(buf, start)
110 buf.highlighter.HighlightMatches(buf, top, top+height)
113 c.lines = make([][]*Char, 0)
119 for viewLine < height {
120 if lineN >= len(buf.lines) {
124 lineStr := buf.Line(lineN)
125 line := []rune(lineStr)
127 colN, startOffset, startStyle := visualToCharPos(left, lineN, lineStr, buf, tabsize)
131 viewCol := -startOffset
132 if startStyle != nil {
133 curStyle = *startStyle
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))
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 {
145 // We're going to draw the entire line now
146 lineLength = StringWidth(lineStr, tabsize)
149 for viewCol < lineLength {
150 if colN >= len(line) {
153 if group, ok := buf.Match(lineN)[colN]; ok {
154 curStyle = GetColor(group.String())
161 if colN == matchingBrace.X && lineN == matchingBrace.Y && !buf.Cursor.HasSelection() {
162 st = curStyle.Reverse(true)
164 if viewCol < len(c.lines[viewLine]) {
165 c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, st, 1}
169 charWidth := tabsize - (viewCol+left)%tabsize
171 c.lines[viewLine][viewCol].drawChar = indentchar
172 c.lines[viewLine][viewCol].width = charWidth
174 indentStyle := curStyle
175 ch := buf.Settings["indentchar"].(string)
176 if group, ok := colorscheme["indent-char"]; ok && !IsStrWhitespace(ch) && ch != "" {
180 c.lines[viewLine][viewCol].style = indentStyle
183 for i := 1; i < charWidth; i++ {
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}
190 } else if runewidth.RuneWidth(char) > 1 {
191 charWidth := runewidth.RuneWidth(char)
193 c.lines[viewLine][viewCol].width = charWidth
195 for i := 1; i < charWidth; i++ {
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}
207 if wrap && viewCol >= width {
210 // If we go too far soft wrapping we have to cut off
211 if viewLine >= height {
215 nextLine := line[colN:]
216 lineLength := min(StringWidth(string(nextLine), tabsize), width)
217 c.lines = append(c.lines, make([]*Char, lineLength))
223 if group, ok := buf.Match(lineN)[len(line)]; ok {
224 curStyle = GetColor(group.String())
232 for i := top; i < top+height; i++ {
233 if i >= buf.NumLines {