Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cmd/micro/initlua.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func luaImportMicroConfig() *lua.LTable {
ulua.L.SetField(pkg, "OptionComplete", luar.New(ulua.L, action.OptionComplete))
ulua.L.SetField(pkg, "OptionValueComplete", luar.New(ulua.L, action.OptionValueComplete))
ulua.L.SetField(pkg, "NoComplete", luar.New(ulua.L, nil))
ulua.L.SetField(pkg, "TryBindKey", luar.New(ulua.L, action.TryBindKey))
ulua.L.SetField(pkg, "TryBindKey", luar.New(ulua.L, action.TryBindKeyPlug))
ulua.L.SetField(pkg, "Reload", luar.New(ulua.L, action.ReloadConfig))
ulua.L.SetField(pkg, "AddRuntimeFileFromMemory", luar.New(ulua.L, config.PluginAddRuntimeFileFromMemory))
ulua.L.SetField(pkg, "AddRuntimeFilesFromDirectory", luar.New(ulua.L, config.PluginAddRuntimeFilesFromDirectory))
Expand All @@ -88,8 +88,8 @@ func luaImportMicroConfig() *lua.LTable {
ulua.L.SetField(pkg, "RegisterCommonOption", luar.New(ulua.L, config.RegisterCommonOptionPlug))
ulua.L.SetField(pkg, "RegisterGlobalOption", luar.New(ulua.L, config.RegisterGlobalOptionPlug))
ulua.L.SetField(pkg, "GetGlobalOption", luar.New(ulua.L, config.GetGlobalOption))
ulua.L.SetField(pkg, "SetGlobalOption", luar.New(ulua.L, action.SetGlobalOption))
ulua.L.SetField(pkg, "SetGlobalOptionNative", luar.New(ulua.L, action.SetGlobalOptionNative))
ulua.L.SetField(pkg, "SetGlobalOption", luar.New(ulua.L, action.SetGlobalOptionPlug))
ulua.L.SetField(pkg, "SetGlobalOptionNative", luar.New(ulua.L, action.SetGlobalOptionNativePlug))
ulua.L.SetField(pkg, "ConfigDir", luar.New(ulua.L, config.ConfigDir))

return pkg
Expand Down
20 changes: 17 additions & 3 deletions internal/action/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,18 @@ func eventsEqual(e1 Event, e2 Event) bool {
return e1 == e2
}

// TryBindKeyPlug tries to bind a key for the plugin without writing to bindings.json.
// This operation can be rejected by lockbindings to prevent unexpected actions by the user.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of "unexpected actions"?

AFAICS, lockbindings is completely pointless now, what it prevents is not overwriting bindings.json (that is already prevented anyway) but doing anything at all?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is if the plugin action is irreversible (i.e. delete a file, edit a buffer, etc...), I want to explicitly bind it myself instead of having the plugin bind it behind my back. Having this settings guarantees that even if I pressed a keybind by mistake, I know for sure nothing nasty would happen.

Ok, haven't noticed this. Well, a plugin can do nasty things behind your back anyway, by virtue of being installed.

But if you really find this lockbindings useful, we can add it.

But it is a separate change, why not do it in a separate commit? This commit does 3 different things, it can be naturally split into three?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, haven't noticed this. Well, a plugin can do nasty things behind your back anyway, by virtue of being installed.

Well yes, but the point of it is to stop accidental effect instead of stopping any malicious intent.

For example if a shortcut for deleting a file in a file manager plugin is ctrl-d, it is easy for the user to press it by accident (say when copying with ctrl-c) without knowing its existence. (An extreme example, of course, but the point still stands)

lockbindings (or whatever we want to call it) makes sure the user is ALWAYS in control of the bindings and mis-pressing any keys combinations will not cause any undesired effects.

But it is a separate change, why not do it in a separate commit? This commit does 3 different things, it can be naturally split into three?

Yeah, that's fine, I can separate them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, separated them into 3 commits now.

func TryBindKeyPlug(k, v string, overwrite bool) (bool, error) {
if l, ok := config.GlobalSettings["lockbindings"]; ok && l.(bool) {
return false, errors.New("bindings is locked by the user")
}
return TryBindKey(k, v, overwrite, false)
}

// TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
// Returns true if the keybinding already existed and a possible error
func TryBindKey(k, v string, overwrite bool) (bool, error) {
// Returns true if the keybinding already existed or is binded successfully and a possible error
func TryBindKey(k, v string, overwrite bool, writeToFile bool) (bool, error) {
var e error
var parsed map[string]any

Expand Down Expand Up @@ -310,7 +319,12 @@ func TryBindKey(k, v string, overwrite bool) (bool, error) {

txt, _ := json.MarshalIndent(parsed, "", " ")
txt = append(txt, '\n')
return true, writeFile(filename, txt)

if writeToFile {
return true, writeFile(filename, txt)
} else {
return true, nil
}
}
return false, e
}
Expand Down
26 changes: 19 additions & 7 deletions internal/action/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ func doSetGlobalOptionNative(option string, nativeValue any) error {
return nil
}

func SetGlobalOptionNative(option string, nativeValue any) error {
func SetGlobalOptionNative(option string, nativeValue any, writeToFile bool) error {
if err := config.OptionIsValid(option, nativeValue); err != nil {
return err
}
Expand All @@ -653,6 +653,10 @@ func SetGlobalOptionNative(option string, nativeValue any) error {
delete(b.LocalSettings, option)
}

if !writeToFile {
return nil
}

err := config.WriteSettings(filepath.Join(config.ConfigDir, "settings.json"))
if err != nil {
if errors.Is(err, util.ErrOverwrite) {
Expand All @@ -665,7 +669,7 @@ func SetGlobalOptionNative(option string, nativeValue any) error {
return nil
}

func SetGlobalOption(option, value string) error {
func SetGlobalOption(option, value string, writeToFile bool) error {
if _, ok := config.GlobalSettings[option]; !ok {
return config.ErrInvalidOption
}
Expand All @@ -675,7 +679,15 @@ func SetGlobalOption(option, value string) error {
return err
}

return SetGlobalOptionNative(option, nativeValue)
return SetGlobalOptionNative(option, nativeValue, writeToFile)
}

func SetGlobalOptionNativePlug(option string, nativeValue any) error {
return SetGlobalOptionNative(option, nativeValue, false)
}

func SetGlobalOptionPlug(option, value string) error {
return SetGlobalOption(option, value, false)
}

// ResetCmd resets a setting to its default value
Expand All @@ -689,7 +701,7 @@ func (h *BufPane) ResetCmd(args []string) {
defaults := config.DefaultAllSettings()

if _, ok := defaults[option]; ok {
SetGlobalOptionNative(option, defaults[option])
SetGlobalOptionNative(option, defaults[option], true)
return
}
InfoBar.Error(config.ErrInvalidOption)
Expand All @@ -705,7 +717,7 @@ func (h *BufPane) SetCmd(args []string) {
option := args[0]
value := args[1]

err := SetGlobalOption(option, value)
err := SetGlobalOption(option, value, true)
if err == config.ErrInvalidOption {
err := h.Buf.SetOption(option, value)
if err != nil {
Expand Down Expand Up @@ -761,7 +773,7 @@ func (h *BufPane) toggleOption(option string, local bool) error {
return err
}
} else {
if err := SetGlobalOptionNative(option, newVal); err != nil {
if err := SetGlobalOptionNative(option, newVal, true); err != nil {
return err
}
}
Expand Down Expand Up @@ -844,7 +856,7 @@ func (h *BufPane) BindCmd(args []string) {
return
}

_, err := TryBindKey(parseKeyArg(args[0]), args[1], true)
_, err := TryBindKey(parseKeyArg(args[0]), args[1], true, true)
if err != nil {
if errors.Is(err, util.ErrOverwrite) {
screen.TermMessage(err)
Expand Down
1 change: 1 addition & 0 deletions internal/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ var DefaultGlobalOnlySettings = map[string]any{
"helpsplit": "hsplit",
"infobar": true,
"keymenu": false,
"lockbindings": false,
"mouse": true,
"multiopen": "tab",
"parsecursor": false,
Expand Down
6 changes: 6 additions & 0 deletions runtime/help/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,12 @@ Here are the available options:

default value: `false`

* `lockbindings`: prevent plugins and lua scripts from binding any keys.
Any custom actions must be binded manually either via commands like `bind`
or by modifying the `bindings.json` file.

default value: `false`

* `matchbrace`: show matching braces for '()', '{}', '[]' when the cursor
is on a brace character or (if `matchbraceleft` is enabled) next to it.

Expand Down
17 changes: 9 additions & 8 deletions runtime/help/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,12 @@ The packages and their contents are listed below (in Go type signatures):
values afterwards
- `NoComplete`: no autocompletion suggestions

- `TryBindKey(k, v string, overwrite bool) (bool, error)`: bind the key
`k` to the string `v` in the `bindings.json` file. If `overwrite` is
true, this will overwrite any existing binding to key `k`. Returns true
if the binding was made, and a possible error (for example writing to
`bindings.json` can cause an error).
- `TryBindKey(k, v string, overwrite bool) (bool, error)`:
bind the key `k` to the string `v`. If `overwrite` is true, this will
overwrite any existing binding to key `k`.
Returns true if the binding was made, and a possible error.
This operation can be rejected by `lockbindings` to prevent undesired
actions by the user.

- `Reload()`: reload configuration files.

Expand Down Expand Up @@ -224,9 +225,9 @@ The packages and their contents are listed below (in Go type signatures):
given plugin in the `GlobalSettings` map.

- `SetGlobalOption(option, value string) error`: sets an option to a
given value. Same as using the `> set` command. This will try to convert
the value into the proper type for the option. Can return an error if the
option name is not valid, or the value can not be converted.
given value. This will try to convert the value into the proper
type for the option. Can return an error if the option name is not
valid, or the value can not be converted.

- `SetGlobalOptionNative(option string, value any) error`: sets
an option to a given value, where the type of value is the actual
Expand Down