mirror of
https://github.com/alexpasmantier/television.git
synced 2025-07-29 06:11:37 +00:00
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:
parent
1d6b996c83
commit
71b361161c
110
.config/bindings.tvb
Normal file
110
.config/bindings.tvb
Normal 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;
|
||||||
|
}
|
@ -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
2
Cargo.lock
generated
@ -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",
|
||||||
|
@ -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]]
|
||||||
|
@ -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"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
150
docs/02-Developers/03-binding-system.md
Normal file
150
docs/02-Developers/03-binding-system.md
Normal 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
|
185
television/bindings_parser/grammar.pest
Normal file
185
television/bindings_parser/grammar.pest
Normal 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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user