]> git.lizzy.rs Git - micro.git/blob - internal/info/infobuffer.go
Merge branch 'master' of https://github.com/dbeef/micro into dbeef-master
[micro.git] / internal / info / infobuffer.go
1 package info
2
3 import (
4         "fmt"
5         "strings"
6
7         "github.com/zyedidia/micro/internal/buffer"
8         luar "layeh.com/gopher-luar"
9
10         "github.com/zyedidia/micro/internal/config"
11         ulua "github.com/zyedidia/micro/internal/lua"
12         "github.com/zyedidia/micro/internal/screen"
13 )
14
15 // The InfoBuf displays messages and other info at the bottom of the screen.
16 // It is respresented as a buffer and a message with a style.
17 type InfoBuf struct {
18         *buffer.Buffer
19
20         HasPrompt  bool
21         HasMessage bool
22         HasError   bool
23         HasYN      bool
24
25         PromptType string
26
27         Msg    string
28         YNResp bool
29
30         // This map stores the history for all the different kinds of uses Prompt has
31         // It's a map of history type -> history array
32         History    map[string][]string
33         HistoryNum int
34
35         // Is the current message a message from the gutter
36         HasGutter bool
37
38         PromptCallback func(resp string, canceled bool)
39         EventCallback  func(resp string)
40         YNCallback     func(yes bool, canceled bool)
41 }
42
43 // NewBuffer returns a new infobuffer
44 func NewBuffer() *InfoBuf {
45         ib := new(InfoBuf)
46         ib.History = make(map[string][]string)
47
48         ib.Buffer = buffer.NewBufferFromString("", "", buffer.BTInfo)
49         ib.LoadHistory()
50
51         return ib
52 }
53
54 // Close performs any cleanup necessary when shutting down the infobuffer
55 func (i *InfoBuf) Close() {
56         i.SaveHistory()
57 }
58
59 // Message sends a message to the user
60 func (i *InfoBuf) Message(msg ...interface{}) {
61         // only display a new message if there isn't an active prompt
62         // this is to prevent overwriting an existing prompt to the user
63         if i.HasPrompt == false {
64                 displayMessage := fmt.Sprint(msg...)
65                 // if there is no active prompt then style and display the message as normal
66                 i.Msg = displayMessage
67                 i.HasMessage, i.HasError = true, false
68         }
69 }
70
71 // GutterMessage displays a message and marks it as a gutter message
72 func (i *InfoBuf) GutterMessage(msg ...interface{}) {
73         i.Message(msg...)
74         i.HasGutter = true
75 }
76
77 // ClearGutter clears the info bar and unmarks the message
78 func (i *InfoBuf) ClearGutter() {
79         i.HasGutter = false
80         i.Message("")
81 }
82
83 // Error sends an error message to the user
84 func (i *InfoBuf) Error(msg ...interface{}) {
85         // only display a new message if there isn't an active prompt
86         // this is to prevent overwriting an existing prompt to the user
87         if i.HasPrompt == false {
88                 // if there is no active prompt then style and display the message as normal
89                 i.Msg = fmt.Sprint(msg...)
90                 i.HasMessage, i.HasError = false, true
91         }
92         // TODO: add to log?
93 }
94
95 // Prompt starts a prompt for the user, it takes a prompt, a possibly partially filled in msg
96 // and callbacks executed when the user executes an event and when the user finishes the prompt
97 // The eventcb passes the current user response as the argument and donecb passes the user's message
98 // and a boolean indicating if the prompt was canceled
99 func (i *InfoBuf) Prompt(prompt string, msg string, ptype string, eventcb func(string), donecb func(string, bool)) {
100         // If we get another prompt mid-prompt we cancel the one getting overwritten
101         if i.HasPrompt {
102                 i.DonePrompt(true)
103         }
104
105         if _, ok := i.History[ptype]; !ok {
106                 i.History[ptype] = []string{""}
107         } else {
108                 i.History[ptype] = append(i.History[ptype], "")
109         }
110         i.HistoryNum = len(i.History[ptype]) - 1
111
112         i.PromptType = ptype
113         i.Msg = prompt
114         i.HasPrompt = true
115         i.HasMessage, i.HasError, i.HasYN = false, false, false
116         i.HasGutter = false
117         i.PromptCallback = donecb
118         i.EventCallback = eventcb
119         i.Buffer.Insert(i.Buffer.Start(), msg)
120 }
121
122 // YNPrompt creates a yes or no prompt, and the callback returns the yes/no result and whether
123 // the prompt was canceled
124 func (i *InfoBuf) YNPrompt(prompt string, donecb func(bool, bool)) {
125         if i.HasPrompt {
126                 i.DonePrompt(true)
127         }
128
129         i.Msg = prompt
130         i.HasPrompt = true
131         i.HasYN = true
132         i.HasMessage, i.HasError = false, false
133         i.HasGutter = false
134         i.YNCallback = donecb
135 }
136
137 // PlugPrompt provides a plugin interface for calling "Prompt" with the appropriate Lua callbacks
138 func (i *InfoBuf) PlugPrompt(prompt string, msg string, ptype string, eventcb string, donecb string) {
139         eventLuaFn := strings.Split(eventcb, ".")
140         doneLuaFn := strings.Split(donecb, ".")
141         var luaEventcb func(string)
142         var luaDonecb func(string, bool)
143
144         if len(eventLuaFn) == 2 {
145                 plName, plFn := doneLuaFn[0], doneLuaFn[1]
146                 pl := config.FindPlugin(plName)
147                 if pl != nil {
148                         luaEventcb = func(resp string) {
149                                 _, err := pl.Call(plFn, luar.New(ulua.L, resp))
150                                 if err != nil && err != config.ErrNoSuchFunction {
151                                         screen.TermMessage(err)
152                                 }
153                         }
154                 }
155         }
156
157         if len(doneLuaFn) == 2 {
158                 plName, plFn := doneLuaFn[0], doneLuaFn[1]
159                 pl := config.FindPlugin(plName)
160                 if pl != nil {
161                         luaDonecb = func(resp string, canceled bool) {
162                                 _, err := pl.Call(plFn, luar.New(ulua.L, resp), luar.New(ulua.L, canceled))
163                                 if err != nil && err != config.ErrNoSuchFunction {
164                                         screen.TermMessage(err)
165                                 }
166                         }
167                 }
168         }
169
170         i.Prompt(prompt, msg, ptype, luaEventcb, luaDonecb)
171 }
172
173 // PlugYNPrompt provides a plugin interface for calling "YNPrompt" with the appropriate Lua callbacks
174 func (i *InfoBuf) PlugYNPrompt(prompt string, donecb string) {
175         doneLuaFn := strings.Split(donecb, ".")
176         var luaDonecb func(bool, bool)
177
178         if len(doneLuaFn) == 2 {
179                 plName, plFn := doneLuaFn[0], doneLuaFn[1]
180                 pl := config.FindPlugin(plName)
181                 if pl != nil {
182                         luaDonecb = func(resp bool, canceled bool) {
183                                 _, err := pl.Call(plFn, luar.New(ulua.L, resp), luar.New(ulua.L, canceled))
184                                 if err != nil && err != config.ErrNoSuchFunction {
185                                         screen.TermMessage(err)
186                                 }
187                         }
188                 }
189         }
190
191         i.YNPrompt(prompt, luaDonecb)
192 }
193
194 // DonePrompt finishes the current prompt and indicates whether or not it was canceled
195 func (i *InfoBuf) DonePrompt(canceled bool) {
196         hadYN := i.HasYN
197         i.HasPrompt = false
198         i.HasYN = false
199         i.HasGutter = false
200         if !hadYN {
201                 if i.PromptCallback != nil {
202                         if canceled {
203                                 i.PromptCallback("", true)
204                                 h := i.History[i.PromptType]
205                                 i.History[i.PromptType] = h[:len(h)-1]
206                         } else {
207                                 resp := string(i.LineBytes(0))
208                                 i.PromptCallback(resp, false)
209                                 h := i.History[i.PromptType]
210                                 h[len(h)-1] = resp
211                         }
212                         i.PromptCallback = nil
213                 }
214                 i.Replace(i.Start(), i.End(), "")
215         }
216         if i.YNCallback != nil && hadYN {
217                 i.YNCallback(i.YNResp, canceled)
218         }
219 }
220
221 // Reset resets the infobuffer's msg and info
222 func (i *InfoBuf) Reset() {
223         i.Msg = ""
224         i.HasPrompt, i.HasMessage, i.HasError = false, false, false
225         i.HasGutter = false
226 }