feat: introduction of a new binding system to better handle key/events/actions

the system is backwards compatible with the existing TOML keybinding
implementation allowing for slow migration to use the new features
This commit is contained in:
lalvarezt 2025-07-20 11:43:11 +02:00
parent 1d6b996c83
commit 71b361161c
7 changed files with 761 additions and 79 deletions

110
.config/bindings.tvb Normal file
View File

@ -0,0 +1,110 @@
// Television Binding System Reference
// =====================================
//
// SYNTAX:
// key => action; // Basic key binding
// key => { action1; action2; }; // Action block (multiple actions, executed sequentially)
// @event => action; // Event binding
// channel "name" { key => action; } // Channel-specific binding, overrides global
//
// KEY CODES:
// Basic: up, down, left, right, enter, esc, tab, space, backspace, delete, home, end, pageup, pagedown
// Function: f1, f2, f3, ..., f12
// Modified: ctrl-a, ctrl-c, alt-enter, etc.
// Mouse: mouse-scroll-up, mouse-scroll-down
// Special: backtab (Shift+Tab)
// Characters: a-z, 0-9, symbols (!@#$%^&*()_+-=[]{}|;':"<>,.?/~`)
//
// EVENTS:
// @start - Application startup
// @load - Channel data loaded
// @result - Search filtering complete
// @one - Exactly one search match
// @zero - No search matches
// @selection-change - Multi-selection changed
//
// ACTIONS:
// Navigation: select_next_entry, select_prev_entry, select_next_page, select_prev_page
// History: select_next_history, select_prev_history
// Selection: confirm_selection, select_and_exit, toggle_selection_down, toggle_selection_up
// Input: go_to_input_start, go_to_input_end, go_to_next_char, go_to_prev_char
// Edit: delete_line, delete_next_char, delete_prev_char, delete_prev_word
// Preview: scroll_preview_up, scroll_preview_down, scroll_preview_half_page_up, scroll_preview_half_page_down
// Control: quit, suspend, resume, reload_source
// Toggle: toggle_preview, toggle_help, toggle_status_bar, toggle_remote_control
// Data: copy_entry_to_clipboard, cycle_sources
// Special: nil (no-op)
//
// For more information, see: https://alexpasmantier.github.io/television/docs/Users/04-keybindings.html
bindings {
// === APPLICATION CONTROL ===
// Quit the application
esc => quit;
ctrl-c => quit;
// === NAVIGATION AND SELECTION ===
// Scrolling through entries
down => select_next_entry;
ctrl-n => select_next_entry;
ctrl-j => select_next_entry;
up => select_prev_entry;
ctrl-p => select_prev_entry;
ctrl-k => select_prev_entry;
// Page navigation (uncomment to enable)
// pagedown => select_next_page;
// pageup => select_prev_page;
// === HISTORY NAVIGATION ===
// Navigate through search query history
ctrl-up => select_prev_history;
ctrl-down => select_next_history;
// === MULTI-SELECTION ===
// Add entry to selection and move to the next entry
tab => toggle_selection_down;
// Add entry to selection and move to the previous entry
backtab => toggle_selection_up;
// Confirm selection
enter => confirm_selection;
// === PREVIEW PANEL CONTROL ===
// Scrolling the preview pane
pagedown => scroll_preview_half_page_down;
mouse-scroll-down => scroll_preview_half_page_down;
pageup => scroll_preview_half_page_up;
mouse-scroll-up => scroll_preview_half_page_up;
// === DATA OPERATIONS ===
// Copy the selected entry to the clipboard
ctrl-y => copy_entry_to_clipboard;
// Reload the current source
ctrl-r => reload_source;
// Cycle through the available sources for the current channel
ctrl-s => cycle_sources;
// === UI FEATURES ===
// Toggle features
ctrl-t => toggle_remote_control;
ctrl-o => toggle_preview;
ctrl-h => toggle_help;
f12 => toggle_status_bar;
// === INPUT FIELD CONTROLS ===
// Text editing
backspace => delete_prev_char;
ctrl-w => delete_prev_word;
ctrl-u => delete_line;
delete => delete_next_char;
// Cursor movement
left => go_to_prev_char;
right => go_to_next_char;
home => go_to_input_start;
ctrl-a => go_to_input_start;
end => go_to_input_end;
ctrl-e => go_to_input_end;
}

View File

@ -125,69 +125,22 @@ sort_alphabetically = true
# Keybindings # Keybindings
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# #
# HARDCODED KEYBINDINGS (cannot be changed via config): # NEW BINDING SYSTEM:
# Input field actions (always active): # -------------------------
# Backspace - Delete previous character # Television now uses a new binding system defined in:
# Ctrl+w - Delete previous word # ~/.config/television/bindings.tvb, or similar depending on the OS
# Ctrl+u - Delete entire line
# Delete - Delete next character
# Left/Right - Move cursor left/right
# Home / Ctrl+a - Go to input start
# End / Ctrl+e - Go to input end
# #
# CONFIGURABLE KEYBINDINGS (can be customized below): # For full documentation, see:
# -------------------------------------------------- # https://alexpasmantier.github.io/television/docs/Users/04-keybindings.html
#
# LEGACY TOML KEYBINDINGS (deprecated but still supported):
# ---------------------------------------------------------
# The following \[keybindings] section is the legacy format.
# It is recommended to migrate to the modern bindings.tvb format.
[keybindings] [keybindings]
# Application control # Legacy TOML format - consider migrating to bindings.tvb
# ------------------
# Quit the application
quit = ["esc", "ctrl-c"] quit = ["esc", "ctrl-c"]
# Navigation and selection
# -----------------------
# Scrolling through entries
select_next_entry = ["down", "ctrl-n", "ctrl-j"]
select_prev_entry = ["up", "ctrl-p", "ctrl-k"]
#select_next_page = "pagedown"
#select_prev_page = "pageup"
# History navigation
# -----------------
# Navigate through search query history
select_prev_history = "ctrl-up"
select_next_history = "ctrl-down"
# Multi-selection
# --------------
# Add entry to selection and move to the next entry
toggle_selection_down = "tab"
# Add entry to selection and move to the previous entry
toggle_selection_up = "backtab"
# Confirm selection
confirm_selection = "enter"
# Preview panel control
# --------------------
# Scrolling the preview pane
scroll_preview_half_page_down = ["pagedown", "mousescrolldown"]
scroll_preview_half_page_up = ["pageup", "mousescrollup"]
# Data operations
# --------------
# Copy the selected entry to the clipboard
copy_entry_to_clipboard = "ctrl-y"
# Reload the current source
reload_source = "ctrl-r"
# Cycle through the available sources for the current channel
cycle_sources = "ctrl-s"
# UI Features
# ----------
toggle_remote_control = "ctrl-t"
toggle_preview = "ctrl-o"
toggle_help = "ctrl-h"
toggle_status_bar = "f12"
# Shell integration # Shell integration
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# #

2
Cargo.lock generated
View File

@ -2000,6 +2000,8 @@ dependencies = [
"lazy-regex", "lazy-regex",
"nucleo", "nucleo",
"parking_lot", "parking_lot",
"pest",
"pest_derive",
"portable-pty", "portable-pty",
"ratatui", "ratatui",
"rustc-hash", "rustc-hash",

View File

@ -51,6 +51,8 @@ serde_json = "1.0.140"
colored = "3.0.0" colored = "3.0.0"
serde_with = "3.13.0" serde_with = "3.13.0"
which = "8.0.0" which = "8.0.0"
pest = "2.8"
pest_derive = "2.8"
# target specific dependencies # target specific dependencies
@ -74,7 +76,7 @@ vt100 = "0.15"
[build-dependencies] [build-dependencies]
clap = { version = "4.5", features = ["derive", "cargo"] } clap = { version = "4.5", features = ["derive", "cargo"] }
clap_mangen = "0.2.26" clap_mangen = "0.2"
[[bin]] [[bin]]

View File

@ -1,5 +1,12 @@
# Keybindings # Keybindings
Television supports two keybinding configuration formats:
1. **Binding syntax** - The modern, recommended format (key => action;)
2. **TOML format** - The legacy format (action = ["key1", "key2"])
Both formats are fully supported and can be used together. The binding syntax is recommended for new configurations due to its enhanced features like action blocks, event bindings, and channel-specific overrides.
## Default Keybindings ## Default Keybindings
Default keybindings are as follows: Default keybindings are as follows:
@ -7,35 +14,308 @@ Default keybindings are as follows:
| Key | Description | | Key | Description |
| :---------------------------------------------------------------------------------------------------------------------------: | -------------------------------------------------- | | :---------------------------------------------------------------------------------------------------------------------------: | -------------------------------------------------- |
| <kbd></kbd> / <kbd></kbd> or <kbd>Ctrl</kbd> + <kbd>p</kbd> / <kbd>n</kbd> or <kbd>Ctrl</kbd> + <kbd>k</kbd> / <kbd>j</kbd> | Navigate through the list of entries | | <kbd></kbd> / <kbd></kbd> or <kbd>Ctrl</kbd> + <kbd>p</kbd> / <kbd>n</kbd> or <kbd>Ctrl</kbd> + <kbd>k</kbd> / <kbd>j</kbd> | Navigate through the list of entries |
| <kbd>Ctrl</kbd> + <kbd>u</kbd> / <kbd>d</kbd> | Scroll the preview pane up / down | | <kbd>PageUp</kbd> / <kbd>PageDown</kbd> or <kbd>Mouse Wheel ↑</kbd> / <kbd>Mouse Wheel ↓</kbd> | Scroll the preview pane up / down |
| <kbd>Enter</kbd> | Select the current entry | | <kbd>Enter</kbd> | Select the current entry |
| <kbd>Tab</kbd> / <kbd>BackTab</kbd> | Toggle selection and move to next / previous entry | | <kbd>Tab</kbd> / <kbd>BackTab</kbd> | Toggle selection and move to next / previous entry |
| <kbd>Ctrl</kbd> + <kbd>y</kbd> | Copy the selected entry to the clipboard | | <kbd>Ctrl</kbd> + <kbd>y</kbd> | Copy the selected entry to the clipboard |
| <kbd>Ctrl</kbd> + <kbd>r</kbd> | Toggle remote control mode | | <kbd>Ctrl</kbd> + <kbd>t</kbd> | Toggle remote control mode |
| <kbd>Ctrl</kbd> + <kbd>s</kbd> | Toggle send to channel mode | | <kbd>Ctrl</kbd> + <kbd>h</kbd> | Toggle the help panel |
| <kbd>Ctrl</kbd> + <kbd>g</kbd> | Toggle the help panel |
| <kbd>Ctrl</kbd> + <kbd>o</kbd> | Toggle the preview panel | | <kbd>Ctrl</kbd> + <kbd>o</kbd> | Toggle the preview panel |
| <kbd>Esc</kbd> | Quit the application | | <kbd>Esc</kbd> | Quit the application |
These keybindings are all configurable via tv's configuration file (see [Configuration](./configuration)). These keybindings are all configurable via tv's configuration file (see [Configuration](./configuration)).
# Keybindings Guide ## Configuration Formats
Following this are some configuration presets you can use for your bindings. Most of these will probably match an existing program. ### TOML Format
The traditional TOML format maps actions to lists of keys:
```toml
[keybindings]
quit = ["esc", "ctrl-c"]
select_next_entry = ["down", "ctrl-n", "ctrl-j"]
select_prev_entry = ["up", "ctrl-p", "ctrl-k"]
toggle_selection_down = "tab"
toggle_selection_up = "backtab"
confirm_selection = "enter"
toggle_remote_control = "ctrl-t"
toggle_preview = "ctrl-o"
toggle_help = "ctrl-h"
toggle_status_bar = "f12"
```
### Binding Syntax
The new binding syntax is an alternative way that uses that follows a key/event to action mapping:
```javascript
bindings {
// === APPLICATION CONTROL ===
esc => quit;
ctrl-c => quit;
// === NAVIGATION AND SELECTION ===
down => select_next_entry;
ctrl-n => select_next_entry;
ctrl-j => select_next_entry;
up => select_prev_entry;
ctrl-p => select_prev_entry;
ctrl-k => select_prev_entry;
// === SELECTION ===
tab => toggle_selection_down;
backtab => toggle_selection_up;
enter => confirm_selection;
// === UI FEATURES ===
ctrl-t => toggle_remote_control;
ctrl-o => toggle_preview;
ctrl-h => toggle_help;
f12 => toggle_status_bar;
}
```
#### Comments
Full comment support with both single-line and multi-line comments:
```javascript
bindings {
// Single-line comments
ctrl-o => toggle_preview; // inline comments are allowed too
/* Multi-line comments
are also supported */
enter => confirm_selection;
}
```
#### Action Blocks
Execute multiple actions in sequence with a single key press:
```javascript
bindings {
// Move to next entry and select the next two items
f5 => {
select_next_entry;
toggle_selection_down;
toggle_selection_down;
};
}
```
> **Note**: Action blocks execute each action in order, one after another. This guarantees consistent behavior and prevents actions from interfering with each other.
#### Event Bindings
Respond to application events automatically:
```javascript
bindings {
// Triggered when input stream is complete
@load => reload_source;
// Triggered when filtering is complete
@result => toggle_status_bar;
// Triggered when multi-selection changes
@selection-change => copy_entry_to_clipboard;
// Triggered when there's only one match
@one => select_and_exit;
// Triggered when there's no match
@zero => quit;
}
```
## Key Specifications
### Basic Keys
- **Movement**: `up`, `down`, `left`, `right`
- **Text**: `enter`, `esc`, `tab`, `space`, `backspace`, `delete`
- **Navigation**: `home`, `end`, `pageup`, `pagedown`, `insert`
- **Function**: `f1` through `f12`
### Character Keys
- **Letters**: `a` through `z`
- **Numbers**: `0` through `9`
- **Symbols**
### Modified Keys
- **Ctrl**: `ctrl-a`, `ctrl-space`, `ctrl-enter`, etc.
- **Alt**: `alt-a`, `alt-space`, `alt-enter`, etc.
### Special Keys
- `backtab` (Shift+Tab)
- `mouse-scroll-up`, `mouse-scroll-down`
## Available Actions
### Navigation
- `select_next_entry` - Move to next entry
- `select_prev_entry` - Move to previous entry
- `select_next_page` - Move to next page
- `select_prev_page` - Move to previous page
- `select_next_history` - Navigate forward in search history
- `select_prev_history` - Navigate backward in search history
### Selection
- `confirm_selection` - Confirm current selection
- `select_and_exit` - Select the entry currently under cursor and exit
- `toggle_selection_down` - Toggle selection and move down
- `toggle_selection_up` - Toggle selection and move up
### Input Navigation
- `go_to_input_start` - Move cursor to start of input
- `go_to_input_end` - Move cursor to end of input
- `go_to_next_char` - Move cursor to next character
- `go_to_prev_char` - Move cursor to previous character
### Input Editing
- `delete_line` - Delete the current line from input buffer
- `delete_next_char` - Delete character after cursor
- `delete_prev_char` - Delete character before cursor
- `delete_prev_word` - Delete previous word from input buffer
### Preview Control
- `scroll_preview_up` - Scroll preview pane up
- `scroll_preview_down` - Scroll preview pane down
- `scroll_preview_half_page_up` - Scroll preview half page up
- `scroll_preview_half_page_down` - Scroll preview half page down
### Application Control
- `quit` - Exit the application
- `suspend` - Suspend the application
- `resume` - Resume the application
- `reload_source` - Reload the current data source
### Feature Toggles
- `toggle_preview` - Toggle the preview pane
- `toggle_help` - Toggle the help panel
- `toggle_status_bar` - Toggle the status bar
- `toggle_remote_control` - Toggle remote control mode
### Data Operations
- `copy_entry_to_clipboard` - Copy selected entry to clipboard
- `cycle_sources` - Cycle through available data sources
### Special Actions
- `nil` - No operation/unbind action
## Event System
Events allow you to automatically trigger actions when certain application events occur.
### Available Events
- `@start` - Triggered once when Television starts up
- `@load` - Triggered when a channel finishes loading data
- `@result` - Triggered when search filtering completes
- `@one` - Triggered when search has exactly one match
- `@zero` - Triggered when search has no matches
- `@selection-change` - Triggered when multi-selection changes
## Migration Guide
### From TOML to New Syntax
**TOML format:**
```toml
[keybindings]
quit = ["esc", "ctrl-c"]
select_next_entry = ["down", "ctrl-j"]
toggle_preview = "ctrl-o"
```
**New syntax equivalent:**
```javascript
bindings {
esc => quit;
ctrl-c => quit;
down => select_next_entry;
ctrl-j => select_next_entry;
ctrl-o => toggle_preview;
}
```
### Using Both Formats
You can use both formats together by creating a `bindings.tvb` file alongside your existing `config.toml`:
**Create**: `~/.config/television/bindings.tvb`
```javascript
bindings {
up => select_next_entry;
down => select_prev_entry;
ctrl-c => quit;
enter => confirm_selection;
}
```
**Keep your existing**: `~/.config/television/config.toml`
```toml
[ui]
theme = "default"
[keybindings]
copy_entry_to_clipboard = "ctrl-y"
```
Television automatically loads and merges both formats. The binding syntax takes precedence over TOML for conflicting bindings.
## Channel-Specific Bindings
The binding syntax supports channel-specific overrides that only apply when using specific channels:
```javascript
bindings {
// Global bindings
ctrl-c => quit;
// Only active when using the "files" channel
channel "files" {
f8 => toggle_preview;
f7 => copy_entry_to_clipboard;
}
// Pattern-based channel binding
for_channels("dirs") {
f1 => toggle_help;
f5 => reload_source;
}
}
```
## Example configuration used as reference
For a complete, up-to-date reference of all available keys, events, and actions, see the default configuration file:
- **In repository**: `.config/bindings.tvb`
- **Documentation header**: Contains comprehensive syntax guide and all available options
This file serves as the authoritative reference and is kept in sync with the latest features.
:::note :::note
**This list is maintained by the community, so feel free to contribute your own ideas too! 😊** **This list is maintained by the community, so feel free to contribute your own ideas too! 😊**
::: :::
## Emacs
```toml
# Television already has some pretty Emacsy keybinds.
# This just makes them "Emacsier".
[keybindings]
scroll_preview_half_page_down = "alt-v"
scroll_preview_half_page_up = "ctrl-v"
toggle_remote_control = "alt-x" # Like execute-extended-command
toggle_help = "ctrl-h"
```

View File

@ -0,0 +1,150 @@
# Binding System Architecture
## Overview
The Television binding system maps keys and events to actions using a dual-format parser (Television syntax + TOML) built on Pest. The system supports channel-specific bindings, action blocks, event bindings, and multi-source configuration loading.
```mermaid
graph TD
A[Configuration Input] --> B{Parser Selection}
B -->|Binding System| C1
B -->|TOML| D1
subgraph primary[" "]
C1[parse_bindings]
C2[Pest Grammar Processing]
C3[Television AST Generation]
C1 --> C2 --> C3
end
subgraph secondary[" "]
D1[parse_toml_bindings]
D2[TOML Grammar Processing]
D3[TOML to AST Conversion]
D1 --> D2 --> D3
end
C3 --> E[Unified AST]
D3 --> E
E --> F[Converter Layer]
F --> G[Television Bindings]
G --> H[Runtime Execution]
B -->|Both Fail| I[Parse Error]
```
## Grammar Definition
### Core Grammar Structure
```pest
// Dual format support
file = { SOI ~ (bindings | toml_bindings)? ~ EOI }
// Television syntax
bindings = { "bindings" ~ "{" ~ binding* ~ "}" }
binding = {
key_binding | event_binding | channel_binding |
for_channels_binding | comment
}
// Binding types
key_binding = { key_sequence ~ "=>" ~ action_target ~ ";" }
event_binding = { "@" ~ event_name ~ "=>" ~ action_target ~ ";" }
channel_binding = { "channel" ~ string_literal ~ "{" ~ binding* ~ "}" }
for_channels_binding = { "for_channels" ~ "(" ~ string_literal ~ ")" ~ "{" ~ binding* ~ "}" }
// Action targets
action_target = { action_block | action_array | action_name }
action_block = { "{" ~ action_name ~ (";" ~ action_name)* ~ "}" }
action_array = { "[" ~ action_name ~ ("," ~ action_name)* ~ "]" }
// TOML format support
toml_bindings = { toml_section+ }
toml_section = { "[" ~ section_name ~ "]" ~ toml_entry* }
```
### Key Grammar Components
```pest
// Key specifications
key_sequence = { (modifier ~ "-")* ~ key_name }
modifier = { "ctrl" | "alt" | "shift" }
key_name = { named_key | function_key | character_key | mouse_event }
// Named keys
named_key = {
"enter" | "esc" | "tab" | "space" | "backspace" | "delete" |
"home" | "end" | "pageup" | "pagedown" | "insert" |
"up" | "down" | "left" | "right" | "backtab"
}
// Function keys and mouse events
function_key = { "f" ~ number }
mouse_event = { "mouse-scroll-up" | "mouse-scroll-down" }
// Event specifications
event_name = {
"start" | "load" | "result" | "one" | "zero" |
"selection-change" | "resize"
}
```
## Hierarchical Configuration Merging
```mermaid
graph TB
A["Embedded Defaults"] --> B["User Config"]
B --> C["Channel Configs"]
subgraph ABS["Multi-Source Loading"]
D1["Explicit Files<br/>--bindings-file FILENAME"]
D2["Custom Directory<br/>--bindings-dir DIRNAME"]
D3["Default Directory<br/>$CONFIG/bindings.tvb<br/>$CONFIG/keybindings.toml"]
end
C --> D1
C --> D2
C --> D3
E["Merge Bindings"] --> F["Final Configuration"]
D1 --> E
D2 --> E
D3 --> E
```
## Action Block State Management
```mermaid
stateDiagram-v2
[*] --> Received: ActionBlock arrives
Received --> HashCheck: Calculate hash
HashCheck --> Processed: Hash not found
HashCheck --> Skipped: Hash exists
Processed --> Queued: Mark hash, queue actions
Queued --> [*]: Actions sent to channel
Skipped --> [*]: Duplicate prevention
note right of HashCheck
Hash-based deduplication
end note
note left of Queued
Individual actions sent
via action_tx channel
end note
note left of Processed
Action blocks processed
sequentially to maintain
execution order
end note
```
## Grammar Extensions (not Implemented)
- 🔄 Parameterized action execution
- 🔄 Advanced pattern matching for channels
- 🔄 Conditional binding logic
- 🔄 Runtime binding modification
- 🔄 LSP support using AST span information

View File

@ -0,0 +1,185 @@
// Core Rules
WHITESPACE = _{ " " | "\t" | "\r" | "\n" }
NEWLINE = _{ "\n" | "\r\n" }
// Comments
COMMENT = _{ line_comment | block_comment | toml_comment }
line_comment = _{ "//" ~ (!"\n" ~ ANY)* }
block_comment = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" }
toml_comment = _{ "#" ~ (!"\n" ~ ANY)* }
// Basic atomic tokens
identifier = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
string_literal = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }
number = @{ ASCII_DIGIT+ }
key_string = @{ (!"\"" ~ (ASCII_ALPHANUMERIC | "-" | " " | "_" | "[" | "]" | ";" | ":" | "'" | "`" | "~" | "!" | "@" | "#" | "$" | "%" | "^" | "&" | "*" | "(" | ")" | "+" | "=" | "{" | "}" | "|" | "\\" | "<" | ">" | "," | "." | "?" | "/"))+ }
// Entry points - Main grammar rules
bindings = { SOI ~ "bindings" ~ "{" ~ binding* ~ "}" ~ EOI }
standalone_key = { SOI ~ key ~ EOI }
// TOML compatibility
toml_bindings = { SOI ~ (toml_binding | toml_section | toml_comment | NEWLINE)* ~ EOI }
toml_section = { "[" ~ "keybindings" ~ "]" ~ NEWLINE? ~ (toml_binding | toml_comment | NEWLINE)* }
toml_binding = { action_name ~ "=" ~ (toml_key_array | toml_string_key) ~ NEWLINE? }
toml_key_array = { "[" ~ toml_string_key ~ ("," ~ toml_string_key)* ~ ","? ~ "]" }
toml_string_key = { "\"" ~ key_string ~ "\"" }
// Binding types
binding = { key_binding | event_binding | channel_binding | for_channels_binding }
key_binding = { key_sequence ~ "=>" ~ action_target ~ ";" }
event_binding = { "@" ~ event_name ~ "=>" ~ action_target ~ ";" }
channel_binding = { "channel" ~ channel_specifier ~ "{" ~ binding_list ~ "}" }
for_channels_binding = { "for_channels" ~ "(" ~ channel_pattern_list ~ ")" ~ "{" ~ binding_list ~ "}" }
// Channel-specific
channel_specifier = { string_literal }
channel_pattern_list = { channel_pattern ~ ("," ~ channel_pattern)* }
channel_pattern = { string_literal }
binding_list = { (key_binding | event_binding)* }
// Key specifications
key_sequence = { key ~ ("+" ~ key)* }
key = { modifier_key | named_key | character_key }
// Modifier keys
modifier_key = {
("ctrl-" ~ (named_key | character_key))
| ("alt-" ~ (named_key | character_key))
}
// Named keys
named_key = {
// Most common keys first
"enter"
| "esc"
| "tab"
| "space"
| "backspace"
| "delete"
| // Arrow keys
"up"
| "down"
| "left"
| "right"
| // Navigation keys
"home"
| "end"
| "pageup"
| "pagedown"
| "insert"
| "backtab"
| // Function keys (pattern)
("f" ~ number)
| // Mouse events
"mouse-scroll-up"
| "mouse-scroll-down"
}
// Character keys
character_key = { ASCII_ALPHA | ASCII_DIGIT | symbol }
// Symbol definition
symbol = {
"!"
| "$"
| "%"
| "^"
| "&"
| "*"
| "("
| ")"
| "_"
| "+"
| "-"
| "="
| "["
| "]"
| "{"
| "}"
| "|"
| "\\"
| ";"
| ":"
| "'"
| "\""
| "<"
| ">"
| ","
| "."
| "?"
| "/"
| "~"
| "`"
}
// Event names - Ordered by usage frequency
event_name = {
"start"
| "load"
| "result"
| "selection-change"
| "resize"
| "one"
| "zero"
}
// Action specifications - Ordered by complexity
action_target = { single_action | action_block | action_array }
single_action = { action_name ~ action_params? }
action_block = { "{" ~ action_statement* ~ "}" }
action_array = { "[" ~ action_list ~ "]" }
action_statement = { action_name ~ action_params? ~ ";"? }
action_list = { action_name ~ ("," ~ action_name)* ~ ","? }
action_params = { "(" ~ string_literal ~ ")" }
// Action names
action_name = {
// High frequency actions first
"quit"
| "nil"
| "confirm_selection"
| "select_and_exit"
| // Navigation actions - grouped by prefix
"select_next_entry"
| "select_next_history"
| "select_next_page"
| "select_prev_entry"
| "select_prev_history"
| "select_prev_page"
| // Toggle actions - grouped by prefix
"toggle_help"
| "toggle_preview"
| "toggle_remote_control"
| "toggle_selection_down"
| "toggle_selection_up"
| "toggle_status_bar"
| // Scroll actions - grouped by prefix
"scroll_preview_down"
| "scroll_preview_half_page_down"
| "scroll_preview_half_page_up"
| "scroll_preview_up"
| // Input actions
"delete_line"
| "delete_next_char"
| "delete_prev_char"
| "delete_prev_word"
| "go_to_input_end"
| "go_to_input_start"
| "go_to_next_char"
| "go_to_prev_char"
| // System actions
"resume"
| "suspend"
| // Channel actions
"copy_entry_to_clipboard"
| "cycle_sources"
| "reload_source"
| // Parameterized actions
"execute"
| "switch_to_channel"
| // Generic extensibility
identifier
}