package main import ( "bytes" "flag" "fmt" "os" "os/exec" "path/filepath" "strings" "github.com/fsnotify/fsnotify" ) func main() { fs := flag.NewFlagSet("nixpl", flag.ExitOnError) if err := fs.Parse(os.Args[1:]); err != nil { panic(err) } tmp, err := os.MkdirTemp(os.TempDir(), "nixpl-*") if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } defer os.RemoveAll(tmp) apn := filepath.Join(tmp, "repl.nix") fi, err := os.Create(apn) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } defer fi.Close() if fs.Arg(0) != "" { content, _ := os.ReadFile(fs.Arg(0)) fi.Write(content) fi.Sync() } m := newModel() go watch(apn, m) cmd := exec.Command(os.Getenv("EDITOR"), apn) err = m.term.Start(cmd) if err != nil { panic(err) } for { ev := m.s.PollEvent() if ev == nil { break } m.Update(ev) } } func watch(file string, m *model) { watcher, err := fsnotify.NewWatcher() if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } defer watcher.Close() watcher.Add(file) fn := func() { content, err := os.ReadFile(file) if err != nil { return } var evalBuf bytes.Buffer var errBuf bytes.Buffer eval := exec.Command("nix", "eval", "--expr", string(content)) eval.Stdout = &evalBuf eval.Stderr = &errBuf if err := eval.Run(); err != nil { if errBuf.Len() > 0 { m.UpdateResult(errBuf.String()) } return } result := evalBuf.String() if _, err := exec.LookPath("alejandra"); err == nil { var formatBuf bytes.Buffer format := exec.Command("alejandra", "-q", "-") format.Stdin = strings.NewReader(result) format.Stdout = &formatBuf if err := format.Run(); err != nil { return } result = formatBuf.String() } m.UpdateResult(result) } fn() for { select { case event, ok := <-watcher.Events: if !ok { return } if event.Has(fsnotify.Write) { fn() } case _, ok := <-watcher.Errors: if !ok { return } } } }