Add messages SetVroot and ResetVroot

pull/524/head v0.20.0-beta.2
Arijit Basu 2 years ago committed by Arijit Basu
parent a62b72bf2a
commit e3a5f3c044

12
Cargo.lock generated

@ -1250,6 +1250,17 @@ dependencies = [
"wasm-bindgen", "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]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -1365,4 +1376,5 @@ dependencies = [
"serde_yaml", "serde_yaml",
"tui", "tui",
"tui-input", "tui-input",
"which",
] ]

@ -35,6 +35,7 @@ gethostname = "0.3.0"
fuzzy-matcher = "0.3.7" fuzzy-matcher = "0.3.7"
serde_json = "1.0.87" serde_json = "1.0.87"
path-absolutize = "3.0.14" path-absolutize = "3.0.14"
which = "4.3.0"
[dependencies.lazy_static] [dependencies.lazy_static]
version = "1.4.0" version = "1.4.0"

@ -52,6 +52,7 @@ The other variables are single-line variables containing simple information:
- [XPLR_MODE][34] - [XPLR_MODE][34]
- [XPLR_PID][35] - [XPLR_PID][35]
- [XPLR_SESSION_PATH][36] - [XPLR_SESSION_PATH][36]
- [XPLR_VROOT][39]
### Environment variables ### 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"/, Contains the current session path, like /tmp/runtime-"$USER"/xplr/session/"$XPLR_PID"/,
you can find temporary files here, such as pipes. you can find temporary files here, such as pipes.
#### XPLR_VROOT
Contains the path of current virtual root, is set.
### Pipes ### Pipes
#### Input pipe #### Input pipe
@ -214,3 +219,4 @@ xplr.config.modes.builtin.default.key_bindings.on_key.X = {
[36]: #xplr_session_path [36]: #xplr_session_path
[37]: messages.md#reading-input [37]: messages.md#reading-input
[38]: #xplr [38]: #xplr
[39]: #xplr_vroot

@ -384,6 +384,7 @@ Hence, only the following fields are avilable.
- [version][40] - [version][40]
- [pwd][41] - [pwd][41]
- [vroot][52]
- [focused_node][42] - [focused_node][42]
- [selection][43] - [selection][43]
- [mode][44] - [mode][44]
@ -448,3 +449,4 @@ Hence, only the following fields are avilable.
[49]: lua-function-calls.md#explorer_config [49]: lua-function-calls.md#explorer_config
[50]: lua-function-calls.md#directory_buffer [50]: lua-function-calls.md#directory_buffer
[51]: layouts.md [51]: layouts.md
[52]: #vroot

@ -17,6 +17,7 @@ It contains the following information:
- [version][29] - [version][29]
- [pwd][31] - [pwd][31]
- [vroot][75]
- [focused_node][32] - [focused_node][32]
- [directory_buffer][33] - [directory_buffer][33]
- [selection][34] - [selection][34]
@ -39,7 +40,13 @@ xplr version. Can be used to test compatibility.
Type: string Type: string
The present working directory/ The present working directory.
### vroot
Type: nullable string
The current virtual root.
### focused_node ### focused_node
@ -384,3 +391,4 @@ xplr.config.modes.builtin.default.key_bindings.on_key.space = {
[72]: #last_modified [72]: #last_modified
[73]: #uid [73]: #uid
[74]: #gid [74]: #gid
[75]: #vroot

@ -310,6 +310,27 @@ Example:
Lua: `"FollowSymlink"` Lua: `"FollowSymlink"`
YAML: `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 ### Reading Input
#### SetInputPrompt #### SetInputPrompt

@ -128,6 +128,7 @@ impl History {
pub struct LuaContextHeavy { pub struct LuaContextHeavy {
pub version: String, pub version: String,
pub pwd: String, pub pwd: String,
pub vroot: Option<String>,
pub focused_node: Option<Node>, pub focused_node: Option<Node>,
pub directory_buffer: Option<DirectoryBuffer>, pub directory_buffer: Option<DirectoryBuffer>,
pub selection: IndexSet<Node>, pub selection: IndexSet<Node>,
@ -145,6 +146,7 @@ pub struct LuaContextHeavy {
pub struct LuaContextLight { pub struct LuaContextLight {
pub version: String, pub version: String,
pub pwd: String, pub pwd: String,
pub vroot: Option<String>,
pub focused_node: Option<Node>, pub focused_node: Option<Node>,
pub selection: IndexSet<Node>, pub selection: IndexSet<Node>,
pub mode: Mode, pub mode: Mode,
@ -167,7 +169,8 @@ pub struct App {
pub version: String, pub version: String,
pub config: Config, pub config: Config,
pub hooks: Hooks, pub hooks: Hooks,
pub vroot: String, pub vroot: Option<String>,
pub initial_vroot: Option<String>,
pub pwd: String, pub pwd: String,
pub directory_buffer: Option<DirectoryBuffer>, pub directory_buffer: Option<DirectoryBuffer>,
pub last_focus: HashMap<String, Option<String>>, pub last_focus: HashMap<String, Option<String>>,
@ -190,7 +193,7 @@ pub struct App {
impl App { impl App {
pub fn create( pub fn create(
bin: String, bin: String,
vroot: PathBuf, vroot: Option<PathBuf>,
pwd: PathBuf, pwd: PathBuf,
lua: &mlua::Lua, lua: &mlua::Lua,
config_file: Option<PathBuf>, config_file: Option<PathBuf>,
@ -298,6 +301,7 @@ impl App {
let hostname = gethostname().to_string_lossy().to_string(); let hostname = gethostname().to_string_lossy().to_string();
if let Some(vroot) = vroot.as_ref() {
if !pwd.starts_with(&vroot) { if !pwd.starts_with(&vroot) {
bail!( bail!(
"{:?} is outside of virtual root {:?}", "{:?} is outside of virtual root {:?}",
@ -305,8 +309,11 @@ impl App {
vroot.to_string_lossy() vroot.to_string_lossy()
) )
} }
}
let pwd = pwd.to_string_lossy().to_string(); 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)?; env::set_current_dir(&pwd)?;
let input = InputBuffer { let input = InputBuffer {
@ -319,6 +326,7 @@ impl App {
version: VERSION.to_string(), version: VERSION.to_string(),
config, config,
vroot, vroot,
initial_vroot,
pwd, pwd,
directory_buffer: Default::default(), directory_buffer: Default::default(),
last_focus: Default::default(), last_focus: Default::default(),
@ -440,6 +448,8 @@ impl App {
LastVisitedPath => self.last_visited_path(), LastVisitedPath => self.last_visited_path(),
NextVisitedPath => self.next_visited_path(), NextVisitedPath => self.next_visited_path(),
FollowSymlink => self.follow_symlink(), FollowSymlink => self.follow_symlink(),
SetVroot(p) => self.set_vroot(&p),
ResetVroot => self.reset_vroot(),
SetInputPrompt(p) => self.set_input_prompt(p), SetInputPrompt(p) => self.set_input_prompt(p),
UpdateInputBuffer(op) => self.update_input_buffer(op), UpdateInputBuffer(op) => self.update_input_buffer(op),
UpdateInputBufferFromKey => self.update_input_buffer_from_key(key), UpdateInputBufferFromKey => self.update_input_buffer_from_key(key),
@ -755,17 +765,44 @@ impl App {
} }
} }
fn set_vroot(mut self, path: &String) -> Result<Self> {
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<Self> {
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<Self> { fn change_directory(mut self, dir: &str, save_history: bool) -> Result<Self> {
let dir = PathBuf::from(dir).absolutize()?.to_path_buf(); let dir = PathBuf::from(dir).absolutize()?.to_path_buf();
let vroot = &self.vroot.clone(); if let Some(vroot) = &self.vroot.clone() {
if !dir.starts_with(&self.vroot) { if !dir.starts_with(&vroot) {
return self.log_error(format!( return self.log_error(format!(
"{:?} is outside of virtual root {:?}", "{:?} is outside of virtual root {:?}",
dir.to_string_lossy(), dir.to_string_lossy(),
vroot, vroot,
)); ));
} }
}
match env::set_current_dir(&dir) { match env::set_current_dir(&dir) {
Ok(()) => { Ok(()) => {
@ -1722,6 +1759,7 @@ impl App {
LuaContextHeavy { LuaContextHeavy {
version: self.version.clone(), version: self.version.clone(),
pwd: self.pwd.clone(), pwd: self.pwd.clone(),
vroot: self.vroot.clone(),
focused_node: self.focused_node().cloned(), focused_node: self.focused_node().cloned(),
directory_buffer: self.directory_buffer.clone(), directory_buffer: self.directory_buffer.clone(),
selection: self.selection.clone(), selection: self.selection.clone(),
@ -1740,6 +1778,7 @@ impl App {
LuaContextLight { LuaContextLight {
version: self.version.clone(), version: self.version.clone(),
pwd: self.pwd.clone(), pwd: self.pwd.clone(),
vroot: self.vroot.clone(),
focused_node: self.focused_node().cloned(), focused_node: self.focused_node().cloned(),
selection: self.selection.clone(), selection: self.selection.clone(),
mode: self.mode.clone(), mode: self.mode.clone(),

@ -36,8 +36,7 @@ fn main() {
"$HOME/.config/xplr/init.lua") "$HOME/.config/xplr/init.lua")
-C, --extra-config <PATH>... Specifies extra config files to load -C, --extra-config <PATH>... Specifies extra config files to load
--on-load <MESSAGE>... Sends messages when xplr loads --on-load <MESSAGE>... Sends messages when xplr loads
--vroot <PATH> Treats the specified path as the virtual root for --vroot <PATH> Treats the specified path as the virtual root"###;
navigation, but uses full path for interaction"###;
let args = r###" let args = r###"
<PATH> Path to focus on, or enter if directory, (default is `.`) <PATH> Path to focus on, or enter if directory, (default is `.`)

@ -47,7 +47,15 @@ impl Cli {
pub fn parse(args: env::Args) -> Result<Self> { pub fn parse(args: env::Args) -> Result<Self> {
let mut cli = Self::default(); let mut cli = Self::default();
let mut args = args.peekable(); 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; let mut flag_ends = false;

@ -102,7 +102,7 @@ pub(crate) fn explore_async(
pub(crate) fn explore_recursive_async( pub(crate) fn explore_recursive_async(
config: ExplorerConfig, config: ExplorerConfig,
root: PathBuf, vroot: Option<PathBuf>,
parent: PathBuf, parent: PathBuf,
focused_path: Option<PathBuf>, focused_path: Option<PathBuf>,
fallback_focus: usize, fallback_focus: usize,
@ -116,10 +116,14 @@ pub(crate) fn explore_recursive_async(
tx_msg_in.clone(), tx_msg_in.clone(),
); );
if let Some(grand_parent) = parent.parent() { 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( explore_recursive_async(
config, config,
root, vroot,
grand_parent.into(), grand_parent.into(),
parent.file_name().map(|p| p.into()), parent.file_name().map(|p| p.into()),
0, 0,

@ -1827,9 +1827,9 @@ xplr.config.modes.builtin.action = {
["!"] = { ["!"] = {
help = "shell", help = "shell",
messages = { messages = {
"PopMode",
{ Call0 = { command = "bash", args = { "-i" } } }, { Call0 = { command = "bash", args = { "-i" } } },
"ExplorePwdAsync", "ExplorePwdAsync",
"PopModeKeepingInputBuffer",
}, },
}, },
["c"] = { ["c"] = {

@ -258,7 +258,6 @@ pub enum ExternalMsg {
/// - YAML: `NextVisitedPath` /// - YAML: `NextVisitedPath`
NextVisitedPath, NextVisitedPath,
///
/// Follow the symlink under focus to its actual location. /// Follow the symlink under focus to its actual location.
/// ///
/// Example: /// Example:
@ -267,6 +266,25 @@ pub enum ExternalMsg {
/// YAML: `FollowSymlink` /// YAML: `FollowSymlink`
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 ----------------------------------------------------- /// ### Reading Input -----------------------------------------------------
/// Set the input prompt temporarily, until the input buffer is reset. /// Set the input prompt temporarily, until the input buffer is reset.

@ -109,6 +109,7 @@ fn call(
let status = Command::new(cmd.command.clone()) let status = Command::new(cmd.command.clone())
.env("XPLR", &app.bin) .env("XPLR", &app.bin)
.env("XPLR_VROOT", &app.vroot.clone().unwrap_or_default())
.env("XPLR_APP_VERSION", &app.version) .env("XPLR_APP_VERSION", &app.version)
.env("XPLR_PID", &app.pid.to_string()) .env("XPLR_PID", &app.pid.to_string())
.env("XPLR_INPUT_BUFFER", input_buffer) .env("XPLR_INPUT_BUFFER", input_buffer)
@ -191,7 +192,7 @@ fn start_fifo(path: &str, focus_path: &str) -> Result<fs::File> {
pub struct Runner { pub struct Runner {
bin: String, bin: String,
vroot: PathBuf, vroot: Option<PathBuf>,
pwd: PathBuf, pwd: PathBuf,
focused_path: Option<PathBuf>, focused_path: Option<PathBuf>,
config_file: Option<PathBuf>, config_file: Option<PathBuf>,
@ -224,11 +225,9 @@ impl Runner {
pwd = pwd.parent().map(|p| p.into()).unwrap_or(currdir); pwd = pwd.parent().map(|p| p.into()).unwrap_or(currdir);
} }
let root = cli.vroot.unwrap_or_else(|| "/".into());
Ok(Self { Ok(Self {
bin: cli.bin, bin: cli.bin,
vroot: root, vroot: cli.vroot,
pwd, pwd,
focused_path, focused_path,
config_file: cli.config, config_file: cli.config,
@ -278,7 +277,7 @@ impl Runner {
explorer::explore_recursive_async( explorer::explore_recursive_async(
app.explorer_config.clone(), app.explorer_config.clone(),
app.vroot.clone().into(), app.vroot.as_ref().map(PathBuf::from),
app.pwd.clone().into(), app.pwd.clone().into(),
self.focused_path, self.focused_path,
app.directory_buffer.as_ref().map(|d| d.focus).unwrap_or(0), app.directory_buffer.as_ref().map(|d| d.focus).unwrap_or(0),
@ -444,7 +443,7 @@ impl Runner {
ExploreParentsAsync => { ExploreParentsAsync => {
explorer::explore_recursive_async( explorer::explore_recursive_async(
app.explorer_config.clone(), app.explorer_config.clone(),
app.vroot.clone().into(), app.vroot.as_ref().map(PathBuf::from),
app.pwd.clone().into(), app.pwd.clone().into(),
app.focused_node() app.focused_node()
.map(|n| n.relative_path.clone().into()), .map(|n| n.relative_path.clone().into()),

@ -669,14 +669,17 @@ fn draw_table<B: Backend>(
.map(|c| c.to_tui(screen_size, layout_size)) .map(|c| c.to_tui(screen_size, layout_size))
.collect(); .collect();
let pwd = app let pwd = if let Some(vroot) = app.vroot.as_ref() {
.pwd app.pwd.strip_prefix(vroot).unwrap_or(&app.pwd)
.strip_prefix(&app.vroot) } else {
.unwrap_or(&app.pwd) &app.pwd
}
.trim_matches('/') .trim_matches('/')
.replace('\\', "\\\\") .replace('\\', "\\\\")
.replace('\n', "\\n"); .replace('\n', "\\n");
let vroot_indicator = if app.vroot.is_some() { "[v] " } else { "" };
let table = Table::new(rows) let table = Table::new(rows)
.widths(&table_constraints) .widths(&table_constraints)
.style(app_config.general.table.style.to_owned().into()) .style(app_config.general.table.style.to_owned().into())
@ -685,7 +688,8 @@ fn draw_table<B: Backend>(
.block(block( .block(block(
config, config,
format!( format!(
" /{} ({}) ", " {}/{} ({}) ",
vroot_indicator,
pwd, pwd,
app.directory_buffer app.directory_buffer
.as_ref() .as_ref()

Loading…
Cancel
Save