keymap

package
v0.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package keymap maps terminal keystrokes to named actions.

Matching does not mutate a Map. Sequence progress lives in Matcher, so a map can be shared by independent readers without sharing their partially typed sequences, provided its bindings are not changed concurrently.

Index

Examples

Constants

View Source
const DefaultTimeout = time.Second

DefaultTimeout is how long a partially typed sequence remains current when a Map does not specify a positive Timeout.

Variables

This section is empty.

Functions

This section is empty.

Types

type Action

type Action string

Action identifies an operation independently of the keystrokes bound to it. Empty is reserved for no action.

func (Action) String

func (a Action) String() string

String returns the action identifier.

type Binding

type Binding struct {
	Keys   input.Keys
	Action Action
}

Binding associates a chord sequence with an action. Values returned by Map.Bindings are caller-owned snapshots, so an application may sort, present or serialize them without changing the map.

func (Binding) String

func (b Binding) String() string

String returns the chord sequence followed by its action identifier.

type Map

type Map struct {

	// Timeout controls how long a partially typed sequence remains current. Zero
	// and negative values use [DefaultTimeout].
	Timeout time.Duration
	// Resolve decides when an exact binding that is also a prefix should run. Nil
	// waits for another key: a continuation takes the longer binding, while a key
	// that cannot continue it settles the exact binding first. Assign the event
	// owner's one-shot scheduler to make the exact binding run after Timeout even
	// when no further input arrives.
	Resolve Resolver
	// contains filtered or unexported fields
}

Map associates chord sequences with actions.

The zero value is an empty map. A Map must not be copied after first use: a copy would share the binding store and lookup tree while rebuilding only one of them.

Example
package main

import (
	"fmt"

	"github.com/Tangerg/oolong/core/input"
	"github.com/Tangerg/oolong/core/keymap"
)

func main() {
	// A consumer names its operations; the map says which keystrokes produce each
	// name. Neither the event protocol nor the map needs to know what an operation does.
	keys := &keymap.Map{}
	keys.Bind("delete-word-back", input.Ctrl.Rune('w'))
	keys.Bind("delete-word-back", input.Alt.With(input.Backspace))
	keys.Bind("submit", input.Chord{Code: input.Enter})

	var matcher keymap.Matcher
	for _, key := range []input.Key{
		{Code: input.Character, Rune: 'w', Mods: input.Ctrl},
		{Code: input.Enter},
		{Code: input.Character, Rune: 'q'},
	} {
		var action keymap.Action
		mine, _ := matcher.Handle(keys, key, func(next keymap.Action) bool {
			action = next
			return true
		})
		fmt.Printf("%-9s %-18s mine=%v\n", key, "\""+string(action)+"\"", mine)
	}

}
Output:
ctrl+w    "delete-word-back" mine=true
enter     "submit"           mine=true
q         ""                 mine=false
Example (Sequences)
package main

import (
	"fmt"

	"github.com/Tangerg/oolong/core/input"
	"github.com/Tangerg/oolong/core/keymap"
)

func main() {
	// A binding can be more than one chord long. The first chord is the map's and
	// names nothing yet, which the caller has to consume rather than pass on.
	keys := &keymap.Map{}
	keys.Bind("go-to-top", input.Chord{Rune: 'g'}, input.Chord{Rune: 'g'})

	var matcher keymap.Matcher
	for range 2 {
		var action keymap.Action
		mine, _ := matcher.Handle(keys, input.Key{Rune: 'g'}, func(next keymap.Action) bool {
			action = next
			return true
		})
		fmt.Printf("%q taken=%v waiting=%q\n", action, mine, matcher.Keys().String())
	}

}
Output:
"" taken=true waiting="g"
"go-to-top" taken=true waiting=""

func (*Map) Action

func (m *Map) Action(keys ...input.Chord) (Action, bool)

Action returns the action named by a complete sequence.

func (*Map) Bind

func (m *Map) Bind(action Action, keys ...input.Chord)

Bind associates keys with action, replacing an existing binding for the same sequence. Empty actions and empty sequences are ignored.

func (*Map) Bindings

func (m *Map) Bindings() []Binding

Bindings returns a deep copy of every binding, in binding order. It is the complete observation surface for applications that present or persist a key map; use Map.Keys when only one action is relevant.

Example
package main

import (
	"fmt"

	"github.com/Tangerg/oolong/core/input"
	"github.com/Tangerg/oolong/core/keymap"
)

func main() {
	// Bindings is the complete snapshot used by a settings or help interface. Keys is
	// the narrower query when the caller already knows the action it wants.
	keys := &keymap.Map{}
	keys.Bind("submit", input.Chord{Code: input.Enter})
	keys.Bind("cancel", input.Chord{Code: input.Esc})

	for _, binding := range keys.Bindings() {
		fmt.Println(binding)
	}

}
Output:
enter submit
esc cancel

func (*Map) Keys

func (m *Map) Keys(action Action) []input.Keys

Keys returns copies of the sequences bound to action, in binding order.

Example
package main

import (
	"fmt"

	"github.com/Tangerg/oolong/core/input"
	"github.com/Tangerg/oolong/core/keymap"
)

func main() {
	// Keys reports the sequences bound to an action in insertion order, so callers do
	// not have to reconstruct binding precedence.
	keys := &keymap.Map{}
	keys.Bind("delete-word-back", input.Ctrl.Rune('w'))
	keys.Bind("delete-word-back", input.Alt.With(input.Backspace))

	for _, bound := range keys.Keys("delete-word-back") {
		fmt.Println(bound)
	}

}
Output:
ctrl+w
alt+backspace

func (*Map) Unbind

func (m *Map) Unbind(keys ...input.Chord) bool

Unbind removes a sequence and reports whether it was bound.

type Matcher added in v0.5.0

type Matcher struct {
	// contains filtered or unexported fields
}

Matcher reads one Map for one event owner.

It owns partial sequence progress, exact-prefix resolution and action dispatch. The zero value is ready to use. A Matcher belongs to one goroutine and must not be copied after first use.

func (*Matcher) Clear added in v0.5.0

func (m *Matcher) Clear()

Clear abandons the partially typed sequence and cancels its resolver.

func (*Matcher) Handle added in v0.5.0

func (m *Matcher) Handle(bindings *Map, key input.Key, do func(Action) bool) (matched, handled bool)

Handle advances the matcher with key and runs a completed action through do. Matched reports whether this key belonged to bindings. Handled reports the callback's answer, except that an unfinished prefix is always handled.

A prefix belongs to the map before it names an action. If an earlier ambiguous exact match is settled while this key is being read, this key is still matched independently and the result reports whether the new key belongs. A nil Matcher, Map or callback handles nothing.

func (*Matcher) Keys added in v0.5.0

func (m *Matcher) Keys() input.Keys

Keys returns a copy of the chords typed so far.

type Resolver added in v0.5.0

type Resolver func(wait time.Duration, resolve func()) (cancel func())

Resolver schedules resolve after wait and returns a function that cancels it.

It is the policy seam for a binding that is both an exact match and a prefix of a longer binding. An event owner's one-shot scheduler has this shape; keymap neither imports that owner nor owns a clock. Resolve must run on the same goroutine that calls Matcher.Handle. Calling it after cancellation is harmless.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL