From 71b361161cd5c8f035df699652f4ab89b8d633bb Mon Sep 17 00:00:00 2001 From: lalvarezt Date: Sun, 20 Jul 2025 11:43:11 +0200 Subject: [PATCH] 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 --- .config/bindings.tvb | 110 ++++++++ .config/config.toml | 71 +----- Cargo.lock | 2 + Cargo.toml | 4 +- docs/01-Users/04-keybindings.md | 318 ++++++++++++++++++++++-- docs/02-Developers/03-binding-system.md | 150 +++++++++++ television/bindings_parser/grammar.pest | 185 ++++++++++++++ 7 files changed, 761 insertions(+), 79 deletions(-) create mode 100644 .config/bindings.tvb create mode 100644 docs/02-Developers/03-binding-system.md create mode 100644 television/bindings_parser/grammar.pest diff --git a/.config/bindings.tvb b/.config/bindings.tvb new file mode 100644 index 0000000..bc28beb --- /dev/null +++ b/.config/bindings.tvb @@ -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; +} diff --git a/.config/config.toml b/.config/config.toml index 341db38..e4505a5 100644 --- a/.config/config.toml +++ b/.config/config.toml @@ -125,69 +125,22 @@ sort_alphabetically = true # Keybindings # ---------------------------------------------------------------------------- # -# HARDCODED KEYBINDINGS (cannot be changed via config): -# Input field actions (always active): -# Backspace - Delete previous character -# Ctrl+w - Delete previous word -# 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 +# NEW BINDING SYSTEM: +# ------------------------- +# Television now uses a new binding system defined in: +# ~/.config/television/bindings.tvb, or similar depending on the OS # -# 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] -# Application control -# ------------------ -# Quit the application +# Legacy TOML format - consider migrating to bindings.tvb 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 # ---------------------------------------------------------------------------- # diff --git a/Cargo.lock b/Cargo.lock index ccac82d..04c53c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2000,6 +2000,8 @@ dependencies = [ "lazy-regex", "nucleo", "parking_lot", + "pest", + "pest_derive", "portable-pty", "ratatui", "rustc-hash", diff --git a/Cargo.toml b/Cargo.toml index dede280..d42d4de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,8 @@ serde_json = "1.0.140" colored = "3.0.0" serde_with = "3.13.0" which = "8.0.0" +pest = "2.8" +pest_derive = "2.8" # target specific dependencies @@ -74,7 +76,7 @@ vt100 = "0.15" [build-dependencies] clap = { version = "4.5", features = ["derive", "cargo"] } -clap_mangen = "0.2.26" +clap_mangen = "0.2" [[bin]] diff --git a/docs/01-Users/04-keybindings.md b/docs/01-Users/04-keybindings.md index 0f23d07..fd74418 100644 --- a/docs/01-Users/04-keybindings.md +++ b/docs/01-Users/04-keybindings.md @@ -1,5 +1,12 @@ # 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 are as follows: @@ -7,35 +14,308 @@ Default keybindings are as follows: | Key | Description | | :---------------------------------------------------------------------------------------------------------------------------: | -------------------------------------------------- | | / or Ctrl + p / n or Ctrl + k / j | Navigate through the list of entries | -| Ctrl + u / d | Scroll the preview pane up / down | +| PageUp / PageDown or Mouse Wheel ↑ / Mouse Wheel ↓ | Scroll the preview pane up / down | | Enter | Select the current entry | | Tab / BackTab | Toggle selection and move to next / previous entry | | Ctrl + y | Copy the selected entry to the clipboard | -| Ctrl + r | Toggle remote control mode | -| Ctrl + s | Toggle send to channel mode | -| Ctrl + g | Toggle the help panel | +| Ctrl + t | Toggle remote control mode | +| Ctrl + h | Toggle the help panel | | Ctrl + o | Toggle the preview panel | | Esc | Quit the application | 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 **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" - -``` diff --git a/docs/02-Developers/03-binding-system.md b/docs/02-Developers/03-binding-system.md new file mode 100644 index 0000000..05de248 --- /dev/null +++ b/docs/02-Developers/03-binding-system.md @@ -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
--bindings-file FILENAME"] + D2["Custom Directory
--bindings-dir DIRNAME"] + D3["Default Directory
$CONFIG/bindings.tvb
$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 diff --git a/television/bindings_parser/grammar.pest b/television/bindings_parser/grammar.pest new file mode 100644 index 0000000..d575029 --- /dev/null +++ b/television/bindings_parser/grammar.pest @@ -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 +}