4 "github.com/mattn/go-runewidth"
5 "github.com/zyedidia/micro/cmd/micro/highlight"
6 "github.com/zyedidia/tcell"
9 func min(a, b int) int {
16 func visualToCharPos(visualIndex int, lineN int, str string, buf *Buffer, tabsize int) (int, int, *tcell.Style) {
20 var style *tcell.Style
23 for i, c := range str {
24 // width := StringWidth(str[:i], tabsize)
26 if group, ok := buf.Match(lineN)[charPos]; ok {
27 s := GetColor(highlight.GetGroup(group))
31 if width >= visualIndex {
32 return charPos, visualIndex - lastWidth, style
42 rw := tabsize - (lineIdx % tabsize)
45 rw = runewidth.RuneWidth(c)
57 // The actual character that is drawn
58 // This is only different from char if it's for example hidden character
64 type CellView struct {
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]
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
80 buf.highlighter.ReHighlightStates(buf, start)
82 buf.highlighter.HighlightMatches(buf, top, top+height)
85 c.lines = make([][]*Char, 0)
91 for viewLine < height {
92 if lineN >= len(buf.lines) {
96 lineStr := buf.Line(lineN)
97 line := []rune(lineStr)
99 colN, startOffset, startStyle := visualToCharPos(left, lineN, lineStr, buf, tabsize)
103 viewCol := -startOffset
104 if startStyle != nil {
105 curStyle = *startStyle
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))
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 {
117 // We're going to draw the entire line now
118 lineLength = StringWidth(lineStr, tabsize)
121 for viewCol < lineLength {
122 if colN >= len(line) {
125 if group, ok := buf.Match(lineN)[colN]; ok {
126 curStyle = GetColor(highlight.GetGroup(group))
132 c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, curStyle, 1}
135 width := tabsize - (viewCol+left)%tabsize
137 c.lines[viewLine][viewCol].drawChar = indentchar
138 c.lines[viewLine][viewCol].width = width
140 indentStyle := curStyle
141 if group, ok := colorscheme["indent-char"]; ok {
145 c.lines[viewLine][viewCol].style = indentStyle
148 for i := 1; i < width; i++ {
151 c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
155 } else if runewidth.RuneWidth(char) > 1 {
156 width := runewidth.RuneWidth(char)
158 c.lines[viewLine][viewCol].width = width
160 for i := 1; i < width; i++ {
163 c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
172 if wrap && viewCol >= width {
175 // If we go too far soft wrapping we have to cut off
176 if viewLine >= height {
180 nextLine := line[colN:]
181 lineLength := min(StringWidth(string(nextLine), tabsize), width)
182 c.lines = append(c.lines, make([]*Char, lineLength))
188 if group, ok := buf.Match(lineN)[len(line)]; ok {
189 curStyle = GetColor(highlight.GetGroup(group))
197 for i := top; i < top+height; i++ {
198 if i >= buf.NumLines {