summaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/input
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2022-10-05 07:44:00 +0200
committerGitHub <noreply@github.com>2022-10-05 07:44:00 +0200
commit79bf6ab868cbdab1f9a3827c9b70198f54548b44 (patch)
tree2d6fc4c1d8a79ebd727a1a5f8b6406617dd0de55 /zellij-utils/src/input
parent917e9b2ff0f583183c0155060d243afd295770b9 (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')
-rw-r--r--zellij-utils/src/input/actions.rs262
-rw-r--r--zellij-utils/src/input/config.rs681
-rw-r--r--zellij-utils/src/input/keybinds.rs338
-rw-r--r--zellij-utils/src/input/layout.rs855
-rw-r--r--zellij-utils/src/input/mod.rs1
-rw-r--r--zellij-utils/src/input/options.rs35
-rw-r--r--zellij-utils/src/input/plugins.rs191
-rw-r--r--zellij-utils/src/input/theme.rs193
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/deeply-nested-tab-layout.yaml41
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/multiple-tabs-should-not-error.yaml18
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/no-layout-template-specified.yaml6
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/no-tab-section-specified.yaml6
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/session-name-to-layout.yaml3
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-command.yaml35
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab-and-default-plugins.yaml32
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-panes-with-tab.yaml21
-rw-r--r--zellij-utils/src/input/unit/fixtures/layouts/three-tabs-merged-correctly.yaml29
-rw-r--r--zellij-utils/src/input/unit/fixtures/themes/dracula.kdl18
-rw-r--r--zellij-utils/src/input/unit/fixtures/themes/dracula.yaml16
-rw-r--r--zellij-utils/src/input/unit/fixtures/themes/nord.kdl17
-rw-r--r--zellij-utils/src/input/unit/fixtures/themes/nord.yaml16
-rw-r--r--zellij-utils/src/input/unit/keybinds_test.rs1244
-rw-r--r--zellij-utils/src/input/unit/layout_test.rs1738
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_input_mode.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__keybinds__keybinds_test__error_received_on_unknown_key_instruction.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap139
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap147
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap201
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_more_than_one_focused_tab.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_multiple_layout_nodes_in_file.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_pane_templates_without_a_name.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_tab_templates_without_a_name.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_node.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_property.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_pane_template_property.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_property.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__error_on_unknown_layout_tab_template_property.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap231
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap125
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap94
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap239
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap96
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__layout_test__dracula_theme_from_file.snap109
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap109
-rw-r--r--zellij-utils/src/input/unit/theme_test.rs15
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) =