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