feat(config): add some more config options (#64)
## Add Make these things configurable - Expand aliases - Suggest hidden files (#59) - Toggle menu and mode ``` [ui] hidden-files = true # or false [core] expand-alias = true # or false [keybindings] toggle-mode = "ctrl+r" toggle-menu = "ctrl+space" ``` - Auto reload config on changes: now just changes config and save the file, IRIS will auto detect and reload in silent - Added all these in config on init and docs ## Fix I tested on holding backspace and the ghost text keep glitching so I fixed it, this is just a small fix
This commit is contained in:
+59
-19
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
@@ -32,18 +33,20 @@ func (d Duration) MarshalText() ([]byte, error) {
|
||||
}
|
||||
|
||||
type CoreConfig struct {
|
||||
Version int `toml:"version"`
|
||||
Shell string `toml:"shell"`
|
||||
Mode string `toml:"mode"`
|
||||
Debug bool `toml:"debug"`
|
||||
Version int `toml:"version"`
|
||||
Shell string `toml:"shell"`
|
||||
Mode string `toml:"mode"`
|
||||
Debug bool `toml:"debug"`
|
||||
ExpandAlias bool `toml:"expand-alias"`
|
||||
}
|
||||
|
||||
type UIConfig struct {
|
||||
Style string `toml:"style"`
|
||||
GhostText bool `toml:"ghost-text"`
|
||||
MaxSuggestions int `toml:"max-suggestions"`
|
||||
MaxHeight int `toml:"max-height"`
|
||||
NerdFonts bool `toml:"nerd-fonts"`
|
||||
Style string `toml:"style"`
|
||||
GhostText bool `toml:"ghost-text"`
|
||||
ShowHiddenFiles bool `toml:"hidden-files"`
|
||||
MaxSuggestions int `toml:"max-suggestions"`
|
||||
MaxHeight int `toml:"max-height"`
|
||||
NerdFonts bool `toml:"nerd-fonts"`
|
||||
}
|
||||
|
||||
type GitConfig struct {
|
||||
@@ -57,6 +60,11 @@ type UpdaterConfig struct {
|
||||
CheckInterval Duration `toml:"check-interval"`
|
||||
}
|
||||
|
||||
type KeybindingsConfig struct {
|
||||
ToggleMode string `toml:"toggle-mode"`
|
||||
ToggleMenu string `toml:"toggle-menu"`
|
||||
}
|
||||
|
||||
type SuggestOnEmptyConfig struct {
|
||||
Enabled bool `toml:"enabled"`
|
||||
DebounceMS int `toml:"debounce_ms"`
|
||||
@@ -101,32 +109,64 @@ func (p *ProviderConfig) GetAPIKey() string {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Core CoreConfig `toml:"core"`
|
||||
UI UIConfig `toml:"ui"`
|
||||
Git GitConfig `toml:"git"`
|
||||
Updater UpdaterConfig `toml:"updater"`
|
||||
AI AIConfig `toml:"ai"`
|
||||
Core CoreConfig `toml:"core"`
|
||||
UI UIConfig `toml:"ui"`
|
||||
Git GitConfig `toml:"git"`
|
||||
Updater UpdaterConfig `toml:"updater"`
|
||||
AI AIConfig `toml:"ai"`
|
||||
Keybindings KeybindingsConfig `toml:"keybindings"`
|
||||
}
|
||||
|
||||
var (
|
||||
activeConfig *Config
|
||||
activeConfig atomic.Pointer[Config]
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func Get() *Config {
|
||||
once.Do(func() {
|
||||
if activeConfig == nil {
|
||||
activeConfig = DefaultConfig()
|
||||
if activeConfig.Load() == nil {
|
||||
activeConfig.Store(DefaultConfig())
|
||||
}
|
||||
})
|
||||
return activeConfig
|
||||
return activeConfig.Load()
|
||||
}
|
||||
|
||||
func Init(cfg *Config) {
|
||||
activeConfig = cfg
|
||||
activeConfig.Store(cfg)
|
||||
once.Do(func() {})
|
||||
}
|
||||
|
||||
func AutoDetectConfigChange(onReload func(cfg *Config)) {
|
||||
path, err := ConfigPath()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
var lastMod time.Time
|
||||
if info, err := os.Stat(path); err == nil {
|
||||
lastMod = info.ModTime()
|
||||
}
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
info, err := os.Stat(path)
|
||||
if err == nil {
|
||||
if !lastMod.IsZero() && info.ModTime().After(lastMod) {
|
||||
lastMod = info.ModTime()
|
||||
if newCfg, err := Load(); err == nil {
|
||||
Init(newCfg)
|
||||
if onReload != nil {
|
||||
onReload(newCfg)
|
||||
}
|
||||
}
|
||||
} else if lastMod.IsZero() {
|
||||
lastMod = info.ModTime()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
cfg := DefaultConfig()
|
||||
|
||||
|
||||
@@ -5,17 +5,19 @@ import "time"
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Core: CoreConfig{
|
||||
Version: 1,
|
||||
Shell: "",
|
||||
Mode: "last",
|
||||
Debug: false,
|
||||
Version: 1,
|
||||
Shell: "",
|
||||
Mode: "last",
|
||||
Debug: false,
|
||||
ExpandAlias: true,
|
||||
},
|
||||
UI: UIConfig{
|
||||
Style: "modern",
|
||||
GhostText: true,
|
||||
MaxSuggestions: 100,
|
||||
MaxHeight: 15,
|
||||
NerdFonts: true,
|
||||
Style: "modern",
|
||||
GhostText: true,
|
||||
ShowHiddenFiles: false,
|
||||
MaxSuggestions: 100,
|
||||
MaxHeight: 15,
|
||||
NerdFonts: true,
|
||||
},
|
||||
Git: GitConfig{
|
||||
FilterActiveBranch: true,
|
||||
@@ -38,6 +40,10 @@ func DefaultConfig() *Config {
|
||||
MinIntervalMS: 5000,
|
||||
},
|
||||
},
|
||||
Keybindings: KeybindingsConfig{
|
||||
ToggleMode: "ctrl+r",
|
||||
ToggleMenu: "shift+tab",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MatchKey matches a sequence of terminal bytes against a configured key string.
|
||||
// Key strings can be e.g. "ctrl+r", "tab", "shift+tab", "right".
|
||||
// Returns matched=true if the byte sequence matches the configured key, and consumed=number of bytes to consume.
|
||||
func MatchKey(input []byte, expected string) (matched bool, consumed int) {
|
||||
if len(input) == 0 {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
expected = strings.ToLower(strings.TrimSpace(expected))
|
||||
|
||||
if strings.HasPrefix(expected, "ctrl+") && len(expected) == 6 {
|
||||
char := expected[5]
|
||||
if char >= 'a' && char <= 'z' {
|
||||
targetByte := char - 'a' + 1
|
||||
if input[0] == targetByte {
|
||||
return true, 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch expected {
|
||||
case "ctrl+space":
|
||||
if input[0] == 0x00 {
|
||||
return true, 1
|
||||
}
|
||||
case "tab":
|
||||
if input[0] == 0x09 {
|
||||
return true, 1
|
||||
}
|
||||
case "shift+tab":
|
||||
// typically \033[Z
|
||||
if len(input) >= 3 && input[0] == 0x1b && input[1] == '[' && input[2] == 'Z' {
|
||||
return true, 3
|
||||
}
|
||||
case "right":
|
||||
// typically \033[C or \033OC
|
||||
if len(input) >= 3 && input[0] == 0x1b && (input[1] == '[' || input[1] == 'O') && input[2] == 'C' {
|
||||
return true, 3
|
||||
}
|
||||
case "enter":
|
||||
if input[0] == 0x0d || input[0] == 0x0a {
|
||||
return true, 1
|
||||
}
|
||||
}
|
||||
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// FormatKeyName takes a config key string like "ctrl+r" and formats it for UI display, e.g. "<Ctrl+R>".
|
||||
func FormatKeyName(key string) string {
|
||||
parts := strings.Split(key, "+")
|
||||
for i, p := range parts {
|
||||
if len(p) > 0 {
|
||||
parts[i] = strings.ToUpper(p[:1]) + strings.ToLower(p[1:])
|
||||
}
|
||||
}
|
||||
return "<" + strings.Join(parts, "+") + ">"
|
||||
}
|
||||
Reference in New Issue
Block a user