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