Files
zyedidia.micro/src/messenger.go
2016-03-25 14:32:35 -04:00

169 lines
3.8 KiB
Go

package main
import (
"bufio"
"fmt"
"github.com/gdamore/tcell"
"os"
"strconv"
)
// TermMessage sends a message to the user in the terminal. This usually occurs before
// micro has been fully initialized -- ie if there is an error in the syntax highlighting
// regular expressions
// The function must be called when the screen is not initialized
// This will write the message, and wait for the user
// to press and key to continue
func TermMessage(msg string) {
fmt.Println(msg)
fmt.Print("\nPress enter to continue")
reader := bufio.NewReader(os.Stdin)
reader.ReadString('\n')
}
// TermError sends an error to the user in the terminal. Like TermMessage except formatted
// as an error
func TermError(filename string, lineNum int, err string) {
TermMessage(filename + ", " + strconv.Itoa(lineNum) + ": " + err)
}
// Messenger is an object that makes it easy to send messages to the user
// and get input from the user
type Messenger struct {
// Are we currently prompting the user?
hasPrompt bool
// Is there a message to print
hasMessage bool
// Message to print
message string
// The user's response to a prompt
response string
// style to use when drawing the message
style tcell.Style
// We have to keep track of the cursor for prompting
cursorx int
}
// Message sends a message to the user
func (m *Messenger) Message(msg string) {
m.message = msg
m.style = tcell.StyleDefault
if _, ok := colorscheme["message"]; ok {
m.style = colorscheme["message"]
}
m.hasMessage = true
}
// Error sends an error message to the user
func (m *Messenger) Error(msg string) {
m.message = msg
m.style = tcell.StyleDefault.
Foreground(tcell.ColorBlack).
Background(tcell.ColorMaroon)
if _, ok := colorscheme["error-message"]; ok {
m.style = colorscheme["error-message"]
}
m.hasMessage = true
}
// Prompt sends the user a message and waits for a response to be typed in
// This function blocks the main loop while waiting for input
func (m *Messenger) Prompt(prompt string) (string, bool) {
m.hasPrompt = true
m.Message(prompt)
response, canceled := "", true
for m.hasPrompt {
m.Clear()
m.Display()
event := screen.PollEvent()
switch e := event.(type) {
case *tcell.EventKey:
if e.Key() == tcell.KeyEscape {
// Cancel
m.hasPrompt = false
} else if e.Key() == tcell.KeyCtrlC {
// Cancel
m.hasPrompt = false
} else if e.Key() == tcell.KeyCtrlQ {
// Cancel
m.hasPrompt = false
} else if e.Key() == tcell.KeyEnter {
// User is done entering their response
m.hasPrompt = false
response, canceled = m.response, false
}
}
m.HandleEvent(event)
}
m.Reset()
return response, canceled
}
// HandleEvent handles an event for the prompter
func (m *Messenger) HandleEvent(event tcell.Event) {
switch e := event.(type) {
case *tcell.EventKey:
switch e.Key() {
case tcell.KeyLeft:
m.cursorx--
case tcell.KeyRight:
m.cursorx++
case tcell.KeyBackspace2:
if m.cursorx > 0 {
m.response = string([]rune(m.response)[:Count(m.response)-1])
m.cursorx--
}
case tcell.KeySpace:
m.response += " "
m.cursorx++
case tcell.KeyRune:
m.response += string(e.Rune())
m.cursorx++
}
}
if m.cursorx < 0 {
m.cursorx = 0
}
}
// Reset resets the messenger's cursor, message and response
func (m *Messenger) Reset() {
m.cursorx = 0
m.message = ""
m.response = ""
}
// Clear clears the line at the bottom of the editor
func (m *Messenger) Clear() {
w, h := screen.Size()
for x := 0; x < w; x++ {
screen.SetContent(x, h-1, ' ', nil, tcell.StyleDefault)
}
}
// Display displays messages or prompts
func (m *Messenger) Display() {
_, h := screen.Size()
if m.hasMessage {
runes := []rune(m.message + m.response)
for x := 0; x < len(runes); x++ {
screen.SetContent(x, h-1, runes[x], nil, m.style)
}
}
if m.hasPrompt {
screen.ShowCursor(Count(m.message)+m.cursorx, h-1)
screen.Show()
}
}