From e3a5f3c0446f43382a631fef206c9685a2a3b793 Mon Sep 17 00:00:00 2001 From: Arijit Basu Date: Thu, 27 Oct 2022 16:14:56 +0530 Subject: [PATCH] Add messages SetVroot and ResetVroot --- Cargo.lock | 12 ++++ Cargo.toml | 1 + .../en/src/environment-variables-and-pipes.md | 6 ++ docs/en/src/layout.md | 2 + docs/en/src/lua-function-calls.md | 10 ++- docs/en/src/messages.md | 21 ++++++ src/app.rs | 71 ++++++++++++++----- src/bin/xplr.rs | 3 +- src/cli.rs | 10 ++- src/explorer.rs | 10 ++- src/init.lua | 2 +- src/msg/in_/external.rs | 20 +++++- src/runner.rs | 11 ++- src/ui.rs | 20 +++--- 14 files changed, 160 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe79d03..6a3c796 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1250,6 +1250,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +dependencies = [ + "either", + "libc", + "once_cell", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1365,4 +1376,5 @@ dependencies = [ "serde_yaml", "tui", "tui-input", + "which", ] diff --git a/Cargo.toml b/Cargo.toml index ff93bc4..0ca406e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ gethostname = "0.3.0" fuzzy-matcher = "0.3.7" serde_json = "1.0.87" path-absolutize = "3.0.14" +which = "4.3.0" [dependencies.lazy_static] version = "1.4.0" diff --git a/docs/en/src/environment-variables-and-pipes.md b/docs/en/src/environment-variables-and-pipes.md index 79c1dea..2019c21 100644 --- a/docs/en/src/environment-variables-and-pipes.md +++ b/docs/en/src/environment-variables-and-pipes.md @@ -52,6 +52,7 @@ The other variables are single-line variables containing simple information: - [XPLR_MODE][34] - [XPLR_PID][35] - [XPLR_SESSION_PATH][36] +- [XPLR_VROOT][39] ### Environment variables @@ -90,6 +91,10 @@ Contains the process ID of the current xplr process. Contains the current session path, like /tmp/runtime-"$USER"/xplr/session/"$XPLR_PID"/, you can find temporary files here, such as pipes. +#### XPLR_VROOT + +Contains the path of current virtual root, is set. + ### Pipes #### Input pipe @@ -214,3 +219,4 @@ xplr.config.modes.builtin.default.key_bindings.on_key.X = { [36]: #xplr_session_path [37]: messages.md#reading-input [38]: #xplr +[39]: #xplr_vroot diff --git a/docs/en/src/layout.md b/docs/en/src/layout.md index a91862c..7818cfe 100644 --- a/docs/en/src/layout.md +++ b/docs/en/src/layout.md @@ -384,6 +384,7 @@ Hence, only the following fields are avilable. - [version][40] - [pwd][41] +- [vroot][52] - [focused_node][42] - [selection][43] - [mode][44] @@ -448,3 +449,4 @@ Hence, only the following fields are avilable. [49]: lua-function-calls.md#explorer_config [50]: lua-function-calls.md#directory_buffer [51]: layouts.md +[52]: #vroot diff --git a/docs/en/src/lua-function-calls.md b/docs/en/src/lua-function-calls.md index 59d7772..75fe004 100644 --- a/docs/en/src/lua-function-calls.md +++ b/docs/en/src/lua-function-calls.md @@ -17,6 +17,7 @@ It contains the following information: - [version][29] - [pwd][31] +- [vroot][75] - [focused_node][32] - [directory_buffer][33] - [selection][34] @@ -39,7 +40,13 @@ xplr version. Can be used to test compatibility. Type: string -The present working directory/ +The present working directory. + +### vroot + +Type: nullable string + +The current virtual root. ### focused_node @@ -384,3 +391,4 @@ xplr.config.modes.builtin.default.key_bindings.on_key.space = { [72]: #last_modified [73]: #uid [74]: #gid +[75]: #vroot diff --git a/docs/en/src/messages.md b/docs/en/src/messages.md index 106d95e..d88c959 100644 --- a/docs/en/src/messages.md +++ b/docs/en/src/messages.md @@ -310,6 +310,27 @@ Example: Lua: `"FollowSymlink"` YAML: `FollowSymlink` +#### SetVroot + +Sets the virtual root for isolating xplr navigation, similar to `--vroot`. +If the $PWD is outside the vroot, xplr will automatically enter vroot. + +Type: { SetVroot = "string" } + +Example: + +Lua: `{ SetVroot = "/tmp" }` +YAML: `SetVroot: /tmp` + +#### ResetVroot + +Resets the virtual root bach to the value passed by `--vroot` or `/`. + +Example: + +- Lua: `"ResetVroot"` +- YAML: `ResetVroot` + ### Reading Input #### SetInputPrompt diff --git a/src/app.rs b/src/app.rs index 34e0761..c493aee 100644 --- a/src/app.rs +++ b/src/app.rs @@ -128,6 +128,7 @@ impl History { pub struct LuaContextHeavy { pub version: String, pub pwd: String, + pub vroot: Option, pub focused_node: Option, pub directory_buffer: Option, pub selection: IndexSet, @@ -145,6 +146,7 @@ pub struct LuaContextHeavy { pub struct LuaContextLight { pub version: String, pub pwd: String, + pub vroot: Option, pub focused_node: Option, pub selection: IndexSet, pub mode: Mode, @@ -167,7 +169,8 @@ pub struct App { pub version: String, pub config: Config, pub hooks: Hooks, - pub vroot: String, + pub vroot: Option, + pub initial_vroot: Option, pub pwd: String, pub directory_buffer: Option, pub last_focus: HashMap>, @@ -190,7 +193,7 @@ pub struct App { impl App { pub fn create( bin: String, - vroot: PathBuf, + vroot: Option, pwd: PathBuf, lua: &mlua::Lua, config_file: Option, @@ -298,15 +301,19 @@ impl App { let hostname = gethostname().to_string_lossy().to_string(); - if !pwd.starts_with(&vroot) { - bail!( - "{:?} is outside of virtual root {:?}", - pwd.to_string_lossy(), - vroot.to_string_lossy() - ) + if let Some(vroot) = vroot.as_ref() { + if !pwd.starts_with(&vroot) { + bail!( + "{:?} is outside of virtual root {:?}", + pwd.to_string_lossy(), + vroot.to_string_lossy() + ) + } } + let pwd = pwd.to_string_lossy().to_string(); - let vroot = vroot.to_string_lossy().to_string(); + let vroot = vroot.map(|v| v.to_string_lossy().to_string()); + let initial_vroot = vroot.clone(); env::set_current_dir(&pwd)?; let input = InputBuffer { @@ -319,6 +326,7 @@ impl App { version: VERSION.to_string(), config, vroot, + initial_vroot, pwd, directory_buffer: Default::default(), last_focus: Default::default(), @@ -440,6 +448,8 @@ impl App { LastVisitedPath => self.last_visited_path(), NextVisitedPath => self.next_visited_path(), FollowSymlink => self.follow_symlink(), + SetVroot(p) => self.set_vroot(&p), + ResetVroot => self.reset_vroot(), SetInputPrompt(p) => self.set_input_prompt(p), UpdateInputBuffer(op) => self.update_input_buffer(op), UpdateInputBufferFromKey => self.update_input_buffer_from_key(key), @@ -755,16 +765,43 @@ impl App { } } + fn set_vroot(mut self, path: &String) -> Result { + let vroot = PathBuf::from(path).absolutize()?.to_path_buf(); + + if vroot.is_dir() { + self.vroot = Some(vroot.to_string_lossy().to_string()); + if !PathBuf::from(&self.pwd).starts_with(&vroot) { + self.change_directory(path, true) + } else { + Ok(self) + } + } else { + self.log_error(format!( + "not a valid directory: {}", + vroot.to_string_lossy() + )) + } + } + + fn reset_vroot(self) -> Result { + if let Some(vroot) = self.initial_vroot.clone() { + self.set_vroot(&vroot) + } else { + Ok(self) + } + } + fn change_directory(mut self, dir: &str, save_history: bool) -> Result { let dir = PathBuf::from(dir).absolutize()?.to_path_buf(); - let vroot = &self.vroot.clone(); - if !dir.starts_with(&self.vroot) { - return self.log_error(format!( - "{:?} is outside of virtual root {:?}", - dir.to_string_lossy(), - vroot, - )); + if let Some(vroot) = &self.vroot.clone() { + if !dir.starts_with(&vroot) { + return self.log_error(format!( + "{:?} is outside of virtual root {:?}", + dir.to_string_lossy(), + vroot, + )); + } } match env::set_current_dir(&dir) { @@ -1722,6 +1759,7 @@ impl App { LuaContextHeavy { version: self.version.clone(), pwd: self.pwd.clone(), + vroot: self.vroot.clone(), focused_node: self.focused_node().cloned(), directory_buffer: self.directory_buffer.clone(), selection: self.selection.clone(), @@ -1740,6 +1778,7 @@ impl App { LuaContextLight { version: self.version.clone(), pwd: self.pwd.clone(), + vroot: self.vroot.clone(), focused_node: self.focused_node().cloned(), selection: self.selection.clone(), mode: self.mode.clone(), diff --git a/src/bin/xplr.rs b/src/bin/xplr.rs index 8a584ed..59946e3 100644 --- a/src/bin/xplr.rs +++ b/src/bin/xplr.rs @@ -36,8 +36,7 @@ fn main() { "$HOME/.config/xplr/init.lua") -C, --extra-config ... Specifies extra config files to load --on-load ... Sends messages when xplr loads - --vroot Treats the specified path as the virtual root for - navigation, but uses full path for interaction"###; + --vroot Treats the specified path as the virtual root"###; let args = r###" Path to focus on, or enter if directory, (default is `.`) diff --git a/src/cli.rs b/src/cli.rs index cc33d3d..db9580e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -47,7 +47,15 @@ impl Cli { pub fn parse(args: env::Args) -> Result { let mut cli = Self::default(); let mut args = args.peekable(); - cli.bin = args.next().context("failed to parse xplr binary path")?; + cli.bin = args + .next() + .map(which::which) + .context("failed to parse xplr binary path")? + .context("failed to find xplr binary path")? + .absolutize()? + .to_path_buf() + .to_string_lossy() + .to_string(); let mut flag_ends = false; diff --git a/src/explorer.rs b/src/explorer.rs index 662b979..8a82e85 100644 --- a/src/explorer.rs +++ b/src/explorer.rs @@ -102,7 +102,7 @@ pub(crate) fn explore_async( pub(crate) fn explore_recursive_async( config: ExplorerConfig, - root: PathBuf, + vroot: Option, parent: PathBuf, focused_path: Option, fallback_focus: usize, @@ -116,10 +116,14 @@ pub(crate) fn explore_recursive_async( tx_msg_in.clone(), ); if let Some(grand_parent) = parent.parent() { - if grand_parent.starts_with(&root) { + if vroot + .as_ref() + .map(|v| grand_parent.starts_with(v)) + .unwrap_or(true) + { explore_recursive_async( config, - root, + vroot, grand_parent.into(), parent.file_name().map(|p| p.into()), 0, diff --git a/src/init.lua b/src/init.lua index 44b5479..8b9e28e 100644 --- a/src/init.lua +++ b/src/init.lua @@ -1827,9 +1827,9 @@ xplr.config.modes.builtin.action = { ["!"] = { help = "shell", messages = { + "PopMode", { Call0 = { command = "bash", args = { "-i" } } }, "ExplorePwdAsync", - "PopModeKeepingInputBuffer", }, }, ["c"] = { diff --git a/src/msg/in_/external.rs b/src/msg/in_/external.rs index 9002754..c691c14 100644 --- a/src/msg/in_/external.rs +++ b/src/msg/in_/external.rs @@ -258,7 +258,6 @@ pub enum ExternalMsg { /// - YAML: `NextVisitedPath` NextVisitedPath, - /// /// Follow the symlink under focus to its actual location. /// /// Example: @@ -267,6 +266,25 @@ pub enum ExternalMsg { /// YAML: `FollowSymlink` FollowSymlink, + /// Sets the virtual root for isolating xplr navigation, similar to `--vroot`. + /// If the $PWD is outside the vroot, xplr will automatically enter vroot. + /// + /// Type: { SetVroot = "string" } + /// + /// Example: + /// + /// Lua: `{ SetVroot = "/tmp" }` + /// YAML: `SetVroot: /tmp` + SetVroot(String), + + /// Resets the virtual root bach to the value passed by `--vroot` or `/`. + /// + /// Example: + /// + /// - Lua: `"ResetVroot"` + /// - YAML: `ResetVroot` + ResetVroot, + /// ### Reading Input ----------------------------------------------------- /// Set the input prompt temporarily, until the input buffer is reset. diff --git a/src/runner.rs b/src/runner.rs index b549e89..27c04b0 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -109,6 +109,7 @@ fn call( let status = Command::new(cmd.command.clone()) .env("XPLR", &app.bin) + .env("XPLR_VROOT", &app.vroot.clone().unwrap_or_default()) .env("XPLR_APP_VERSION", &app.version) .env("XPLR_PID", &app.pid.to_string()) .env("XPLR_INPUT_BUFFER", input_buffer) @@ -191,7 +192,7 @@ fn start_fifo(path: &str, focus_path: &str) -> Result { pub struct Runner { bin: String, - vroot: PathBuf, + vroot: Option, pwd: PathBuf, focused_path: Option, config_file: Option, @@ -224,11 +225,9 @@ impl Runner { pwd = pwd.parent().map(|p| p.into()).unwrap_or(currdir); } - let root = cli.vroot.unwrap_or_else(|| "/".into()); - Ok(Self { bin: cli.bin, - vroot: root, + vroot: cli.vroot, pwd, focused_path, config_file: cli.config, @@ -278,7 +277,7 @@ impl Runner { explorer::explore_recursive_async( app.explorer_config.clone(), - app.vroot.clone().into(), + app.vroot.as_ref().map(PathBuf::from), app.pwd.clone().into(), self.focused_path, app.directory_buffer.as_ref().map(|d| d.focus).unwrap_or(0), @@ -444,7 +443,7 @@ impl Runner { ExploreParentsAsync => { explorer::explore_recursive_async( app.explorer_config.clone(), - app.vroot.clone().into(), + app.vroot.as_ref().map(PathBuf::from), app.pwd.clone().into(), app.focused_node() .map(|n| n.relative_path.clone().into()), diff --git a/src/ui.rs b/src/ui.rs index 7246df1..28f8c86 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -669,13 +669,16 @@ fn draw_table( .map(|c| c.to_tui(screen_size, layout_size)) .collect(); - let pwd = app - .pwd - .strip_prefix(&app.vroot) - .unwrap_or(&app.pwd) - .trim_matches('/') - .replace('\\', "\\\\") - .replace('\n', "\\n"); + let pwd = if let Some(vroot) = app.vroot.as_ref() { + app.pwd.strip_prefix(vroot).unwrap_or(&app.pwd) + } else { + &app.pwd + } + .trim_matches('/') + .replace('\\', "\\\\") + .replace('\n', "\\n"); + + let vroot_indicator = if app.vroot.is_some() { "[v] " } else { "" }; let table = Table::new(rows) .widths(&table_constraints) @@ -685,7 +688,8 @@ fn draw_table( .block(block( config, format!( - " /{} ({}) ", + " {}/{} ({}) ", + vroot_indicator, pwd, app.directory_buffer .as_ref()