diff --git a/src/config.go b/src/config.go index 0edb46d..0ac8fef 100644 --- a/src/config.go +++ b/src/config.go @@ -3,8 +3,6 @@ package main import ( "log" "os" - "path/filepath" - "runtime" "gopkg.in/yaml.v3" ) @@ -21,7 +19,7 @@ type TyperConfig struct { var Config TyperConfig -func readConfig() { +func readMainConfig() { Config = TyperConfig{ SelectedStyle: "default", FallbackStyle: "default-fallback", @@ -32,49 +30,24 @@ func readConfig() { TabIndentation: 4, } - homeDir, err := os.UserHomeDir() + // Get main config path + mainConfigPath := GetConfigPath("config.yml") + + // Ensure config exists at path + if mainConfigPath == "" { + log.Fatalf("config.yml not found in any config directory") + } + + // Read config file + data, err := os.ReadFile(mainConfigPath) if err != nil { - log.Fatalf("Could not get home directory: %s", err) + log.Fatalf("Could not read config.yml: %s", err) } - execPath, err := os.Executable() + // Unmarshal contents into struct + err = yaml.Unmarshal(data, &Config) if err != nil { - log.Fatalf("Could not get path to executable: %s", err) - } - - configPaths := make([]string, 0) - switch runtime.GOOS { - case "windows": - configPaths = append(configPaths, filepath.Join(homeDir, "AppData/Roaming/Typer/config.yml")) - configPaths = append(configPaths, filepath.Join(filepath.Dir(execPath), "etc/typer/config.yml")) - case "darwin": - configPaths = append(configPaths, filepath.Join(homeDir, "Library/Typer/config.yml")) - configPaths = append(configPaths, "/Library/Typer/config.yml") - configPaths = append(configPaths, filepath.Join(sysconfdir, "typer/config.yml")) - default: - configPaths = append(configPaths, filepath.Join(homeDir, ".config/typer/config.yml")) - configPaths = append(configPaths, filepath.Join(sysconfdir, "typer/config.yml")) - } - - for _, configPath := range configPaths { - // Ensure config exists at path - if _, err := os.Stat(configPath); err != nil { - continue - } - - // Read config file - data, err := os.ReadFile(configPath) - if err != nil { - log.Fatalf("Could not read config.yml: %s", err) - } - - // Unmarshal contents into struct - err = yaml.Unmarshal(data, &Config) - if err != nil { - log.Fatalf("Could not unmarshal config.yml: %s", err) - } - - break + log.Fatalf("Could not unmarshal config.yml: %s", err) } // Validate config options diff --git a/src/go.mod b/src/go.mod index 4799408..88c84e4 100644 --- a/src/go.mod +++ b/src/go.mod @@ -9,6 +9,7 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect golang.org/x/text v0.26.0 // indirect diff --git a/src/go.sum b/src/go.sum index 575e8fa..240d0ec 100644 --- a/src/go.sum +++ b/src/go.sum @@ -11,6 +11,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff --git a/src/keybindings.go b/src/keybindings.go index 37b15f5..cfe6f1d 100644 --- a/src/keybindings.go +++ b/src/keybindings.go @@ -3,8 +3,6 @@ package main import ( "log" "os" - "path/filepath" - "runtime" "strings" "github.com/gdamore/tcell/v2" @@ -23,54 +21,29 @@ type Keybinding struct { var Keybindings TyperKeybindings -func readKeybindings() { +func readKeybindingsConfig() { Keybindings = TyperKeybindings{ Keybindings: make([]Keybinding, 0), } - homeDir, err := os.UserHomeDir() + // Get keybindings config path + keybindingsConfigPath := GetConfigPath("keybindings.yml") + + // Ensure config exists at path + if keybindingsConfigPath == "" { + log.Fatalf("keybindings.yml not found in any config directory") + } + + // Read config file + data, err := os.ReadFile(keybindingsConfigPath) if err != nil { - log.Fatalf("Could not get home directory: %s", err) + log.Fatalf("Could not read keybindings.yml: %s", err) } - execPath, err := os.Executable() + // Unmarshal contents into struct + err = yaml.Unmarshal(data, &Keybindings) if err != nil { - log.Fatalf("Could not get path to executable: %s", err) - } - - configPaths := make([]string, 0) - switch runtime.GOOS { - case "windows": - configPaths = append(configPaths, filepath.Join(homeDir, "AppData/Roaming/Typer/keybindings.yml")) - configPaths = append(configPaths, filepath.Join(filepath.Dir(execPath), "etc/typer/keybindings.yml")) - case "darwin": - configPaths = append(configPaths, filepath.Join(homeDir, "Library/Typer/keybindings.yml")) - configPaths = append(configPaths, "/Library/Typer/keybindings.yml") - configPaths = append(configPaths, filepath.Join(sysconfdir, "typer/keybindings.yml")) - default: - configPaths = append(configPaths, filepath.Join(homeDir, ".config/typer/keybindings.yml")) - configPaths = append(configPaths, filepath.Join(sysconfdir, "typer/keybindings.yml")) - } - - for _, configPath := range configPaths { - // Ensure config exists at path - if _, err := os.Stat(configPath); err != nil { - continue - } - - // Read config file - data, err := os.ReadFile(configPath) - if err != nil { - log.Fatalf("Could not read keybindings.yml: %s", err) - } - - // Unmarshal contents into struct - err = yaml.Unmarshal(data, &Keybindings) - if err != nil { - log.Fatalf("Could not unmarshal keybindings.yml: %s", err) - } - - break + log.Fatalf("Could not unmarshal keybindings.yml: %s", err) } } diff --git a/src/main.go b/src/main.go index 73707c8..8593f65 100644 --- a/src/main.go +++ b/src/main.go @@ -2,19 +2,25 @@ package main import ( "log" - "os" + + flag "github.com/spf13/pflag" ) var sysconfdir = "/etc/" +var configDirFlag = flag.StringP("config", "c", "", "Path to config directory") + func main() { - // Read config - readConfig() + // Read flags + readFlags() - // Read key bindings - readKeybindings() + // Read main config + readMainConfig() - // Read styles + // Read keybindings config + readKeybindingsConfig() + + // Read styles directory readStyles() // Initialize commands @@ -25,8 +31,8 @@ func main() { log.Fatalf("Failed to create window: %v", err) } - if len(os.Args) > 1 { - for i, file := range os.Args[1:] { + if flag.NArg() > 0 { + for i, file := range flag.Args() { b, err := CreateFileBuffer(file, true) if err != nil { window.PrintMessage("Could not open file: " + file) @@ -48,3 +54,7 @@ func main() { window.screen.Fini() window.screen = nil } + +func readFlags() { + flag.Parse() +} diff --git a/src/style.go b/src/style.go index 85c82bc..62274d0 100644 --- a/src/style.go +++ b/src/style.go @@ -6,7 +6,6 @@ import ( "os" "path/filepath" "reflect" - "runtime" "slices" "strconv" "strings" @@ -73,54 +72,31 @@ var AvailableStyles = make(map[string]TyperStyle) var CurrentStyle = FallbackStyle func readStyles() { - homeDir, err := os.UserHomeDir() + // Get styles directory path + stylesDirPath := GetConfigPath("styles") + + // Ensure directory exists at path + if stat, err := os.Stat(stylesDirPath); stylesDirPath == "" || err != nil || !stat.IsDir() { + return + } + + // Get directory entries + entries, err := os.ReadDir(stylesDirPath) if err != nil { - log.Fatalf("Could not get home directory: %s", err) + log.Fatalf("Could not read user style directory: %s", err) } - execPath, err := os.Executable() - if err != nil { - log.Fatalf("Could not get path to executable: %s", err) - } + // Read entries in directory + for _, entry := range entries { + entryPath := filepath.Join(stylesDirPath, entry.Name()) - stylesPaths := make([]string, 0) - switch runtime.GOOS { - case "windows": - stylesPaths = append(stylesPaths, filepath.Join(homeDir, "AppData/Roaming/Typer/styles")) - stylesPaths = append(stylesPaths, filepath.Join(filepath.Dir(execPath), "config/styles")) - case "darwin": - stylesPaths = append(stylesPaths, filepath.Join(homeDir, "Library/Typer/styles")) - stylesPaths = append(stylesPaths, "/Library/Typer/styles") - stylesPaths = append(stylesPaths, filepath.Join(sysconfdir, "typer/styles")) - default: - stylesPaths = append(stylesPaths, filepath.Join(homeDir, ".config/typer/styles")) - stylesPaths = append(stylesPaths, filepath.Join(sysconfdir, "typer/styles")) - } - - for _, stylesPath := range stylesPaths { - // Ensure directory exists at path - if stat, err := os.Stat(stylesPath); err != nil || !stat.IsDir() { - continue - } - - // Get directory entries - entries, err := os.ReadDir(stylesPath) + style, err := readStyleYamlFile(entryPath) if err != nil { - log.Fatalf("Could not read user style directory: %s", err) + log.Fatalf("Could not read style file (%s): %s", entryPath, err) } - // Read entries in directory - for _, entry := range entries { - entryPath := filepath.Join(stylesPath, entry.Name()) - - style, err := readStyleYamlFile(entryPath) - if err != nil { - log.Fatalf("Could not read style file (%s): %s", entryPath, err) - } - - if _, ok := AvailableStyles[style.Name]; !ok { - AvailableStyles[style.Name] = style - } + if _, ok := AvailableStyles[style.Name]; !ok { + AvailableStyles[style.Name] = style } } } diff --git a/src/utils.go b/src/utils.go index d84a7ea..963ea95 100644 --- a/src/utils.go +++ b/src/utils.go @@ -1,6 +1,54 @@ package main -import "github.com/gdamore/tcell/v2" +import ( + "log" + "os" + "path" + "path/filepath" + "runtime" + + "github.com/gdamore/tcell/v2" +) + +func GetConfigPath(relativeConfigPath string) string { + homeDir, err := os.UserHomeDir() + if err != nil { + log.Fatalf("Could not get home directory: %s", err) + } + + execPath, err := os.Executable() + if err != nil { + log.Fatalf("Could not get path to executable: %s", err) + } + + paths := make([]string, 0) + if *configDirFlag != "" { + paths = append(paths, path.Join(*configDirFlag, relativeConfigPath)) + } + switch runtime.GOOS { + case "windows": + paths = append(paths, filepath.Join(homeDir, "AppData/Roaming/Typer", relativeConfigPath)) + paths = append(paths, "C:/ProgramData/Typer", relativeConfigPath) + case "darwin": + paths = append(paths, filepath.Join(homeDir, "Library/Typer", relativeConfigPath)) + paths = append(paths, filepath.Join(homeDir, "Library/typer", relativeConfigPath)) + paths = append(paths, filepath.Join(sysconfdir, "Typer", relativeConfigPath)) + paths = append(paths, filepath.Join(sysconfdir, "typer", relativeConfigPath)) + default: + paths = append(paths, filepath.Join(homeDir, ".config/typer", relativeConfigPath)) + paths = append(paths, filepath.Join(sysconfdir, "typer", relativeConfigPath)) + } + paths = append(paths, filepath.Join(filepath.Dir(execPath), "config", relativeConfigPath)) + + for _, p := range paths { + // Return true if path exists + if _, err := os.Stat(p); err == nil { + return p + } + } + + return "" +} func drawText(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, text string) { row := y1