diff --git a/cmd/config/config.go b/cmd/config/config.go index bbea04e..7a123dc 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -18,8 +18,10 @@ import ( "github.com/mateconpizza/gm/internal/bookmark/port" "github.com/mateconpizza/gm/internal/cli" "github.com/mateconpizza/gm/internal/editor" - "github.com/mateconpizza/gm/internal/sys" "github.com/mateconpizza/gm/internal/ui" + "github.com/mateconpizza/gm/internal/ui/frame" + "github.com/mateconpizza/gm/internal/ui/printer" + "github.com/mateconpizza/gm/pkg/ansi" ) func NewCmd(app *application.App) *cobra.Command { @@ -31,12 +33,10 @@ func NewCmd(app *application.App) *cobra.Command { Example: app.Example(` $ {cmd} config create $ {cmd} config create --force $ {cmd} config edit + $ {cmd} config view $ {cmd} config --print $ {cmd} config --json`), RunE: func(cmd *cobra.Command, args []string) error { - if app.Flags.JSON { - return cfgToJSON(cmd.Context(), app) - } if app.Flags.Print { return showPathFile(os.Stdout, app.Path.ConfigFile()) } @@ -45,10 +45,9 @@ func NewCmd(app *application.App) *cobra.Command { }, } c.Flags().BoolVar(&app.Flags.Print, "print", false, "print config file location") - c.Flags().BoolVarP(&app.Flags.JSON, "json", "j", false, "JSON output") cmdutil.HideFlag(c, "color", "db", "menu", "head", "tail", "fields", "sort", "tag") - c.AddCommand(newCreateCmd(app), newEditCmd(app)) + c.AddCommand(newCreateCmd(app), newEditCmd(app), newViewCmd(app)) return c } @@ -61,7 +60,7 @@ func newCreateCmd(app *application.App) *cobra.Command { if err := app.Validate(); err != nil { return err } - return createConfig(cmd.Context(), ui.DefaultConsole, app) + return createConfig(ui.DefaultConsole, app) }, } cmdutil.HideFlag(c, "db") @@ -83,6 +82,29 @@ func newEditCmd(app *application.App) *cobra.Command { } } +func newViewCmd(app *application.App) *cobra.Command { + c := &cobra.Command{ + Use: "view", + Short: "view current configuration", + Aliases: []string{"v"}, + RunE: func(cmd *cobra.Command, args []string) error { + if app.Flags.JSON { + return cfgToJSON(cmd.Context(), app) + } + + f := frame.New( + frame.WithBordersSmallBlock2(), + frame.WithWriter(os.Stdout), + ) + return printer.AppConfig(app, f, ansi.NewPalette()) + }, + } + + c.Flags().BoolVarP(&app.Flags.JSON, "json", "j", false, "JSON output") + + return c +} + func cfgToJSON(ctx context.Context, app *application.App) error { app.DBName = strings.TrimSuffix(app.DBName, ".db") j, err := port.ToJSON(app) @@ -93,7 +115,7 @@ func cfgToJSON(ctx context.Context, app *application.App) error { } // createConfig dumps the app configuration to a YAML file. -func createConfig(ctx context.Context, c *ui.Console, app *application.App) error { +func createConfig(c *ui.Console, app *application.App) error { cfgFile := app.Path.ConfigFile() if files.Exists(cfgFile) && !app.Flags.Force { p := c.Palette() @@ -101,10 +123,6 @@ func createConfig(ctx context.Context, c *ui.Console, app *application.App) erro return fmt.Errorf("%w. use %s to overwrite", os.ErrExist, f) } - if !app.Flags.Force && !c.Confirm(ctx, fmt.Sprintf("create configfile %q", cfgFile), "y") { - return sys.ErrActionAborted - } - if err := app.WriteConfig(app.Flags.Force); err != nil { return err } diff --git a/cmd/config/config_test.go b/cmd/config/config_test.go index 6d3d9eb..1bba12e 100644 --- a/cmd/config/config_test.go +++ b/cmd/config/config_test.go @@ -12,7 +12,6 @@ import ( files "github.com/mateconpizza/gofiles" "github.com/mateconpizza/gm/internal/application" - "github.com/mateconpizza/gm/internal/sys" "github.com/mateconpizza/gm/internal/sys/terminal" "github.com/mateconpizza/gm/internal/testutil" "github.com/mateconpizza/gm/internal/ui" @@ -38,7 +37,7 @@ func TestConfig_Create(t *testing.T) { app.Path.Data = dir fn := application.ConfigFilename - err := createConfig(t.Context(), c, app) + err := createConfig(c, app) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -49,32 +48,6 @@ func TestConfig_Create(t *testing.T) { } }) - t.Run("returns ErrActionAborted when user declines", func(t *testing.T) { - t.Parallel() - var buf bytes.Buffer - - c := ui.NewConsole( - ui.WithWriter(&buf), - ui.WithTerminal(terminal.New( - terminal.WithReader(strings.NewReader("n\n")), // decline - terminal.WithWriter(io.Discard), - )), - ) - - app := testutil.SetupApp(t) - app.Flags.Yes = true - dir := t.TempDir() - app.Path.Data = dir - - err := createConfig(t.Context(), c, app) - if err == nil { - t.Fatal("expected error but got none") - } - if !errors.Is(err, sys.ErrActionAborted) { - t.Fatalf("got error %v, want %v", err, sys.ErrActionAborted) - } - }) - t.Run("returns ErrFileExists when config file already exists", func(t *testing.T) { t.Parallel() @@ -89,7 +62,7 @@ func TestConfig_Create(t *testing.T) { app.Path.Data = dir c := ui.NewConsole() - err = createConfig(t.Context(), c, app) + err = createConfig(c, app) if !errors.Is(err, os.ErrExist) { t.Fatalf("got error %v, want %v", err, os.ErrExist) } diff --git a/internal/ui/printer/printer.go b/internal/ui/printer/printer.go index 688fed6..2b75639 100644 --- a/internal/ui/printer/printer.go +++ b/internal/ui/printer/printer.go @@ -13,17 +13,21 @@ import ( "strings" "text/tabwriter" + menu "github.com/mateconpizza/go-fzf" files "github.com/mateconpizza/gofiles" + "github.com/mateconpizza/gm/internal/application" "github.com/mateconpizza/gm/internal/bookmark/port" "github.com/mateconpizza/gm/internal/deps" "github.com/mateconpizza/gm/internal/gitops" "github.com/mateconpizza/gm/internal/locker" + "github.com/mateconpizza/gm/internal/picker/menucfg" "github.com/mateconpizza/gm/internal/summary" "github.com/mateconpizza/gm/internal/ui" "github.com/mateconpizza/gm/internal/ui/formatter" "github.com/mateconpizza/gm/internal/ui/frame" "github.com/mateconpizza/gm/internal/ui/txt" + "github.com/mateconpizza/gm/pkg/ansi" "github.com/mateconpizza/gm/pkg/bookmark" "github.com/mateconpizza/gm/pkg/db" ) @@ -360,3 +364,76 @@ func Display(ctx context.Context, c *ui.Console, f string, bs []*bookmark.Bookma return Print(ctx, c, bs, fm.Render) } + +func AppConfig(app *application.App, f *frame.Frame, p *ansi.Palette) error { + header := func() string { + return p.BrightBlue.Wrap(txt.GlyphSmallSquare.Prefix(" "), p.Bold) + } + + const padding = 20 + pad := func(label string, value any) string { + return txt.PaddedLineWithPad(label+":", value, padding) + } + + f.CustomFunc(header, app.PrettyVersion()). + Rowln(). + Rowln(pad("current db", p.BrightYellow.Wrap(app.DBBaseName(), p.Italic))). + Rowln(pad("format", app.Format)) + + // config file + if files.Exists(app.Path.ConfigFile()) { + f.Rowln(pad("config:", files.CollapseHomeDir(app.Path.ConfigFile()))) + } + + // menu. + m := app.Menu + f.MidCln(p.BrightRed.With(p.Bold), p.BrightRed.Wrap("menu", p.Bold)). + Rowln(pad("use defaults", boolFmt(m.Defaults))). + Rowln(pad("format", m.Format)). + Rowln(pad("prompt", m.Prompt)). + Rowln(pad("preview enabled", boolFmt(m.Preview))). + Rowln(pad("header enabled", boolFmt(m.Header.Enabled))). + Rowln(pad("header separator", m.Header.Sep)) + + // keymaps. + ph := app.Formatter().Menu.Placeholder() + keymaps := app.Menu.LoadKeymaps( + menucfg.NewBindBuilder(). + WithCommand(app.Command()). + WithDBName(app.DBBaseName()). + WithPlaceholder(ph.Multi()), + ) + f.MidCln(p.BrightMagenta.With(p.Bold), p.BrightMagenta.Wrap("keymaps", p.Bold)) + for i := range keymaps { + k := keymaps[i] + f.Rowln(pad(k.Desc, formatKeymap(k))) + } + + // git. + if g := app.Git; g.Enabled { + f.MidCln(p.BrightYellow.With(p.Bold), p.BrightYellow.Wrap("git", p.Bold)). + Rowln(pad("enabled", boolFmt(g.Enabled))). + Rowln(pad("logging", boolFmt(g.Log))). + Rowln(pad("remote", p.Italic.Sprint(g.Remote))) + } + + f.Flush() + + return nil +} + +func formatKeymap(k *menu.Keymap) string { + keybind, action, _ := strings.Cut(k.String(), ":") + return txt.PaddedLineWithPad( + ansi.Bold.Sprint(keybind), + ansi.Italic.Sprint(":"+action), + 8, + ) +} + +func boolFmt(b bool) string { + if !b { + return ansi.BrightRed.Sprint("false") + } + return ansi.BrightGreen.Sprint("true") +}