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"
15 type InfoWindow struct {
22 func (i *InfoWindow) errStyle() tcell.Style {
23 errStyle := config.DefStyle.
24 Foreground(tcell.ColorBlack).
25 Background(tcell.ColorMaroon)
27 if _, ok := config.Colorscheme["error-message"]; ok {
28 errStyle = config.Colorscheme["error-message"]
34 func (i *InfoWindow) defStyle() tcell.Style {
35 defStyle := config.DefStyle
37 if _, ok := config.Colorscheme["message"]; ok {
38 defStyle = config.Colorscheme["message"]
44 func NewInfoWindow(b *info.InfoBuf) *InfoWindow {
49 iw.Width, iw.Y = screen.Screen.Size()
55 func (i *InfoWindow) Resize(w, h int) {
60 func (i *InfoWindow) SetBuffer(b *buffer.Buffer) {
64 func (i *InfoWindow) Relocate() bool { return false }
65 func (i *InfoWindow) GetView() *View { return i.View }
66 func (i *InfoWindow) SetView(v *View) {}
67 func (i *InfoWindow) SetActive(b bool) {}
68 func (i *InfoWindow) IsActive() bool { return true }
70 func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc {
71 c := i.Buffer.GetActiveCursor()
72 l := i.Buffer.LineBytes(0)
73 n := utf8.RuneCountInString(i.Msg)
74 return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 0}
77 func (i *InfoWindow) Clear() {
78 for x := 0; x < i.Width; x++ {
79 screen.SetContent(x, i.Y, ' ', nil, i.defStyle())
83 func (i *InfoWindow) displayBuffer() {
85 line := b.LineBytes(0)
86 activeC := b.GetActiveCursor()
89 vlocX := utf8.RuneCountInString(i.Msg)
92 line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, blocX, tabsize)
95 draw := func(r rune, style tcell.Style) {
96 if nColsBeforeStart <= 0 {
97 bloc := buffer.Loc{X: blocX, Y: 0}
98 if activeC.HasSelection() &&
99 (bloc.GreaterEqual(activeC.CurSelection[0]) && bloc.LessThan(activeC.CurSelection[1]) ||
100 bloc.LessThan(activeC.CurSelection[0]) && bloc.GreaterEqual(activeC.CurSelection[1])) {
101 // The current character is selected
102 style = i.defStyle().Reverse(true)
104 if s, ok := config.Colorscheme["selection"]; ok {
110 rw := runewidth.RuneWidth(r)
111 for j := 0; j < rw; j++ {
116 screen.SetContent(vlocX, i.Y, c, nil, style)
123 totalwidth := blocX - nColsBeforeStart
127 r, size := utf8.DecodeRune(line)
129 draw(r, i.defStyle())
136 ts := tabsize - (totalwidth % tabsize)
139 width = runewidth.RuneWidth(r)
146 // Draw any extra characters either spaces for tabs or @ for incomplete wide runes
148 for j := 1; j < width; j++ {
149 draw(char, i.defStyle())
152 if activeC.X == curBX {
153 screen.ShowCursor(curVX, i.Y)
156 if vlocX >= i.Width {
160 if activeC.X == blocX {
161 screen.ShowCursor(vlocX, i.Y)
165 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"}
167 func (i *InfoWindow) displayKeyMenu() {
168 // TODO: maybe make this based on the actual keybindings
170 for y := 0; y < len(keydisplay); y++ {
171 for x := 0; x < i.Width; x++ {
172 if x < len(keydisplay[y]) {
173 screen.SetContent(x, i.Y-len(keydisplay)+y, rune(keydisplay[y][x]), nil, i.defStyle())
175 screen.SetContent(x, i.Y-len(keydisplay)+y, ' ', nil, i.defStyle())
181 func (i *InfoWindow) totalSize() int {
183 for _, n := range i.Suggestions {
184 sum += runewidth.StringWidth(n) + 1
189 func (i *InfoWindow) scrollToSuggestion() {
193 for j, n := range i.Suggestions {
194 c := utf8.RuneCountInString(n)
195 if j == i.CurSuggestion {
196 if x+c >= i.hscroll+i.Width {
197 i.hscroll = util.Clamp(x+c+1-i.Width, 0, s-i.Width)
198 } else if x < i.hscroll {
199 i.hscroll = util.Clamp(x-1, 0, s-i.Width)
211 func (i *InfoWindow) Display() {
214 if config.GetGlobalOption("keymenu").(bool) {
218 if i.HasPrompt || config.GlobalSettings["infobar"].(bool) {
219 if !i.HasPrompt && !i.HasMessage && !i.HasError {
223 style := i.defStyle()
230 for _, c := range display {
231 screen.SetContent(x, i.Y, c, nil, style)
232 x += runewidth.RuneWidth(c)
240 if i.HasSuggestions && len(i.Suggestions) > 1 {
241 i.scrollToSuggestion()
246 statusLineStyle := config.DefStyle.Reverse(true)
247 if style, ok := config.Colorscheme["statusline"]; ok {
248 statusLineStyle = style
251 if config.GetGlobalOption("keymenu").(bool) {
252 keymenuOffset = len(keydisplay)
255 draw := func(r rune, s tcell.Style) {
256 y := i.Y - keymenuOffset - 1
257 rw := runewidth.RuneWidth(r)
258 for j := 0; j < rw; j++ {
264 if x == i.Width-1 && !done {
265 screen.SetContent(i.Width-1, y, '>', nil, s)
268 } else if x == 0 && i.hscroll > 0 {
269 screen.SetContent(0, y, '<', nil, s)
270 } else if x >= 0 && x < i.Width {
271 screen.SetContent(x, y, c, nil, s)
277 for j, s := range i.Suggestions {
278 style := statusLineStyle
279 if i.CurSuggestion == j {
280 style = style.Reverse(true)
282 for _, r := range s {
284 // screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style)
286 draw(' ', statusLineStyle)
290 draw(' ', statusLineStyle)