From 9afcb80c9590e710e560e4e9c9744fe04e5dfaab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Tue, 18 Jun 2024 22:50:40 +0200 Subject: [PATCH 01/21] action/command: Let `reload` really reload the `settings.json` --- internal/action/command.go | 18 ++++++++++++++---- internal/config/settings.go | 10 +++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/internal/action/command.go b/internal/action/command.go index f88660da..81d2467b 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -357,10 +357,16 @@ func reloadRuntime(reloadPlugins bool) { err := config.ReadSettings() if err != nil { screen.TermMessage(err) - } - err = config.InitGlobalSettings() - if err != nil { - screen.TermMessage(err) + } else { + parsedSettings := config.ParsedSettings() + defaultSettings := config.DefaultAllSettings() + for k := range defaultSettings { + if _, ok := parsedSettings[k]; ok { + SetGlobalOptionNative(k, parsedSettings[k]) + } else { + SetGlobalOptionNative(k, defaultSettings[k]) + } + } } if reloadPlugins { @@ -393,6 +399,10 @@ func reloadRuntime(reloadPlugins bool) { screen.TermMessage(err) } for _, b := range buffer.OpenBuffers { + config.InitLocalSettings(b.Settings, b.Path) + for k, v := range b.Settings { + b.SetOptionNative(k, v) + } b.UpdateRules() } } diff --git a/internal/config/settings.go b/internal/config/settings.go index d25f0d07..d4acf26c 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -154,10 +154,10 @@ var ( func init() { ModifiedSettings = make(map[string]bool) VolatileSettings = make(map[string]bool) - parsedSettings = make(map[string]interface{}) } func ReadSettings() error { + parsedSettings = make(map[string]interface{}) filename := filepath.Join(ConfigDir, "settings.json") if _, e := os.Stat(filename); e == nil { input, err := ioutil.ReadFile(filename) @@ -189,6 +189,14 @@ func ReadSettings() error { return nil } +func ParsedSettings() map[string]interface{} { + s := make(map[string]interface{}) + for k, v := range parsedSettings { + s[k] = v + } + return s +} + func verifySetting(option string, value reflect.Type, def reflect.Type) bool { var interfaceArr []interface{} switch option { From 7f6e5bc8602497e17be629a993fbc3c2beb0d5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Mon, 22 Jul 2024 20:55:05 +0200 Subject: [PATCH 02/21] action/command: On `reload` check and inform about errors --- internal/action/command.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/action/command.go b/internal/action/command.go index 81d2467b..7167336f 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -362,9 +362,12 @@ func reloadRuntime(reloadPlugins bool) { defaultSettings := config.DefaultAllSettings() for k := range defaultSettings { if _, ok := parsedSettings[k]; ok { - SetGlobalOptionNative(k, parsedSettings[k]) + err = SetGlobalOptionNative(k, parsedSettings[k]) } else { - SetGlobalOptionNative(k, defaultSettings[k]) + err = SetGlobalOptionNative(k, defaultSettings[k]) + } + if err != nil { + screen.TermMessage(err) } } } @@ -526,8 +529,7 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error { // check for local option first... for _, s := range config.LocalSettings { if s == option { - MainTab().CurPane().Buf.SetOptionNative(option, nativeValue) - return nil + return MainTab().CurPane().Buf.SetOptionNative(option, nativeValue) } } @@ -585,7 +587,9 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error { } for _, b := range buffer.OpenBuffers { - b.SetOptionNative(option, nativeValue) + if err := b.SetOptionNative(option, nativeValue); err != nil { + return err + } } return config.WriteSettings(filepath.Join(config.ConfigDir, "settings.json")) From c51f848df27518da28fedc6925ab2105b407b0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Sun, 21 Jul 2024 20:42:25 +0200 Subject: [PATCH 03/21] action/command: On `reload` prevent overwriting `settings.json` --- internal/action/command.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/internal/action/command.go b/internal/action/command.go index 7167336f..3ee90122 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -362,9 +362,9 @@ func reloadRuntime(reloadPlugins bool) { defaultSettings := config.DefaultAllSettings() for k := range defaultSettings { if _, ok := parsedSettings[k]; ok { - err = SetGlobalOptionNative(k, parsedSettings[k]) + err = doSetGlobalOptionNative(k, parsedSettings[k]) } else { - err = SetGlobalOptionNative(k, defaultSettings[k]) + err = doSetGlobalOptionNative(k, defaultSettings[k]) } if err != nil { screen.TermMessage(err) @@ -525,15 +525,7 @@ func (h *BufPane) NewTabCmd(args []string) { } } -func SetGlobalOptionNative(option string, nativeValue interface{}) error { - // check for local option first... - for _, s := range config.LocalSettings { - if s == option { - return MainTab().CurPane().Buf.SetOptionNative(option, nativeValue) - } - } - - // ...if it's not local continue with the globals +func doSetGlobalOptionNative(option string, nativeValue interface{}) error { config.GlobalSettings[option] = nativeValue config.ModifiedSettings[option] = true delete(config.VolatileSettings, option) @@ -586,6 +578,23 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error { } } + return nil +} + +func SetGlobalOptionNative(option string, nativeValue interface{}) error { + // check for local option first... + for _, s := range config.LocalSettings { + if s == option { + return MainTab().CurPane().Buf.SetOptionNative(option, nativeValue) + } + } + + // ...if it's not local continue with the globals... + if err := doSetGlobalOptionNative(option, nativeValue); err != nil { + return err + } + + // ...at last check the buffer locals for _, b := range buffer.OpenBuffers { if err := b.SetOptionNative(option, nativeValue); err != nil { return err From c67a30e611bdf58711111df3fdf53ed47abd6589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Sun, 11 Aug 2024 17:18:28 +0200 Subject: [PATCH 04/21] action/command: On `reload` prevent overwriting volatile settings --- internal/action/command.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/action/command.go b/internal/action/command.go index 3ee90122..8c9230ee 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -361,6 +361,11 @@ func reloadRuntime(reloadPlugins bool) { parsedSettings := config.ParsedSettings() defaultSettings := config.DefaultAllSettings() for k := range defaultSettings { + if _, ok := config.VolatileSettings[k]; ok { + // reload should not override volatile settings + continue + } + if _, ok := parsedSettings[k]; ok { err = doSetGlobalOptionNative(k, parsedSettings[k]) } else { From 62c1c667b8437c5f4dfd221f890bd39a415c65a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Tue, 16 Jul 2024 18:20:31 +0200 Subject: [PATCH 05/21] buffer/settings: On `filetype` change remove the following steps - reload runtime files - reset globals - parse the settings again since this isn't the task of a filetype change. --- internal/buffer/settings.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/internal/buffer/settings.go b/internal/buffer/settings.go index 9d671dcf..355cf864 100644 --- a/internal/buffer/settings.go +++ b/internal/buffer/settings.go @@ -26,15 +26,6 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { } else if option == "statusline" { screen.Redraw() } else if option == "filetype" { - config.InitRuntimeFiles(true) - err := config.ReadSettings() - if err != nil { - screen.TermMessage(err) - } - err = config.InitGlobalSettings() - if err != nil { - screen.TermMessage(err) - } config.InitLocalSettings(b.Settings, b.Path) b.UpdateRules() } else if option == "fileformat" { From 521b63a28cef7c2953dcba6c826ccde46d19c9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:34:38 +0200 Subject: [PATCH 06/21] buffer/settings: On `filetype` change reload parsed local settings only Additionally keep volatile settings as they have been set by the user. --- internal/buffer/settings.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/internal/buffer/settings.go b/internal/buffer/settings.go index 355cf864..38385c57 100644 --- a/internal/buffer/settings.go +++ b/internal/buffer/settings.go @@ -26,7 +26,23 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { } else if option == "statusline" { screen.Redraw() } else if option == "filetype" { - config.InitLocalSettings(b.Settings, b.Path) + settings := config.ParsedSettings() + settings["filetype"] = nativeValue + config.InitLocalSettings(settings, b.Path) + for k, v := range config.DefaultCommonSettings() { + if k == "filetype" { + continue + } + if _, ok := config.VolatileSettings[k]; ok { + // filetype should not override volatile settings + continue + } + if _, ok := settings[k]; ok { + b.SetOptionNative(k, settings[k]) + } else { + b.SetOptionNative(k, v) + } + } b.UpdateRules() } else if option == "fileformat" { switch b.Settings["fileformat"].(string) { From c741e36d2d32720ba7d9dfb50945aeb52cb9912a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Tue, 23 Jul 2024 21:04:21 +0200 Subject: [PATCH 07/21] action/command: Prevent setting of unchanged option --- internal/action/command.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/action/command.go b/internal/action/command.go index 8c9230ee..b04892d5 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -7,6 +7,7 @@ import ( "os" "os/exec" "path/filepath" + "reflect" "regexp" "strconv" "strings" @@ -531,6 +532,10 @@ func (h *BufPane) NewTabCmd(args []string) { } func doSetGlobalOptionNative(option string, nativeValue interface{}) error { + if reflect.DeepEqual(config.GlobalSettings[option], nativeValue) { + return nil + } + config.GlobalSettings[option] = nativeValue config.ModifiedSettings[option] = true delete(config.VolatileSettings, option) From 395d848980d1a9812f868a0c0a250f2f5c138b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Tue, 23 Jul 2024 21:06:21 +0200 Subject: [PATCH 08/21] buffer/settings: Prevent setting of unchanged option --- internal/buffer/settings.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/buffer/settings.go b/internal/buffer/settings.go index 38385c57..0843b461 100644 --- a/internal/buffer/settings.go +++ b/internal/buffer/settings.go @@ -2,12 +2,17 @@ package buffer import ( "crypto/md5" + "reflect" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" ) func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { + if reflect.DeepEqual(b.Settings[option], nativeValue) { + return nil + } + b.Settings[option] = nativeValue if option == "fastdirty" { From 4663927098334ae513a2b1d5bfa121905d034167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Tue, 23 Jul 2024 22:33:16 +0200 Subject: [PATCH 09/21] config: Refactor parsed option handling Validate the parsed options directly after read and inform about errors. --- cmd/micro/micro.go | 4 ++ internal/action/command.go | 4 ++ internal/buffer/settings.go | 4 ++ internal/config/settings.go | 117 ++++++++++++++++++++++++------------ 4 files changed, 90 insertions(+), 39 deletions(-) diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 7a3b2171..0cd7c1ba 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -273,6 +273,10 @@ func main() { screen.TermMessage(err) continue } + if err = config.OptionIsValid(k, nativeValue); err != nil { + screen.TermMessage(err) + continue + } config.GlobalSettings[k] = nativeValue config.VolatileSettings[k] = true } diff --git a/internal/action/command.go b/internal/action/command.go index b04892d5..502365aa 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -592,6 +592,10 @@ func doSetGlobalOptionNative(option string, nativeValue interface{}) error { } func SetGlobalOptionNative(option string, nativeValue interface{}) error { + if err := config.OptionIsValid(option, nativeValue); err != nil { + return err + } + // check for local option first... for _, s := range config.LocalSettings { if s == option { diff --git a/internal/buffer/settings.go b/internal/buffer/settings.go index 0843b461..8d0e8aa1 100644 --- a/internal/buffer/settings.go +++ b/internal/buffer/settings.go @@ -9,6 +9,10 @@ import ( ) func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { + if err := config.OptionIsValid(option, nativeValue); err != nil { + return err + } + if reflect.DeepEqual(b.Settings[option], nativeValue) { return nil } diff --git a/internal/config/settings.go b/internal/config/settings.go index d4acf26c..52eaba00 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -156,6 +156,63 @@ func init() { VolatileSettings = make(map[string]bool) } +func validateParsedSettings() error { + var err error + defaults := DefaultAllSettings() + for k, v := range parsedSettings { + if strings.HasPrefix(reflect.TypeOf(v).String(), "map") { + if strings.HasPrefix(k, "ft:") { + for k1, v1 := range v.(map[string]interface{}) { + if _, ok := defaults[k1]; ok { + if e := verifySetting(k1, v1, defaults[k1]); e != nil { + err = e + parsedSettings[k].(map[string]interface{})[k1] = defaults[k1] + continue + } + } + } + } else { + if _, e := glob.Compile(k); e != nil { + err = errors.New("Error with glob setting " + k + ": " + e.Error()) + delete(parsedSettings, k) + continue + } + for k1, v1 := range v.(map[string]interface{}) { + if _, ok := defaults[k1]; ok { + if e := verifySetting(k1, v1, defaults[k1]); e != nil { + err = e + parsedSettings[k].(map[string]interface{})[k1] = defaults[k1] + continue + } + } + } + } + continue + } + + if k == "autosave" { + // if autosave is a boolean convert it to float + s, ok := v.(bool) + if ok { + if s { + parsedSettings["autosave"] = 8.0 + } else { + parsedSettings["autosave"] = 0.0 + } + } + continue + } + if _, ok := defaults[k]; ok { + if e := verifySetting(k, v, defaults[k]); e != nil { + err = e + parsedSettings[k] = defaults[k] + continue + } + } + } + return err +} + func ReadSettings() error { parsedSettings = make(map[string]interface{}) filename := filepath.Join(ConfigDir, "settings.json") @@ -172,17 +229,9 @@ func ReadSettings() error { settingsParseError = true return errors.New("Error reading settings.json: " + err.Error()) } - - // check if autosave is a boolean and convert it to float if so - if v, ok := parsedSettings["autosave"]; ok { - s, ok := v.(bool) - if ok { - if s { - parsedSettings["autosave"] = 8.0 - } else { - parsedSettings["autosave"] = 0.0 - } - } + err = validateParsedSettings() + if err != nil { + return err } } } @@ -197,14 +246,27 @@ func ParsedSettings() map[string]interface{} { return s } -func verifySetting(option string, value reflect.Type, def reflect.Type) bool { +func verifySetting(option string, value interface{}, def interface{}) error { var interfaceArr []interface{} + valType := reflect.TypeOf(value) + defType := reflect.TypeOf(def) + assignable := false + switch option { case "pluginrepos", "pluginchannels": - return value.AssignableTo(reflect.TypeOf(interfaceArr)) + assignable = valType.AssignableTo(reflect.TypeOf(interfaceArr)) default: - return def.AssignableTo(value) + assignable = defType.AssignableTo(valType) } + if !assignable { + return fmt.Errorf("Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", option, valType, def, defType) + } + + if err := OptionIsValid(option, value); err != nil { + return err + } + + return nil } // InitGlobalSettings initializes the options map and sets all options to their default values @@ -215,11 +277,6 @@ func InitGlobalSettings() error { for k, v := range parsedSettings { if !strings.HasPrefix(reflect.TypeOf(v).String(), "map") { - if _, ok := GlobalSettings[k]; ok && !verifySetting(k, reflect.TypeOf(v), reflect.TypeOf(GlobalSettings[k])) { - err = fmt.Errorf("Global Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", k, reflect.TypeOf(v), GlobalSettings[k], reflect.TypeOf(GlobalSettings[k])) - continue - } - GlobalSettings[k] = v } } @@ -229,40 +286,25 @@ func InitGlobalSettings() error { // InitLocalSettings scans the json in settings.json and sets the options locally based // on whether the filetype or path matches ft or glob local settings // Must be called after ReadSettings -func InitLocalSettings(settings map[string]interface{}, path string) error { - var parseError error +func InitLocalSettings(settings map[string]interface{}, path string) { for k, v := range parsedSettings { if strings.HasPrefix(reflect.TypeOf(v).String(), "map") { if strings.HasPrefix(k, "ft:") { if settings["filetype"].(string) == k[3:] { for k1, v1 := range v.(map[string]interface{}) { - if _, ok := settings[k1]; ok && !verifySetting(k1, reflect.TypeOf(v1), reflect.TypeOf(settings[k1])) { - parseError = fmt.Errorf("Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", k, reflect.TypeOf(v1), settings[k1], reflect.TypeOf(settings[k1])) - continue - } settings[k1] = v1 } } } else { - g, err := glob.Compile(k) - if err != nil { - parseError = errors.New("Error with glob setting " + k + ": " + err.Error()) - continue - } - + g, _ := glob.Compile(k) if g.MatchString(path) { for k1, v1 := range v.(map[string]interface{}) { - if _, ok := settings[k1]; ok && !verifySetting(k1, reflect.TypeOf(v1), reflect.TypeOf(settings[k1])) { - parseError = fmt.Errorf("Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", k, reflect.TypeOf(v1), settings[k1], reflect.TypeOf(settings[k1])) - continue - } settings[k1] = v1 } } } } } - return parseError } // WriteSettings writes the settings to the specified filename as JSON @@ -436,9 +478,6 @@ func GetNativeValue(option string, realValue interface{}, value string) (interfa return nil, ErrInvalidValue } - if err := OptionIsValid(option, native); err != nil { - return nil, err - } return native, nil } From f661b64e0ac562fd9a1be473fbb494391c3ba3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Sun, 11 Aug 2024 16:12:09 +0200 Subject: [PATCH 10/21] config/settings: Remove duplicating `DefaultGlobalSettings()` Additionally pricise the documentation of the remaining DefaultCommonSettings() and DefaultAllSettings() functions. --- internal/config/settings.go | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/internal/config/settings.go b/internal/config/settings.go index 52eaba00..1f69e86a 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -273,7 +273,7 @@ func verifySetting(option string, value interface{}, def interface{}) error { // Must be called after ReadSettings func InitGlobalSettings() error { var err error - GlobalSettings = DefaultGlobalSettings() + GlobalSettings = DefaultAllSettings() for k, v := range parsedSettings { if !strings.HasPrefix(reflect.TypeOf(v).String(), "map") { @@ -319,7 +319,7 @@ func WriteSettings(filename string) error { var err error if _, e := os.Stat(ConfigDir); e == nil { - defaults := DefaultGlobalSettings() + defaults := DefaultAllSettings() // remove any options froms parsedSettings that have since been marked as default for k, v := range parsedSettings { @@ -354,7 +354,7 @@ func OverwriteSettings(filename string) error { var err error if _, e := os.Stat(ConfigDir); e == nil { - defaults := DefaultGlobalSettings() + defaults := DefaultAllSettings() for k, v := range GlobalSettings { if def, ok := defaults[k]; !ok || !reflect.DeepEqual(v, def) { if _, wr := ModifiedSettings[k]; wr { @@ -420,8 +420,8 @@ func GetInfoBarOffset() int { return offset } -// DefaultCommonSettings returns the default global settings for micro -// Note that colorscheme is a global only option +// DefaultCommonSettings returns a map of all common buffer settings +// and their default values func DefaultCommonSettings() map[string]interface{} { commonsettings := make(map[string]interface{}) for k, v := range defaultCommonSettings { @@ -430,21 +430,8 @@ func DefaultCommonSettings() map[string]interface{} { return commonsettings } -// DefaultGlobalSettings returns the default global settings for micro -// Note that colorscheme is a global only option -func DefaultGlobalSettings() map[string]interface{} { - globalsettings := make(map[string]interface{}) - for k, v := range defaultCommonSettings { - globalsettings[k] = v - } - for k, v := range DefaultGlobalOnlySettings { - globalsettings[k] = v - } - return globalsettings -} - -// DefaultAllSettings returns a map of all settings and their -// default values (both common and global settings) +// DefaultAllSettings returns a map of all common buffer & global-only settings +// and their default values func DefaultAllSettings() map[string]interface{} { allsettings := make(map[string]interface{}) for k, v := range defaultCommonSettings { From 05529596cf321ae4b36a46ac88876d0e012fc832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Fri, 26 Jul 2024 21:06:06 +0200 Subject: [PATCH 11/21] action/command: Prevent overwriting settings set locally or by plugin - on `reload` - on `filetype` change --- internal/action/command.go | 7 +++---- internal/buffer/buffer.go | 3 +++ internal/buffer/settings.go | 25 +++++++++++++++++-------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/internal/action/command.go b/internal/action/command.go index 502365aa..d29aeb03 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -410,7 +410,7 @@ func reloadRuntime(reloadPlugins bool) { for _, b := range buffer.OpenBuffers { config.InitLocalSettings(b.Settings, b.Path) for k, v := range b.Settings { - b.SetOptionNative(k, v) + b.DoSetOptionNative(k, v) } b.UpdateRules() } @@ -610,9 +610,8 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error { // ...at last check the buffer locals for _, b := range buffer.OpenBuffers { - if err := b.SetOptionNative(option, nativeValue); err != nil { - return err - } + b.DoSetOptionNative(option, nativeValue) + delete(b.LocalSettings, option) } return config.WriteSettings(filepath.Join(config.ConfigDir, "settings.json")) diff --git a/internal/buffer/buffer.go b/internal/buffer/buffer.go index 15cfe335..e8f6e98b 100644 --- a/internal/buffer/buffer.go +++ b/internal/buffer/buffer.go @@ -86,6 +86,8 @@ type SharedBuffer struct { // Settings customized by the user Settings map[string]interface{} + // LocalSettings customized by the user for this buffer only + LocalSettings map[string]bool Suggestions []string Completions []string @@ -326,6 +328,7 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT // assigning the filetype. settings := config.DefaultCommonSettings() b.Settings = config.DefaultCommonSettings() + b.LocalSettings = make(map[string]bool) for k, v := range config.GlobalSettings { if _, ok := config.DefaultGlobalOnlySettings[k]; !ok { // make sure setting is not global-only diff --git a/internal/buffer/settings.go b/internal/buffer/settings.go index 8d0e8aa1..9fc1cfd8 100644 --- a/internal/buffer/settings.go +++ b/internal/buffer/settings.go @@ -8,13 +8,9 @@ import ( "github.com/zyedidia/micro/v2/internal/screen" ) -func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { - if err := config.OptionIsValid(option, nativeValue); err != nil { - return err - } - +func (b *Buffer) DoSetOptionNative(option string, nativeValue interface{}) { if reflect.DeepEqual(b.Settings[option], nativeValue) { - return nil + return } b.Settings[option] = nativeValue @@ -46,10 +42,14 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { // filetype should not override volatile settings continue } + if _, ok := b.LocalSettings[k]; ok { + // filetype should not override local settings + continue + } if _, ok := settings[k]; ok { - b.SetOptionNative(k, settings[k]) + b.DoSetOptionNative(k, settings[k]) } else { - b.SetOptionNative(k, v) + b.DoSetOptionNative(k, v) } } b.UpdateRules() @@ -101,6 +101,15 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { if b.OptionCallback != nil { b.OptionCallback(option, nativeValue) } +} + +func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { + if err := config.OptionIsValid(option, nativeValue); err != nil { + return err + } + + b.DoSetOptionNative(option, nativeValue) + b.LocalSettings[option] = true return nil } From 724e29446fba0c327415f5e13be3b9646afa69cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Thu, 8 Aug 2024 22:11:54 +0200 Subject: [PATCH 12/21] action/command: On `reload` prevent overwriting local settings --- internal/action/command.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/action/command.go b/internal/action/command.go index d29aeb03..4dec9ee8 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -410,6 +410,10 @@ func reloadRuntime(reloadPlugins bool) { for _, b := range buffer.OpenBuffers { config.InitLocalSettings(b.Settings, b.Path) for k, v := range b.Settings { + if _, ok := b.LocalSettings[k]; ok { + // reload should not override local settings + continue + } b.DoSetOptionNative(k, v) } b.UpdateRules() From a3071b173bf6aaf390a57b36f94cdf8310b675c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Mon, 12 Aug 2024 20:26:51 +0200 Subject: [PATCH 13/21] action/command: Refactor `reload` & the `filetype` change --- internal/action/command.go | 10 +------ internal/buffer/settings.go | 60 +++++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/internal/action/command.go b/internal/action/command.go index 4dec9ee8..389c047d 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -408,15 +408,7 @@ func reloadRuntime(reloadPlugins bool) { screen.TermMessage(err) } for _, b := range buffer.OpenBuffers { - config.InitLocalSettings(b.Settings, b.Path) - for k, v := range b.Settings { - if _, ok := b.LocalSettings[k]; ok { - // reload should not override local settings - continue - } - b.DoSetOptionNative(k, v) - } - b.UpdateRules() + b.ReloadSettings(true) } } diff --git a/internal/buffer/settings.go b/internal/buffer/settings.go index 9fc1cfd8..838df4a5 100644 --- a/internal/buffer/settings.go +++ b/internal/buffer/settings.go @@ -8,6 +8,43 @@ import ( "github.com/zyedidia/micro/v2/internal/screen" ) +func (b *Buffer) ReloadSettings(reloadFiletype bool) { + settings := config.ParsedSettings() + + if _, ok := b.LocalSettings["filetype"]; !ok && reloadFiletype { + // need to update filetype before updating other settings based on it + b.Settings["filetype"] = "unknown" + if v, ok := settings["filetype"]; ok { + b.Settings["filetype"] = v + } + } + + // update syntax rules, which will also update filetype if needed + b.UpdateRules() + settings["filetype"] = b.Settings["filetype"] + + config.InitLocalSettings(settings, b.Path) + for k, v := range config.DefaultCommonSettings() { + if k == "filetype" { + // prevent recursion + continue + } + if _, ok := config.VolatileSettings[k]; ok { + // reload should not override volatile settings + continue + } + if _, ok := b.LocalSettings[k]; ok { + // reload should not override local settings + continue + } + if _, ok := settings[k]; ok { + b.DoSetOptionNative(k, settings[k]) + } else { + b.DoSetOptionNative(k, v) + } + } +} + func (b *Buffer) DoSetOptionNative(option string, nativeValue interface{}) { if reflect.DeepEqual(b.Settings[option], nativeValue) { return @@ -31,28 +68,7 @@ func (b *Buffer) DoSetOptionNative(option string, nativeValue interface{}) { } else if option == "statusline" { screen.Redraw() } else if option == "filetype" { - settings := config.ParsedSettings() - settings["filetype"] = nativeValue - config.InitLocalSettings(settings, b.Path) - for k, v := range config.DefaultCommonSettings() { - if k == "filetype" { - continue - } - if _, ok := config.VolatileSettings[k]; ok { - // filetype should not override volatile settings - continue - } - if _, ok := b.LocalSettings[k]; ok { - // filetype should not override local settings - continue - } - if _, ok := settings[k]; ok { - b.DoSetOptionNative(k, settings[k]) - } else { - b.DoSetOptionNative(k, v) - } - } - b.UpdateRules() + b.ReloadSettings(false) } else if option == "fileformat" { switch b.Settings["fileformat"].(string) { case "unix": From 10511c9bafe3a528d97dfc0663f4ac9d84256b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Thu, 8 Aug 2024 22:09:46 +0200 Subject: [PATCH 14/21] buffer/buffer: Store `fileformat` in `LocalSettings` on auto detection --- internal/buffer/buffer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/buffer/buffer.go b/internal/buffer/buffer.go index e8f6e98b..c623fd58 100644 --- a/internal/buffer/buffer.go +++ b/internal/buffer/buffer.go @@ -367,6 +367,9 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT case "dos": ff = FFDos } + } else { + // in case of autodetection treat as locally set + b.LocalSettings["fileformat"] = true } b.LineArray = NewLineArray(uint64(size), ff, reader) From 0542765d9551342faed8fb78b16dc9e70a34bff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Thu, 8 Aug 2024 20:38:19 +0200 Subject: [PATCH 15/21] action/command: Simplify `ResetCmd` It doesn't need to loop over the DefaultCommonSettings() again, since they're already included in the default settings and set via SetGlobalOptionNative(). --- internal/action/command.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/internal/action/command.go b/internal/action/command.go index 389c047d..73c1a5ac 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -634,16 +634,10 @@ func (h *BufPane) ResetCmd(args []string) { } option := args[0] + defaults := config.DefaultAllSettings() - defaultGlobals := config.DefaultGlobalSettings() - defaultLocals := config.DefaultCommonSettings() - - if _, ok := defaultGlobals[option]; ok { - SetGlobalOptionNative(option, defaultGlobals[option]) - return - } - if _, ok := defaultLocals[option]; ok { - h.Buf.SetOptionNative(option, defaultLocals[option]) + if _, ok := defaults[option]; ok { + SetGlobalOptionNative(option, defaults[option]) return } InfoBar.Error(config.ErrInvalidOption) From a678d42861b3b609cf872f2dd95fbd346c3c58dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Sun, 11 Aug 2024 17:49:49 +0200 Subject: [PATCH 16/21] plugins/comment: Don't write to `b.Settings` directly ...and use `SetOptionNative()` instead. --- runtime/plugins/comment/comment.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/plugins/comment/comment.lua b/runtime/plugins/comment/comment.lua index 034c4904..f86da945 100644 --- a/runtime/plugins/comment/comment.lua +++ b/runtime/plugins/comment/comment.lua @@ -66,9 +66,9 @@ local last_ft function updateCommentType(buf) if buf.Settings["commenttype"] == nil or (last_ft ~= buf.Settings["filetype"] and last_ft ~= nil) then if ft[buf.Settings["filetype"]] ~= nil then - buf.Settings["commenttype"] = ft[buf.Settings["filetype"]] + buf:SetOptionNative("commenttype", ft[buf.Settings["filetype"]]) else - buf.Settings["commenttype"] = "# %s" + buf:SetOptionNative("commenttype", "# %s") end last_ft = buf.Settings["filetype"] From 832c7deaf8b2369f1344606812df110d73541204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Wed, 19 Jun 2024 20:36:29 +0200 Subject: [PATCH 17/21] config: Remove unused `GetAutoTime()` --- internal/config/autosave.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/internal/config/autosave.go b/internal/config/autosave.go index 2d0f8cec..d530b459 100644 --- a/internal/config/autosave.go +++ b/internal/config/autosave.go @@ -21,13 +21,6 @@ func SetAutoTime(a int) { autolock.Unlock() } -func GetAutoTime() int { - autolock.Lock() - a := autotime - autolock.Unlock() - return a -} - func StartAutoSave() { go func() { for { From 4170df89eba294595316912de49ef06a7d5879e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Wed, 19 Jun 2024 21:10:52 +0200 Subject: [PATCH 18/21] config: Use `float64` within the `autosave` processing --- cmd/micro/micro.go | 2 +- internal/action/command.go | 2 +- internal/config/autosave.go | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 0cd7c1ba..0e616cad 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -357,7 +357,7 @@ func main() { } if a := config.GetGlobalOption("autosave").(float64); a > 0 { - config.SetAutoTime(int(a)) + config.SetAutoTime(a) config.StartAutoSave() } diff --git a/internal/action/command.go b/internal/action/command.go index 73c1a5ac..fda55542 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -552,7 +552,7 @@ func doSetGlobalOptionNative(option string, nativeValue interface{}) error { } } else if option == "autosave" { if nativeValue.(float64) > 0 { - config.SetAutoTime(int(nativeValue.(float64))) + config.SetAutoTime(nativeValue.(float64)) config.StartAutoSave() } else { config.SetAutoTime(0) diff --git a/internal/config/autosave.go b/internal/config/autosave.go index d530b459..2a19da36 100644 --- a/internal/config/autosave.go +++ b/internal/config/autosave.go @@ -6,7 +6,7 @@ import ( ) var Autosave chan bool -var autotime int +var autotime float64 // lock for autosave var autolock sync.Mutex @@ -15,7 +15,7 @@ func init() { Autosave = make(chan bool) } -func SetAutoTime(a int) { +func SetAutoTime(a float64) { autolock.Lock() autotime = a autolock.Unlock() @@ -27,10 +27,10 @@ func StartAutoSave() { autolock.Lock() a := autotime autolock.Unlock() - if a < 1 { + if a <= 0 { break } - time.Sleep(time.Duration(a) * time.Second) + time.Sleep(time.Duration(a * float64(time.Second))) Autosave <- true } }() From 8b31dc79bfb3b2c71b3af48cd311155add5b9a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Mon, 24 Jun 2024 19:12:47 +0200 Subject: [PATCH 19/21] config: Rework `autosave` to be rearmed upon change --- cmd/micro/micro.go | 2 +- internal/action/command.go | 1 - internal/config/autosave.go | 42 ++++++++++++++++++++++++------------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 0e616cad..4445a348 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -356,9 +356,9 @@ func main() { log.Println(clipErr, " or change 'clipboard' option") } + config.StartAutoSave() if a := config.GetGlobalOption("autosave").(float64); a > 0 { config.SetAutoTime(a) - config.StartAutoSave() } screen.Events = make(chan tcell.Event) diff --git a/internal/action/command.go b/internal/action/command.go index fda55542..5f56fcdb 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -553,7 +553,6 @@ func doSetGlobalOptionNative(option string, nativeValue interface{}) error { } else if option == "autosave" { if nativeValue.(float64) > 0 { config.SetAutoTime(nativeValue.(float64)) - config.StartAutoSave() } else { config.SetAutoTime(0) } diff --git a/internal/config/autosave.go b/internal/config/autosave.go index 2a19da36..0dbb2989 100644 --- a/internal/config/autosave.go +++ b/internal/config/autosave.go @@ -1,37 +1,49 @@ package config import ( - "sync" "time" ) var Autosave chan bool -var autotime float64 - -// lock for autosave -var autolock sync.Mutex +var autotime chan float64 func init() { Autosave = make(chan bool) + autotime = make(chan float64) } func SetAutoTime(a float64) { - autolock.Lock() - autotime = a - autolock.Unlock() + autotime <- a } func StartAutoSave() { go func() { + var a float64 + var t *time.Timer + var elapsed <-chan time.Time for { - autolock.Lock() - a := autotime - autolock.Unlock() - if a <= 0 { - break + select { + case a = <-autotime: + if t != nil { + t.Stop() + for len(elapsed) > 0 { + <-elapsed + } + } + if a > 0 { + if t != nil { + t.Reset(time.Duration(a * float64(time.Second))) + } else { + t = time.NewTimer(time.Duration(a * float64(time.Second))) + elapsed = t.C + } + } + case <-elapsed: + if a > 0 { + t.Reset(time.Duration(a * float64(time.Second))) + Autosave <- true + } } - time.Sleep(time.Duration(a * float64(time.Second))) - Autosave <- true } }() } From 33b7c9db7c6aa77946d33e75b2dc769c38a69c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:40:22 +0200 Subject: [PATCH 20/21] config: Don't truncate `float64` to `int` --- internal/config/settings.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/config/settings.go b/internal/config/settings.go index 1f69e86a..92dec00e 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -456,11 +456,11 @@ func GetNativeValue(option string, realValue interface{}, value string) (interfa } else if kind == reflect.String { native = value } else if kind == reflect.Float64 { - i, err := strconv.Atoi(value) + f, err := strconv.ParseFloat(value, 64) if err != nil { return nil, ErrInvalidValue } - native = float64(i) + native = f } else { return nil, ErrInvalidValue } From b80ea9348615cd47185bcb80a7d6140ac6982856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:23:46 +0200 Subject: [PATCH 21/21] config: Correct typo in `validatePositiveValue()` --- internal/config/settings.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/config/settings.go b/internal/config/settings.go index 92dec00e..b8cfcd51 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -480,13 +480,13 @@ func OptionIsValid(option string, value interface{}) error { // Option validators func validatePositiveValue(option string, value interface{}) error { - tabsize, ok := value.(float64) + nativeValue, ok := value.(float64) if !ok { return errors.New("Expected numeric type for " + option) } - if tabsize < 1 { + if nativeValue < 1 { return errors.New(option + " must be greater than 0") }