297 lines
9.2 KiB
Go
297 lines
9.2 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
"github.com/ayn2op/tview/keybind"
|
|
)
|
|
|
|
type Keybind struct {
|
|
keybind.Keybind
|
|
}
|
|
|
|
var _ toml.Unmarshaler = (*Keybind)(nil)
|
|
|
|
func (k *Keybind) UnmarshalTOML(value any) error {
|
|
var keys []string
|
|
switch value := value.(type) {
|
|
case string:
|
|
keys = []string{value}
|
|
case []any:
|
|
for _, key := range value {
|
|
if key, ok := key.(string); ok {
|
|
keys = append(keys, key)
|
|
}
|
|
}
|
|
default:
|
|
return nil
|
|
}
|
|
kb := keybind.New(keys...)
|
|
// Keep displayed help key aligned with configured key(s).
|
|
help := k.Help()
|
|
if keys := kb.Keys(); len(keys) > 0 {
|
|
help.Key = keys[0]
|
|
}
|
|
k.Keybind = kb.WithHelp(help.Key, help.Desc)
|
|
return nil
|
|
}
|
|
|
|
// MarshalTOML writes the keys as UnmarshalTOML reads them: one key as a string, an unbound keybind as an empty one, and several keys as a list.
|
|
func (k Keybind) MarshalTOML() ([]byte, error) {
|
|
if keys := k.Keys(); len(keys) > 1 {
|
|
return json.Marshal(keys)
|
|
}
|
|
return json.Marshal(strings.Join(k.Keys(), ""))
|
|
}
|
|
|
|
// key builds a Keybind bound to key, or unbound if key is empty, with desc as its help description.
|
|
func key(key, desc string) Keybind {
|
|
k := Keybind{keybind.New().WithHelp("", desc)}
|
|
k.UnmarshalTOML(key)
|
|
return k
|
|
}
|
|
|
|
type ScrollKeybinds struct {
|
|
// Scroll without changing the selection.
|
|
ScrollUp Keybind `toml:"scroll_up"`
|
|
ScrollDown Keybind `toml:"scroll_down"`
|
|
ScrollTop Keybind `toml:"scroll_top"`
|
|
ScrollBottom Keybind `toml:"scroll_bottom"`
|
|
}
|
|
|
|
type SelectionKeybinds struct {
|
|
SelectUp Keybind `toml:"select_up"`
|
|
SelectDown Keybind `toml:"select_down"`
|
|
SelectTop Keybind `toml:"select_top"`
|
|
SelectBottom Keybind `toml:"select_bottom"`
|
|
}
|
|
|
|
type PickerKeybinds struct {
|
|
SelectionKeybinds
|
|
Select Keybind `toml:"select"`
|
|
Cancel Keybind `toml:"cancel"`
|
|
}
|
|
|
|
type TreeKeybinds struct {
|
|
ScrollKeybinds
|
|
// Scroll sideways to read names that are cut off.
|
|
ScrollLeft Keybind `toml:"scroll_left"`
|
|
ScrollRight Keybind `toml:"scroll_right"`
|
|
|
|
SelectionKeybinds
|
|
// Select the highlighted text-based channel, or expand or collapse the highlighted node.
|
|
SelectCurrent Keybind `toml:"select_current"`
|
|
|
|
YankID Keybind `toml:"yank_id"`
|
|
|
|
CollapseAll Keybind `toml:"collapse_all"`
|
|
CollapseParentNode Keybind `toml:"collapse_parent_node"`
|
|
MoveToParentNode Keybind `toml:"move_to_parent_node"`
|
|
}
|
|
|
|
type GuildsTreeKeybinds struct {
|
|
TreeKeybinds
|
|
// Highlight the previous or next unread guild or channel, wrapping around.
|
|
SelectPreviousUnread Keybind `toml:"select_previous_unread"`
|
|
SelectNextUnread Keybind `toml:"select_next_unread"`
|
|
}
|
|
|
|
type MessagesListKeybinds struct {
|
|
SelectionKeybinds
|
|
ScrollKeybinds
|
|
|
|
// Select the message that the selected message replies to.
|
|
SelectReply Keybind `toml:"select_reply"`
|
|
// Reply to the selected message.
|
|
Reply Keybind `toml:"reply"`
|
|
// Reply (with mention) to the selected message.
|
|
ReplyMention Keybind `toml:"reply_mention"`
|
|
|
|
Cancel Keybind `toml:"cancel"`
|
|
Edit Keybind `toml:"edit"`
|
|
|
|
// Delete without confirmation. Unbound by default.
|
|
Delete Keybind `toml:"delete"`
|
|
DeleteConfirm Keybind `toml:"delete_confirm"`
|
|
|
|
// Open attachments with their associated application. MIME types outside the
|
|
// allowlist require confirmation. Hyperlinks still use the browser.
|
|
Open Keybind `toml:"open"`
|
|
// Open attachments or hyperlinks in the browser.
|
|
OpenInBrowser Keybind `toml:"open_in_browser"`
|
|
// Download one of the selected message's attachments.
|
|
Download Keybind `toml:"download"`
|
|
|
|
// Yank (copy) the selected message's content/url/id.
|
|
YankContent Keybind `toml:"yank_content"`
|
|
YankURL Keybind `toml:"yank_url"`
|
|
YankID Keybind `toml:"yank_id"`
|
|
}
|
|
|
|
type ComposerKeybinds struct {
|
|
// Edit your newest editable message loaded in the current channel, with no age limit.
|
|
// Only when the composer is empty, with no reply, attachments, or edit in progress.
|
|
EditLast Keybind `toml:"edit_last"`
|
|
// Paste from clipboard (supports both text and images).
|
|
Paste Keybind `toml:"paste"`
|
|
Send Keybind `toml:"send"`
|
|
// Insert a new line at the cursor.
|
|
Newline Keybind `toml:"newline"`
|
|
// Remove existing text or cancel reply.
|
|
Cancel Keybind `toml:"cancel"`
|
|
// Complete usernames when mentioning.
|
|
TabComplete Keybind `toml:"tab_complete"`
|
|
// Toggle whether a reply mentions its author.
|
|
ToggleReplyMention Keybind `toml:"toggle_reply_mention"`
|
|
Undo Keybind `toml:"undo"`
|
|
|
|
OpenEditor Keybind `toml:"open_editor"`
|
|
OpenFilePicker Keybind `toml:"open_file_picker"`
|
|
}
|
|
|
|
type MentionsListKeybinds struct {
|
|
SelectionKeybinds
|
|
}
|
|
|
|
type Keybinds struct {
|
|
// Hide/show the guilds tree.
|
|
ToggleGuildsTree Keybind `toml:"toggle_guilds_tree"`
|
|
// Hide/show the members tree.
|
|
ToggleMembersTree Keybind `toml:"toggle_members_tree"`
|
|
ToggleChannelsPicker Keybind `toml:"toggle_channels_picker"`
|
|
// Hide/show keybind help.
|
|
ToggleHelp Keybind `toml:"toggle_help"`
|
|
// Switch between compact and full keybind help.
|
|
ToggleFullHelp Keybind `toml:"toggle_full_help"`
|
|
Suspend Keybind `toml:"suspend"`
|
|
|
|
FocusGuildsTree Keybind `toml:"focus_guilds_tree"`
|
|
FocusMembersTree Keybind `toml:"focus_members_tree"`
|
|
FocusMessagesList Keybind `toml:"focus_messages_list"`
|
|
FocusComposer Keybind `toml:"focus_composer"`
|
|
|
|
// Cycle focus between the widgets.
|
|
FocusPrevious Keybind `toml:"focus_previous"`
|
|
FocusNext Keybind `toml:"focus_next"`
|
|
|
|
Picker PickerKeybinds `toml:"picker"`
|
|
// Only while focusing on the guilds tree.
|
|
GuildsTree GuildsTreeKeybinds `toml:"guilds_tree"`
|
|
// Only while focusing on the members tree.
|
|
MembersTree TreeKeybinds `toml:"members_tree"`
|
|
// Only while focusing on sent messages.
|
|
MessagesList MessagesListKeybinds `toml:"messages_list"`
|
|
// Only while typing a message.
|
|
Composer ComposerKeybinds `toml:"composer"`
|
|
// Only while the mention suggestions are shown.
|
|
MentionsList MentionsListKeybinds `toml:"mentions_list"`
|
|
|
|
// Log out and remove the authentication token from keyring.
|
|
// Requires re-login upon restart.
|
|
Logout Keybind `toml:"logout"`
|
|
Quit Keybind `toml:"quit"`
|
|
}
|
|
|
|
func selectionKeybinds(up, down, top, bottom string) SelectionKeybinds {
|
|
return SelectionKeybinds{
|
|
SelectUp: key(up, "up"),
|
|
SelectDown: key(down, "down"),
|
|
SelectTop: key(top, "top"),
|
|
SelectBottom: key(bottom, "btm"),
|
|
}
|
|
}
|
|
|
|
func scrollKeybinds(up, down, top, bottom string) ScrollKeybinds {
|
|
return ScrollKeybinds{
|
|
ScrollUp: key(up, "scr up"),
|
|
ScrollDown: key(down, "scr down"),
|
|
ScrollTop: key(top, "scr top"),
|
|
ScrollBottom: key(bottom, "scr btm"),
|
|
}
|
|
}
|
|
|
|
func defaultKeybinds() Keybinds {
|
|
vi := selectionKeybinds("k", "j", "g", "G")
|
|
emacs := selectionKeybinds("ctrl+p", "ctrl+n", "home", "end")
|
|
tree := TreeKeybinds{
|
|
SelectionKeybinds: vi,
|
|
ScrollKeybinds: scrollKeybinds("pgup", "pgdn", "home", "end"),
|
|
ScrollLeft: key("h", "scr left"),
|
|
ScrollRight: key("l", "scr right"),
|
|
SelectCurrent: key("enter", "select"),
|
|
YankID: key("i", "copy id"),
|
|
|
|
CollapseAll: key("_", "collapse all"),
|
|
CollapseParentNode: key("-", "collapse parent"),
|
|
MoveToParentNode: key("p", "parent"),
|
|
}
|
|
return Keybinds{
|
|
ToggleGuildsTree: key("alt+g", "toggle guilds"),
|
|
ToggleMembersTree: key("alt+m", "toggle members"),
|
|
ToggleChannelsPicker: key("ctrl+k", "channels picker"),
|
|
ToggleHelp: key("alt+.", "toggle help"),
|
|
ToggleFullHelp: key("ctrl+.", "full help"),
|
|
Suspend: key("ctrl+z", "suspend"),
|
|
|
|
FocusGuildsTree: key("ctrl+g", "guilds"),
|
|
FocusMembersTree: key("ctrl+y", "members"),
|
|
FocusMessagesList: key("ctrl+t", "messages"),
|
|
FocusComposer: key("ctrl+i", "composer"),
|
|
|
|
FocusPrevious: key("ctrl+h", "focus prev"),
|
|
FocusNext: key("ctrl+l", "focus next"),
|
|
|
|
Picker: PickerKeybinds{
|
|
SelectionKeybinds: emacs,
|
|
Select: key("enter", "sel"),
|
|
Cancel: key("esc", "cancel"),
|
|
},
|
|
GuildsTree: GuildsTreeKeybinds{
|
|
TreeKeybinds: tree,
|
|
SelectPreviousUnread: key("K", "prev unread"),
|
|
SelectNextUnread: key("J", "next unread"),
|
|
},
|
|
MembersTree: tree,
|
|
MessagesList: MessagesListKeybinds{
|
|
SelectionKeybinds: vi,
|
|
ScrollKeybinds: scrollKeybinds("K", "J", "home", "end"),
|
|
|
|
SelectReply: key("s", "sel reply"),
|
|
Reply: key("R", "reply"),
|
|
ReplyMention: key("r", "@reply"),
|
|
|
|
Cancel: key("esc", "cancel"),
|
|
Edit: key("e", "edit"),
|
|
|
|
Delete: key("", "force delete"),
|
|
DeleteConfirm: key("d", "delete"),
|
|
|
|
Open: key("o", "open"),
|
|
OpenInBrowser: key("O", "open browser"),
|
|
Download: key("D", "download"),
|
|
|
|
YankContent: key("y", "copy text"),
|
|
YankURL: key("u", "copy url"),
|
|
YankID: key("i", "copy id"),
|
|
},
|
|
Composer: ComposerKeybinds{
|
|
EditLast: key("up", "edit last"),
|
|
Paste: key("ctrl+v", "paste"),
|
|
Send: key("enter", "send"),
|
|
Newline: key("shift+enter", "nl"),
|
|
Cancel: key("esc", "cancel"),
|
|
TabComplete: key("tab", "complete"),
|
|
ToggleReplyMention: key("shift+tab", "toggle @reply"),
|
|
Undo: key("ctrl+u", "undo"),
|
|
|
|
OpenEditor: key("ctrl+e", "editor"),
|
|
OpenFilePicker: key("ctrl+\\", "attach"),
|
|
},
|
|
MentionsList: MentionsListKeybinds{SelectionKeybinds: emacs},
|
|
|
|
Logout: key("ctrl+d", "logout"),
|
|
Quit: key("ctrl+c", "quit"),
|
|
}
|
|
}
|