mirror of
https://github.com/sayanarijit/xplr
synced 2024-11-04 18:00:14 +00:00
Move, Copy, Create, Delete
This commit is contained in:
parent
eb6bc6b6be
commit
56d63a2316
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1117,7 +1117,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "xplr"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"crossterm",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "xplr"
|
||||
version = "0.2.9" # Update app.rs
|
||||
version = "0.2.10" # Update app.rs
|
||||
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "An experimental, minimal, configurable TUI file explorer, stealing ideas from nnn and fzf."
|
||||
|
93
src/app.rs
93
src/app.rs
@ -11,7 +11,7 @@ use std::collections::VecDeque;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub const VERSION: &str = "v0.2.9"; // Update Cargo.toml
|
||||
pub const VERSION: &str = "v0.2.10"; // Update Cargo.toml
|
||||
|
||||
pub const TEMPLATE_TABLE_ROW: &str = "TEMPLATE_TABLE_ROW";
|
||||
|
||||
@ -162,6 +162,7 @@ pub enum InternalMsg {
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ExternalMsg {
|
||||
Explore,
|
||||
Refresh,
|
||||
ClearScreen,
|
||||
FocusNext,
|
||||
@ -184,7 +185,10 @@ pub enum ExternalMsg {
|
||||
ResetInputBuffer,
|
||||
SwitchMode(String),
|
||||
Call(Command),
|
||||
Select,
|
||||
UnSelect,
|
||||
ToggleSelection,
|
||||
ClearSelection,
|
||||
PrintResultAndQuit,
|
||||
PrintAppStateAndQuit,
|
||||
Debug(String),
|
||||
@ -207,6 +211,7 @@ pub struct Command {
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum MsgOut {
|
||||
Explore,
|
||||
Refresh,
|
||||
ClearScreen,
|
||||
PrintResultAndQuit,
|
||||
@ -324,6 +329,7 @@ impl App {
|
||||
|
||||
fn handle_external(self, msg: ExternalMsg, key: Option<Key>) -> Result<Self, Error> {
|
||||
match msg {
|
||||
ExternalMsg::Explore => self.explore(),
|
||||
ExternalMsg::Refresh => self.refresh(),
|
||||
ExternalMsg::ClearScreen => self.clear_screen(),
|
||||
ExternalMsg::FocusFirst => self.focus_first(),
|
||||
@ -353,7 +359,10 @@ impl App {
|
||||
ExternalMsg::ResetInputBuffer => self.reset_input_buffer(),
|
||||
ExternalMsg::SwitchMode(mode) => self.switch_mode(&mode),
|
||||
ExternalMsg::Call(cmd) => self.call(cmd),
|
||||
ExternalMsg::Select => self.select(),
|
||||
ExternalMsg::UnSelect => self.un_select(),
|
||||
ExternalMsg::ToggleSelection => self.toggle_selection(),
|
||||
ExternalMsg::ClearSelection => self.clear_selection(),
|
||||
ExternalMsg::PrintResultAndQuit => self.print_result_and_quit(),
|
||||
ExternalMsg::PrintAppStateAndQuit => self.print_app_state_and_quit(),
|
||||
ExternalMsg::Debug(path) => self.debug(&path),
|
||||
@ -388,6 +397,11 @@ impl App {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn explore(mut self) -> Result<Self, Error> {
|
||||
self.msg_out.push_back(MsgOut::Explore);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn refresh(mut self) -> Result<Self, Error> {
|
||||
self.msg_out.push_back(MsgOut::Refresh);
|
||||
Ok(self)
|
||||
@ -572,30 +586,42 @@ impl App {
|
||||
}
|
||||
|
||||
fn add_directory(mut self, parent: String, dir: DirectoryBuffer) -> Result<Self, Error> {
|
||||
// TODO: Optimize
|
||||
self.directory_buffers.insert(parent, dir);
|
||||
self.msg_out.push_back(MsgOut::Refresh);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn select(mut self) -> Result<Self, Error> {
|
||||
if let Some(n) = self.focused_node().map(|n| n.to_owned()) {
|
||||
self.selection.push(n.clone());
|
||||
self.msg_out.push_back(MsgOut::Refresh);
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn un_select(mut self) -> Result<Self, Error> {
|
||||
if let Some(n) = self.focused_node().map(|n| n.to_owned()) {
|
||||
self.selection = self.selection.into_iter().filter(|s| s != &n).collect();
|
||||
self.msg_out.push_back(MsgOut::Refresh);
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn toggle_selection(mut self) -> Result<Self, Error> {
|
||||
self.clone()
|
||||
.focused_node()
|
||||
.map(|n| {
|
||||
if self.selection().contains(n) {
|
||||
self.selection = self
|
||||
.clone()
|
||||
.selection
|
||||
.into_iter()
|
||||
.filter(|s| s != n)
|
||||
.collect();
|
||||
Ok(self.clone())
|
||||
} else {
|
||||
self.selection.push(n.to_owned());
|
||||
Ok(self.clone())
|
||||
}
|
||||
})
|
||||
.unwrap_or(Ok(self))
|
||||
if let Some(n) = self.focused_node() {
|
||||
if self.selection().contains(n) {
|
||||
self = self.un_select()?;
|
||||
} else {
|
||||
self = self.select()?;
|
||||
}
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn clear_selection(mut self) -> Result<Self, Error> {
|
||||
self.selection.clear();
|
||||
self.msg_out.push_back(MsgOut::Refresh);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_result_and_quit(mut self) -> Result<Self, Error> {
|
||||
@ -652,8 +678,8 @@ impl App {
|
||||
}
|
||||
|
||||
/// Get a reference to the app's input buffer.
|
||||
pub fn input_buffer(&self) -> Option<&String> {
|
||||
self.input_buffer.as_ref()
|
||||
pub fn input_buffer(&self) -> Option<String> {
|
||||
self.input_buffer.clone()
|
||||
}
|
||||
|
||||
/// Get a reference to the app's pipes.
|
||||
@ -670,4 +696,29 @@ impl App {
|
||||
pub fn session_path(&self) -> &String {
|
||||
&self.session_path
|
||||
}
|
||||
|
||||
pub fn refresh_selection(mut self) -> Result<Self, Error> {
|
||||
self.selection = self
|
||||
.selection
|
||||
.into_iter()
|
||||
.filter(|n| PathBuf::from(&n.absolute_path).exists())
|
||||
.collect();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn result(&self) -> Vec<&Node> {
|
||||
if self.selection.is_empty() {
|
||||
self.focused_node().map(|n| vec![n]).unwrap_or_default()
|
||||
} else {
|
||||
self.selection.iter().map(|n| n).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn result_str(&self) -> String {
|
||||
self.result()
|
||||
.into_iter()
|
||||
.map(|n| n.absolute_path.clone())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ impl Default for KeyBindings {
|
||||
args:
|
||||
- "-c"
|
||||
- |
|
||||
PTH="$(echo -e ${XPLR_DIRECTORY_NODES:?} | sed -s 's/,/\n/g' | fzf)"
|
||||
PTH=$(echo -e "${XPLR_DIRECTORY_NODES:?}" | fzf)
|
||||
if [ -d "$PTH" ]; then
|
||||
echo "ChangeDirectory: ${PTH:?}" >> "${XPLR_PIPE_MSG_IN:?}"
|
||||
elif [ -f "$PTH" ]; then
|
||||
@ -302,8 +302,8 @@ impl Default for KeyBindings {
|
||||
- ToggleSelection
|
||||
- FocusNext
|
||||
|
||||
c:
|
||||
help: create
|
||||
n:
|
||||
help: create new
|
||||
messages:
|
||||
- SwitchMode: create
|
||||
|
||||
@ -312,6 +312,35 @@ impl Default for KeyBindings {
|
||||
messages:
|
||||
- SwitchMode: delete
|
||||
|
||||
c:
|
||||
help: copy here
|
||||
messages:
|
||||
- Call:
|
||||
command: bash
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
while IFS= read -r line; do
|
||||
cp -v "${line:?}" ./
|
||||
done <<< "${XPLR_SELECTION:?}"
|
||||
read -p "[enter to continue]"
|
||||
- ClearSelection
|
||||
- Explore
|
||||
|
||||
m:
|
||||
help: move here
|
||||
messages:
|
||||
- Call:
|
||||
command: bash
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
while IFS= read -r line; do
|
||||
mv -v "${line:?}" ./
|
||||
done <<< "${XPLR_SELECTION:?}"
|
||||
read -p "[enter to continue]"
|
||||
- Explore
|
||||
|
||||
enter:
|
||||
help: quit with result
|
||||
messages:
|
||||
@ -503,6 +532,7 @@ impl Default for Config {
|
||||
touch "${XPLR_INPUT_BUFFER:?}"
|
||||
- ResetInputBuffer
|
||||
- SwitchMode: default
|
||||
- Explore
|
||||
|
||||
d:
|
||||
help: create directory
|
||||
@ -515,6 +545,7 @@ impl Default for Config {
|
||||
mkdir -p "${XPLR_INPUT_BUFFER:?}"
|
||||
- ResetInputBuffer
|
||||
- SwitchMode: default
|
||||
- Explore
|
||||
|
||||
esc:
|
||||
help: cancel
|
||||
@ -547,15 +578,16 @@ impl Default for Config {
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
if [ ! -e "$XPLR_SELECTION" ]; then
|
||||
while IFS= read -r line; do
|
||||
rm -i "${line:?}"
|
||||
done < ${XPLR_PIPE_SELECTION_OUT:?}
|
||||
else
|
||||
rm -i ${XPLR_FOCUS_PATH:?}
|
||||
fi
|
||||
while IFS= read -r line; do
|
||||
if [ -d "$line" ]; then
|
||||
rmdir -v "${line:?}"
|
||||
else
|
||||
rm -v "${line:?}"
|
||||
fi
|
||||
done <<< "${XPLR_RESULT:?}"
|
||||
read -p "[Enter to continue]"
|
||||
- SwitchMode: default
|
||||
- Explore
|
||||
|
||||
D:
|
||||
help: force delete
|
||||
@ -565,15 +597,10 @@ impl Default for Config {
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
if [ ! -e "$XPLR_SELECTION" ]; then
|
||||
while IFS= read -r line; do
|
||||
rm -rf "${line:?}"
|
||||
done < ${XPLR_PIPE_SELECTION_OUT:?}
|
||||
else
|
||||
rm -rf ${XPLR_FOCUS_PATH:?}
|
||||
fi
|
||||
read -p "[Enter to continue]"
|
||||
echo -e "${XPLR_RESULT:?}" | xargs -l rm -rfv
|
||||
read -p "[enter to continue]"
|
||||
- SwitchMode: default
|
||||
- Explore
|
||||
|
||||
ctrl-c:
|
||||
help: cancel & quit
|
||||
|
32
src/main.rs
32
src/main.rs
@ -138,18 +138,7 @@ fn main() -> Result<(), Error> {
|
||||
}
|
||||
|
||||
app::MsgOut::PrintResultAndQuit => {
|
||||
let out = if app.selection().is_empty() {
|
||||
app.focused_node()
|
||||
.map(|n| n.absolute_path.clone())
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
app.selection()
|
||||
.into_iter()
|
||||
.map(|n| n.absolute_path.clone())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
};
|
||||
output = Some(out);
|
||||
output = Some(app.result_str());
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
@ -163,6 +152,14 @@ fn main() -> Result<(), Error> {
|
||||
terminal.clear()?;
|
||||
}
|
||||
|
||||
app::MsgOut::Explore => {
|
||||
explorer::explore(
|
||||
app.pwd().clone(),
|
||||
app.focused_node().map(|n| n.relative_path.clone()),
|
||||
tx_explorer.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
app::MsgOut::Refresh => {
|
||||
if app.pwd() != &last_pwd {
|
||||
explorer::explore(
|
||||
@ -184,6 +181,8 @@ fn main() -> Result<(), Error> {
|
||||
|
||||
fs::write(&app.pipes().focus_out, focused).unwrap();
|
||||
|
||||
app = app.refresh_selection()?;
|
||||
|
||||
let selection = app
|
||||
.selection()
|
||||
.iter()
|
||||
@ -222,7 +221,7 @@ fn main() -> Result<(), Error> {
|
||||
.iter()
|
||||
.map(|n| n.absolute_path.clone())
|
||||
.collect::<Vec<String>>()
|
||||
.join(",");
|
||||
.join("\n");
|
||||
|
||||
let directory_nodes = app
|
||||
.directory_buffer()
|
||||
@ -231,7 +230,7 @@ fn main() -> Result<(), Error> {
|
||||
.iter()
|
||||
.map(|n| n.absolute_path.clone())
|
||||
.collect::<Vec<String>>()
|
||||
.join(",")
|
||||
.join("\n")
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
@ -240,6 +239,8 @@ fn main() -> Result<(), Error> {
|
||||
let pipe_selection_out = app.pipes().selection_out.clone();
|
||||
|
||||
let app_yaml = serde_yaml::to_string(&app).unwrap_or_default();
|
||||
let session_path = app.session_path();
|
||||
let result = app.result_str();
|
||||
|
||||
let _ = std::process::Command::new(cmd.command.clone())
|
||||
.current_dir(app.pwd())
|
||||
@ -248,11 +249,12 @@ fn main() -> Result<(), Error> {
|
||||
.env("XPLR_FOCUS_PATH", focus_path)
|
||||
.env("XPLR_FOCUS_INDEX", focus_index)
|
||||
.env("XPLR_SELECTION", selection)
|
||||
.env("XPLR_SESSION_PATH", app.session_path())
|
||||
.env("XPLR_SESSION_PATH", session_path)
|
||||
.env("XPLR_PIPE_MSG_IN", pipe_msg_in)
|
||||
.env("XPLR_PIPE_SELECTION_OUT", pipe_selection_out)
|
||||
.env("XPLR_PIPE_FOCUS_OUT", pipe_focus_out)
|
||||
.env("XPLR_APP_YAML", app_yaml)
|
||||
.env("XPLR_RESULT", result)
|
||||
.env("XPLR_DIRECTORY_NODES", directory_nodes)
|
||||
.args(cmd.args.clone())
|
||||
.status();
|
||||
|
11
src/ui.rs
11
src/ui.rs
@ -358,7 +358,7 @@ fn draw_help_menu<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &
|
||||
}
|
||||
|
||||
fn draw_input_buffer<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &Handlebars) {
|
||||
let input_buf = Paragraph::new(format!("> {}", app.input_buffer().unwrap_or(&"".into())))
|
||||
let input_buf = Paragraph::new(format!("> {}", app.input_buffer().unwrap_or("".into())))
|
||||
.block(Block::default().borders(Borders::ALL).title(" input "));
|
||||
f.render_widget(input_buf, rect);
|
||||
}
|
||||
@ -373,10 +373,15 @@ pub fn draw<B: Backend>(f: &mut Frame<B>, app: &app::App, hb: &Handlebars) {
|
||||
|
||||
let left_chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([TUIConstraint::Length(rect.height - 3), TUIConstraint::Min(3)].as_ref())
|
||||
.constraints(
|
||||
[
|
||||
TUIConstraint::Length(rect.height - 3),
|
||||
TUIConstraint::Length(3),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(chunks[0]);
|
||||
|
||||
|
||||
let right_chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([TUIConstraint::Percentage(50), TUIConstraint::Percentage(50)].as_ref())
|
||||
|
Loading…
Reference in New Issue
Block a user