feat: allow references to the selected files during shell execution (#73)

This commit is contained in:
三咲雅 · Misaki Masa 2023-08-19 22:27:49 +08:00 committed by GitHub
parent 88d67af2eb
commit a437347306
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 65 deletions

View file

@ -106,7 +106,7 @@ impl Executor {
}
"create" => cx.manager.create(),
"rename" => cx.manager.rename(),
"copy" => cx.manager.copy(exec.args.get(0).map(|s| s.as_str()).unwrap_or("")),
"copy" => cx.manager.active().copy(exec.args.get(0).map(|s| s.as_str()).unwrap_or("")),
"shell" => cx.manager.active().shell(
exec.args.get(0).map(|e| e.as_str()).unwrap_or(""),
exec.named.contains_key("block"),

View file

@ -1,4 +1,4 @@
use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::{OsStr, OsString}, io::{stdout, BufWriter, Write}, mem, path::{Path, PathBuf}};
use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, io::{stdout, BufWriter, Write}, mem, path::{Path, PathBuf}};
use anyhow::{anyhow, bail, Error, Result};
use config::{BOOT, OPEN};
@ -194,7 +194,7 @@ impl Manager {
}
pub fn rename(&self) -> bool {
if self.in_selecting() {
if self.active().in_selecting() {
return self.bulk_rename();
}
@ -319,26 +319,6 @@ impl Manager {
Ok(())
}
pub fn copy(&self, type_: &str) -> bool {
let mut s = OsString::new();
let mut it = self.selected().into_iter().peekable();
while let Some(f) = it.next() {
s.push(match type_ {
"path" => f.path.as_os_str(),
"dirname" => f.path.parent().map_or(OsStr::new(""), |p| p.as_os_str()),
"filename" => f.path.file_name().unwrap_or(OsStr::new("")),
"name_without_ext" => f.path.file_stem().unwrap_or(OsStr::new("")),
_ => return false,
});
if it.peek().is_some() {
s.push("\n");
}
}
futures::executor::block_on(external::clipboard_set(s)).ok();
false
}
pub fn update_read(&mut self, op: FilesOp) -> bool {
let path = op.path();
let cwd = self.cwd().to_owned();
@ -457,28 +437,8 @@ impl Manager {
pub fn hovered(&self) -> Option<&File> { self.tabs.active().current.hovered.as_ref() }
#[inline]
pub fn yanked(&self) -> &(bool, HashSet<PathBuf>) { &self.yanked }
pub fn selected(&self) -> Vec<&File> {
let mode = &self.active().mode;
let files = &self.current().files;
let selected: Vec<_> = if !mode.is_visual() {
files.iter().filter(|(_, f)| f.is_selected).map(|(_, f)| f).collect()
} else {
files
.iter()
.enumerate()
.filter(|(i, (_, f))| mode.pending(*i, f.is_selected))
.map(|(_, (_, f))| f)
.collect()
};
if selected.is_empty() { self.hovered().map(|h| vec![h]).unwrap_or_default() } else { selected }
}
pub fn selected(&self) -> Vec<&File> { self.tabs.active().selected() }
#[inline]
pub fn in_selecting(&self) -> bool {
self.active().mode.is_visual() || self.current().has_selected()
}
pub fn yanked(&self) -> &(bool, HashSet<PathBuf>) { &self.yanked }
}

View file

@ -1,4 +1,4 @@
use std::{collections::{BTreeMap, BTreeSet}, mem, path::{Path, PathBuf}};
use std::{collections::{BTreeMap, BTreeSet}, ffi::{OsStr, OsString}, mem, path::{Path, PathBuf}};
use anyhow::{Error, Result};
use config::open::Opener;
@ -185,6 +185,44 @@ impl Tab {
// TODO
pub fn forward(&mut self) -> bool { false }
pub fn select(&mut self, state: Option<bool>) -> bool {
let idx = Some(self.current.cursor());
self.current.select(idx, state)
}
pub fn select_all(&mut self, state: Option<bool>) -> bool { self.current.select(None, state) }
pub fn visual_mode(&mut self, unset: bool) -> bool {
let idx = self.current.cursor();
if unset {
self.mode = Mode::Unset(idx, BTreeSet::from([idx]));
} else {
self.mode = Mode::Select(idx, BTreeSet::from([idx]));
};
true
}
pub fn copy(&self, type_: &str) -> bool {
let mut s = OsString::new();
let mut it = self.selected().into_iter().peekable();
while let Some(f) = it.next() {
s.push(match type_ {
"path" => f.path.as_os_str(),
"dirname" => f.path.parent().map_or(OsStr::new(""), |p| p.as_os_str()),
"filename" => f.path.file_name().unwrap_or(OsStr::new("")),
"name_without_ext" => f.path.file_stem().unwrap_or(OsStr::new("")),
_ => return false,
});
if it.peek().is_some() {
s.push("\n");
}
}
futures::executor::block_on(external::clipboard_set(s)).ok();
false
}
pub fn search(&mut self, grep: bool) -> bool {
if let Some(handle) = self.search.take() {
handle.abort();
@ -249,6 +287,12 @@ impl Tab {
}
pub fn shell(&self, exec: &str, block: bool, confirm: bool) -> bool {
let selected: Vec<_> = self
.selected()
.into_iter()
.map(|f| (f.path.as_os_str().to_owned(), Default::default()))
.collect();
let mut exec = exec.to_owned();
tokio::spawn(async move {
if !confirm || exec.is_empty() {
@ -260,31 +304,13 @@ impl Tab {
}
emit!(Open(
Default::default(),
selected,
Some(Opener { exec, block, display_name: Default::default(), spread: true })
));
});
false
}
pub fn select(&mut self, state: Option<bool>) -> bool {
let idx = Some(self.current.cursor());
self.current.select(idx, state)
}
pub fn select_all(&mut self, state: Option<bool>) -> bool { self.current.select(None, state) }
pub fn visual_mode(&mut self, unset: bool) -> bool {
let idx = self.current.cursor();
if unset {
self.mode = Mode::Unset(idx, BTreeSet::from([idx]));
} else {
self.mode = Mode::Select(idx, BTreeSet::from([idx]));
};
true
}
}
impl Tab {
@ -302,6 +328,31 @@ impl Tab {
.unwrap_or_default()
}
pub fn selected(&self) -> Vec<&File> {
let mode = self.mode();
let files = &self.current.files;
let selected: Vec<_> = if !mode.is_visual() {
files.iter().filter(|(_, f)| f.is_selected).map(|(_, f)| f).collect()
} else {
files
.iter()
.enumerate()
.filter(|(i, (_, f))| mode.pending(*i, f.is_selected))
.map(|(_, (_, f))| f)
.collect()
};
if selected.is_empty() {
self.current.hovered.as_ref().map(|h| vec![h]).unwrap_or_default()
} else {
selected
}
}
#[inline]
pub fn in_selecting(&self) -> bool { self.mode().is_visual() || self.current.has_selected() }
#[inline]
pub fn history(&self, path: &Path) -> Option<&Folder> { self.history.get(path) }