feat(input): port keyboard, keybinding and mouse input handling
Ports ZIG/src/input (except macOS keymaps) into ghostty-input: - key, key_mods (incl. RemapSet), the physical keycode table - key_encode: legacy, modifyOtherKeys and kitty keyboard protocol encoders, function key tables - Binding: all keybinding actions with their user-visible docs (for +list-actions), triggers, sets with sequences, chains, leader keys and key tables, and formatting back to config strings - command palette defaults, mouse, paste safety checks, kitty helpers, links, helpgen action docs Deviations (docs/rewrite/notes/ghostty-input.md): Mods.super is named super_; case folding uses char::to_lowercase plus Ghostty's special cases instead of uucode tables; chain parents are trigger paths instead of pointers; a Zig bug where chained leaves inside sequences corrupted the shared prefix when formatting is fixed (covered by a test). Integration left: building encode/paste Options from terminal modes, and implementing the EntryFormatter trait in ghostty-config. Test Plan: - cargo test -p ghostty-input (220 tests; 8 macOS-only skipped) - cargo clippy -p ghostty-input --all-targets -- -D warnings
This commit is contained in:
1 parent
3323c3f525
commit
3afe938b95
21 files changed
+13413
-1
No files matched your search
@@ -0,0 +1,590 @@
|
||||
//! Command palette entries (ZIG/src/input/command.zig).
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::binding::{
|
||||
Action, CloseTabMode, CopyToClipboard, GotoWindow, InspectorMode, NavigateSearch,
|
||||
SplitDirection, SplitFocusDirection, WriteScreen, WriteScreenAction, WriteScreenFormat,
|
||||
};
|
||||
|
||||
/// A command is a named binding action that can be executed from
|
||||
/// something like a command palette.
|
||||
///
|
||||
/// A command must be associated with a binding; all commands can be
|
||||
/// mapped to traditional `keybind` configurations. This restriction
|
||||
/// makes it so that there is nothing special about commands and likewise
|
||||
/// it makes it trivial and consistent to define custom commands.
|
||||
///
|
||||
/// For apprt implementers: a command palette doesn't have to make use
|
||||
/// of all the fields here. We try to provide as much information as
|
||||
/// possible to make it easier to implement a command palette in the way
|
||||
/// that makes the most sense for the application.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Command {
|
||||
pub action: Action,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn new(action: Action, title: &str, description: &str) -> Command {
|
||||
Command {
|
||||
action,
|
||||
title: title.to_string(),
|
||||
description: description.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn equal(&self, other: &Command) -> bool {
|
||||
if self.action.hash() != other.action.hash() {
|
||||
return false;
|
||||
}
|
||||
self.title == other.title && self.description == other.description
|
||||
}
|
||||
|
||||
/// Implements a comparison function for sorting. The sorting is defined
|
||||
/// by Ghostty to be what we prefer. If a caller wants some other
|
||||
/// sorting, they should do it themselves.
|
||||
pub fn less_than(lhs: &Command, rhs: &Command) -> bool {
|
||||
cmp_ignore_case(&lhs.title, &rhs.title) == std::cmp::Ordering::Less
|
||||
}
|
||||
|
||||
/// The `ghostty_command_s` equivalent: (action key name, action string,
|
||||
/// title, description).
|
||||
pub fn cval(&self) -> CommandC {
|
||||
CommandC {
|
||||
action_key: self.action.name().to_string(),
|
||||
action: self.action.to_string(),
|
||||
title: self.title.clone(),
|
||||
description: self.description.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Owned form of `Command.C`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CommandC {
|
||||
pub action_key: String,
|
||||
pub action: String,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
/// `std.ascii.orderIgnoreCase`
|
||||
fn cmp_ignore_case(a: &str, b: &str) -> std::cmp::Ordering {
|
||||
let a = a.bytes().map(|c| c.to_ascii_lowercase());
|
||||
let b = b.bytes().map(|c| c.to_ascii_lowercase());
|
||||
a.cmp(b)
|
||||
}
|
||||
|
||||
/// The default command palette entries, sorted by title (ignoring case).
|
||||
pub fn defaults() -> &'static [Command] {
|
||||
static DEFAULTS: OnceLock<Vec<Command>> = OnceLock::new();
|
||||
DEFAULTS.get_or_init(|| {
|
||||
let mut result = default_commands();
|
||||
result.sort_by(|a, b| cmp_ignore_case(&a.title, &b.title));
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
/// Defaults in C-compatible form.
|
||||
pub fn defaults_c() -> Vec<CommandC> {
|
||||
defaults().iter().map(Command::cval).collect()
|
||||
}
|
||||
|
||||
// The set of commands associated with each action key by default. Not all
|
||||
// actions should have commands. As a general guideline, an action should have
|
||||
// a command only if it is useful and reasonable to appear in a command
|
||||
// palette. Actions without commands (parameterized actions without obvious
|
||||
// values, ignore/unbind, etc.) are: csi, esc, cursor_key, set_font_size,
|
||||
// set_surface_title, set_tab_title, search, scroll_to_row,
|
||||
// scroll_page_fractional, scroll_page_lines, adjust_selection, jump_to_prompt,
|
||||
// write_scrollback_file, goto_tab, resize_split, activate_key_table,
|
||||
// activate_key_table_once, deactivate_key_table, deactivate_all_key_tables,
|
||||
// end_key_sequence, crash, toggle_command_palette, toggle_quick_terminal,
|
||||
// toggle_visibility, previous_tab, next_tab, last_tab, ignore, unbind.
|
||||
fn default_commands() -> Vec<Command> {
|
||||
vec![
|
||||
Command::new(
|
||||
Action::Reset,
|
||||
"Reset Terminal",
|
||||
"Reset the terminal to a clean state.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CopyToClipboard(CopyToClipboard::Mixed),
|
||||
"Copy to Clipboard",
|
||||
"Copy the selected text to the clipboard in both plain and styled formats.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CopyToClipboard(CopyToClipboard::Plain),
|
||||
"Copy Selection as Plain Text to Clipboard",
|
||||
"Copy the selected text as plain text to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CopyToClipboard(CopyToClipboard::Vt),
|
||||
"Copy Selection as ANSI Sequences to Clipboard",
|
||||
"Copy the selected text as ANSI escape sequences to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CopyToClipboard(CopyToClipboard::Html),
|
||||
"Copy Selection as HTML to Clipboard",
|
||||
"Copy the selected text as HTML to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CopyUrlToClipboard,
|
||||
"Copy URL to Clipboard",
|
||||
"Copy the URL under the cursor to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CopyTitleToClipboard,
|
||||
"Copy Terminal Title to Clipboard",
|
||||
"Copy the terminal title to the clipboard. If the terminal title is not set this has no effect.",
|
||||
),
|
||||
Command::new(
|
||||
Action::PasteFromClipboard,
|
||||
"Paste from Clipboard",
|
||||
"Paste the contents of the main clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::PasteFromSelection,
|
||||
"Paste from Selection",
|
||||
"Paste the contents of the selection clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::StartSearch,
|
||||
"Start Search",
|
||||
"Start a search if one isn't already active.",
|
||||
),
|
||||
Command::new(
|
||||
Action::SearchSelection,
|
||||
"Search Selection",
|
||||
"Start a search for the current text selection.",
|
||||
),
|
||||
Command::new(
|
||||
Action::EndSearch,
|
||||
"End Search",
|
||||
"End the current search if any and hide any GUI elements.",
|
||||
),
|
||||
Command::new(
|
||||
Action::NavigateSearch(NavigateSearch::Next),
|
||||
"Next Search Result",
|
||||
"Navigate to the next search result, if any.",
|
||||
),
|
||||
Command::new(
|
||||
Action::NavigateSearch(NavigateSearch::Previous),
|
||||
"Previous Search Result",
|
||||
"Navigate to the previous search result, if any.",
|
||||
),
|
||||
Command::new(
|
||||
Action::IncreaseFontSize(1.0),
|
||||
"Increase Font Size",
|
||||
"Increase the font size by 1 point.",
|
||||
),
|
||||
Command::new(
|
||||
Action::DecreaseFontSize(1.0),
|
||||
"Decrease Font Size",
|
||||
"Decrease the font size by 1 point.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ResetFontSize,
|
||||
"Reset Font Size",
|
||||
"Reset the font size to the default.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ClearScreen,
|
||||
"Clear Screen",
|
||||
"Clear the screen and scrollback.",
|
||||
),
|
||||
Command::new(
|
||||
Action::SelectAll,
|
||||
"Select All",
|
||||
"Select all text on the screen.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ScrollToTop,
|
||||
"Scroll to Top",
|
||||
"Scroll to the top of the screen.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ScrollToBottom,
|
||||
"Scroll to Bottom",
|
||||
"Scroll to the bottom of the screen.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ScrollToSelection,
|
||||
"Scroll to Selection",
|
||||
"Scroll to the selected text.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ScrollPageUp,
|
||||
"Scroll Page Up",
|
||||
"Scroll the screen up by a page.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ScrollPageDown,
|
||||
"Scroll Page Down",
|
||||
"Scroll the screen down by a page.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteScreenFile(WriteScreen::COPY),
|
||||
"Copy Screen to Temporary File and Copy Path",
|
||||
"Copy the screen contents to a temporary file and copy the path to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteScreenFile(WriteScreen::PASTE),
|
||||
"Copy Screen to Temporary File and Paste Path",
|
||||
"Copy the screen contents to a temporary file and paste the path to the file.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteScreenFile(WriteScreen::OPEN),
|
||||
"Copy Screen to Temporary File and Open",
|
||||
"Copy the screen contents to a temporary file and open it.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteScreenFile(WriteScreen {
|
||||
action: WriteScreenAction::Copy,
|
||||
emit: WriteScreenFormat::Html,
|
||||
}),
|
||||
"Copy Screen as HTML to Temporary File and Copy Path",
|
||||
"Copy the screen contents as HTML to a temporary file and copy the path to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteScreenFile(WriteScreen {
|
||||
action: WriteScreenAction::Paste,
|
||||
emit: WriteScreenFormat::Html,
|
||||
}),
|
||||
"Copy Screen as HTML to Temporary File and Paste Path",
|
||||
"Copy the screen contents as HTML to a temporary file and paste the path to the file.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteScreenFile(WriteScreen {
|
||||
action: WriteScreenAction::Open,
|
||||
emit: WriteScreenFormat::Html,
|
||||
}),
|
||||
"Copy Screen as HTML to Temporary File and Open",
|
||||
"Copy the screen contents as HTML to a temporary file and open it.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteScreenFile(WriteScreen {
|
||||
action: WriteScreenAction::Copy,
|
||||
emit: WriteScreenFormat::Vt,
|
||||
}),
|
||||
"Copy Screen as ANSI Sequences to Temporary File and Copy Path",
|
||||
"Copy the screen contents as ANSI escape sequences to a temporary file and copy the path to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteScreenFile(WriteScreen {
|
||||
action: WriteScreenAction::Paste,
|
||||
emit: WriteScreenFormat::Vt,
|
||||
}),
|
||||
"Copy Screen as ANSI Sequences to Temporary File and Paste Path",
|
||||
"Copy the screen contents as ANSI escape sequences to a temporary file and paste the path to the file.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteScreenFile(WriteScreen {
|
||||
action: WriteScreenAction::Open,
|
||||
emit: WriteScreenFormat::Vt,
|
||||
}),
|
||||
"Copy Screen as ANSI Sequences to Temporary File and Open",
|
||||
"Copy the screen contents as ANSI escape sequences to a temporary file and open it.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteSelectionFile(WriteScreen::COPY),
|
||||
"Copy Selection to Temporary File and Copy Path",
|
||||
"Copy the selection contents to a temporary file and copy the path to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteSelectionFile(WriteScreen::PASTE),
|
||||
"Copy Selection to Temporary File and Paste Path",
|
||||
"Copy the selection contents to a temporary file and paste the path to the file.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteSelectionFile(WriteScreen::OPEN),
|
||||
"Copy Selection to Temporary File and Open",
|
||||
"Copy the selection contents to a temporary file and open it.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteSelectionFile(WriteScreen {
|
||||
action: WriteScreenAction::Copy,
|
||||
emit: WriteScreenFormat::Html,
|
||||
}),
|
||||
"Copy Selection as HTML to Temporary File and Copy Path",
|
||||
"Copy the selection contents as HTML to a temporary file and copy the path to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteSelectionFile(WriteScreen {
|
||||
action: WriteScreenAction::Paste,
|
||||
emit: WriteScreenFormat::Html,
|
||||
}),
|
||||
"Copy Selection as HTML to Temporary File and Paste Path",
|
||||
"Copy the selection contents as HTML to a temporary file and paste the path to the file.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteSelectionFile(WriteScreen {
|
||||
action: WriteScreenAction::Open,
|
||||
emit: WriteScreenFormat::Html,
|
||||
}),
|
||||
"Copy Selection as HTML to Temporary File and Open",
|
||||
"Copy the selection contents as HTML to a temporary file and open it.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteSelectionFile(WriteScreen {
|
||||
action: WriteScreenAction::Copy,
|
||||
emit: WriteScreenFormat::Vt,
|
||||
}),
|
||||
"Copy Selection as ANSI Sequences to Temporary File and Copy Path",
|
||||
"Copy the selection contents as ANSI escape sequences to a temporary file and copy the path to the clipboard.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteSelectionFile(WriteScreen {
|
||||
action: WriteScreenAction::Paste,
|
||||
emit: WriteScreenFormat::Vt,
|
||||
}),
|
||||
"Copy Selection as ANSI Sequences to Temporary File and Paste Path",
|
||||
"Copy the selection contents as ANSI escape sequences to a temporary file and paste the path to the file.",
|
||||
),
|
||||
Command::new(
|
||||
Action::WriteSelectionFile(WriteScreen {
|
||||
action: WriteScreenAction::Open,
|
||||
emit: WriteScreenFormat::Vt,
|
||||
}),
|
||||
"Copy Selection as ANSI Sequences to Temporary File and Open",
|
||||
"Copy the selection contents as ANSI escape sequences to a temporary file and open it.",
|
||||
),
|
||||
Command::new(Action::NewWindow, "New Window", "Open a new window."),
|
||||
Command::new(Action::NewTab, "New Tab", "Open a new tab."),
|
||||
Command::new(
|
||||
Action::MoveTab(-1),
|
||||
"Move Tab Left",
|
||||
"Move the current tab to the left.",
|
||||
),
|
||||
Command::new(
|
||||
Action::MoveTab(1),
|
||||
"Move Tab Right",
|
||||
"Move the current tab to the right.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleTabOverview,
|
||||
"Toggle Tab Overview",
|
||||
"Toggle the tab overview.",
|
||||
),
|
||||
Command::new(
|
||||
Action::PromptSurfaceTitle,
|
||||
"Change Terminal Title…",
|
||||
"Prompt for a new title for the current terminal.",
|
||||
),
|
||||
Command::new(
|
||||
Action::PromptTabTitle,
|
||||
"Change Tab Title…",
|
||||
"Prompt for a new title for the current tab.",
|
||||
),
|
||||
Command::new(
|
||||
Action::NewSplit(SplitDirection::Left),
|
||||
"Split Left",
|
||||
"Split the terminal to the left.",
|
||||
),
|
||||
Command::new(
|
||||
Action::NewSplit(SplitDirection::Right),
|
||||
"Split Right",
|
||||
"Split the terminal to the right.",
|
||||
),
|
||||
Command::new(
|
||||
Action::NewSplit(SplitDirection::Up),
|
||||
"Split Up",
|
||||
"Split the terminal up.",
|
||||
),
|
||||
Command::new(
|
||||
Action::NewSplit(SplitDirection::Down),
|
||||
"Split Down",
|
||||
"Split the terminal down.",
|
||||
),
|
||||
Command::new(
|
||||
Action::GotoSplit(SplitFocusDirection::Previous),
|
||||
"Focus Split: Previous",
|
||||
"Focus the previous split, if any.",
|
||||
),
|
||||
Command::new(
|
||||
Action::GotoSplit(SplitFocusDirection::Next),
|
||||
"Focus Split: Next",
|
||||
"Focus the next split, if any.",
|
||||
),
|
||||
Command::new(
|
||||
Action::GotoSplit(SplitFocusDirection::Left),
|
||||
"Focus Split: Left",
|
||||
"Focus the split to the left, if it exists.",
|
||||
),
|
||||
Command::new(
|
||||
Action::GotoSplit(SplitFocusDirection::Right),
|
||||
"Focus Split: Right",
|
||||
"Focus the split to the right, if it exists.",
|
||||
),
|
||||
Command::new(
|
||||
Action::GotoSplit(SplitFocusDirection::Up),
|
||||
"Focus Split: Up",
|
||||
"Focus the split above, if it exists.",
|
||||
),
|
||||
Command::new(
|
||||
Action::GotoSplit(SplitFocusDirection::Down),
|
||||
"Focus Split: Down",
|
||||
"Focus the split below, if it exists.",
|
||||
),
|
||||
Command::new(
|
||||
Action::GotoWindow(GotoWindow::Previous),
|
||||
"Focus Window: Previous",
|
||||
"Focus the previous window, if any.",
|
||||
),
|
||||
Command::new(
|
||||
Action::GotoWindow(GotoWindow::Next),
|
||||
"Focus Window: Next",
|
||||
"Focus the next window, if any.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleSplitZoom,
|
||||
"Toggle Split Zoom",
|
||||
"Toggle the zoom state of the current split.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleReadonly,
|
||||
"Toggle Read-Only Mode",
|
||||
"Toggle read-only mode for the current surface.",
|
||||
),
|
||||
Command::new(
|
||||
Action::EqualizeSplits,
|
||||
"Equalize Splits",
|
||||
"Equalize the size of all splits.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ResetWindowSize,
|
||||
"Reset Window Size",
|
||||
"Reset the window size to the default.",
|
||||
),
|
||||
Command::new(
|
||||
Action::Inspector(InspectorMode::Toggle),
|
||||
"Toggle Inspector",
|
||||
"Toggle the inspector.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ShowGtkInspector,
|
||||
"Show the GTK Inspector",
|
||||
"Show the GTK inspector.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ShowOnScreenKeyboard,
|
||||
"Show On-Screen Keyboard",
|
||||
"Show the on-screen keyboard if present.",
|
||||
),
|
||||
Command::new(Action::OpenConfig, "Open Config", "Open the config file."),
|
||||
Command::new(
|
||||
Action::ReloadConfig,
|
||||
"Reload Config",
|
||||
"Reload the config file.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CloseSurface,
|
||||
"Close Terminal",
|
||||
"Close the current terminal.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CloseTab(CloseTabMode::This),
|
||||
"Close Tab",
|
||||
"Close the current tab.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CloseTab(CloseTabMode::Other),
|
||||
"Close Other Tabs",
|
||||
"Close all tabs in this window except the current one.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CloseTab(CloseTabMode::Right),
|
||||
"Close Tabs to the Right",
|
||||
"Close all tabs to the right of the current one.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CloseWindow,
|
||||
"Close Window",
|
||||
"Close the current window.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CloseAllWindows,
|
||||
"Close All Windows",
|
||||
"Close all windows.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleMaximize,
|
||||
"Toggle Maximize",
|
||||
"Toggle the maximized state of the current window.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleFullscreen,
|
||||
"Toggle Fullscreen",
|
||||
"Toggle the fullscreen state of the current window.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleWindowDecorations,
|
||||
"Toggle Window Decorations",
|
||||
"Toggle the window decorations.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleWindowFloatOnTop,
|
||||
"Toggle Float on Top",
|
||||
"Toggle the float on top state of the current window.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleSecureInput,
|
||||
"Toggle Secure Input",
|
||||
"Toggle secure input mode.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleMouseReporting,
|
||||
"Toggle Mouse Reporting",
|
||||
"Toggle whether mouse events are reported to terminal applications.",
|
||||
),
|
||||
Command::new(
|
||||
Action::ToggleBackgroundOpacity,
|
||||
"Toggle Background Opacity",
|
||||
"Toggle the background opacity of a window that started transparent.",
|
||||
),
|
||||
Command::new(
|
||||
Action::CheckForUpdates,
|
||||
"Check for Updates",
|
||||
"Check for updates to the application.",
|
||||
),
|
||||
Command::new(Action::Undo, "Undo", "Undo the last action."),
|
||||
Command::new(Action::Redo, "Redo", "Redo the last undone action."),
|
||||
Command::new(Action::Quit, "Quit", "Quit the application."),
|
||||
Command::new(
|
||||
Action::Text("👻".to_string()),
|
||||
"Ghostty",
|
||||
"Put a little Ghostty in your terminal.",
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn command_defaults() {
|
||||
// This just ensures that defaults is analyzed and works.
|
||||
assert_ne!(defaults().len(), 0);
|
||||
assert_eq!(defaults().len(), defaults_c().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_defaults_sorted_and_roundtrip() {
|
||||
let d = defaults();
|
||||
for w in d.windows(2) {
|
||||
assert_ne!(
|
||||
cmp_ignore_case(&w[0].title, &w[1].title),
|
||||
std::cmp::Ordering::Greater
|
||||
);
|
||||
}
|
||||
// Round trip every default action through its string form.
|
||||
// (`text` is stored raw and escaped on format, so it is excluded.)
|
||||
for c in d.iter().filter(|c| !matches!(c.action, Action::Text(_))) {
|
||||
let s = c.action.to_string();
|
||||
assert_eq!(Action::parse(&s).expect("parse"), c.action, "{s}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user