]> git.lizzy.rs Git - micro.git/blob - internal/display/infowindow.go
Autoclose plugin support
[micro.git] / internal / display / infowindow.go
1 package display
2
3 import (
4         "unicode/utf8"
5
6         runewidth "github.com/mattn/go-runewidth"
7         "github.com/zyedidia/micro/internal/buffer"
8         "github.com/zyedidia/micro/internal/config"
9         "github.com/zyedidia/micro/internal/info"
10         "github.com/zyedidia/micro/internal/screen"
11         "github.com/zyedidia/micro/internal/util"
12         "github.com/zyedidia/tcell"
13 )
14
15 type InfoWindow struct {
16         *info.InfoBuf
17         *View
18 }
19
20 func (i *InfoWindow) errStyle() tcell.Style {
21         errStyle := config.DefStyle.
22                 Foreground(tcell.ColorBlack).
23                 Background(tcell.ColorMaroon)
24
25         if _, ok := config.Colorscheme["error-message"]; ok {
26                 errStyle = config.Colorscheme["error-message"]
27         }
28
29         return errStyle
30 }
31
32 func (i *InfoWindow) defStyle() tcell.Style {
33         defStyle := config.DefStyle
34
35         if _, ok := config.Colorscheme["message"]; ok {
36                 defStyle = config.Colorscheme["message"]
37         }
38
39         return defStyle
40 }
41
42 func NewInfoWindow(b *info.InfoBuf) *InfoWindow {
43         iw := new(InfoWindow)
44         iw.InfoBuf = b
45         iw.View = new(View)
46
47         iw.Width, iw.Y = screen.Screen.Size()
48         iw.Y--
49
50         return iw
51 }
52
53 func (i *InfoWindow) Resize(w, h int) {
54         i.Width = w
55         i.Y = h
56 }
57
58 func (i *InfoWindow) SetBuffer(b *buffer.Buffer) {
59         i.InfoBuf.Buffer = b
60 }
61
62 func (i *InfoWindow) Relocate() bool   { return false }
63 func (i *InfoWindow) GetView() *View   { return i.View }
64 func (i *InfoWindow) SetView(v *View)  {}
65 func (i *InfoWindow) SetActive(b bool) {}
66
67 func (i *InfoWindow) GetMouseLoc(vloc buffer.Loc) buffer.Loc {
68         c := i.Buffer.GetActiveCursor()
69         l := i.Buffer.LineBytes(0)
70         n := utf8.RuneCountInString(i.Msg)
71         return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 0}
72 }
73
74 func (i *InfoWindow) Clear() {
75         for x := 0; x < i.Width; x++ {
76                 screen.Screen.SetContent(x, i.Y, ' ', nil, i.defStyle())
77         }
78 }
79
80 func (i *InfoWindow) displayBuffer() {
81         b := i.Buffer
82         line := b.LineBytes(0)
83         activeC := b.GetActiveCursor()
84
85         blocX := 0
86         vlocX := utf8.RuneCountInString(i.Msg)
87
88         tabsize := 4
89         line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, blocX, tabsize)
90         blocX = bslice
91
92         draw := func(r rune, style tcell.Style) {
93                 if nColsBeforeStart <= 0 {
94                         bloc := buffer.Loc{X: blocX, Y: 0}
95                         if activeC.HasSelection() &&
96                                 (bloc.GreaterEqual(activeC.CurSelection[0]) && bloc.LessThan(activeC.CurSelection[1]) ||
97                                         bloc.LessThan(activeC.CurSelection[0]) && bloc.GreaterEqual(activeC.CurSelection[1])) {
98                                 // The current character is selected
99                                 style = i.defStyle().Reverse(true)
100
101                                 if s, ok := config.Colorscheme["selection"]; ok {
102                                         style = s
103                                 }
104
105                         }
106
107                         rw := runewidth.RuneWidth(r)
108                         for j := 0; j < rw; j++ {
109                                 c := r
110                                 if j > 0 {
111                                         c = ' '
112                                 }
113                                 screen.Screen.SetContent(vlocX, i.Y, c, nil, style)
114                         }
115                         vlocX++
116                 }
117                 nColsBeforeStart--
118         }
119
120         totalwidth := blocX - nColsBeforeStart
121         for len(line) > 0 {
122                 if activeC.X == blocX {
123                         screen.Screen.ShowCursor(vlocX, i.Y)
124                 }
125
126                 r, size := utf8.DecodeRune(line)
127
128                 draw(r, i.defStyle())
129
130                 width := 0
131
132                 char := ' '
133                 switch r {
134                 case '\t':
135                         ts := tabsize - (totalwidth % tabsize)
136                         width = ts
137                 default:
138                         width = runewidth.RuneWidth(r)
139                         char = '@'
140                 }
141
142                 blocX++
143                 line = line[size:]
144
145                 // Draw any extra characters either spaces for tabs or @ for incomplete wide runes
146                 if width > 1 {
147                         for j := 1; j < width; j++ {
148                                 draw(char, i.defStyle())
149                         }
150                 }
151                 totalwidth += width
152                 if vlocX >= i.Width {
153                         break
154                 }
155         }
156         if activeC.X == blocX {
157                 screen.Screen.ShowCursor(vlocX, i.Y)
158         }
159 }
160
161 var keydisplay = []string{"^Q Quit, ^S Save, ^O Open, ^G Help, ^E Command Bar, ^K Cut Line", "^F Find, ^Z Undo, ^Y Redo, ^A Select All, ^D Duplicate Line, ^T New Tab"}
162
163 func (i *InfoWindow) displayKeyMenu() {
164         // TODO: maybe make this based on the actual keybindings
165
166         for y := 0; y < len(keydisplay); y++ {
167                 for x := 0; x < i.Width; x++ {
168                         if x < len(keydisplay[y]) {
169                                 screen.Screen.SetContent(x, i.Y-len(keydisplay)+y, rune(keydisplay[y][x]), nil, i.defStyle())
170                         } else {
171                                 screen.Screen.SetContent(x, i.Y-len(keydisplay)+y, ' ', nil, i.defStyle())
172                         }
173                 }
174         }
175 }
176
177 func (i *InfoWindow) Display() {
178         x := 0
179         if config.GetGlobalOption("keymenu").(bool) {
180                 i.displayKeyMenu()
181         }
182
183         if i.HasPrompt || config.GlobalSettings["infobar"].(bool) {
184                 if !i.HasPrompt && !i.HasMessage && !i.HasError {
185                         return
186                 }
187                 i.Clear()
188                 style := i.defStyle()
189
190                 if i.HasError {
191                         style = i.errStyle()
192                 }
193
194                 display := i.Msg
195                 for _, c := range display {
196                         screen.Screen.SetContent(x, i.Y, c, nil, style)
197                         x += runewidth.RuneWidth(c)
198                 }
199
200                 if i.HasPrompt {
201                         i.displayBuffer()
202                 }
203         }
204
205         if i.HasSuggestions && len(i.Suggestions) > 1 {
206                 statusLineStyle := config.DefStyle.Reverse(true)
207                 if style, ok := config.Colorscheme["statusline"]; ok {
208                         statusLineStyle = style
209                 }
210                 keymenuOffset := 0
211                 if config.GetGlobalOption("keymenu").(bool) {
212                         keymenuOffset = len(keydisplay)
213                 }
214                 x := 0
215                 for j, s := range i.Suggestions {
216                         style := statusLineStyle
217                         if i.CurSuggestion == j {
218                                 style = style.Reverse(true)
219                         }
220                         for _, r := range s {
221                                 screen.Screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style)
222                                 x++
223                                 if x >= i.Width {
224                                         return
225                                 }
226                         }
227                         screen.Screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle)
228                         x++
229                         if x >= i.Width {
230                                 return
231                         }
232                 }
233
234                 for x < i.Width {
235                         screen.Screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle)
236                         x++
237                 }
238         }
239 }