Skip to main content
Glama
emicklei

melrōse musical expression player

by emicklei
app.go3.46 kB
package cli import ( "fmt" "os" "os/exec" "os/signal" "runtime" "strings" "syscall" "github.com/emicklei/melrose/core" "github.com/emicklei/melrose/dsl" "github.com/emicklei/melrose/notify" "github.com/peterh/liner" ) var ( history = ".melrose.history" ) func StartREPL(ctx core.Context) { notify.PrintWelcome(core.BuildTag) // start REPL line := liner.NewLiner() defer line.Close() defer tearDown(line, ctx) // TODO liner catches control+c //setupCloseHandler(line) ctx.Device().Report() setup(line) repl(line, ctx) } func tearDown(line *liner.State, ctx core.Context) { ctx.Control().Reset() ctx.Device().Reset() if f, err := os.Create(history); err != nil { notify.Print(notify.NewErrorf("error writing history file:%v", err)) } else { line.WriteHistory(f) f.Close() } } func setup(line *liner.State) { line.SetCtrlCAborts(true) line.SetWordCompleter(completeMe) if f, err := os.Open(history); err == nil { line.ReadHistory(f) f.Close() } } func repl(line *liner.State, ctx core.Context) { eval := dsl.NewEvaluator(ctx) ctx.Control().Start() for { entry, err := line.Prompt(notify.Prompt()) if err != nil { notify.Print(notify.NewError(err)) tearDown(line, ctx) goto exit } entry: entry = strings.TrimSpace(entry) if strings.HasPrefix(entry, ":") { // special case if entry == ":q" || entry == ":Q" { goto exit } args := strings.Split(entry, " ") if cmd, ok := lookupCommand(args[0]); ok { if msg := cmd.Func(ctx, args[1:]); msg != nil { notify.Print(msg) } line.AppendHistory(entry) continue } } if strings.HasPrefix(entry, "!") { entry = fmt.Sprintf("play(%s)", entry[1:]) goto entry } if strings.HasSuffix(entry, "!") { if len(entry) == 1 { notify.Errorf("missing expression before '!'") continue } if result, err := eval.RecoveringEvaluateProgram(entry[:len(entry)-1]); err != nil { notify.Print(notify.NewError(err)) continue } else { if result != nil { // create hidden variable // assign it the value of the expression before ! // open the browser on it ctx.Variables().Put("_", result) open("http://localhost:8118/v1/notes?var=_") continue } } } if result, err := eval.RecoveringEvaluateProgram(entry); err != nil { notify.Print(notify.NewError(err)) // even on error, add entry to history so we can edit/fix it } else { //log.Println("write inspection") core.InspectValue(ctx, result) } line.AppendHistory(entry) } exit: } // setupCloseHandler creates a 'listener' on a new goroutine which will notify the // program if it receives an interrupt from the OS. We then handle this by calling // our clean up procedure and exiting the program. func setupCloseHandler(line *liner.State, ctx core.Context) { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c fmt.Println("\r- Ctrl+C pressed in Terminal") tearDown(line, ctx) os.Exit(0) }() } // Open calls the OS default program for uri func open(uri string) error { switch { case "windows" == runtime.GOOS: return exec.Command("rundll32", "url.dll,FileProtocolHandler", uri).Start() case "darwin" == runtime.GOOS: return exec.Command("open", uri).Start() case "linux" == runtime.GOOS: return exec.Command("xdg-open", uri).Start() default: return fmt.Errorf("unable to open uri:%v on:%v", uri, runtime.GOOS) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/emicklei/melrose'

If you have feedback or need assistance with the MCP directory API, please join our Discord server