]> git.lizzy.rs Git - micro.git/blob - cmd/micro/messenger.go
Fix crash
[micro.git] / cmd / micro / messenger.go
1 package main
2
3 import (
4         "bufio"
5         "bytes"
6         "fmt"
7         "os"
8         "strconv"
9
10         "github.com/zyedidia/tcell"
11 )
12
13 // TermMessage sends a message to the user in the terminal. This usually occurs before
14 // micro has been fully initialized -- ie if there is an error in the syntax highlighting
15 // regular expressions
16 // The function must be called when the screen is not initialized
17 // This will write the message, and wait for the user
18 // to press and key to continue
19 func TermMessage(msg ...interface{}) {
20         screenWasNil := screen == nil
21         if !screenWasNil {
22                 screen.Fini()
23         }
24
25         fmt.Println(msg...)
26         fmt.Print("\nPress enter to continue")
27
28         reader := bufio.NewReader(os.Stdin)
29         reader.ReadString('\n')
30
31         if !screenWasNil {
32                 InitScreen()
33         }
34 }
35
36 // TermError sends an error to the user in the terminal. Like TermMessage except formatted
37 // as an error
38 func TermError(filename string, lineNum int, err string) {
39         TermMessage(filename + ", " + strconv.Itoa(lineNum) + ": " + err)
40 }
41
42 // Messenger is an object that makes it easy to send messages to the user
43 // and get input from the user
44 type Messenger struct {
45         // Are we currently prompting the user?
46         hasPrompt bool
47         // Is there a message to print
48         hasMessage bool
49
50         // Message to print
51         message string
52         // The user's response to a prompt
53         response string
54         // style to use when drawing the message
55         style tcell.Style
56
57         // We have to keep track of the cursor for prompting
58         cursorx int
59
60         // Is the current message a message from the gutter
61         gutterMessage bool
62 }
63
64 // Message sends a message to the user
65 func (m *Messenger) Message(msg ...interface{}) {
66         buf := new(bytes.Buffer)
67         fmt.Fprint(buf, msg...)
68         m.message = buf.String()
69         m.style = defStyle
70
71         if _, ok := colorscheme["message"]; ok {
72                 m.style = colorscheme["message"]
73         }
74         m.hasMessage = true
75 }
76
77 // Error sends an error message to the user
78 func (m *Messenger) Error(msg ...interface{}) {
79         buf := new(bytes.Buffer)
80         fmt.Fprint(buf, msg...)
81         m.message = buf.String()
82         m.style = defStyle.
83                 Foreground(tcell.ColorBlack).
84                 Background(tcell.ColorMaroon)
85
86         if _, ok := colorscheme["error-message"]; ok {
87                 m.style = colorscheme["error-message"]
88         }
89         m.hasMessage = true
90 }
91
92 // YesNoPrompt asks the user a yes or no question (waits for y or n) and returns the result
93 func (m *Messenger) YesNoPrompt(prompt string) (bool, bool) {
94         m.Message(prompt)
95
96         for {
97                 m.Clear()
98                 m.Display()
99                 screen.Show()
100                 event := screen.PollEvent()
101
102                 switch e := event.(type) {
103                 case *tcell.EventKey:
104                         switch e.Key() {
105                         case tcell.KeyRune:
106                                 if e.Rune() == 'y' {
107                                         return true, false
108                                 } else if e.Rune() == 'n' {
109                                         return false, false
110                                 }
111                         case tcell.KeyCtrlC, tcell.KeyCtrlQ, tcell.KeyEscape:
112                                 return false, true
113                         }
114                 }
115         }
116 }
117
118 // Prompt sends the user a message and waits for a response to be typed in
119 // This function blocks the main loop while waiting for input
120 func (m *Messenger) Prompt(prompt string) (string, bool) {
121         m.hasPrompt = true
122         m.Message(prompt)
123
124         response, canceled := "", true
125
126         for m.hasPrompt {
127                 m.Clear()
128                 m.Display()
129
130                 event := screen.PollEvent()
131
132                 switch e := event.(type) {
133                 case *tcell.EventKey:
134                         switch e.Key() {
135                         case tcell.KeyCtrlQ, tcell.KeyCtrlC, tcell.KeyEscape:
136                                 // Cancel
137                                 m.hasPrompt = false
138                         case tcell.KeyEnter:
139                                 // User is done entering their response
140                                 m.hasPrompt = false
141                                 response, canceled = m.response, false
142                         }
143                 }
144
145                 m.HandleEvent(event)
146
147                 if m.cursorx < 0 {
148                         // Cancel
149                         m.hasPrompt = false
150                 }
151         }
152
153         m.Reset()
154         return response, canceled
155 }
156
157 // HandleEvent handles an event for the prompter
158 func (m *Messenger) HandleEvent(event tcell.Event) {
159         switch e := event.(type) {
160         case *tcell.EventKey:
161                 switch e.Key() {
162                 case tcell.KeyLeft:
163                         if m.cursorx > 0 {
164                                 m.cursorx--
165                         }
166                 case tcell.KeyRight:
167                         if m.cursorx < Count(m.response) {
168                                 m.cursorx++
169                         }
170                 case tcell.KeyBackspace2, tcell.KeyBackspace:
171                         if m.cursorx > 0 {
172                                 m.response = string([]rune(m.response)[:m.cursorx-1]) + string(m.response[m.cursorx:])
173                         }
174                         m.cursorx--
175                 case tcell.KeyRune:
176                         m.response = Insert(m.response, m.cursorx, string(e.Rune()))
177                         m.cursorx++
178                 }
179         }
180 }
181
182 // Reset resets the messenger's cursor, message and response
183 func (m *Messenger) Reset() {
184         m.cursorx = 0
185         m.message = ""
186         m.response = ""
187 }
188
189 // Clear clears the line at the bottom of the editor
190 func (m *Messenger) Clear() {
191         w, h := screen.Size()
192         for x := 0; x < w; x++ {
193                 screen.SetContent(x, h-1, ' ', nil, defStyle)
194         }
195 }
196
197 // Display displays messages or prompts
198 func (m *Messenger) Display() {
199         _, h := screen.Size()
200         if m.hasMessage {
201                 runes := []rune(m.message + m.response)
202                 for x := 0; x < len(runes); x++ {
203                         screen.SetContent(x, h-1, runes[x], nil, m.style)
204                 }
205         }
206         if m.hasPrompt {
207                 screen.ShowCursor(Count(m.message)+m.cursorx, h-1)
208                 screen.Show()
209         }
210 }
211
212 // A GutterMessage is a message displayed on the side of the editor
213 type GutterMessage struct {
214         lineNum int
215         msg     string
216         kind    int
217 }
218
219 // These are the different types of messages
220 const (
221         // GutterInfo represents a simple info message
222         GutterInfo = iota
223         // GutterWarning represents a compiler warning
224         GutterWarning
225         // GutterError represents a compiler error
226         GutterError
227 )