diff --git a/main.go b/main.go index 84123d1..11f0d4c 100644 --- a/main.go +++ b/main.go @@ -45,12 +45,21 @@ func main() { showVersionInfo() return + case "init": + tui.RunInitWizard() + return + case "help", "-help", "--help", "-h": printHelp() return } } + if !tui.ConfigFound { + fmt.Println("No tuido configuration found. Run `tuido init` to set up.") + os.Exit(1) + } + var showVersion = flag.Bool("version", false, "show version and platform information") flag.Usage = printHelp flag.Parse() @@ -117,6 +126,7 @@ Commands: --max N Limit output to N items create Create a new todo item add Alias for create + init Create a local or global config (interactive) version Show version and platform information help Show this help diff --git a/tui/config.go b/tui/config.go index cbb2e14..8ec7e33 100644 --- a/tui/config.go +++ b/tui/config.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "os" + "path/filepath" "strconv" "strings" ) @@ -44,17 +45,6 @@ var runConfig config = config{ frictionThreshold: 5, } -func adoptConfigSettings(location string) { - config := parseConfigIfExists(location) - - if config != nil { - runConfig.extensions = append(runConfig.extensions, config.extensions...) - if config.writeto != "" { - runConfig.writeto = config.writeto - } - } -} - func parseConfigIfExists(configPath string) *config { if config, err := os.Open(configPath); err == nil { @@ -106,6 +96,83 @@ func parseConfig(file *os.File) config { return cfg } +func (cfg config) hasSettings() bool { + return len(cfg.extensions) > 0 || cfg.writeto != "" +} + +// RunInitWizard interactively creates a local or global tuido config file. +func RunInitWizard() { + reader := bufio.NewReader(os.Stdin) + + fmt.Print("Configure locally (./.tuido) or globally (~/.config/tuido.conf)? [l/g]: ") + choice, _ := reader.ReadString('\n') + isLocal := strings.ToLower(strings.TrimSpace(choice)) != "g" + + var configPath, defaultWriteto string + if isLocal { + cwd, _ := os.Getwd() + configPath = filepath.Join(cwd, ".tuido") + defaultWriteto = filepath.Join(cwd, ".tuido") + } else { + cfgDir, _ := os.UserConfigDir() + configPath = filepath.Join(cfgDir, "tuido.conf") + home, _ := os.UserHomeDir() + defaultWriteto = filepath.Join(home, ".tuido") + } + + fmt.Printf("Where should new items be written? [%s]: ", defaultWriteto) + writeto, _ := reader.ReadString('\n') + writeto = strings.TrimSpace(writeto) + if writeto == "" { + writeto = defaultWriteto + } + + defaultExt := strings.Join(runConfig.extensions, ",") + fmt.Printf("File extensions to scan (comma-separated)? [%s]: ", defaultExt) + extInput, _ := reader.ReadString('\n') + extInput = strings.TrimSpace(extInput) + if extInput == "" { + extInput = defaultExt + } + + if err := writeConfigFile(configPath, extInput, writeto); err != nil { + fmt.Printf("Error writing config to %s: %v\n", configPath, err) + os.Exit(1) + } + fmt.Printf("Config written to %s\n", configPath) +} + +// writeConfigFile writes config lines to path, preserving any non-config +// content (todo items) that already exists below the config header. +func writeConfigFile(path, extensions, writeto string) error { + var itemLines []string + if f, err := os.Open(path); err == nil { + defer f.Close() + scanner := bufio.NewScanner(f) + pastConfig := false + for scanner.Scan() { + line := scanner.Text() + if !pastConfig { + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 { + continue + } + pastConfig = true + } + itemLines = append(itemLines, line) + } + } + + var sb strings.Builder + sb.WriteString(fmt.Sprintf("extensions=%s\n", extensions)) + sb.WriteString(fmt.Sprintf("writeto=%s\n", writeto)) + for _, line := range itemLines { + sb.WriteString(line + "\n") + } + + return os.WriteFile(path, []byte(sb.String()), 0644) +} + func GetConfigExtensions() []string { return runConfig.extensions } diff --git a/tui/init.go b/tui/init.go index ea8e66f..6d27776 100644 --- a/tui/init.go +++ b/tui/init.go @@ -8,45 +8,61 @@ import ( "time" ) +// ConfigFound is true if a local or global config was loaded at startup. +// When false, the caller should prompt the user to run `tuido init`. +var ConfigFound bool + func init() { - rand.Seed(time.Now().Unix()) // a fresh set of tag colors on each run. Spice of life. + rand.Seed(time.Now().Unix()) home, err := os.UserHomeDir() if err != nil { fmt.Printf("error getting user home dir: %s", err) } - tuidoDir := filepath.Join(home, ".tuido") - runConfig.writeto = tuidoDir + runConfig.writeto = filepath.Join(home, ".tuido") - loadFromDefaultConfigLocation() + ConfigFound = loadConfig() - // make sure the write target exists _, err = os.Open(runConfig.writeto) if err != nil { err = os.Mkdir(runConfig.writeto, 0777) if err != nil { - fmt.Printf("error creating appDirectory %s': %v\n", - runConfig.writeto, err) + fmt.Printf("error creating appDirectory %s': %v\n", runConfig.writeto, err) + } + } +} + +// loadConfig loads configuration with priority: cwd .tuido > global config. +// Returns true if any config was found. +func loadConfig() bool { + cwd, err := os.Getwd() + if err == nil { + if cfg := parseConfigIfExists(filepath.Join(cwd, ".tuido")); cfg != nil && cfg.hasSettings() { + applyConfig(cfg) + return true } } + return loadFromDefaultConfigLocation() } -func loadFromDefaultConfigLocation() { +func loadFromDefaultConfigLocation() bool { cfgDir, err := os.UserConfigDir() if err != nil { - fmt.Println("error seeking configdir") - return + return false } + cfg := parseConfigIfExists(filepath.Join(cfgDir, "tuido.conf")) + if cfg == nil || !cfg.hasSettings() { + return false + } + applyConfig(cfg) + return true +} - cfgPath := filepath.Join(cfgDir, "tuido.conf") - cfg := parseConfigIfExists(cfgPath) - - if cfg != nil { - if len(cfg.extensions) != 0 { - runConfig.extensions = cfg.extensions - } - if cfg.writeto != "" { - runConfig.writeto = cfg.writeto - } +func applyConfig(cfg *config) { + if len(cfg.extensions) != 0 { + runConfig.extensions = cfg.extensions + } + if cfg.writeto != "" { + runConfig.writeto = cfg.writeto } } diff --git a/tui/tui.go b/tui/tui.go index f4154cd..4ea55de 100644 --- a/tui/tui.go +++ b/tui/tui.go @@ -35,7 +35,6 @@ func Run() { panic(err) } - adoptConfigSettings(filepath.Join(wrkdirStr, ".tuido")) // [ ] read cli flags for added extensions / extension specificity files := make(map[string]struct{})