Compare commits
No commits in common. "ddaf609d0c8357ffbb5629f72a3a120e69af0611" and "e07cdfdfde2b8f374bef3cc78f8cc9048e50dd18" have entirely different histories.
ddaf609d0c
...
e07cdfdfde
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
133
simple/menu.go
133
simple/menu.go
|
@ -3,7 +3,6 @@ package simple
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -34,14 +33,12 @@ 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 {
|
||||||
|
@ -113,15 +99,8 @@ func CreateMenu(options ...SimpleMenuOption) *Menu {
|
||||||
return &Menu{
|
return &Menu{
|
||||||
Title: conf.Title,
|
Title: conf.Title,
|
||||||
BackKey: conf.BackKey,
|
BackKey: conf.BackKey,
|
||||||
usingEscapeCodes: conf.UsingEscapeCodes,
|
|
||||||
Log: &BufferedLogger{},
|
|
||||||
lineCounter: 0,
|
|
||||||
counterForIDs: 1,
|
counterForIDs: 1,
|
||||||
cmdTree: createCommandTree(),
|
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 {
|
if err != nil {
|
||||||
// fmt.Printf("Error: ReadString: %v\n", err)
|
fmt.Printf("Error: ReadString: %v\n", err)
|
||||||
m.errorChan <- err
|
|
||||||
return
|
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 {
|
|
||||||
// fmt.Printf("Error: ReadString: %v\n", err)
|
|
||||||
m.errorChan <- err
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
m.lineCounter++
|
|
||||||
msg := strings.TrimRight(rawMsg, "\n")
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Menu) iteration() {
|
|
||||||
m.Log.Flush()
|
|
||||||
m.printMenu()
|
|
||||||
|
|
||||||
// for {
|
|
||||||
select {
|
|
||||||
case msg := <-m.inputChan:
|
|
||||||
msg = strings.TrimRight(msg, "\n")
|
msg = strings.TrimRight(msg, "\n")
|
||||||
m.handleInput(msg)
|
for _, node := range m.cmdTree.Pointer.Children {
|
||||||
case err := <-m.errorChan:
|
cmd := node.Val
|
||||||
m.Log.Logf("err: %v", err)
|
if cmd == nil {
|
||||||
case f := <-m.interrupt:
|
continue
|
||||||
if m.usingEscapeCodes {
|
|
||||||
m.clearLines()
|
|
||||||
}
|
}
|
||||||
log.Printf("Interrupt")
|
if cmd.Key == msg {
|
||||||
f(m)
|
cmd.Action(m)
|
||||||
|
m.cmdTree.Pointer = node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user