Compare commits

..

No commits in common. "ddaf609d0c8357ffbb5629f72a3a120e69af0611" and "e07cdfdfde2b8f374bef3cc78f8cc9048e50dd18" have entirely different histories.

4 changed files with 25 additions and 199 deletions

View File

@ -2,8 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"log"
"time"
"git.qowevisa.me/Qowevisa/tuimenu/simple" "git.qowevisa.me/Qowevisa/tuimenu/simple"
) )
@ -11,21 +9,9 @@ import (
func main() { func main() {
m := simple.CreateMenu( m := simple.CreateMenu(
simple.WithCustomTitle("My Custom Title"), simple.WithCustomTitle("My Custom Title"),
simple.WithUsageOfEscapeCodes(),
) )
// Using this function will redirect every log.PrintX func in THIS file
// to m.Log buffer that will Flush everything at the start of next iteration
m.RedirectLogOutputToBufferedLogger()
nameName, err := m.AddCommand("0", "NameName", func(m *simple.Menu) error { nameName, err := m.AddCommand("0", "NameName", func(m *simple.Menu) error {
log.Printf("hello, world\n") fmt.Printf("hello, world\n")
time.Sleep(time.Second * 3)
m.Log.Logf("some data = %d\n", 42)
time.Sleep(time.Second * 1)
m.Log.Logf("some data = %d\n", 43)
time.Sleep(time.Second * 1)
m.Log.Logf("some data = %d\n", 44)
log.Printf("asdas")
time.Sleep(time.Second * 1)
return nil return nil
}) })
if err != nil { if err != nil {

View File

@ -1,47 +0,0 @@
package simple
import (
"bytes"
"fmt"
"log"
"strings"
"sync"
"time"
)
// BufferedLogger structure buffer log messages
type BufferedLogger struct {
mu sync.Mutex
buffer bytes.Buffer
}
// Logf buffers the log message with a formatted string
// To print it instantly call Flush
func (bl *BufferedLogger) Logf(format string, args ...interface{}) {
bl.mu.Lock()
defer bl.mu.Unlock()
// Create log entry with a timestamp
timestamp := time.Now().Format(time.RFC3339)
entry := fmt.Sprintf("[%s] %s", timestamp, fmt.Sprintf(format, args...))
if !(strings.Contains(entry, "\n") && entry[len(entry)-1] == '\n') {
entry += "\n"
}
bl.buffer.WriteString(entry)
}
// Flush outputs all buffered log messages
func (bl *BufferedLogger) Flush() {
bl.mu.Lock()
defer bl.mu.Unlock()
if bl.buffer.Len() > 0 {
fmt.Print(bl.buffer.String())
bl.buffer.Reset()
}
}
func (m *Menu) RedirectLogOutputToBufferedLogger() {
log.SetOutput(&m.Log.buffer)
}

View File

@ -3,7 +3,6 @@ package simple
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"log"
"os" "os"
"strings" "strings"
) )
@ -32,16 +31,14 @@ func (c *Command) Print() {
} }
type MenuConfig struct { type MenuConfig struct {
Title string Title string
BackKey string BackKey string
UsingEscapeCodes bool
} }
func getDefConfig() *MenuConfig { func getDefConfig() *MenuConfig {
return &MenuConfig{ return &MenuConfig{
Title: "Default Menu Title", Title: "Default Menu Title",
BackKey: "<", BackKey: "<",
UsingEscapeCodes: false,
} }
} }
@ -87,20 +84,9 @@ func createCommandTree() *commandTree {
type Menu struct { type Menu struct {
Title string Title string
BackKey string BackKey string
// Escape Code part
//
usingEscapeCodes bool
lineCounter uint
//
Log *BufferedLogger
// //
counterForIDs uint counterForIDs uint
cmdTree *commandTree cmdTree *commandTree
//
interrupt chan func(m *Menu)
resumeSignal chan struct{}
inputChan chan string
errorChan chan error
} }
func CreateMenu(options ...SimpleMenuOption) *Menu { func CreateMenu(options ...SimpleMenuOption) *Menu {
@ -111,17 +97,10 @@ func CreateMenu(options ...SimpleMenuOption) *Menu {
} }
return &Menu{ return &Menu{
Title: conf.Title, Title: conf.Title,
BackKey: conf.BackKey, BackKey: conf.BackKey,
usingEscapeCodes: conf.UsingEscapeCodes, counterForIDs: 1,
Log: &BufferedLogger{}, cmdTree: createCommandTree(),
lineCounter: 0,
counterForIDs: 1,
cmdTree: createCommandTree(),
interrupt: make(chan func(m *Menu)),
resumeSignal: make(chan struct{}),
inputChan: make(chan string),
errorChan: make(chan error),
} }
} }
@ -171,56 +150,8 @@ func (m *Menu) AddCommand(key, name string, action CommandAction) (*commandNode,
return m.cmdTree.Root.AddCommand(key, name, action), nil return m.cmdTree.Root.AddCommand(key, name, action), nil
} }
func (m *Menu) clearLines() { func (m *Menu) iteration() {
for i := 0; i < int(m.lineCounter); i++ {
fmt.Printf("\033[F\033[K")
}
m.lineCounter = 0
}
func (m *Menu) handleInput(input string) {
var preHandler func()
var action CommandAction
var afterHandler func()
if m.usingEscapeCodes {
preHandler = func() {
m.clearLines()
}
}
for _, node := range m.cmdTree.Pointer.Children {
cmd := node.Val
if cmd == nil {
continue
}
if cmd.Key == input {
action = cmd.Action
if cmd.MoveTo {
afterHandler = func() {
m.cmdTree.Pointer = node
}
}
break
}
}
if m.cmdTree.Pointer != m.cmdTree.Root && m.BackKey == input {
afterHandler = func() {
m.cmdTree.Pointer = m.cmdTree.Pointer.Parent
}
}
if preHandler != nil {
preHandler()
}
if action != nil {
action(m)
}
if afterHandler != nil {
afterHandler()
}
}
func (m *Menu) printMenu() {
fmt.Printf("%s\n", m.Title) fmt.Printf("%s\n", m.Title)
m.lineCounter++
path := "" path := ""
for node := m.cmdTree.Pointer; node != nil; node = node.Parent { for node := m.cmdTree.Pointer; node != nil; node = node.Parent {
if node.Parent == nil { if node.Parent == nil {
@ -230,7 +161,6 @@ func (m *Menu) printMenu() {
} }
} }
fmt.Printf("At %s\n", path) fmt.Printf("At %s\n", path)
m.lineCounter++
for _, node := range m.cmdTree.Pointer.Children { for _, node := range m.cmdTree.Pointer.Children {
cmd := node.Val cmd := node.Val
if cmd == nil { if cmd == nil {
@ -238,70 +168,33 @@ func (m *Menu) printMenu() {
continue continue
} }
cmd.Print() cmd.Print()
m.lineCounter++
} }
if m.cmdTree.Pointer != m.cmdTree.Root { if m.cmdTree.Pointer != m.cmdTree.Root {
fmt.Printf("%s: Go back one layer\n", m.BackKey) fmt.Printf("%s: Go back one layer\n", m.BackKey)
m.lineCounter++
} }
}
func (m *Menu) readInput() {
stdinReader := bufio.NewReader(os.Stdin) stdinReader := bufio.NewReader(os.Stdin)
for { msg, err := stdinReader.ReadString('\n')
msg, err := stdinReader.ReadString('\n')
if err != nil {
// fmt.Printf("Error: ReadString: %v\n", err)
m.errorChan <- err
return
}
m.lineCounter++
m.inputChan <- msg
}
}
func (m *Menu) GetInput(prompt string) string {
nlCount := strings.Count(prompt, "\n")
m.lineCounter += uint(nlCount)
stdinReader := bufio.NewReader(os.Stdin)
rawMsg, err := stdinReader.ReadString('\n')
if err != nil { if err != nil {
// fmt.Printf("Error: ReadString: %v\n", err) fmt.Printf("Error: ReadString: %v\n", err)
m.errorChan <- err return
return ""
} }
m.lineCounter++ msg = strings.TrimRight(msg, "\n")
msg := strings.TrimRight(rawMsg, "\n") for _, node := range m.cmdTree.Pointer.Children {
return msg cmd := node.Val
} if cmd == nil {
continue
func (m *Menu) iteration() { }
m.Log.Flush() if cmd.Key == msg {
m.printMenu() cmd.Action(m)
m.cmdTree.Pointer = node
// for {
select {
case msg := <-m.inputChan:
msg = strings.TrimRight(msg, "\n")
m.handleInput(msg)
case err := <-m.errorChan:
m.Log.Logf("err: %v", err)
case f := <-m.interrupt:
if m.usingEscapeCodes {
m.clearLines()
} }
log.Printf("Interrupt")
f(m)
} }
// } if m.BackKey == msg {
} m.cmdTree.Pointer = m.cmdTree.Pointer.Parent
}
func (m *Menu) SendInterrupt(f func(m *Menu)) {
m.interrupt <- f
} }
func (m *Menu) Start() { func (m *Menu) Start() {
go m.readInput()
for { for {
m.iteration() m.iteration()
} }

View File

@ -13,9 +13,3 @@ func WithCustomBackKey(back string) SimpleMenuOption {
conf.BackKey = back conf.BackKey = back
} }
} }
func WithUsageOfEscapeCodes() SimpleMenuOption {
return func(conf *MenuConfig) {
conf.UsingEscapeCodes = true
}
}