key.go

  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}