Skip to content

Commit

Permalink
Fix exploring and escaping paths
Browse files Browse the repository at this point in the history
This PR targets 2 pain points.

1. The `Explore` message was async, which caused some unexpected
   behavior. This was fixed by splitting `Explore` into `ExplorePwd`,
   `ExplorePwdAsync` and `ExploreParentsAsync`. `ExploreParentsAsync`
   is similar to the former `Explore`, which is mainly used when loading
   `xplr` for the first time. However, what we'll be using frequently
   are `ExplorePwd` and `ExplorePwdAsync` messages.

2. Files with spaces caused some unexpected behavior. This was fixed by
   escaping the paths properly. This also fixed focusing of a file after
   creating or renaming it.

Anothor breaking change is that `XPLR_PIPE_FOCUS_OUT` has been removed.
`XPLR_FOCUS_PATH` is all we need. So, the rule of thumb is if a variable
contains one liner value, it can be used directly from the env vars.
Variables that can contain multi-line values, will be exposed via the
pipes.

Minor changes are

- Add `switch_mode` mode to the global key binding help menu
- Moved some UI related code from config.rs to ui.rs.
- Fixed compilation issue on `rustc 1.50.0`.
  • Loading branch information
sayanarijit committed May 9, 2021
1 parent 52fbaef commit 7c5468c
Show file tree
Hide file tree
Showing 9 changed files with 388 additions and 354 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "xplr"
version = "0.7.2" # Update config.yml, config.rs and default.nix
version = "0.8.0" # Update config.yml, config.rs
authors = ["Arijit Basu <[email protected]>"]
edition = "2018"
description = "A hackable, minimal, fast TUI file explorer"
Expand Down
102 changes: 65 additions & 37 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::config::Config;
use crate::config::Layout;
use crate::config::Mode;
use crate::explorer;
use crate::input::Key;
use crate::ui::Layout;
use anyhow::{bail, Result};
use chrono::{DateTime, Local};
use indexmap::set::IndexSet;
Expand Down Expand Up @@ -84,11 +85,6 @@ impl Pipe {
&self.msg_in
}

/// Get a reference to the pipe's focus out.
pub fn focus_out(&self) -> &String {
&self.focus_out
}

/// Get a reference to the pipe's selection out.
pub fn selection_out(&self) -> &String {
&self.selection_out
Expand Down Expand Up @@ -938,9 +934,23 @@ impl ExplorerConfig {
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum ExternalMsg {
/// Explore the present working directory and register the filtered nodes.
/// This operation is expensive. So, try avoiding using it too often.
/// This operation is expensive. So, try to avoid using it too often.
/// Once exploration is done, it will auto `Refresh` the state.
ExplorePwd,

/// Explore the present working directory and register the filtered nodes asynchronously.
/// This operation happens asynchronously. That means, the xplr directory buffers won't be updated
/// immediately. Hence, it needs to be used with care and probably with special checks in place.
/// To explore `$PWD` synchronously, use `ExplorePwd` instead.
/// Once exploration is done, it will auto `Refresh` the state.
Explore,
ExplorePwdAsync,

/// Explore the present working directory along with its parents and register the filtered nodes.
/// This operation happens asynchronously. That means, the xplr directory buffers won't be updated
/// immediately. Hence, it needs to be used with care and probably with special checks in place.
/// To explore just the `$PWD` synchronously, use `ExplorePwd` instead.
/// Once exploration is done, it will auto `Refresh` the state.
ExploreParentsAsync,

/// Refresh the app state (uncluding UI).
/// But it will not re-explore the directory if the working directory is the same.
Expand Down Expand Up @@ -1288,7 +1298,8 @@ impl Command {

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum MsgOut {
Explore,
ExplorePwdAsync,
ExploreParentsAsync,
Refresh,
ClearScreen,
Quit,
Expand Down Expand Up @@ -1569,7 +1580,9 @@ impl App {
self.log_error("Cannot call shell command in read-only mode.".into())
} else {
match msg {
ExternalMsg::Explore => self.explore(),
ExternalMsg::ExplorePwd => self.explore_pwd(),
ExternalMsg::ExploreParentsAsync => self.explore_parents_async(),
ExternalMsg::ExplorePwdAsync => self.explore_pwd_async(),
ExternalMsg::Refresh => self.refresh(),
ExternalMsg::ClearScreen => self.clear_screen(),
ExternalMsg::FocusFirst => self.focus_first(),
Expand Down Expand Up @@ -1687,8 +1700,22 @@ impl App {
Ok(self)
}

fn explore(mut self) -> Result<Self> {
self.msg_out.push_back(MsgOut::Explore);
fn explore_pwd(self) -> Result<Self> {
let dirbuf = explorer::explore_sync(
self.explorer_config().clone(),
self.pwd().clone(),
self.focused_node().map(|n| n.absolute_path().clone()),
)?;
self.add_directory(dirbuf.parent().clone(), dirbuf)
}

fn explore_pwd_async(mut self) -> Result<Self> {
self.msg_out.push_back(MsgOut::ExplorePwdAsync);
Ok(self)
}

fn explore_parents_async(mut self) -> Result<Self> {
self.msg_out.push_back(MsgOut::ExploreParentsAsync);
Ok(self)
}

Expand Down Expand Up @@ -1917,7 +1944,9 @@ impl App {
{
dir_buf.focus = focus;
self.msg_out.push_back(MsgOut::Refresh);
};
} else {
self = self.log_error(format!("{} not found in $PWD", name))?;
}
};
Ok(self)
}
Expand All @@ -1929,10 +1958,10 @@ impl App {
self.change_directory(&parent.to_string_lossy().to_string())?
.focus_by_file_name(&filename.to_string_lossy().to_string())
} else {
bail!("invalid path {}", path)
self.log_error(format!("{} not found", path))
}
} else {
self.change_directory("/")
self.log_error(format!("Cannot focus on {}", path))
}
}

Expand Down Expand Up @@ -2408,31 +2437,30 @@ impl App {
}

pub fn global_help_menu_str(&self) -> String {
let builtin = self.config().modes().builtin().clone();
let custom = self.config().modes().custom().clone();
let builtin = self.config().modes().builtin();
let custom = self.config().modes().custom();

[
(builtin.default().name().clone(), builtin.default().clone()),
(builtin.number().name().clone(), builtin.number().clone()),
(builtin.go_to().name().clone(), builtin.go_to().clone()),
(builtin.search().name().clone(), builtin.search().clone()),
(builtin.selection_ops().name().clone(), builtin.selection_ops().clone()),
(builtin.action().name().clone(), builtin.action().clone()),
(builtin.create().name().clone(), builtin.create().clone()),
(builtin.create_file().name().clone(), builtin.create_file().clone()),
(
builtin.create_directory().name().clone(),
builtin.create_directory().clone(),
),
(builtin.rename().name().clone(), builtin.rename().clone()),
(builtin.delete().name().clone(), builtin.delete().clone()),
(builtin.sort().name().clone(), builtin.sort().clone()),
(builtin.filter().name().clone(), builtin.filter().clone()),
(builtin.relative_path_does_contain().name().clone(), builtin.relative_path_does_contain().clone()),
(builtin.relative_path_does_not_contain().name().clone(), builtin.relative_path_does_not_contain().clone()),
builtin.default(),
builtin.filter(),
builtin.number(),
builtin.go_to(),
builtin.search(),
builtin.selection_ops(),
builtin.action(),
builtin.create(),
builtin.create_file(),
builtin.create_directory(),
builtin.rename(),
builtin.delete(),
builtin.sort(),
builtin.filter(),
builtin.relative_path_does_contain(),
builtin.relative_path_does_not_contain(),
builtin.switch_layout(),
]
.iter()
.chain(custom.into_iter().collect::<Vec<(String, Mode)>>().iter())
.iter().map(|m| (m.name(), m.to_owned()))
.chain(custom.iter())
.map(|(name, mode)| {
let help = mode
.help_menu()
Expand Down
Loading

0 comments on commit 7c5468c

Please sign in to comment.