diff options
author | Aram Drevekenin <aram@poor.dev> | 2022-10-05 07:44:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-05 07:44:00 +0200 |
commit | 79bf6ab868cbdab1f9a3827c9b70198f54548b44 (patch) | |
tree | 2d6fc4c1d8a79ebd727a1a5f8b6406617dd0de55 /zellij-utils/src/input | |
parent | 917e9b2ff0f583183c0155060d243afd295770b9 (diff) |
feat(config): switch to kdl (#1759)
* chore(config): default kdl keybindings config
* tests
* work
* refactor(config): move stuff around
* work
* tab merge layout
* work
* work
* layouts working
* work
* layout tests
* work
* work
* feat(parsing): kdl layouts without config
* refactor(kdl): move stuff around
* work
* tests(layout): add cases and fix bugs
* work
* fix(kdl): various bugs
* chore(layouts): move all layouts to kdl
* feat(kdl): shared keybidns
* fix(layout): do not count fixed panes toward percentile
* fix(keybinds): missing keybinds and actions
* fix(config): adjust default tips
* refactor(config): move stuff around
* fix(tests): make e2e tests pass
* fix(kdl): add verbose parsing errors
* fix(kdl): focused tab
* fix(layout): corret default_tab_template behavior
* style(code): fix compile warnings
* feat(cli): send actions through the cli
* fix(cli): exit only when action is done
* fix(cli): open embedded pane from floating pane
* fix(cli): send actions to other sessions
* feat(cli): command alias
* feat(converter): convert old config
* feat(converter): convert old layout and theme files
* feat(kdl): pretty errors
* feat(client): convert old YAML files on startup
* fix: various bugs and styling issues
* fix: e2e tests
* fix(screen): propagate errors after merge
* style(clippy): lower clippy level
* fix(tests): own session_name variable
* style(fmt): rustfmt
* fix(cli): various action fixes
* style(fmt): rustfmt
* fix(themes): loading of theme files
* style(fmt): rustfmt
* fix(tests): theme fixtures
* fix(layouts): better errors on unknown nodes
* fix(kdl): clarify valid node terminator error
* fix(e2e): adjust close tab test
* fix(e2e): adjust close tab test again
* style(code): cleanup some comments
Diffstat (limited to 'zellij-utils/src/input')
45 files changed, 4219 insertions, 3148 deletions
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 754151a12..8dacdc652 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -1,21 +1,42 @@ //! Definition of the actions that can be bound to keys. use super::command::RunCommandAction; -use super::layout::TabLayout; +use super::layout::{Layout, PaneLayout}; +use crate::cli::CliAction; use crate::data::InputMode; +use crate::input::config::{ConfigError, KdlError}; use crate::input::options::OnForceClose; +use miette::{NamedSource, Report}; use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use std::str::FromStr; + use crate::position::Position; /// The four directions (left, right, up, down). -#[derive(Eq, Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Eq, Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] pub enum Direction { Left, Right, Up, Down, } +impl FromStr for Direction { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "Left" | "left" => Ok(Direction::Left), + "Right" | "right" => Ok(Direction::Right), + "Up" | "up" => Ok(Direction::Up), + "Down" | "down" => Ok(Direction::Down), + _ => Err(format!( + "Failed to parse Direction. Unknown Direction: {}", + s + )), + } + } +} #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum ResizeDirection { @@ -26,6 +47,23 @@ pub enum ResizeDirection { Increase, Decrease, } +impl FromStr for ResizeDirection { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "Left" | "left" => Ok(ResizeDirection::Left), + "Right" | "right" => Ok(ResizeDirection::Right), + "Up" | "up" => Ok(ResizeDirection::Up), + "Down" | "down" => Ok(ResizeDirection::Down), + "Increase" | "increase" | "+" => Ok(ResizeDirection::Increase), + "Decrease" | "decrease" | "-" => Ok(ResizeDirection::Decrease), + _ => Err(format!( + "Failed to parse ResizeDirection. Unknown ResizeDirection: {}", + s + )), + } + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum SearchDirection { @@ -33,6 +71,20 @@ pub enum SearchDirection { Up, } +impl FromStr for SearchDirection { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "Down" | "down" => Ok(SearchDirection::Down), + "Up" | "up" => Ok(SearchDirection::Up), + _ => Err(format!( + "Failed to parse SearchDirection. Unknown SearchDirection: {}", + s + )), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum SearchOption { CaseSensitivity, @@ -40,6 +92,54 @@ pub enum SearchOption { Wrap, } +impl FromStr for SearchOption { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "CaseSensitivity" | "casesensitivity" | "Casesensitivity" => { + Ok(SearchOption::CaseSensitivity) + }, + "WholeWord" | "wholeword" | "Wholeword" => Ok(SearchOption::WholeWord), + "Wrap" | "wrap" => Ok(SearchOption::Wrap), + _ => Err(format!( + "Failed to parse SearchOption. Unknown SearchOption: {}", + s + )), + } + } +} + +fn split_escaped_whitespace(s: &str) -> Vec<String> { + s.split_ascii_whitespace() + .map(|s| String::from(s)) + .fold(vec![], |mut acc, part| { + if let Some(previous_part) = acc.last_mut() { + if previous_part.ends_with('\\') { + previous_part.push(' '); + previous_part.push_str(&part); + return acc; + } + } + acc.push(part); + acc + }) +} + +fn split_command_and_args(command: String) -> (PathBuf, Vec<String>) { + let mut full_command = split_escaped_whitespace(&command); + let mut command = None; + let mut command_args = vec![]; + for part in full_command.drain(..) { + if command.is_none() { + command = Some(part); + } else { + command_args.push(part); + } + } + let command = PathBuf::from(command.unwrap()); + (command, command_args) +} + // As these actions are bound to the default config, please // do take care when refactoring - or renaming. // They might need to be adjusted in the default config @@ -55,6 +155,8 @@ pub enum Action { WriteChars(String), /// Switch to the specified input mode. SwitchToMode(InputMode), + /// Switch all connected clients to the specified input mode. + SwitchModeForAllClients(InputMode), /// Resize focus pane in specified direction. Resize(ResizeDirection), /// Switch focus to next pane in specified direction. @@ -97,6 +199,12 @@ pub enum Action { /// Open a new pane in the specified direction (relative to focus). /// If no direction is specified, will try to use the biggest available space. NewPane(Option<Direction>), + /// Open the file in a new pane using the default editor + EditFile(PathBuf, Option<usize>, Option<Direction>, Option<bool>), // usize is an optional line number, bool is floating true/false + /// Open a new floating pane + NewFloatingPane(Option<RunCommandAction>), + /// Open a new tiled (embedded, non-floating) pane + NewTiledPane(Option<Direction>, Option<RunCommandAction>), /// Embed focused pane in tab if floating or float focused pane if embedded TogglePaneEmbedOrFloating, /// Toggle the visibility of all floating panes (if any) in the current Tab @@ -106,7 +214,7 @@ pub enum Action { PaneNameInput(Vec<u8>), UndoRenamePane, /// Create a new tab, optionally with a specified tab layout. - NewTab(Option<TabLayout>), + NewTab(Option<PaneLayout>, Option<String>), // the String is the tab name /// Do nothing. NoOp, /// Go to the next tab. @@ -147,6 +255,144 @@ pub enum Action { SearchToggleOption(SearchOption), } +impl Action { + pub fn actions_from_cli(cli_action: CliAction) -> Result<Vec<Action>, String> { + match cli_action { + CliAction::Write { bytes } => Ok(vec![Action::Write(bytes)]), + CliAction::WriteChars { chars } => Ok(vec![Action::WriteChars(chars)]), + CliAction::Resize { resize_direction } => Ok(vec![Action::Resize(resize_direction)]), + CliAction::FocusNextPane => Ok(vec![Action::FocusNextPane]), + CliAction::FocusPreviousPane => Ok(vec![Action::FocusPreviousPane]), + CliAction::MoveFocus { direction } => Ok(vec![Action::MoveFocus(direction)]), + CliAction::MoveFocusOrTab { direction } => Ok(vec![Action::MoveFocusOrTab(direction)]), + CliAction::MovePane { direction } => Ok(vec![Action::MovePane(Some(direction))]), + CliAction::DumpScreen { path } => Ok(vec![Action::DumpScreen( + path.as_os_str().to_string_lossy().into(), + )]), + CliAction::EditScrollback => Ok(vec![Action::EditScrollback]), + CliAction::ScrollUp => Ok(vec![Action::ScrollUp]), + CliAction::ScrollDown => Ok(vec![Action::ScrollDown]), + CliAction::ScrollToBottom => Ok(vec![Action::ScrollToBottom]), + CliAction::PageScrollUp => Ok(vec![Action::PageScrollUp]), + CliAction::PageScrollDown => Ok(vec![Action::PageScrollDown]), + CliAction::HalfPageScrollUp => Ok(vec![Action::HalfPageScrollUp]), + CliAction::HalfPageScrollDown => Ok(vec![Action::HalfPageScrollDown]), + CliAction::ToggleFullscreen => Ok(vec![Action::ToggleFocusFullscreen]), + CliAction::TogglePaneFrames => Ok(vec![Action::TogglePaneFrames]), + CliAction::ToggleActiveSyncTab => Ok(vec![Action::ToggleActiveSyncTab]), + CliAction::NewPane { + direction, + command, + cwd, + floating, + } => match command { + Some(command) => { + let (command, args) = split_command_and_args(command); + let run_command_action = RunCommandAction { + command, + args, + cwd, + direction, + }; + match floating { + Some(true) => Ok(vec![Action::NewFloatingPane(Some(run_command_action))]), + _ => Ok(vec![Action::NewTiledPane( + direction, + Some(run_command_action), + )]), + } + }, + None => match floating { + Some(true) => Ok(vec![Action::NewFloatingPane(None)]), + _ => Ok(vec![Action::NewTiledPane(direction, None)]), + }, + }, + CliAction::Edit { + direction, + file, + line_number, + floating, + } => Ok(vec![Action::EditFile( + file, + line_number, + direction, + floating, + )]), + CliAction::SwitchMode { input_mode } => { + Ok(vec![Action::SwitchModeForAllClients(input_mode)]) + }, + CliAction::TogglePaneEmbedOrFloating => Ok(vec![Action::TogglePaneEmbedOrFloating]), + CliAction::ToggleFloatingPanes => Ok(vec![Action::ToggleFloatingPanes]), + CliAction::ClosePane => Ok(vec![Action::CloseFocus]), + CliAction::RenamePane { name } => Ok(vec![ + Action::UndoRenamePane, + Action::PaneNameInput(name.as_bytes().to_vec()), + ]), + CliAction::UndoRenamePane => Ok(vec![Action::UndoRenamePane]), + CliAction::GoToNextTab => Ok(vec![Action::GoToNextTab]), + CliAction::GoToPreviousTab => Ok(vec![Action::GoToPreviousTab]), + CliAction::CloseTab => Ok(vec![Action::CloseTab]), + CliAction::GoToTab { index } => Ok(vec![Action::GoToTab(index)]), + CliAction::RenameTab { name } => Ok(vec![ + Action::TabNameInput(vec![0]), + Action::TabNameInput(name.as_bytes().to_vec()), + ]), + CliAction::UndoRenameTab => Ok(vec![Action::UndoRenameTab]), + CliAction::NewTab { name, layout } => { + if let Some(layout_path) = layout { + let (path_to_raw_layout, raw_layout) = |