@@ -1,110 +1,91 @@
package cmd
import (
- "fmt"
"os"
"path/filepath"
"strings"
"charm.land/lipgloss/v2"
- "charm.land/lipgloss/v2/table"
"github.com/charmbracelet/crush/internal/config"
+ "github.com/charmbracelet/x/exp/charmtone"
"github.com/charmbracelet/x/term"
"github.com/spf13/cobra"
)
var dirsCmd = &cobra.Command{
Use: "dirs",
- Short: "Print directories used by Crush",
- Long: `Print the directories where Crush stores its configuration and data files.
-This includes the global configuration directory and data directory.`,
+ Short: "Show config and data directories",
+ Long: `Show where Crush stores its configuration and data,
+including any project-level config files discovered
+from the current directory up to the project root.`,
Example: `
-# Print all global directories
+# Show all directories
crush dirs
-
-# Print data directory and all project specific config directories
-crush dirs -p
-
-# Print only global config directory
-crush dirs config
-
-# Print only project specific config directories
-crush dirs -p config
-
-# Print only the data directory
-crush dirs data
`,
- RunE: func(cmd *cobra.Command, args []string) error {
- dirs, err := configDirs(cmd)
- if err != nil {
- return fmt.Errorf("cannot collect config directories: %w", err)
- }
-
+ Run: func(cmd *cobra.Command, args []string) {
+ entries := collectDirs(cmd)
if term.IsTerminal(os.Stdout.Fd()) {
- // We're in a TTY: make it fancy.
- t := table.New().
- Border(lipgloss.RoundedBorder()).
- StyleFunc(func(row, col int) lipgloss.Style {
- return lipgloss.NewStyle().Padding(0, 2)
- }).
- Row("Config", dirs).
- Row("Data", filepath.Dir(config.GlobalConfigData()))
- lipgloss.Println(t)
- return nil
+ printDirs(cmd, entries)
+ return
}
- // Not a TTY.
- cmd.Println(dirs)
- cmd.Println(filepath.Dir(config.GlobalConfigData()))
-
- return nil
- },
-}
-
-var configDirCmd = &cobra.Command{
- Use: "config",
- Short: "Print the configuration directory used by Crush",
- RunE: func(cmd *cobra.Command, args []string) error {
- dirs, err := configDirs(cmd)
- if err != nil {
- return fmt.Errorf("cannot collect config directories: %w", err)
+ for _, e := range entries {
+ cmd.Println(e)
}
- cmd.Println(dirs)
- return nil
},
}
-var dataDirCmd = &cobra.Command{
- Use: "data",
- Short: "Print the datauration directory used by Crush",
- Run: func(cmd *cobra.Command, args []string) {
- cmd.Println(filepath.Dir(config.GlobalConfigData()))
- },
-}
+func collectDirs(cmd *cobra.Command) []string {
+ var dirs []string
-// configDirs returns formatted string with one or more project config paths
-func configDirs(cmd *cobra.Command) (string, error) {
- configDir := filepath.Dir(config.GlobalConfig())
- if ok, _ := cmd.Flags().GetBool("project"); !ok {
- return configDir, nil
- }
+ dirs = append(dirs, filepath.Dir(config.GlobalConfig()))
+ dirs = append(dirs, filepath.Dir(config.GlobalConfigData()))
cwd, err := ResolveCwd(cmd)
if err != nil {
- return "", fmt.Errorf("cannot resolve current working directory: %w", err)
+ return dirs
}
- var sb strings.Builder
- for i, path := range config.ProjectConfigs(cwd) {
- if i > 0 {
- sb.WriteByte('\n')
+ for _, p := range config.ProjectConfigs(cwd) {
+ d := filepath.Dir(p)
+ // Skip global paths, already shown.
+ if d == filepath.Dir(config.GlobalConfig()) || d == filepath.Dir(config.GlobalConfigData()) {
+ continue
}
- sb.WriteString(filepath.Dir(path))
+ dirs = append(dirs, d)
}
- return sb.String(), nil
+
+ return dirs
}
-func init() {
- dirsCmd.PersistentFlags().BoolP("project", "p", false, "Print project specific configs")
+func printDirs(cmd *cobra.Command, dirs []string) {
+ labelStyle := lipgloss.NewStyle().Bold(true).Foreground(charmtone.Charple)
+
+ labels := make([]string, len(dirs))
+ longest := 0
+ for i := range dirs {
+ l := dirLabel(i)
+ labels[i] = l + ":"
+ if len(labels[i]) > longest {
+ longest = len(labels[i])
+ }
+ }
+
+ for i, d := range dirs {
+ lipgloss.Println(labelStyle.Render(labels[i]) +
+ strings.Repeat(" ", longest-len(labels[i])) +
+ " " + d)
+ }
+
+ lipgloss.Println(lipgloss.NewStyle().Foreground(charmtone.Squid).Render("Configs merge from top to bottom"))
+}
- dirsCmd.AddCommand(configDirCmd, dataDirCmd)
+func dirLabel(i int) string {
+ switch i {
+ case 0:
+ return "Config"
+ case 1:
+ return "Data"
+ default:
+ return "Project"
+ }
}
@@ -1,48 +0,0 @@
-package cmd
-
-import (
- "bytes"
- "os"
- "path/filepath"
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func init() {
- os.Setenv("XDG_CONFIG_HOME", "/tmp/fakeconfig")
- os.Setenv("XDG_DATA_HOME", "/tmp/fakedata")
- os.Unsetenv("CRUSH_GLOBAL_CONFIG")
- os.Unsetenv("CRUSH_GLOBAL_DATA")
-}
-
-func TestDirs(t *testing.T) {
- var b bytes.Buffer
- dirsCmd.SetOut(&b)
- dirsCmd.SetErr(&b)
- dirsCmd.SetIn(bytes.NewReader(nil))
- dirsCmd.Run(dirsCmd, nil)
- expected := filepath.FromSlash("/tmp/fakeconfig/crush") + "\n" +
- filepath.FromSlash("/tmp/fakedata/crush") + "\n"
- require.Equal(t, expected, b.String())
-}
-
-func TestConfigDir(t *testing.T) {
- var b bytes.Buffer
- configDirCmd.SetOut(&b)
- configDirCmd.SetErr(&b)
- configDirCmd.SetIn(bytes.NewReader(nil))
- configDirCmd.Run(configDirCmd, nil)
- expected := filepath.FromSlash("/tmp/fakeconfig/crush") + "\n"
- require.Equal(t, expected, b.String())
-}
-
-func TestDataDir(t *testing.T) {
- var b bytes.Buffer
- dataDirCmd.SetOut(&b)
- dataDirCmd.SetErr(&b)
- dataDirCmd.SetIn(bytes.NewReader(nil))
- dataDirCmd.Run(dataDirCmd, nil)
- expected := filepath.FromSlash("/tmp/fakedata/crush") + "\n"
- require.Equal(t, expected, b.String())
-}