diff options
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | GOVERNANCE.md | 1 | ||||
-rw-r--r-- | src/main.rs | 49 | ||||
-rw-r--r-- | zellij-server/src/route.rs | 6 | ||||
-rw-r--r-- | zellij-server/src/screen.rs | 93 | ||||
-rw-r--r-- | zellij-server/src/tab.rs | 14 | ||||
-rw-r--r-- | zellij-server/src/wasm_vm.rs | 12 | ||||
-rw-r--r-- | zellij-utils/assets/config/default.yaml | 2 | ||||
-rw-r--r-- | zellij-utils/src/errors.rs | 1 | ||||
-rw-r--r-- | zellij-utils/src/input/actions.rs | 2 | ||||
-rw-r--r-- | zellij-utils/src/input/layout.rs | 22 | ||||
-rw-r--r-- | zellij-utils/src/input/unit/layout_test.rs | 5 | ||||
-rw-r--r-- | zellij-utils/src/setup.rs | 80 |
13 files changed, 199 insertions, 92 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index ad53c6c99..97f68875b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * Add displaying of the `session-name` to the `tab-bar` (https://github.com/zellij-org/zellij/pull/608) * Add command to dump `layouts` to stdout (https://github.com/zellij-org/zellij/pull/623) * `zellij setup --dump-layout [LAYOUT]` [default, strider, disable-status] +* Add `action`: `ScrollToBottom` (https://github.com/zellij-org/zellij/pull/626) + * Bound by default to `^c` in `scroll` mode, scrolls to bottom and exists the scroll mode +* Simplify deserialization slightly (https://github.com/zellij-org/zellij/pull/633) +* Fix update plugin attributes on inactive tab (https://github.com/zellij-org/zellij/pull/634) ## [0.15.0] - 2021-07-19 * Kill children properly (https://github.com/zellij-org/zellij/pull/601) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index b405d29db..698630697 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -29,3 +29,4 @@ Once the organization reaches 10 members, a reasonable and achievable process mu * Roee Shapira <ro33.sha@gmail.com> * Alex Kenji Berthold <aks.kenji@protonmail.com> * Kyle Sutherland-Cash <kyle.sutherlandcash@gmail.com> +* Dante Pippi <dante.dpf@gmail.com>
\ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 76a44d750..f0d00f77a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,16 +5,14 @@ mod tests; use crate::install::populate_data_dir; use sessions::{assert_session, assert_session_ne, get_active_session, list_sessions}; -use std::convert::TryFrom; use std::process; use zellij_client::{os_input_output::get_client_os_input, start_client, ClientInfo}; use zellij_server::{os_input_output::get_server_os_input, start_server}; use zellij_utils::{ cli::{CliArgs, Command, Sessions}, consts::{ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR}, - input::{config::Config, layout::Layout, options::Options}, logging::*, - setup::{find_default_config_dir, get_default_data_dir, get_layout_dir, Setup}, + setup::{get_default_data_dir, Setup}, structopt::StructOpt, }; @@ -26,25 +24,6 @@ pub fn main() { list_sessions(); } - let config = match Config::try_from(&opts) { - Ok(config) => config, - Err(e) => { - eprintln!("There was an error in the config file:\n{}", e); - process::exit(1); - } - }; - let config_options = Options::from_cli(&config.options, opts.command.clone()); - - if let Some(Command::Setup(ref setup)) = opts.command { - Setup::from_cli(setup, &opts, &config_options).map_or_else( - |e| { - eprintln!("{:?}", e); - process::exit(1); - }, - |_| {}, - ); - }; - atomic_create_dir(&*ZELLIJ_TMP_DIR).unwrap(); atomic_create_dir(&*ZELLIJ_TMP_LOG_DIR).unwrap(); if let Some(path) = opts.server { @@ -75,6 +54,14 @@ pub fn main() { session_name = Some(get_active_session()); } + let (config, _, config_options) = match Setup::from_options(&opts) { + Ok(results) => results, + Err(e) => { + eprintln!("{}", e); + process::exit(1); + } + }; + start_client( Box::new(os_input), opts, @@ -83,6 +70,14 @@ pub fn main() { None, ); } else { + let (config, layout, _) = match Setup::from_options(&opts) { + Ok(results) => results, + Err(e) => { + eprintln!("{}", e); + process::exit(1); + } + }; + let session_name = opts .session .clone() @@ -94,16 +89,6 @@ pub fn main() { #[cfg(not(disable_automatic_asset_installation))] populate_data_dir(&data_dir); - let layout_dir = config_options.layout_dir.or_else(|| { - get_layout_dir(opts.config_dir.clone().or_else(find_default_config_dir)) - }); - let layout = Layout::from_path_or_default( - opts.layout.as_ref(), - opts.layout_path.as_ref(), - layout_dir, - ) - .map(|layout| layout.construct_main_layout()); - start_client( Box::new(os_input), opts, diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 47eeabad1..7dbf09a7f 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -127,6 +127,12 @@ fn route_action( .send_to_screen(ScreenInstruction::ScrollDownAt(point)) .unwrap(); } + Action::ScrollToBottom => { + session + .senders + .send_to_screen(ScreenInstruction::ScrollToBottom) + .unwrap(); + } Action::PageScrollUp => { session .senders diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 86ecd6888..b348540fb 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -50,15 +50,16 @@ pub(crate) enum ScreenInstruction { ScrollUpAt(Position), ScrollDown, ScrollDownAt(Position), + ScrollToBottom, PageScrollUp, PageScrollDown, ClearScroll, CloseFocusedPane, ToggleActiveTerminalFullscreen, - SetSelectable(PaneId, bool), - SetFixedHeight(PaneId, usize), - SetFixedWidth(PaneId, usize), - SetInvisibleBorders(PaneId, bool), + SetSelectable(PaneId, bool, usize), + SetFixedHeight(PaneId, usize, usize), + SetFixedWidth(PaneId, usize, usize), + SetInvisibleBorders(PaneId, bool, usize), ClosePane(PaneId), ApplyLayout(Layout, Vec<RawFd>), NewTab(RawFd), @@ -103,6 +104,7 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::Exit => ScreenContext::Exit, ScreenInstruction::ScrollUp => ScreenContext::ScrollUp, ScreenInstruction::ScrollDown => ScreenContext::ScrollDown, + ScreenInstruction::ScrollToBottom => ScreenContext::ScrollToBottom, ScreenInstruction::PageScrollUp => ScreenContext::PageScrollUp, ScreenInstruction::PageScrollDown => ScreenContext::PageScrollDown, ScreenInstruction::ClearScroll => ScreenContext::ClearScroll, @@ -335,6 +337,11 @@ impl Screen { } } + /// Returns a mutable reference to this [`Screen`]'s indexed [`Tab`]. + pub fn get_indexed_tab_mut(&mut self, tab_index: usize) -> Option<&mut Tab> { + self.get_tabs_mut().get_mut(&tab_index) + } + /// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`] /// and switching to it. pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>) { @@ -353,7 +360,7 @@ impl Screen { self.colors, self.session_state.clone(), ); - tab.apply_layout(layout, new_pids); + tab.apply_layout(layout, new_pids, tab_index); self.active_tab_index = Some(tab_index); self.tabs.insert(tab_index, tab); self.update_tabs(); @@ -569,6 +576,12 @@ pub(crate) fn screen_thread_main( .unwrap() .scroll_terminal_down(&point, 3); } + ScreenInstruction::ScrollToBottom => { + screen + .get_active_tab_mut() + .unwrap() + .scroll_active_terminal_to_bottom(); + } ScreenInstruction::PageScrollUp => { screen .get_active_tab_mut() @@ -591,29 +604,53 @@ pub(crate) fn screen_thread_main( screen.get_active_tab_mut().unwrap().close_focused_pane(); screen.render(); } - ScreenInstruction::SetSelectable(id, selectable) => { - screen - .get_active_tab_mut() - .unwrap() - .set_pane_selectable(id, selectable); - } - ScreenInstruction::SetFixedHeight(id, fixed_height) => { - screen - .get_active_tab_mut() - .unwrap() - .set_pane_fixed_height(id, fixed_height); - } - ScreenInstruction::SetFixedWidth(id, fixed_width) => { - screen - .get_active_tab_mut() - .unwrap() - .set_pane_fixed_width(id, fixed_width); - } - ScreenInstruction::SetInvisibleBorders(id, invisible_borders) => { - screen - .get_active_tab_mut() - .unwrap() - .set_pane_invisible_borders(id, invisible_borders); + ScreenInstruction::SetSelectable(id, selectable, tab_index) => { + screen.get_indexed_tab_mut(tab_index).map_or_else( + || { + log::warn!( + "Tab index #{} not found, could not set selectable for plugin #{:?}.", + tab_index, + id + ) + }, + |tab| tab.set_pane_selectable(id, selectable), + ); + } + ScreenInstruction::SetFixedHeight(id, fixed_height, tab_index) => { + screen.get_indexed_tab_mut(tab_index).map_or_else( + || { + log::warn!( + "Tab index #{} not found, could not set fixed height for plugin #{:?}.", + tab_index, + id + ) + }, + |tab| tab.set_pane_fixed_height(id, fixed_height), + ); + } + ScreenInstruction::SetFixedWidth(id, fixed_width, tab_index) => { + screen.get_indexed_tab_mut(tab_index).map_or_else( + || { + log::warn!( + "Tab index #{} not found, could not set fixed width for plugin #{:?}.", + tab_index, + id + ) + }, + |tab| tab.set_pane_fixed_width(id, fixed_width), + ); + } + ScreenInstruction::SetInvisibleBorders(id, invisible_borders, tab_index) => { + screen.get_indexed_tab_mut(tab_index).map_or_else( + || { + log::warn!( + r#"Tab index #{} not found, could not set invisible borders for plugin #{:?}."#, + tab_index, + id + ) + }, + |tab| tab.set_pane_invisible_borders(id, invisible_borders), + ); screen.render(); } ScreenInstruction::ClosePane(id) => { diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index f3b30fe3a..0662be225 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -301,7 +301,7 @@ impl Tab { } } - pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>) { + pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>, tab_index: usize) { // TODO: this should be an attribute on Screen instead of full_screen_ws let free_space = PositionAndSize { x: 0, @@ -340,7 +340,7 @@ impl Tab { if let Some(Run::Plugin(Some(plugin))) = &layout.run { let (pid_tx, pid_rx) = channel(); self.senders - .send_to_plugin(PluginInstruction::Load(pid_tx, plugin.clone())) + .send_to_plugin(PluginInstruction::Load(pid_tx, plugin.clone(), tab_index)) .unwrap(); let pid = pid_rx.recv().unwrap(); let new_plugin = PluginPane::new( @@ -2280,6 +2280,16 @@ impl Tab { self.render(); } } + pub fn scroll_active_terminal_to_bottom(&mut self) { + if let Some(active_terminal_id) = self.get_active_terminal_id() { + let active_terminal = self + .panes + .get_mut(&PaneId::Terminal(active_terminal_id)) + .unwrap(); + active_terminal.clear_scroll(); + self.render(); + } + } pub fn clear_active_terminal_scroll(&mut self) { if let Some(active_terminal_id) = self.get_active_terminal_id() { let active_terminal = self diff --git a/zellij-server/src/wasm_vm.rs b/zellij-server/src/wasm_vm.rs index f3e4a115b..41a468ee2 100644 --- a/zellij-server/src/wasm_vm.rs +++ b/zellij-server/src/wasm_vm.rs @@ -28,8 +28,8 @@ use zellij_utils::{input::command::TerminalAction, serde, zellij_tile}; #[derive(Clone, Debug)] pub(crate) enum PluginInstruction { - Load(Sender<u32>, PathBuf), - Update(Option<u32>, Event), // Focused plugin / broadcast, event data + Load(Sender<u32>, PathBuf, usize), // tx_pid, path_of_plugin , tab_index + Update(Option<u32>, Event), // Focused plugin / broadcast, event data Render(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols Unload(u32), Exit, @@ -50,6 +50,7 @@ impl From<&PluginInstruction> for PluginContext { #[derive(WasmerEnv, Clone)] pub(crate) struct PluginEnv { pub plugin_id: u32, + pub tab_index: usize, pub senders: ThreadSenders, pub wasi_env: WasiEnv, pub subscriptions: Arc<Mutex<HashSet<EventType>>>, @@ -64,7 +65,7 @@ pub(crate) fn wasm_thread_main(bus: Bus<PluginInstruction>, store: Store, data_d let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel"); err_ctx.add_call(ContextType::Plugin((&event).into())); match event { - PluginInstruction::Load(pid_tx, path) => { + PluginInstruction::Load(pid_tx, path, tab_index) => { let plugin_dir = data_dir.join("plugins/"); let wasm_bytes = fs::read(&path) .or_else(|_| fs::read(&path.with_extension("wasm"))) @@ -100,6 +101,7 @@ pub(crate) fn wasm_thread_main(bus: Bus<PluginInstruction>, store: Store, data_d let plugin_env = PluginEnv { plugin_id, + tab_index, senders: bus.senders.clone(), wasi_env, subscriptions: Arc::new(Mutex::new(HashSet::new())), @@ -193,6 +195,7 @@ fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) { .send_to_screen(ScreenInstruction::SetSelectable( PaneId::Plugin(plugin_env.plugin_id), selectable, + plugin_env.tab_index, )) .unwrap() } @@ -204,6 +207,7 @@ fn host_set_fixed_height(plugin_env: &PluginEnv, fixed_height: i32) { .send_to_screen(ScreenInstruction::SetFixedHeight( PaneId::Plugin(plugin_env.plugin_id), fixed_height, + plugin_env.tab_index, )) .unwrap() } @@ -215,6 +219,7 @@ fn host_set_fixed_width(plugin_env: &PluginEnv, fixed_width: i32) { .send_to_screen(ScreenInstruction::SetFixedWidth( PaneId::Plugin(plugin_env.plugin_id), fixed_width, + plugin_env.tab_index, )) .unwrap() } @@ -226,6 +231,7 @@ fn host_set_invisible_borders(plugin_env: &PluginEnv, invisible_borders: i32) { .send_to_screen(ScreenInstruction::SetInvisibleBorders( PaneId::Plugin(plugin_env.plugin_id), invisible_borders, + plugin_env.tab_index, )) .unwrap() } diff --git a/zellij-utils/assets/config/default.yaml b/zellij-utils/assets/config/default.yaml index e90b60a18..13c8efcae 100644 --- a/zellij-utils/assets/config/default.yaml +++ b/zellij-utils/assets/config/default.yaml @@ -178,6 +178,8 @@ keybinds: key: [Ctrl: 'p',] - action: [SwitchToMode: Session,] key: [Ctrl: 'o',] + - action: [ScrollToBottom, SwitchToMode: Normal,] + key: [Ctrl: 'c',] - action: [Quit,] key: [Ctrl: 'q',] - action: [ScrollDown,] diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index b32dd337d..6a053038a 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -202,6 +202,7 @@ pub enum ScreenContext { ScrollUpAt, ScrollDown, ScrollDownAt, + ScrollToBottom, PageScrollUp, PageScrollDown, ClearScroll, diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index ab711c75c..dbced56df 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -49,6 +49,8 @@ pub enum Action { ScrollDown, /// Scroll down at point ScrollDownAt(Position), + /// Scroll down to bottom in focus pane. + ScrollToBottom, /// Scroll up one page in focus pane. PageScrollUp, /// Scroll down one page in focus pane. diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 26d967646..887350fb0 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -123,8 +123,8 @@ impl Layout { layout: Option<&PathBuf>, layout_path: Option<&PathBuf>, layout_dir: Option<PathBuf>, - ) -> Option<Layout> { - let layout_result = layout + ) -> Option<Result<Layout, ConfigError>> { + layout .map(|p| Layout::from_dir(p, layout_dir.as_ref())) .or_else(|| layout_path.map(|p| Layout::new(p))) .or_else(|| { @@ -132,16 +132,7 @@ impl Layout { &std::path::PathBuf::from("default"), layout_dir.as_ref(), )) - }); - - match layout_result { - None => None, - Some(Ok(layout)) => Some(layout), - Some(Err(e)) => { - eprintln!("There was an error in the layout file:\n{}", e); - std::process::exit(1); - } - } + }) } // Currently still needed but on nightly @@ -297,12 +288,9 @@ impl Layout { pub fn construct_main_layout(&self) -> MainLayout { let (pre_tab, post_tab, tabs) = self.split_main_and_tab_layout(); + // Todo: A proper LayoutError if tabs.is_empty() { - panic!("The layout file should have a `tabs` section specified"); - } - - if tabs.len() > 1 { - panic!("The layout file should have one single tab in the `tabs` section specified"); + panic!("The layout file should have a [`tabs`] section specified"); } MainLayout { diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 89217e04e..5d9fda35a 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -528,10 +528,7 @@ fn no_tabs_specified_should_panic() { } #[test] -#[should_panic] -// TODO Make error out of this -// Only untill #631 is fixed -fn multiple_tabs_specified_should_panic() { +fn multiple_tabs_specified_should_not_panic() { let path = layout_test_dir("multiple-tabs-should-panic.yaml".into()); let layout = Layout::new(&path); let _main_layout = layout.unwrap().construct_main_layout(); diff --git a/zellij-utils/src/setup.rs b/zellij-utils/src/setup.rs index aa0bb642b..e00eb7aea 100644 --- a/zellij-utils/src/setup.rs +++ b/zellij-utils/src/setup.rs @@ -1,11 +1,18 @@ -use crate::cli::CliArgs; -use crate::consts::{ - FEATURES, SYSTEM_DEFAULT_CONFIG_DIR, SYSTEM_DEFAULT_DATA_DIR_PREFIX, VERSION, ZELLIJ_PROJ_DIR, +use crate::{ + cli::{CliArgs, Command}, + consts::{ + FEATURES, SYSTEM_DEFAULT_CONFIG_DIR, SYSTEM_DEFAULT_DATA_DIR_PREFIX, VERSION, + ZELLIJ_PROJ_DIR, + }, + input::{ + config::{Config, ConfigError}, + layout::{Layout, MainLayout}, + options::Options, + }, }; -use crate::input::options::Options; use directories_next::BaseDirs; use serde::{Deserialize, Serialize}; -use std::{io::Write, path::Path, path::PathBuf}; +use std::{convert::TryFrom, io::Write, path::Path, path::PathBuf, process}; use structopt::StructOpt; const CONFIG_LOCATION: &str = ".config/zellij"; @@ -139,6 +146,68 @@ pub struct Setup { impl Setup { /// Entrypoint from main + /// Merges options from the config file and the command line options + /// into `[Options]`, the command line options superceding the config + /// file options: + /// 1. command line options (`zellij options`) + /// 2. config options (`config.yaml`) + pub fn from_options( + opts: &CliArgs, + ) -> Result<(Config, Option<MainLayout>, Options), ConfigError> { + let clean = match &opts.command { + Some(Command::Setup(ref setup)) => setup.clean, + _ => false, + }; + + log::info!("{:?}", clean); + + let config = if !clean { + match Config::try_from(opts) { + Ok(config) => config, + Err(e) => { + eprintln!("There was an error in the config file:"); + return Err(e); + } + } + } else { + Config::default() + }; + + let config_options = Options::from_cli(&config.options, opts.command.clone()); + + let layout_dir = config_options + .layout_dir + .clone() + .or_else(|| get_layout_dir(opts.config_dir.clone().or_else(find_default_config_dir))); + let layout_result = Layout::from_path_or_default( + opts.layout.as_ref(), + opts.layout_path.as_ref(), + layout_dir, + ); + let layout = match layout_result { + None => None, + Some(Ok(layout)) => Some(layout), + Some(Err(e)) => { + eprintln!("There was an error in the layout file:"); + return Err(e); + } + } + .map(|layout| layout.construct_main_layout()); + + if let Some(Command::Setup(ref setup)) = &opts.command { + setup.from_cli(opts, &config_options).map_or_else( + |e| { + eprintln!("{:?}", e); + process::exit(1); + }, + |_| {}, + ); + }; + + Ok((config, layout, config_options)) + } + + /// General setup helpers pub fn from_cli(&self, opts: &CliArgs, config_options: &Options) -> std::io::Result<()> { if self.clean { return Ok(()); @@ -201,7 +270,6 @@ impl Setup { } } if let Some(config_file) = config_file { - use crate::input::config::Config; message.push_str(&format!("[CONFIG FILE]: {:?}\n", config_file)); match Config::new(&config_file) { Ok(_) => message.push_str("[CONFIG FILE]: Well defined.\n"), |