]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/messenger.go
Add savehistory option
[micro.git] / cmd / micro / messenger.go
index ce48b41fac95e44e691845fb4913c6c026344209..1212725b21a0746e873c636735e43416a6916f14 100644 (file)
@@ -3,11 +3,12 @@ package main
 import (
        "bufio"
        "bytes"
+       "encoding/gob"
        "fmt"
        "os"
        "strconv"
-       "strings"
 
+       "github.com/mattn/go-runewidth"
        "github.com/zyedidia/clipboard"
        "github.com/zyedidia/tcell"
 )
@@ -70,16 +71,18 @@ type Messenger struct {
        gutterMessage bool
 }
 
-func (m *Messenger) AddLog(msg string) {
+// AddLog sends a message to the log view
+func (m *Messenger) AddLog(msg ...interface{}) {
+       logMessage := fmt.Sprint(msg...)
        buffer := m.getBuffer()
-       buffer.insert(buffer.End(), []byte(msg+"\n"))
+       buffer.insert(buffer.End(), []byte(logMessage+"\n"))
        buffer.Cursor.Loc = buffer.End()
        buffer.Cursor.Relocate()
 }
 
 func (m *Messenger) getBuffer() *Buffer {
        if m.log == nil {
-               m.log = NewBuffer(strings.NewReader(""), "")
+               m.log = NewBufferFromString("", "")
                m.log.name = "Log"
        }
        return m.log
@@ -119,7 +122,7 @@ func (m *Messenger) Error(msg ...interface{}) {
                m.style = defStyle.
                        Foreground(tcell.ColorBlack).
                        Background(tcell.ColorMaroon)
-               
+
                if _, ok := colorscheme["error-message"]; ok {
                        m.style = colorscheme["error-message"]
                }
@@ -129,10 +132,26 @@ func (m *Messenger) Error(msg ...interface{}) {
        m.AddLog(buf.String())
 }
 
+func (m *Messenger) PromptText(msg ...interface{}) {
+       displayMessage := fmt.Sprint(msg...)
+       // if there is no active prompt then style and display the message as normal
+       m.message = displayMessage
+
+       m.style = defStyle
+
+       if _, ok := colorscheme["message"]; ok {
+               m.style = colorscheme["message"]
+       }
+
+       m.hasMessage = true
+       // add the message to the log regardless of active prompts
+       m.AddLog(displayMessage)
+}
+
 // YesNoPrompt asks the user a yes or no question (waits for y or n) and returns the result
 func (m *Messenger) YesNoPrompt(prompt string) (bool, bool) {
        m.hasPrompt = true
-       m.Message(prompt)
+       m.PromptText(prompt)
 
        _, h := screen.Size()
        for {
@@ -146,17 +165,19 @@ func (m *Messenger) YesNoPrompt(prompt string) (bool, bool) {
                case *tcell.EventKey:
                        switch e.Key() {
                        case tcell.KeyRune:
-                               if e.Rune() == 'y' {
+                               if e.Rune() == 'y' || e.Rune() == 'Y' {
                                        m.AddLog("\t--> y")
                                        m.hasPrompt = false
                                        return true, false
-                               } else if e.Rune() == 'n' {
+                               } else if e.Rune() == 'n' || e.Rune() == 'N' {
                                        m.AddLog("\t--> n")
                                        m.hasPrompt = false
                                        return false, false
                                }
                        case tcell.KeyCtrlC, tcell.KeyCtrlQ, tcell.KeyEscape:
                                m.AddLog("\t--> (cancel)")
+                               m.Clear()
+                               m.Reset()
                                m.hasPrompt = false
                                return false, true
                        }
@@ -167,7 +188,7 @@ func (m *Messenger) YesNoPrompt(prompt string) (bool, bool) {
 // LetterPrompt gives the user a prompt and waits for a one letter response
 func (m *Messenger) LetterPrompt(prompt string, responses ...rune) (rune, bool) {
        m.hasPrompt = true
-       m.Message(prompt)
+       m.PromptText(prompt)
 
        _, h := screen.Size()
        for {
@@ -211,13 +232,14 @@ const (
        OptionCompletion
        PluginCmdCompletion
        PluginNameCompletion
+       OptionValueCompletion
 )
 
 // 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, placeholder, historyType string, completionTypes ...Completion) (string, bool) {
        m.hasPrompt = true
-       m.Message(prompt)
+       m.PromptText(prompt)
        if _, ok := m.history[historyType]; !ok {
                m.history[historyType] = []string{""}
        } else {
@@ -276,6 +298,10 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
                                        chosen, suggestions = HelpComplete(currentArg)
                                } else if completionType == OptionCompletion {
                                        chosen, suggestions = OptionComplete(currentArg)
+                               } else if completionType == OptionValueCompletion {
+                                       if currentArgNum-1 > 0 {
+                                               chosen, suggestions = OptionValueComplete(args[currentArgNum-1], currentArg)
+                                       }
                                } else if completionType == PluginCmdCompletion {
                                        chosen, suggestions = PluginCmdComplete(currentArg)
                                } else if completionType == PluginNameCompletion {
@@ -446,8 +472,10 @@ func (m *Messenger) Display() {
        if m.hasMessage {
                if m.hasPrompt || globalSettings["infobar"].(bool) {
                        runes := []rune(m.message + m.response)
+                       posx := 0
                        for x := 0; x < len(runes); x++ {
-                               screen.SetContent(x, h-1, runes[x], nil, m.style)
+                               screen.SetContent(posx, h-1, runes[x], nil, m.style)
+                               posx += runewidth.RuneWidth(runes[x])
                        }
                }
        }
@@ -458,6 +486,55 @@ func (m *Messenger) Display() {
        }
 }
 
+// LoadHistory attempts to load user history from configDir/buffers/history
+// into the history map
+// The savehistory option must be on
+func (m *Messenger) LoadHistory() {
+       if GetGlobalOption("savehistory").(bool) {
+               file, err := os.Open(configDir + "/buffers/history")
+               var decodedMap map[string][]string
+               if err == nil {
+                       decoder := gob.NewDecoder(file)
+                       err = decoder.Decode(&decodedMap)
+                       file.Close()
+               }
+
+               if err != nil {
+                       m.Error("Error loading history:", err)
+                       return
+               }
+
+               m.history = decodedMap
+       } else {
+               m.history = make(map[string][]string)
+       }
+}
+
+// SaveHistory saves the user's command history to configDir/buffers/history
+// only if the savehistory option is on
+func (m *Messenger) SaveHistory() {
+       if GetGlobalOption("savehistory").(bool) {
+               // Don't save history past 100
+               for k, v := range m.history {
+                       if len(v) > 100 {
+                               m.history[k] = v[0:100]
+                       }
+               }
+
+               file, err := os.Create(configDir + "/buffers/history")
+               if err == nil {
+                       encoder := gob.NewEncoder(file)
+
+                       err = encoder.Encode(m.history)
+                       if err != nil {
+                               m.Error("Error saving history:", err)
+                               return
+                       }
+                       file.Close()
+               }
+       }
+}
+
 // A GutterMessage is a message displayed on the side of the editor
 type GutterMessage struct {
        lineNum int