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