1// Package key provides some types and functions for generating user-definable
2// keymappings useful in Bubble Tea components. There are a few different ways
3// you can define a keymapping with this package. Here's one example:
4//
5// type KeyMap struct {
6// Up key.Binding
7// Down key.Binding
8// }
9//
10// var DefaultKeyMap = KeyMap{
11// Up: key.NewBinding(
12// key.WithKeys("k", "up"), // actual keybindings
13// key.WithHelp("↑/k", "move up"), // corresponding help text
14// ),
15// Down: key.NewBinding(
16// key.WithKeys("j", "down"),
17// key.WithHelp("↓/j", "move down"),
18// ),
19// }
20//
21// func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
22// switch msg := msg.(type) {
23// case tea.KeyPressMsg:
24// switch {
25// case key.Matches(msg, DefaultKeyMap.Up):
26// // The user pressed up
27// case key.Matches(msg, DefaultKeyMap.Down):
28// // The user pressed down
29// }
30// }
31//
32// // ...
33// }
34//
35// The help information, which is not used in the example above, can be used
36// to render help text for keystrokes in your views.
37package key
38
39import "fmt"
40
41// Binding describes a set of keybindings and, optionally, their associated
42// help text.
43type Binding struct {
44 keys []string
45 help Help
46 disabled bool
47}
48
49// BindingOpt is an initialization option for a keybinding. It's used as an
50// argument to NewBinding.
51type BindingOpt func(*Binding)
52
53// NewBinding returns a new keybinding from a set of BindingOpt options.
54func NewBinding(opts ...BindingOpt) Binding {
55 b := &Binding{}
56 for _, opt := range opts {
57 opt(b)
58 }
59 return *b
60}
61
62// WithKeys initializes a keybinding with the given keystrokes.
63func WithKeys(keys ...string) BindingOpt {
64 return func(b *Binding) {
65 b.keys = keys
66 }
67}
68
69// WithHelp initializes a keybinding with the given help text.
70func WithHelp(key, desc string) BindingOpt {
71 return func(b *Binding) {
72 b.help = Help{Key: key, Desc: desc}
73 }
74}
75
76// WithDisabled initializes a disabled keybinding.
77func WithDisabled() BindingOpt {
78 return func(b *Binding) {
79 b.disabled = true
80 }
81}
82
83// SetKeys sets the keys for the keybinding.
84func (b *Binding) SetKeys(keys ...string) {
85 b.keys = keys
86}
87
88// Keys returns the keys for the keybinding.
89func (b Binding) Keys() []string {
90 return b.keys
91}
92
93// SetHelp sets the help text for the keybinding.
94func (b *Binding) SetHelp(key, desc string) {
95 b.help = Help{Key: key, Desc: desc}
96}
97
98// Help returns the Help information for the keybinding.
99func (b Binding) Help() Help {
100 return b.help
101}
102
103// Enabled returns whether or not the keybinding is enabled. Disabled
104// keybindings won't be activated and won't show up in help. Keybindings are
105// enabled by default.
106func (b Binding) Enabled() bool {
107 return !b.disabled && b.keys != nil
108}
109
110// SetEnabled enables or disables the keybinding.
111func (b *Binding) SetEnabled(v bool) {
112 b.disabled = !v
113}
114
115// Unbind removes the keys and help from this binding, effectively nullifying
116// it. This is a step beyond disabling it, since applications can enable
117// or disable key bindings based on application state.
118func (b *Binding) Unbind() {
119 b.keys = nil
120 b.help = Help{}
121}
122
123// Help is help information for a given keybinding.
124type Help struct {
125 Key string
126 Desc string
127}
128
129// Matches checks if the given key matches the given bindings.
130func Matches[Key fmt.Stringer](k Key, b ...Binding) bool {
131 keys := k.String()
132 for _, binding := range b {
133 for _, v := range binding.keys {
134 if keys == v && binding.Enabled() {
135 return true
136 }
137 }
138 }
139 return false
140}