diff --git a/cmd/container.go b/cmd/container.go index 6970afc..b12af73 100644 --- a/cmd/container.go +++ b/cmd/container.go @@ -2,6 +2,7 @@ package cmd import ( "io" + "sync" "github.com/mickael-menu/zk/adapter/fzf" "github.com/mickael-menu/zk/adapter/handlebars" @@ -20,6 +21,10 @@ type Container struct { Logger util.Logger Terminal *term.Terminal templateLoader *handlebars.Loader + + zkOnce sync.Once + zk *zk.Zk + zkErr error } func NewContainer() *Container { @@ -34,6 +39,13 @@ func NewContainer() *Container { } } +func (c *Container) OpenZk() (*zk.Zk, error) { + c.zkOnce.Do(func() { + c.zk, c.zkErr = zk.Open(".") + }) + return c.zk, c.zkErr +} + func (c *Container) TemplateLoader(lang string) *handlebars.Loader { if c.templateLoader == nil { handlebars.Init(lang, c.Logger, c.Terminal) diff --git a/cmd/edit.go b/cmd/edit.go index 68b26d1..21520b9 100644 --- a/cmd/edit.go +++ b/cmd/edit.go @@ -6,7 +6,6 @@ import ( "github.com/mickael-menu/zk/adapter/sqlite" "github.com/mickael-menu/zk/core/note" - "github.com/mickael-menu/zk/core/zk" "github.com/mickael-menu/zk/util/errors" ) @@ -18,7 +17,7 @@ type Edit struct { } func (cmd *Edit) Run(container *Container) error { - zk, err := zk.Open(".") + zk, err := container.OpenZk() if err != nil { return err } @@ -61,6 +60,8 @@ func (cmd *Edit) Run(container *Container) error { } note.Edit(zk, paths...) + } else { + fmt.Println("Found 0 note") } return err diff --git a/cmd/index.go b/cmd/index.go index 6eca011..962d12f 100644 --- a/cmd/index.go +++ b/cmd/index.go @@ -7,7 +7,6 @@ import ( "github.com/mickael-menu/zk/adapter/sqlite" "github.com/mickael-menu/zk/core/note" - "github.com/mickael-menu/zk/core/zk" "github.com/mickael-menu/zk/util/paths" "github.com/schollz/progressbar/v3" ) @@ -20,7 +19,7 @@ type Index struct { } func (cmd *Index) Run(container *Container) error { - zk, err := zk.Open(".") + zk, err := container.OpenZk() if err != nil { return err } diff --git a/cmd/list.go b/cmd/list.go index 6e6c917..5fab2b2 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -7,7 +7,6 @@ import ( "github.com/mickael-menu/zk/adapter/sqlite" "github.com/mickael-menu/zk/core/note" - "github.com/mickael-menu/zk/core/zk" "github.com/mickael-menu/zk/util/errors" "github.com/mickael-menu/zk/util/opt" "github.com/mickael-menu/zk/util/strings" @@ -22,7 +21,7 @@ type List struct { } func (cmd *List) Run(container *Container) error { - zk, err := zk.Open(".") + zk, err := container.OpenZk() if err != nil { return err } diff --git a/cmd/new.go b/cmd/new.go index 6913b46..3cc4943 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -12,7 +12,7 @@ import ( // New adds a new note to the slip box. type New struct { Directory string `arg optional type:"path" default:"." help:"Directory in which to create the note"` - PrintPath bool `help:"Prints the path of the created note to stdin instead of editing it"` + PrintPath bool `help:"Prints the path of the created note to stdin instead of editing it" short:"p"` Title string `short:"t" help:"Title of the new note" placeholder:""` Template string `type:"path" help:"Custom template to use to render the note" placeholder:"<path>"` Extra map[string]string `help:"Extra variables passed to the templates"` @@ -26,7 +26,7 @@ func (cmd *New) ConfigOverrides() zk.ConfigOverrides { } func (cmd *New) Run(container *Container) error { - zk, err := zk.Open(".") + zk, err := container.OpenZk() if err != nil { return err } diff --git a/core/zk/config.go b/core/zk/config.go index 45b7069..4b4d117 100644 --- a/core/zk/config.go +++ b/core/zk/config.go @@ -15,6 +15,7 @@ type Config struct { Editor opt.String Pager opt.String NoPager bool + Aliases map[string]string } // DirConfig holds the user configuration for a given directory. @@ -113,19 +114,26 @@ func ParseConfig(content []byte, templatesDir string) (*Config, error) { } } - config := Config{ - DirConfig: root, - Dirs: make(map[string]DirConfig), - Editor: opt.NewNotEmptyString(hcl.Editor), - Pager: opt.NewNotEmptyString(hcl.Pager), - NoPager: hcl.NoPager, + dirs := make(map[string]DirConfig) + for _, dirHCL := range hcl.Dirs { + dirs[dirHCL.Dir] = root.merge(dirHCL, templatesDir) } - for _, dirHCL := range hcl.Dirs { - config.Dirs[dirHCL.Dir] = root.merge(dirHCL, templatesDir) + aliases := make(map[string]string) + if hcl.Aliases != nil { + for k, v := range hcl.Aliases { + aliases[k] = v + } } - return &config, nil + return &Config{ + DirConfig: root, + Dirs: dirs, + Editor: opt.NewNotEmptyString(hcl.Editor), + Pager: opt.NewNotEmptyString(hcl.Pager), + NoPager: hcl.NoPager, + Aliases: aliases, + }, nil } func (c DirConfig) merge(hcl hclDirConfig, templatesDir string) DirConfig { @@ -189,6 +197,7 @@ type hclConfig struct { Editor string `hcl:"editor,optional"` Pager string `hcl:"pager,optional"` NoPager bool `hcl:"no-pager,optional"` + Aliases map[string]string `hcl:"aliases,optional"` } type hclDirConfig struct { diff --git a/core/zk/config_test.go b/core/zk/config_test.go index dedfa30..9c70a06 100644 --- a/core/zk/config_test.go +++ b/core/zk/config_test.go @@ -28,7 +28,8 @@ func TestParseDefaultConfig(t *testing.T) { Lang: "en", Extra: make(map[string]string), }, - Dirs: make(map[string]DirConfig), + Dirs: make(map[string]DirConfig), + Aliases: make(map[string]string), }) } @@ -45,6 +46,11 @@ func TestParseComplete(t *testing.T) { editor = "vim" pager = "less" no-pager = true + aliases = { + ls = "zk list $@" + ed = "zk edit $@" + } + filename = "{{id}}.note" extension = "txt" template = "default.note" @@ -135,6 +141,10 @@ func TestParseComplete(t *testing.T) { Editor: opt.NewString("vim"), Pager: opt.NewString("less"), NoPager: true, + Aliases: map[string]string{ + "ls": "zk list $@", + "ed": "zk edit $@", + }, }) } @@ -223,6 +233,7 @@ func TestParseMergesDirConfig(t *testing.T) { }, }, }, + Aliases: make(map[string]string), }) } diff --git a/main.go b/main.go index 108f584..c2a6d61 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,13 @@ package main import ( + "fmt" + "os" + "os/exec" + "github.com/alecthomas/kong" "github.com/mickael-menu/zk/cmd" + executil "github.com/mickael-menu/zk/util/exec" ) var Version = "dev" @@ -18,25 +23,73 @@ var cli struct { Version kong.VersionFlag `help:"Print zk version"` } +// NoInput is a flag preventing any user prompt when enabled. +type NoInput bool + +func (f NoInput) BeforeApply(container *cmd.Container) error { + container.Terminal.NoInput = true + return nil +} + func main() { // Create the dependency graph. container := cmd.NewContainer() - ctx := kong.Parse(&cli, - kong.Bind(container), - kong.Name("zk"), - kong.Vars{ - "version": Version, - }, - ) - err := ctx.Run(container) - ctx.FatalIfErrorf(err) + indexZk(container) + + if isAlias, err := runAlias(container, os.Args[1:]); isAlias { + fatalIfError(err) + + } else { + ctx := kong.Parse(&cli, + kong.Bind(container), + kong.Name("zk"), + kong.Vars{ + "version": Version, + }, + ) + + err := ctx.Run(container) + ctx.FatalIfErrorf(err) + } } -// NoInput is a flag preventing any user prompt when enabled. -type NoInput bool +func fatalIfError(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "zk: error: %v\n", err) + os.Exit(1) + } +} -func (f NoInput) BeforeApply(container *cmd.Container) error { - container.Terminal.NoInput = true - return nil +// indexZk will index any slip box in the working directory. +func indexZk(container *cmd.Container) { + (&cmd.Index{Quiet: true}).Run(container) +} + +// runAlias will execute a user alias if the command is one of them. +func runAlias(container *cmd.Container, args []string) (bool, error) { + if zk, err := container.OpenZk(); err == nil && len(args) >= 1 { + for alias, cmdStr := range zk.Config.Aliases { + if alias != args[0] { + continue + } + + cmd := executil.CommandFromString(cmdStr, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + if err, ok := err.(*exec.ExitError); ok { + os.Exit(err.ExitCode()) + return true, nil + } else { + return true, err + } + } + return true, nil + } + } + + return false, nil } diff --git a/util/exec/exec_unix.go b/util/exec/exec_unix.go index 5522e9d..edf12ad 100644 --- a/util/exec/exec_unix.go +++ b/util/exec/exec_unix.go @@ -8,10 +8,11 @@ import ( ) // CommandFromString returns a Cmd running the given command with $SHELL. -func CommandFromString(command string) *exec.Cmd { +func CommandFromString(command string, args ...string) *exec.Cmd { shell := os.Getenv("SHELL") if len(shell) == 0 { shell = "sh" } - return exec.Command(shell, "-c", command) + args = append([]string{"-c", command}, args...) + return exec.Command(shell, args...) }