mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: confirm on deleting
This commit is contained in:
parent
9f5707ad74
commit
2279d95fab
6 changed files with 74 additions and 35 deletions
|
|
@ -24,7 +24,13 @@ pub struct Input {
|
||||||
pub struct InputOpt {
|
pub struct InputOpt {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub position: (u16, u16),
|
pub position: InputPos,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum InputPos {
|
||||||
|
Top,
|
||||||
|
Hovered,
|
||||||
|
Coords(u16, u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
|
|
@ -46,7 +52,10 @@ impl Input {
|
||||||
pub fn show(&mut self, opt: InputOpt, tx: Sender<Result<String>>) {
|
pub fn show(&mut self, opt: InputOpt, tx: Sender<Result<String>>) {
|
||||||
self.title = opt.title;
|
self.title = opt.title;
|
||||||
self.value = opt.value;
|
self.value = opt.value;
|
||||||
self.position = opt.position;
|
self.position = match opt.position {
|
||||||
|
InputPos::Coords(x, y) => (x, y),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
self.mode = InputMode::Insert;
|
self.mode = InputMode::Insert;
|
||||||
self.cursor = self.count();
|
self.cursor = self.count();
|
||||||
|
|
@ -276,7 +285,9 @@ impl Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn top_position() -> (u16, u16) { ((tty_size().ws_col / 2).saturating_sub(25), 2) }
|
pub fn top_position() -> InputPos {
|
||||||
|
InputPos::Coords((tty_size().ws_col / 2).saturating_sub(25), 2)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn count(&self) -> usize { self.value.chars().count() }
|
fn count(&self) -> usize { self.value.chars().count() }
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,9 @@ impl Folder {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn has_selected(&self) -> bool { self.files.iter().any(|(_, item)| item.is_selected) }
|
||||||
|
|
||||||
pub fn selected(&self) -> Option<Vec<PathBuf>> {
|
pub fn selected(&self) -> Option<Vec<PathBuf>> {
|
||||||
let v = self
|
let v = self
|
||||||
.files
|
.files
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{collections::{BTreeSet, HashMap, HashSet}, mem, path::PathBuf};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
use super::{PreviewData, Tab, Tabs, Watcher};
|
use super::{PreviewData, Tab, Tabs, Watcher};
|
||||||
use crate::{core::{files::{File, FilesOp}, input::{Input, InputOpt}, manager::Folder, tasks::Precache}, emit};
|
use crate::{core::{files::{File, FilesOp}, input::{Input, InputOpt, InputPos}, manager::Folder, tasks::Precache}, emit};
|
||||||
|
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
tabs: Tabs,
|
tabs: Tabs,
|
||||||
|
|
@ -92,14 +92,12 @@ impl Manager {
|
||||||
pub fn yanked(&self) -> &(bool, HashSet<PathBuf>) { &self.yanked }
|
pub fn yanked(&self) -> &(bool, HashSet<PathBuf>) { &self.yanked }
|
||||||
|
|
||||||
pub fn create(&self) -> bool {
|
pub fn create(&self) -> bool {
|
||||||
let pos = Input::top_position();
|
|
||||||
let cwd = self.current().cwd.clone();
|
let cwd = self.current().cwd.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let result = emit!(Input(InputOpt {
|
let result = emit!(Input(InputOpt {
|
||||||
title: "Create:".to_string(),
|
title: "Create:".to_string(),
|
||||||
value: "".to_string(),
|
value: "".to_string(),
|
||||||
position: pos,
|
position: InputPos::Top,
|
||||||
}))
|
}))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|
@ -113,35 +111,33 @@ impl Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename(&self) -> bool {
|
pub fn rename(&self) -> bool {
|
||||||
let selected = self.selected();
|
if self.current().has_selected() {
|
||||||
if selected.is_empty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if selected.len() > 1 {
|
|
||||||
return self.bulk_rename();
|
return self.bulk_rename();
|
||||||
}
|
}
|
||||||
|
|
||||||
let rect = self.current().rect_current(&selected[0]).unwrap();
|
let hovered = if let Some(h) = self.hovered() {
|
||||||
|
h.path.clone()
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let result = emit!(Input(InputOpt {
|
let result = emit!(Input(InputOpt {
|
||||||
title: "Rename:".to_string(),
|
title: "Rename:".to_string(),
|
||||||
value: selected[0].file_name().unwrap().to_string_lossy().to_string(),
|
value: hovered.file_name().unwrap().to_string_lossy().to_string(),
|
||||||
position: (rect.x, rect.y),
|
position: InputPos::Hovered,
|
||||||
}))
|
}))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Ok(new) = result {
|
if let Ok(new) = result {
|
||||||
let to = selected[0].parent().unwrap().join(new);
|
let to = hovered.parent().unwrap().join(new);
|
||||||
fs::rename(&selected[0], to).await.ok();
|
fs::rename(&hovered, to).await.ok();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use tokio::task::JoinHandle;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use super::{Folder, Mode, Preview};
|
use super::{Folder, Mode, Preview};
|
||||||
use crate::{core::{external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::{Input, InputOpt}, Event, BLOCKER}, emit, misc::Defer};
|
use crate::{core::{external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::{Input, InputOpt, InputPos}, Event, BLOCKER}, emit, misc::Defer};
|
||||||
|
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
pub(super) current: Folder,
|
pub(super) current: Folder,
|
||||||
|
|
@ -184,12 +184,11 @@ impl Tab {
|
||||||
let cwd = self.current.cwd.clone();
|
let cwd = self.current.cwd.clone();
|
||||||
let hidden = self.current.files.show_hidden;
|
let hidden = self.current.files.show_hidden;
|
||||||
|
|
||||||
let pos = Input::top_position();
|
|
||||||
self.search = Some(tokio::spawn(async move {
|
self.search = Some(tokio::spawn(async move {
|
||||||
let subject = emit!(Input(InputOpt {
|
let subject = emit!(Input(InputOpt {
|
||||||
title: "Search:".to_string(),
|
title: "Search:".to_string(),
|
||||||
value: "".to_string(),
|
value: "".to_string(),
|
||||||
position: pos,
|
position: InputPos::Top,
|
||||||
}))
|
}))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{collections::{BTreeMap, HashSet}, path::PathBuf};
|
use std::{collections::{BTreeMap, HashSet}, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use super::{Scheduler, TASKS_PADDING, TASKS_PERCENT};
|
use super::{Scheduler, TASKS_PADDING, TASKS_PERCENT};
|
||||||
use crate::{config::OPEN, misc::tty_size};
|
use crate::{config::OPEN, core::input::{Input, InputOpt, InputPos}, emit, misc::tty_size};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
|
|
@ -43,7 +43,7 @@ pub enum TaskStage {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Tasks {
|
pub struct Tasks {
|
||||||
scheduler: Scheduler,
|
scheduler: Arc<Scheduler>,
|
||||||
|
|
||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
pub cursor: usize,
|
pub cursor: usize,
|
||||||
|
|
@ -52,7 +52,12 @@ pub struct Tasks {
|
||||||
|
|
||||||
impl Tasks {
|
impl Tasks {
|
||||||
pub fn start() -> Self {
|
pub fn start() -> Self {
|
||||||
Self { scheduler: Scheduler::start(), visible: false, cursor: 0, progress: (100, 0) }
|
Self {
|
||||||
|
scheduler: Arc::new(Scheduler::start()),
|
||||||
|
visible: false,
|
||||||
|
cursor: 0,
|
||||||
|
progress: (100, 0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -143,13 +148,28 @@ impl Tasks {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_remove(&self, targets: Vec<PathBuf>, permanently: bool) -> bool {
|
pub fn file_remove(&self, targets: Vec<PathBuf>, permanently: bool) -> bool {
|
||||||
for p in targets {
|
let scheduler = self.scheduler.clone();
|
||||||
if permanently {
|
tokio::spawn(async move {
|
||||||
self.scheduler.file_delete(p);
|
let result = emit!(Input(InputOpt {
|
||||||
} else {
|
title: "Are you sure delete these files? (Y/n)".to_string(),
|
||||||
self.scheduler.file_trash(p);
|
value: "".to_string(),
|
||||||
|
position: InputPos::Hovered,
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if let Ok(choice) = result {
|
||||||
|
if choice.to_lowercase() != "y" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for p in targets {
|
||||||
|
if permanently {
|
||||||
|
scheduler.file_delete(p);
|
||||||
|
} else {
|
||||||
|
scheduler.file_trash(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crossterm::event::KeyEvent;
|
||||||
use tokio::sync::oneshot::{self};
|
use tokio::sync::oneshot::{self};
|
||||||
|
|
||||||
use super::{root::Root, Ctx, Executor, Logs, Signals, Term};
|
use super::{root::Root, Ctx, Executor, Logs, Signals, Term};
|
||||||
use crate::{config::keymap::Key, core::{files::FilesOp, Event}, emit};
|
use crate::{config::keymap::Key, core::{files::FilesOp, input::{Input, InputPos}, Event}, emit};
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
cx: Ctx,
|
cx: Ctx,
|
||||||
|
|
@ -104,7 +104,17 @@ impl App {
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Input(opt, tx) => {
|
Event::Input(mut opt, tx) => {
|
||||||
|
opt.position = match opt.position {
|
||||||
|
InputPos::Top => Input::top_position(),
|
||||||
|
InputPos::Hovered => manager
|
||||||
|
.hovered()
|
||||||
|
.and_then(|h| manager.current().rect_current(&h.path))
|
||||||
|
.map(|r| InputPos::Coords(r.x, r.y))
|
||||||
|
.unwrap_or_else(|| Input::top_position()),
|
||||||
|
p @ InputPos::Coords(..) => p,
|
||||||
|
};
|
||||||
|
|
||||||
self.cx.input.show(opt, tx);
|
self.cx.input.show(opt, tx);
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue