diff --git a/CHANGELOG.md b/CHANGELOG.md index 3896b04c..e2f9440d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * Doesn't quit anymore on single `q` press while in tab mode (https://github.com/zellij-org/zellij/pull/342) * Completions are not assets anymore, but commands `option --generate-completion [shell]` (https://github.com/zellij-org/zellij/pull/369) * Fixes in the default configuration `default.yaml` file. Adds initial tmux-compat keybindings `tmux.yaml` (https://github.com/zellij-org/zellij/pull/362) +* Added more configuration locations, changed `ZELLIJ_CONFIG` to `ZELLIJ_CONFIG_FILE` (https://github.com/zellij-org/zellij/pull/391) ## [0.5.1] - 2021-04-23 * Change config to flag (https://github.com/zellij-org/zellij/pull/300) diff --git a/assets/config/default.yaml b/assets/config/default.yaml new file mode 100644 index 00000000..a3a0242f --- /dev/null +++ b/assets/config/default.yaml @@ -0,0 +1,215 @@ +--- +keybinds: + unbind: true + normal: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'r',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [NewPane: ] + key: [ Alt: 'n',] + - action: [MoveFocus: Left,] + key: [ Alt: 'h',] + - action: [MoveFocus: Right,] + key: [ Alt: 'l',] + - action: [MoveFocus: Down,] + key: [ Alt: 'j',] + - action: [MoveFocus: Up,] + key: [ Alt: 'k',] + - action: [FocusPreviousPane,] + key: [ Alt: '[',] + - action: [FocusNextPane,] + key: [ Alt: ']',] + locked: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'g',] + resize: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'r', Char: "\n", Char: ' ',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit] + key: [Ctrl: 'q'] + - action: [Resize: Left,] + key: [Char: 'h', Left,] + - action: [Resize: Down,] + key: [Char: 'j', Down,] + - action: [Resize: Up,] + key: [Char: 'k', Up, ] + - action: [Resize: Right,] + key: [Char: 'l', Right,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocus: Left,] + key: [ Alt: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Alt: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Alt: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Alt: 'k', Up,] + - action: [FocusPreviousPane,] + key: [ Alt: '[',] + - action: [FocusNextPane,] + key: [ Alt: ']',] + pane: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Resize,] + key: [Ctrl: 'r',] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'p', Char: "\n", Char: ' ',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [MoveFocus: Left,] + key: [ Char: 'h', Left,] + - action: [MoveFocus: Right,] + key: [ Char: 'l', Right,] + - action: [MoveFocus: Down,] + key: [ Char: 'j', Down,] + - action: [MoveFocus: Up,] + key: [ Char: 'k', Up,] + - action: [SwitchFocus,] + key: [Char: 'p'] + - action: [NewPane: ,] + key: [Char: 'n', Alt: 'n',] + - action: [NewPane: Down,] + key: [Char: 'd',] + - action: [NewPane: Right,] + key: [Char: 'r',] + - action: [CloseFocus,] + key: [Char: 'x',] + - action: [ToggleFocusFullscreen,] + key: [Char: 'f',] + - action: [FocusPreviousPane,] + key: [ Alt: '[',] + - action: [FocusNextPane,] + key: [ Alt: ']',] + tab: + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g'] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'r', Ctrl: 't', Char: "\n", Char: ' ',] + - action: [SwitchToMode: Scroll,] + key: [Ctrl: 's'] + - action: [SwitchToMode: RenameTab, TabNameInput: [0],] + key: [Char: 'r'] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [FocusPreviousPane,] + key: [ Alt: '[',] + - action: [FocusNextPane,] + key: [ Alt: ']',] + - action: [GoToPreviousTab,] + key: [ Char: 'h',] + - action: [GoToNextTab,] + key: [ Char: 'l',] + - action: [GoToNextTab,] + key: [ Char: 'j',] + - action: [GoToPreviousTab,] + key: [ Char: 'k',] + - action: [NewTab,] + key: [ Char: 'n',] + - action: [CloseTab,] + key: [ Char: 'x',] + - action: [MoveFocus: Left,] + key: [ Alt: 'h',] + - action: [MoveFocus: Right,] + key: [ Alt: 'l',] + - action: [MoveFocus: Down,] + key: [ Alt: 'j',] + - action: [MoveFocus: Up,] + key: [ Alt: 'k',] + - action: [GoToTab: 1,] + key: [ Char: '1',] + - action: [GoToTab: 2,] + key: [ Char: '2',] + - action: [GoToTab: 3,] + key: [ Char: '3',] + - action: [GoToTab: 4,] + key: [ Char: '4',] + - action: [GoToTab: 5,] + key: [ Char: '5',] + - action: [GoToTab: 6,] + key: [ Char: '6',] + - action: [GoToTab: 7,] + key: [ Char: '7',] + - action: [GoToTab: 8,] + key: [ Char: '8',] + - action: [GoToTab: 9,] + key: [ Char: '9',] + scroll: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'r', Ctrl: 's', Char: ' ', + Char: "\n",] + - action: [SwitchToMode: Tab,] + key: [Ctrl: 't',] + - action: [SwitchToMode: Locked,] + key: [Ctrl: 'g',] + - action: [SwitchToMode: Pane,] + key: [Ctrl: 'p',] + - action: [Quit,] + key: [Ctrl: 'q',] + - action: [ScrollDown,] + key: [Char: 'j', Down,] + - action: [ScrollUp,] + key: [Char: 'k', Up,] + - action: [PageScrollDown,] + key: [Ctrl: 'f', PageDown,] + - action: [PageScrollUp,] + key: [Ctrl: 'b', PageUp,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocus: Left,] + key: [ Alt: 'h',] + - action: [MoveFocus: Right,] + key: [ Alt: 'l',] + - action: [MoveFocus: Down,] + key: [ Alt: 'j',] + - action: [MoveFocus: Up,] + key: [ Alt: 'k',] + - action: [FocusPreviousPane,] + key: [ Alt: '[',] + - action: [FocusNextPane,] + key: [ Alt: ']',] + renametab: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'r', Ctrl: 's', Char: ' ',] + - action: [SwitchToMode: Tab,] + key: [Char: "\n",] + - action: [TabNameInput: [27] , SwitchToMode: Tab,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocus: Left,] + key: [ Alt: 'h',] + - action: [MoveFocus: Right,] + key: [ Alt: 'l',] + - action: [MoveFocus: Down,] + key: [ Alt: 'j',] + - action: [MoveFocus: Up,] + key: [ Alt: 'k',] + - action: [FocusPreviousPane,] + key: [ Alt: '[',] + - action: [FocusNextPane,] + key: [ Alt: ']',] diff --git a/src/cli.rs b/src/cli.rs index ba6911d9..bc4e4a9e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,9 +1,7 @@ +use super::common::utils::consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV}; use std::path::PathBuf; use structopt::StructOpt; -// TODO add to consts.rs -const ZELLIJ_CONFIG_ENV: &str = "ZELLIJ_CONFIG"; - #[derive(StructOpt, Default, Debug)] #[structopt(name = "zellij")] pub struct CliArgs { @@ -32,9 +30,13 @@ pub struct CliArgs { pub layout: Option, /// Change where zellij looks for the configuration - #[structopt(short, long, env=ZELLIJ_CONFIG_ENV)] + #[structopt(short, long, env=ZELLIJ_CONFIG_FILE_ENV)] pub config: Option, + /// Change where zellij looks for the configuration + #[structopt(long, env=ZELLIJ_CONFIG_DIR_ENV)] + pub config_dir: Option, + #[structopt(subcommand)] pub option: Option, @@ -47,11 +49,19 @@ pub enum ConfigCli { /// Change the behaviour of zellij #[structopt(name = "option")] Config { - #[structopt(long)] /// Disables loading of configuration file at default location + #[structopt(long)] clean: bool, }, #[structopt(name = "generate-completion")] GenerateCompletion { shell: String }, + + #[structopt(name = "setup")] + Setup { + /// Disables loading of configuration file at default location + /// Dump the default configuration file to stdout + #[structopt(long)] + dump_config: bool, + }, } diff --git a/src/common/input/config.rs b/src/common/input/config.rs index 542116d9..62ec3dfe 100644 --- a/src/common/input/config.rs +++ b/src/common/input/config.rs @@ -7,8 +7,8 @@ use std::path::{Path, PathBuf}; use super::keybinds::{Keybinds, KeybindsFromYaml}; use crate::cli::ConfigCli; +use crate::common::install; -use directories_next::ProjectDirs; use serde::Deserialize; type ConfigResult = Result; @@ -33,6 +33,8 @@ pub enum ConfigError { Io(io::Error), // Io error with path context IoPath(io::Error, PathBuf), + // Internal Deserialisation Error + FromUtf8(std::string::FromUtf8Error), } impl Default for Config { @@ -63,18 +65,11 @@ impl Config { } } - /// Deserializes the config from a default platform specific path, - /// merges the default configuration - options take precedence. - fn from_default_path() -> ConfigResult { - let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); - let mut config_path: PathBuf = project_dirs.config_dir().to_owned(); - config_path.push("config.yaml"); - - match Config::new(&config_path) { - Ok(config) => Ok(config), - Err(ConfigError::IoPath(_, _)) => Ok(Config::default()), - Err(e) => Err(e), - } + /// Gets default configuration from assets + // TODO Deserialize the Configuration from bytes &[u8], + // once serde-yaml supports zero-copy + pub fn from_default_assets() -> ConfigResult { + Self::from_yaml(String::from_utf8(install::DEFAULT_CONFIG.to_vec())?.as_str()) } /// Entry point of the configuration @@ -82,20 +77,37 @@ impl Config { pub fn from_cli_config( location: Option, cli_config: Option, + config_dir: Option, ) -> ConfigResult { if let Some(path) = location { return Config::new(&path); } - match cli_config { - Some(ConfigCli::Config { clean, .. }) if clean => Ok(Config::default()), - Some(_) | None => Ok(Config::from_default_path()?), + if let Some(ConfigCli::Config { clean, .. }) = cli_config { + if clean { + return Config::from_default_assets(); + } + } + + if let Some(config) = config_dir { + let path = config.join("config.yaml"); + if path.exists() { + Config::new(&path) + } else { + Config::from_default_assets() + } + } else { + Config::from_default_assets() } } /// In order not to mess up tests from changing configurations #[cfg(test)] - pub fn from_cli_config(_: Option, _: Option) -> ConfigResult { + pub fn from_cli_config( + _: Option, + _: Option, + _: Option, + ) -> ConfigResult { Ok(Config::default()) } } @@ -108,6 +120,7 @@ impl Display for ConfigError { write!(formatter, "IoError: {}, File: {}", err, path.display(),) } ConfigError::Serde(ref err) => write!(formatter, "Deserialisation error: {}", err), + ConfigError::FromUtf8(ref err) => write!(formatter, "FromUtf8Error: {}", err), } } } @@ -118,6 +131,7 @@ impl std::error::Error for ConfigError { ConfigError::Io(ref err) => Some(err), ConfigError::IoPath(ref err, _) => Some(err), ConfigError::Serde(ref err) => Some(err), + ConfigError::FromUtf8(ref err) => Some(err), } } } @@ -134,6 +148,12 @@ impl From for ConfigError { } } +impl From for ConfigError { + fn from(err: std::string::FromUtf8Error) -> ConfigError { + ConfigError::FromUtf8(err) + } +} + // The unit test location. #[cfg(test)] mod config_test { @@ -142,14 +162,14 @@ mod config_test { #[test] fn clean_option_equals_default_config() { let cli_config = ConfigCli::Config { clean: true }; - let config = Config::from_cli_config(None, Some(cli_config)).unwrap(); + let config = Config::from_cli_config(None, Some(cli_config), None).unwrap(); let default = Config::default(); assert_eq!(config, default); } #[test] fn no_config_option_file_equals_default_config() { - let config = Config::from_cli_config(None, None).unwrap(); + let config = Config::from_cli_config(None, None, None).unwrap(); let default = Config::default(); assert_eq!(config, default); } diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs index ee039f83..9842fbe2 100644 --- a/src/common/input/keybinds.rs +++ b/src/common/input/keybinds.rs @@ -1,7 +1,8 @@ //! Mapping of inputs to sequences of actions. use std::collections::HashMap; -use super::actions::{Action, Direction}; +use super::actions::Action; +use super::config; use serde::Deserialize; use strum::IntoEnumIterator; @@ -57,14 +58,9 @@ enum Unbind { impl Default for Keybinds { fn default() -> Keybinds { - let mut defaults = Keybinds::new(); - - for mode in InputMode::iter() { - defaults - .0 - .insert(mode, Keybinds::get_defaults_for_mode(&mode)); - } - defaults + config::Config::from_default_assets() + .expect("Keybinds from default assets Error") + .keybinds } } @@ -109,278 +105,6 @@ impl Keybinds { keybinds } - /// Returns the default keybinds for a given [`InputMode`]. - fn get_defaults_for_mode(mode: &InputMode) -> ModeKeybinds { - let mut defaults = HashMap::new(); - - match *mode { - InputMode::Normal => { - defaults.insert( - Key::Ctrl('g'), - vec![Action::SwitchToMode(InputMode::Locked)], - ); - defaults.insert(Key::Ctrl('p'), vec![Action::SwitchToMode(InputMode::Pane)]); - defaults.insert( - Key::Ctrl('r'), - vec![Action::SwitchToMode(InputMode::Resize)], - ); - defaults.insert(Key::Ctrl('t'), vec![Action::SwitchToMode(InputMode::Tab)]); - defaults.insert( - Key::Ctrl('s'), - vec![Action::SwitchToMode(InputMode::Scroll)], - ); - defaults.insert(Key::Ctrl('q'), vec![Action::Quit]); - - defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); - defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); - defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); - defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); - defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); - defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); - defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); - } - InputMode::Locked => { - defaults.insert( - Key::Ctrl('g'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - } - InputMode::Resize => { - defaults.insert( - Key::Ctrl('g'), - vec![Action::SwitchToMode(InputMode::Locked)], - ); - defaults.insert(Key::Ctrl('p'), vec![Action::SwitchToMode(InputMode::Pane)]); - defaults.insert( - Key::Ctrl('r'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - defaults.insert(Key::Ctrl('t'), vec![Action::SwitchToMode(InputMode::Tab)]); - defaults.insert( - Key::Ctrl('s'), - vec![Action::SwitchToMode(InputMode::Scroll)], - ); - defaults.insert(Key::Ctrl('q'), vec![Action::Quit]); - defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Normal)]); - defaults.insert( - Key::Char('\n'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - defaults.insert( - Key::Char(' '), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - - defaults.insert(Key::Char('h'), vec![Action::Resize(Direction::Left)]); - defaults.insert(Key::Char('j'), vec![Action::Resize(Direction::Down)]); - defaults.insert(Key::Char('k'), vec![Action::Resize(Direction::Up)]); - defaults.insert(Key::Char('l'), vec![Action::Resize(Direction::Right)]); - - defaults.insert(Key::Left, vec![Action::Resize(Direction::Left)]); - defaults.insert(Key::Down, vec![Action::Resize(Direction::Down)]); - defaults.insert(Key::Up, vec![Action::Resize(Direction::Up)]); - defaults.insert(Key::Right, vec![Action::Resize(Direction::Right)]); - - defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); - defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); - defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); - defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); - defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); - defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); - defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); - } - InputMode::Pane => { - defaults.insert( - Key::Ctrl('g'), - vec![Action::SwitchToMode(InputMode::Locked)], - ); - defaults.insert( - Key::Ctrl('p'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - defaults.insert( - Key::Ctrl('r'), - vec![Action::SwitchToMode(InputMode::Resize)], - ); - defaults.insert(Key::Ctrl('t'), vec![Action::SwitchToMode(InputMode::Tab)]); - defaults.insert( - Key::Ctrl('s'), - vec![Action::SwitchToMode(InputMode::Scroll)], - ); - defaults.insert(Key::Ctrl('q'), vec![Action::Quit]); - defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Normal)]); - defaults.insert( - Key::Char('\n'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - defaults.insert( - Key::Char(' '), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - - defaults.insert(Key::Char('h'), vec![Action::MoveFocus(Direction::Left)]); - defaults.insert(Key::Char('j'), vec![Action::MoveFocus(Direction::Down)]); - defaults.insert(Key::Char('k'), vec![Action::MoveFocus(Direction::Up)]); - defaults.insert(Key::Char('l'), vec![Action::MoveFocus(Direction::Right)]); - - defaults.insert(Key::Left, vec![Action::MoveFocus(Direction::Left)]); - defaults.insert(Key::Down, vec![Action::MoveFocus(Direction::Down)]); - defaults.insert(Key::Up, vec![Action::MoveFocus(Direction::Up)]); - defaults.insert(Key::Right, vec![Action::MoveFocus(Direction::Right)]); - - defaults.insert(Key::Char('p'), vec![Action::SwitchFocus]); - defaults.insert(Key::Char('n'), vec![Action::NewPane(None)]); - defaults.insert(Key::Char('d'), vec![Action::NewPane(Some(Direction::Down))]); - defaults.insert( - Key::Char('r'), - vec![Action::NewPane(Some(Direction::Right))], - ); - defaults.insert(Key::Char('x'), vec![Action::CloseFocus]); - defaults.insert(Key::Char('f'), vec![Action::ToggleFocusFullscreen]); - - defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); - defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); - defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); - defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); - defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); - defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); - defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); - } - InputMode::Tab => { - defaults.insert( - Key::Ctrl('g'), - vec![Action::SwitchToMode(InputMode::Locked)], - ); - defaults.insert(Key::Ctrl('p'), vec![Action::SwitchToMode(InputMode::Pane)]); - defaults.insert( - Key::Ctrl('r'), - vec![Action::SwitchToMode(InputMode::Resize)], - ); - defaults.insert( - Key::Ctrl('t'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - defaults.insert( - Key::Ctrl('s'), - vec![Action::SwitchToMode(InputMode::Scroll)], - ); - defaults.insert(Key::Ctrl('q'), vec![Action::Quit]); - defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Normal)]); - defaults.insert( - Key::Char('\n'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - defaults.insert( - Key::Char(' '), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - - defaults.insert(Key::Char('h'), vec![Action::GoToPreviousTab]); - defaults.insert(Key::Char('j'), vec![Action::GoToNextTab]); - defaults.insert(Key::Char('k'), vec![Action::GoToPreviousTab]); - defaults.insert(Key::Char('l'), vec![Action::GoToNextTab]); - - defaults.insert(Key::Left, vec![Action::GoToPreviousTab]); - defaults.insert(Key::Down, vec![Action::GoToNextTab]); - defaults.insert(Key::Up, vec![Action::GoToPreviousTab]); - defaults.insert(Key::Right, vec![Action::GoToNextTab]); - - defaults.insert(Key::Char('n'), vec![Action::NewTab]); - defaults.insert(Key::Char('x'), vec![Action::CloseTab]); - - defaults.insert( - Key::Char('r'), - vec![ - Action::SwitchToMode(InputMode::RenameTab), - Action::TabNameInput(vec![0]), - ], - ); - defaults.insert( - Key::Ctrl('g'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - for i in '1'..='9' { - defaults.insert(Key::Char(i), vec![Action::GoToTab(i.to_digit(10).unwrap())]); - } - defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); - defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); - defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); - defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); - defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); - defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); - defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); - } - InputMode::Scroll => { - defaults.insert( - Key::Ctrl('g'), - vec![Action::SwitchToMode(InputMode::Locked)], - ); - defaults.insert(Key::Ctrl('p'), vec![Action::SwitchToMode(InputMode::Pane)]); - defaults.insert( - Key::Ctrl('r'), - vec![Action::SwitchToMode(InputMode::Resize)], - ); - defaults.insert(Key::Ctrl('t'), vec![Action::SwitchToMode(InputMode::Tab)]); - defaults.insert( - Key::Ctrl('s'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - defaults.insert(Key::Ctrl('q'), vec![Action::Quit]); - defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Normal)]); - defaults.insert( - Key::Char('\n'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - defaults.insert( - Key::Char(' '), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - - defaults.insert(Key::Char('j'), vec![Action::ScrollDown]); - defaults.insert(Key::Char('k'), vec![Action::ScrollUp]); - - defaults.insert(Key::Ctrl('f'), vec![Action::PageScrollDown]); - defaults.insert(Key::Ctrl('b'), vec![Action::PageScrollUp]); - defaults.insert(Key::PageDown, vec![Action::PageScrollDown]); - defaults.insert(Key::PageUp, vec![Action::PageScrollUp]); - - defaults.insert(Key::Down, vec![Action::ScrollDown]); - defaults.insert(Key::Up, vec![Action::ScrollUp]); - - defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); - defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); - defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); - defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); - defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); - defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); - defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); - } - InputMode::RenameTab => { - defaults.insert(Key::Char('\n'), vec![Action::SwitchToMode(InputMode::Tab)]); - defaults.insert( - Key::Ctrl('g'), - vec![Action::SwitchToMode(InputMode::Normal)], - ); - defaults.insert( - Key::Esc, - vec![ - Action::TabNameInput(vec![0x1b]), - Action::SwitchToMode(InputMode::Tab), - ], - ); - - defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]); - defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]); - defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]); - defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]); - defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]); - defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]); - defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]); - } - } - ModeKeybinds(defaults) - } - /// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current /// [`InputMode`] and [`Keybinds`]. pub fn key_to_actions( diff --git a/src/common/install.rs b/src/common/install.rs index 86fdd269..c7f4d4be 100644 --- a/src/common/install.rs +++ b/src/common/install.rs @@ -1,4 +1,7 @@ -use std::{fs, path::Path}; +use crate::common::utils::consts::SYSTEM_DEFAULT_CONFIG_DIR; +use directories_next::{BaseDirs, ProjectDirs}; +use std::io::Write; +use std::{fs, path::Path, path::PathBuf}; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -39,3 +42,44 @@ pub fn populate_data_dir(data_dir: &Path) { } } } + +pub fn default_config_dir() -> Option { + vec![ + Some(xdg_config_dir()), + home_config_dir(), + Some(Path::new(SYSTEM_DEFAULT_CONFIG_DIR).to_path_buf()), + ] + .into_iter() + .filter(|p| p.is_some()) + .find(|p| p.clone().unwrap().exists()) + .flatten() +} + +pub fn xdg_config_dir() -> PathBuf { + let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap(); + project_dirs.config_dir().to_owned() +} + +pub fn home_config_dir() -> Option { + if let Some(user_dirs) = BaseDirs::new() { + let config_dir = user_dirs.home_dir().join("/.config/zellij"); + Some(config_dir) + } else { + None + } +} + +pub fn dump_asset(asset: &[u8]) -> std::io::Result<()> { + std::io::stdout().write_all(&asset)?; + Ok(()) +} + +pub const DEFAULT_CONFIG: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/", + "assets/config/default.yaml" +)); + +pub fn dump_default_config() -> std::io::Result<()> { + dump_asset(DEFAULT_CONFIG) +} diff --git a/src/common/mod.rs b/src/common/mod.rs index 79e48780..a45b23a4 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -131,7 +131,9 @@ pub fn start(mut os_input: Box, opts: CliArgs) { env::set_var(&"ZELLIJ", "0"); - let config = Config::from_cli_config(opts.config, opts.option) + let config_dir = opts.config_dir.or_else(install::default_config_dir); + + let config = Config::from_cli_config(opts.config, opts.option, config_dir) .map_err(|e| { eprintln!("There was an error in the config file:\n{}", e); std::process::exit(1); diff --git a/src/common/utils/consts.rs b/src/common/utils/consts.rs index 663a545d..4bd1b5b1 100644 --- a/src/common/utils/consts.rs +++ b/src/common/utils/consts.rs @@ -4,3 +4,9 @@ pub const ZELLIJ_TMP_DIR: &str = "/tmp/zellij"; pub const ZELLIJ_TMP_LOG_DIR: &str = "/tmp/zellij/zellij-log"; pub const ZELLIJ_TMP_LOG_FILE: &str = "/tmp/zellij/zellij-log/log.txt"; pub const ZELLIJ_IPC_PIPE: &str = "/tmp/zellij/ipc"; + +pub const ZELLIJ_CONFIG_FILE_ENV: &str = "ZELLIJ_CONFIG_FILE"; +pub const ZELLIJ_CONFIG_DIR_ENV: &str = "ZELLIJ_CONFIG_DIR"; + +// TODO: ${PREFIX} argument in makefile +pub const SYSTEM_DEFAULT_CONFIG_DIR: &str = "/etc/zellij"; diff --git a/src/main.rs b/src/main.rs index 8da85c87..12269ae9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use crate::utils::{ }; use client::{boundaries, layout, panes, tab}; use common::{ - command_is_executing, errors, os_input_output, pty_bus, screen, start, utils, wasm_vm, + command_is_executing, errors, install, os_input_output, pty_bus, screen, start, utils, wasm_vm, ApiCommand, }; use std::io::Write; @@ -59,6 +59,9 @@ pub fn main() { }; let mut out = std::io::stdout(); CliArgs::clap().gen_completions_to("zellij", shell, &mut out); + } else if let Some(crate::cli::ConfigCli::Setup { .. }) = opts.option { + install::dump_default_config().expect("Failed to print to stdout"); + std::process::exit(1); } else { let os_input = get_os_input(); atomic_create_dir(ZELLIJ_TMP_DIR).unwrap();