mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
perf: new UI rendering architecture (#468)
This commit is contained in:
parent
10a78b5dbc
commit
5c62cf2c65
93 changed files with 536 additions and 520 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::completion::Completion;
|
use crate::completion::Completion;
|
||||||
|
|
||||||
|
|
@ -13,10 +13,10 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Completion {
|
impl Completion {
|
||||||
fn next(&mut self, step: usize) -> bool {
|
fn next(&mut self, step: usize) {
|
||||||
let len = self.cands.len();
|
let len = self.cands.len();
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let old = self.cursor;
|
let old = self.cursor;
|
||||||
|
|
@ -27,10 +27,10 @@ impl Completion {
|
||||||
self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old);
|
self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old);
|
||||||
}
|
}
|
||||||
|
|
||||||
old != self.cursor
|
render!(old != self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev(&mut self, step: usize) -> bool {
|
fn prev(&mut self, step: usize) {
|
||||||
let old = self.cursor;
|
let old = self.cursor;
|
||||||
self.cursor = self.cursor.saturating_sub(step);
|
self.cursor = self.cursor.saturating_sub(step);
|
||||||
|
|
||||||
|
|
@ -38,11 +38,15 @@ impl Completion {
|
||||||
self.offset = self.offset.saturating_sub(old - self.cursor);
|
self.offset = self.offset.saturating_sub(old - self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
old != self.cursor
|
render!(old != self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arrow(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn arrow(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.step > 0 { self.next(opt.step as usize) } else { self.prev(opt.step.unsigned_abs()) }
|
if opt.step > 0 {
|
||||||
|
self.next(opt.step as usize);
|
||||||
|
} else {
|
||||||
|
self.prev(opt.step.unsigned_abs());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::{emit, event::Exec, Layer};
|
use yazi_shared::{emit, event::Exec, render, Layer};
|
||||||
|
|
||||||
use crate::{completion::Completion, input::Input};
|
use crate::{completion::Completion, input::Input};
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ impl Completion {
|
||||||
emit!(Call(Exec::call("close", vec![]).vec(), Layer::Completion));
|
emit!(Call(Exec::call("close", vec![]).vec(), Layer::Completion));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn close(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.submit {
|
if opt.submit {
|
||||||
Input::_complete(self.selected(), self.ticket);
|
Input::_complete(self.selected(), self.ticket);
|
||||||
|
|
@ -24,6 +24,6 @@ impl Completion {
|
||||||
|
|
||||||
self.caches.clear();
|
self.caches.clear();
|
||||||
self.visible = false;
|
self.visible = false;
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{mem, ops::ControlFlow};
|
use std::{mem, ops::ControlFlow};
|
||||||
|
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::completion::Completion;
|
use crate::completion::Completion;
|
||||||
|
|
||||||
|
|
@ -54,28 +54,28 @@ impl Completion {
|
||||||
prefixed.into_iter().map(ToOwned::to_owned).collect()
|
prefixed.into_iter().map(ToOwned::to_owned).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn show<'a>(&mut self, opt: impl Into<Opt<'a>>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if self.ticket != opt.ticket {
|
if self.ticket != opt.ticket {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opt.cache.is_empty() {
|
if !opt.cache.is_empty() {
|
||||||
self.caches.insert(opt.cache_name.to_owned(), opt.cache.clone());
|
self.caches.insert(opt.cache_name.to_owned(), opt.cache.clone());
|
||||||
}
|
}
|
||||||
let Some(cache) = self.caches.get(opt.cache_name) else {
|
let Some(cache) = self.caches.get(opt.cache_name) else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.ticket = opt.ticket;
|
self.ticket = opt.ticket;
|
||||||
self.cands = Self::match_candidates(opt.word, cache);
|
self.cands = Self::match_candidates(opt.word, cache);
|
||||||
if self.cands.is_empty() {
|
if self.cands.is_empty() {
|
||||||
return mem::replace(&mut self.visible, false);
|
return render!(mem::replace(&mut self.visible, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.offset = 0;
|
self.offset = 0;
|
||||||
self.cursor = 0;
|
self.cursor = 0;
|
||||||
self.visible = true;
|
self.visible = true;
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{mem, path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR}};
|
use std::{mem, path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR}};
|
||||||
|
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use yazi_shared::{emit, event::Exec, Layer};
|
use yazi_shared::{emit, event::Exec, render, Layer};
|
||||||
|
|
||||||
use crate::completion::Completion;
|
use crate::completion::Completion;
|
||||||
|
|
||||||
|
|
@ -28,10 +28,10 @@ impl Completion {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trigger<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn trigger<'a>(&mut self, opt: impl Into<Opt<'a>>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.ticket < self.ticket {
|
if opt.ticket < self.ticket {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ticket = opt.ticket;
|
self.ticket = opt.ticket;
|
||||||
|
|
@ -76,7 +76,7 @@ impl Completion {
|
||||||
Ok::<(), anyhow::Error>(())
|
Ok::<(), anyhow::Error>(())
|
||||||
});
|
});
|
||||||
|
|
||||||
mem::replace(&mut self.visible, false)
|
render!(mem::replace(&mut self.visible, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -391,6 +391,9 @@ impl Files {
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Filter
|
// --- Filter
|
||||||
|
#[inline]
|
||||||
|
pub fn filter(&self) -> Option<&Filter> { self.filter.as_ref() }
|
||||||
|
|
||||||
pub fn set_filter(&mut self, filter: Option<Filter>) -> bool {
|
pub fn set_filter(&mut self, filter: Option<Filter>) -> bool {
|
||||||
if self.filter == filter {
|
if self.filter == filter {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::help::Help;
|
use crate::help::Help;
|
||||||
|
|
||||||
|
|
@ -17,19 +17,23 @@ impl From<isize> for Opt {
|
||||||
|
|
||||||
impl Help {
|
impl Help {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn arrow(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn arrow(&mut self, opt: impl Into<Opt>) {
|
||||||
let max = self.bindings.len().saturating_sub(1);
|
let max = self.bindings.len().saturating_sub(1);
|
||||||
self.offset = self.offset.min(max);
|
self.offset = self.offset.min(max);
|
||||||
self.cursor = self.cursor.min(max);
|
self.cursor = self.cursor.min(max);
|
||||||
|
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.step > 0 { self.next(opt.step as usize) } else { self.prev(opt.step.unsigned_abs()) }
|
if opt.step > 0 {
|
||||||
|
self.next(opt.step as usize);
|
||||||
|
} else {
|
||||||
|
self.prev(opt.step.unsigned_abs());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self, step: usize) -> bool {
|
fn next(&mut self, step: usize) {
|
||||||
let len = self.bindings.len();
|
let len = self.bindings.len();
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let old = self.cursor;
|
let old = self.cursor;
|
||||||
|
|
@ -40,10 +44,10 @@ impl Help {
|
||||||
self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old);
|
self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old);
|
||||||
}
|
}
|
||||||
|
|
||||||
old != self.cursor
|
render!(old != self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev(&mut self, step: usize) -> bool {
|
fn prev(&mut self, step: usize) {
|
||||||
let old = self.cursor;
|
let old = self.cursor;
|
||||||
self.cursor = self.cursor.saturating_sub(step);
|
self.cursor = self.cursor.saturating_sub(step);
|
||||||
|
|
||||||
|
|
@ -51,6 +55,6 @@ impl Help {
|
||||||
self.offset = self.offset.saturating_sub(old - self.cursor);
|
self.offset = self.offset.saturating_sub(old - self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
old != self.cursor
|
render!(old != self.cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::help::Help;
|
use crate::help::Help;
|
||||||
|
|
||||||
impl Help {
|
impl Help {
|
||||||
pub fn escape(&mut self, _: &Exec) -> bool {
|
pub fn escape(&mut self, _: &Exec) {
|
||||||
if self.in_filter.is_some() {
|
if self.in_filter.is_none() {
|
||||||
self.in_filter = None;
|
return self.toggle(self.layer);
|
||||||
self.filter_apply();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
self.toggle(self.layer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.in_filter = None;
|
||||||
|
self.filter_apply();
|
||||||
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use yazi_config::popup::{Offset, Origin, Position};
|
use yazi_config::popup::{Offset, Origin, Position};
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::{help::Help, input::Input};
|
use crate::{help::Help, input::Input};
|
||||||
|
|
||||||
impl Help {
|
impl Help {
|
||||||
pub fn filter(&mut self, _: &Exec) -> bool {
|
pub fn filter(&mut self, _: &Exec) {
|
||||||
let mut input = Input::default();
|
let mut input = Input::default();
|
||||||
input.position = Position::new(Origin::BottomLeft, Offset::line());
|
input.position = Position::new(Origin::BottomLeft, Offset::line());
|
||||||
|
|
||||||
self.in_filter = Some(input);
|
self.in_filter = Some(input);
|
||||||
self.filter_apply();
|
self.filter_apply();
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crossterm::event::KeyCode;
|
use crossterm::event::KeyCode;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use yazi_config::{keymap::{Control, Key}, KEYMAP};
|
use yazi_config::{keymap::{Control, Key}, KEYMAP};
|
||||||
use yazi_shared::{term::Term, Layer};
|
use yazi_shared::{render, term::Term, Layer};
|
||||||
|
|
||||||
use super::HELP_MARGIN;
|
use super::HELP_MARGIN;
|
||||||
use crate::input::Input;
|
use crate::input::Input;
|
||||||
|
|
@ -24,7 +24,7 @@ impl Help {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn limit() -> usize { Term::size().rows.saturating_sub(HELP_MARGIN) as usize }
|
pub fn limit() -> usize { Term::size().rows.saturating_sub(HELP_MARGIN) as usize }
|
||||||
|
|
||||||
pub fn toggle(&mut self, layer: Layer) -> bool {
|
pub fn toggle(&mut self, layer: Layer) {
|
||||||
self.visible = !self.visible;
|
self.visible = !self.visible;
|
||||||
self.layer = layer;
|
self.layer = layer;
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ impl Help {
|
||||||
|
|
||||||
self.offset = 0;
|
self.offset = 0;
|
||||||
self.cursor = 0;
|
self.cursor = 0;
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn filter_apply(&mut self) -> bool {
|
pub(super) fn filter_apply(&mut self) -> bool {
|
||||||
|
|
@ -61,17 +61,20 @@ impl Help {
|
||||||
|
|
||||||
if key.is_enter() {
|
if key.is_enter() {
|
||||||
self.in_filter = None;
|
self.in_filter = None;
|
||||||
|
render!();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let b = match &key {
|
match &key {
|
||||||
Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false } => {
|
Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false } => {
|
||||||
input.backspace(false)
|
input.backspace(false);
|
||||||
}
|
}
|
||||||
_ => input.type_(key),
|
_ => {
|
||||||
};
|
input.type_(key);
|
||||||
|
}
|
||||||
if b { self.filter_apply() } else { false }
|
}
|
||||||
|
self.filter_apply();
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::input::Input;
|
use crate::input::Input;
|
||||||
|
|
||||||
|
|
@ -14,14 +14,14 @@ impl From<bool> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn backspace(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn backspace(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let snap = self.snaps.current_mut();
|
let snap = self.snaps.current_mut();
|
||||||
|
|
||||||
if !opt.under && snap.cursor < 1 {
|
if !opt.under && snap.cursor < 1 {
|
||||||
return false;
|
return;
|
||||||
} else if opt.under && snap.cursor >= snap.value.len() {
|
} else if opt.under && snap.cursor >= snap.value.len() {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if opt.under {
|
if opt.under {
|
||||||
|
|
@ -33,6 +33,6 @@ impl Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flush_value();
|
self.flush_value();
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use yazi_shared::{event::Exec, CharKind};
|
||||||
use crate::input::Input;
|
use crate::input::Input;
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn backward(&mut self, _: &Exec) -> bool {
|
pub fn backward(&mut self, _: &Exec) {
|
||||||
let snap = self.snap();
|
let snap = self.snap();
|
||||||
if snap.cursor == 0 {
|
if snap.cursor == 0 {
|
||||||
return self.move_(0);
|
return self.move_(0);
|
||||||
|
|
@ -21,8 +21,7 @@ impl Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
if prev != CharKind::Space {
|
if prev != CharKind::Space {
|
||||||
return self.move_(-(snap.len() as isize));
|
self.move_(-(snap.len() as isize));
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::{event::Exec, InputError};
|
use yazi_shared::{event::Exec, render, InputError};
|
||||||
|
|
||||||
use crate::{completion::Completion, input::Input};
|
use crate::{completion::Completion, input::Input};
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ impl From<bool> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn close(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn close(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
if self.completion {
|
if self.completion {
|
||||||
|
|
@ -28,6 +28,6 @@ impl Input {
|
||||||
|
|
||||||
self.ticket = self.ticket.wrapping_add(1);
|
self.ticket = self.ticket.wrapping_add(1);
|
||||||
self.visible = false;
|
self.visible = false;
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::MAIN_SEPARATOR;
|
use std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
use yazi_shared::{emit, event::Exec, Layer};
|
use yazi_shared::{emit, event::Exec, render, Layer};
|
||||||
|
|
||||||
use crate::input::Input;
|
use crate::input::Input;
|
||||||
|
|
||||||
|
|
@ -27,10 +27,10 @@ impl Input {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn complete<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn complete<'a>(&mut self, opt: impl Into<Opt<'a>>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if self.ticket != opt.ticket {
|
if self.ticket != opt.ticket {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [before, after] = self.partition();
|
let [before, after] = self.partition();
|
||||||
|
|
@ -42,7 +42,7 @@ impl Input {
|
||||||
|
|
||||||
let snap = self.snaps.current_mut();
|
let snap = self.snaps.current_mut();
|
||||||
if new == snap.value {
|
if new == snap.value {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let delta = new.chars().count() as isize - snap.value.chars().count() as isize;
|
let delta = new.chars().count() as isize - snap.value.chars().count() as isize;
|
||||||
|
|
@ -50,6 +50,6 @@ impl Input {
|
||||||
|
|
||||||
self.move_(delta);
|
self.move_(delta);
|
||||||
self.flush_value();
|
self.flush_value();
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::input::{op::InputOp, Input};
|
use crate::input::{op::InputOp, Input};
|
||||||
|
|
||||||
|
|
@ -14,22 +14,22 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn delete(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn delete(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
match self.snap().op {
|
match self.snap().op {
|
||||||
InputOp::None => {
|
InputOp::None => {
|
||||||
self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, self.snap().cursor);
|
self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, self.snap().cursor);
|
||||||
false
|
|
||||||
}
|
}
|
||||||
InputOp::Select(start) => {
|
InputOp::Select(start) => {
|
||||||
self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, start);
|
self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, start);
|
||||||
return self.handle_op(self.snap().cursor, true).then(|| self.move_(0)).is_some();
|
render!(self.handle_op(self.snap().cursor, true));
|
||||||
|
self.move_(0);
|
||||||
}
|
}
|
||||||
InputOp::Delete(..) => {
|
InputOp::Delete(..) => {
|
||||||
self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, 0);
|
self.snap_mut().op = InputOp::Delete(opt.cut, opt.insert, 0);
|
||||||
return self.move_(self.snap().len() as isize);
|
self.move_(self.snap().len() as isize);
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::{completion::Completion, input::{op::InputOp, Input, InputMode}};
|
use crate::{completion::Completion, input::{op::InputOp, Input, InputMode}};
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ impl From<()> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn escape(&mut self, _: impl Into<Opt>) -> bool {
|
pub fn escape(&mut self, _: impl Into<Opt>) {
|
||||||
let snap = self.snap_mut();
|
let snap = self.snap_mut();
|
||||||
match snap.mode {
|
match snap.mode {
|
||||||
InputMode::Normal if snap.op == InputOp::None => {
|
InputMode::Normal if snap.op == InputOp::None => {
|
||||||
|
|
@ -30,7 +30,8 @@ impl Input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.snaps.tag(self.limit());
|
self.snaps.tag(self.limit());
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn forward(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn forward(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let snap = self.snap();
|
let snap = self.snap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::input::{op::InputOp, Input, InputMode};
|
use crate::input::{op::InputOp, Input, InputMode};
|
||||||
|
|
||||||
|
|
@ -14,13 +14,13 @@ impl From<bool> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn insert(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn insert(&mut self, opt: impl Into<Opt>) {
|
||||||
let snap = self.snap_mut();
|
let snap = self.snap_mut();
|
||||||
if snap.mode == InputMode::Normal {
|
if snap.mode == InputMode::Normal {
|
||||||
snap.op = InputOp::None;
|
snap.op = InputOp::None;
|
||||||
snap.mode = InputMode::Insert;
|
snap.mode = InputMode::Insert;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
@ -28,6 +28,6 @@ impl Input {
|
||||||
self.move_(1);
|
self.move_(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::ops::RangeBounds;
|
use std::ops::RangeBounds;
|
||||||
|
|
||||||
use yazi_shared::{event::Exec, CharKind};
|
use yazi_shared::{event::Exec, render, CharKind};
|
||||||
|
|
||||||
use crate::input::Input;
|
use crate::input::Input;
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ impl<'a> From<&'a Exec> for Opt<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
fn kill_range(&mut self, range: impl RangeBounds<usize>) -> bool {
|
fn kill_range(&mut self, range: impl RangeBounds<usize>) {
|
||||||
let snap = self.snap_mut();
|
let snap = self.snap_mut();
|
||||||
snap.cursor = match range.start_bound() {
|
snap.cursor = match range.start_bound() {
|
||||||
std::ops::Bound::Included(i) => *i,
|
std::ops::Bound::Included(i) => *i,
|
||||||
|
|
@ -23,12 +23,12 @@ impl Input {
|
||||||
std::ops::Bound::Unbounded => 0,
|
std::ops::Bound::Unbounded => 0,
|
||||||
};
|
};
|
||||||
if snap.value.drain(range).next().is_none() {
|
if snap.value.drain(range).next().is_none() {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.move_(0);
|
self.move_(0);
|
||||||
self.flush_value();
|
self.flush_value();
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Searches for a word boundary and returns the movement in the cursor
|
/// Searches for a word boundary and returns the movement in the cursor
|
||||||
|
|
@ -66,7 +66,7 @@ impl Input {
|
||||||
spaces + count_characters(input.skip(spaces))
|
spaces + count_characters(input.skip(spaces))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kill<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn kill<'a>(&mut self, opt: impl Into<Opt<'a>>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let snap = self.snap_mut();
|
let snap = self.snap_mut();
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ impl Input {
|
||||||
let end = start + Self::find_word_boundary(snap.value[start..].chars());
|
let end = start + Self::find_word_boundary(snap.value[start..].chars());
|
||||||
self.kill_range(start..end)
|
self.kill_range(start..end)
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::input::{op::InputOp, snap::InputSnap, Input};
|
use crate::input::{op::InputOp, snap::InputSnap, Input};
|
||||||
|
|
||||||
|
|
@ -21,22 +21,22 @@ impl From<isize> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn move_(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn move_(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
let snap = self.snap();
|
let snap = self.snap();
|
||||||
if opt.in_operating && snap.op == InputOp::None {
|
if opt.in_operating && snap.op == InputOp::None {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let b = self.handle_op(
|
render!(self.handle_op(
|
||||||
if opt.step <= 0 {
|
if opt.step <= 0 {
|
||||||
snap.cursor.saturating_sub(opt.step.unsigned_abs())
|
snap.cursor.saturating_sub(opt.step.unsigned_abs())
|
||||||
} else {
|
} else {
|
||||||
snap.count().min(snap.cursor + opt.step as usize)
|
snap.count().min(snap.cursor + opt.step as usize)
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
));
|
||||||
|
|
||||||
let (limit, snap) = (self.limit(), self.snap_mut());
|
let (limit, snap) = (self.limit(), self.snap_mut());
|
||||||
if snap.offset > snap.cursor {
|
if snap.offset > snap.cursor {
|
||||||
|
|
@ -51,7 +51,5 @@ impl Input {
|
||||||
snap.offset = snap.cursor - InputSnap::find_window(&s, 0, limit).end.saturating_sub(delta);
|
snap.offset = snap.cursor - InputSnap::find_window(&s, 0, limit).end.saturating_sub(delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::{input::{op::InputOp, Input}, CLIPBOARD};
|
use crate::{input::{op::InputOp, Input}, CLIPBOARD};
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn paste(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn paste(&mut self, opt: impl Into<Opt>) {
|
||||||
if let Some(start) = self.snap().op.start() {
|
if let Some(start) = self.snap().op.start() {
|
||||||
self.snap_mut().op = InputOp::Delete(false, false, start);
|
self.snap_mut().op = InputOp::Delete(false, false, start);
|
||||||
self.handle_op(self.snap().cursor, true);
|
self.handle_op(self.snap().cursor, true);
|
||||||
|
|
@ -19,13 +19,13 @@ impl Input {
|
||||||
|
|
||||||
let s = futures::executor::block_on(CLIPBOARD.get());
|
let s = futures::executor::block_on(CLIPBOARD.get());
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
self.insert(!opt.before);
|
self.insert(!opt.before);
|
||||||
self.type_str(&s.to_string_lossy());
|
self.type_str(&s.to_string_lossy());
|
||||||
self.escape(());
|
self.escape(());
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::input::Input;
|
use crate::input::Input;
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn redo(&mut self, _: &Exec) -> bool { self.snaps.redo() }
|
pub fn redo(&mut self, _: &Exec) {
|
||||||
|
render!(self.snaps.redo());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_shared::{emit, event::Exec, InputError, Layer};
|
use yazi_shared::{emit, event::Exec, render, InputError, Layer};
|
||||||
|
|
||||||
use crate::input::Input;
|
use crate::input::Input;
|
||||||
|
|
||||||
|
|
@ -25,9 +25,9 @@ impl Input {
|
||||||
rx
|
rx
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&mut self, opt: impl TryInto<Opt>) -> bool {
|
pub fn show(&mut self, opt: impl TryInto<Opt>) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let Ok(opt) = opt.try_into() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.close(false);
|
self.close(false);
|
||||||
|
|
@ -45,6 +45,6 @@ impl Input {
|
||||||
|
|
||||||
// Reset snaps
|
// Reset snaps
|
||||||
self.snaps.reset(opt.cfg.value, self.limit());
|
self.snaps.reset(opt.cfg.value, self.limit());
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,10 @@ impl Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(c) = key.plain() {
|
if let Some(c) = key.plain() {
|
||||||
let mut bits = [0; 4];
|
self.type_str(c.encode_utf8(&mut [0; 4]));
|
||||||
return self.type_str(c.encode_utf8(&mut bits));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::input::{Input, InputMode};
|
use crate::input::{Input, InputMode};
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn undo(&mut self, _: &Exec) -> bool {
|
pub fn undo(&mut self, _: &Exec) {
|
||||||
if !self.snaps.undo() {
|
if !self.snaps.undo() {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
if self.snap().mode == InputMode::Insert {
|
if self.snap().mode == InputMode::Insert {
|
||||||
self.escape(());
|
self.escape(());
|
||||||
}
|
}
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::input::{op::InputOp, Input, InputMode};
|
use crate::input::{op::InputOp, Input, InputMode};
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn visual(&mut self, _: &Exec) -> bool {
|
pub fn visual(&mut self, _: &Exec) {
|
||||||
let snap = self.snap_mut();
|
let snap = self.snap_mut();
|
||||||
if snap.mode != InputMode::Normal {
|
if snap.mode != InputMode::Normal {
|
||||||
return false;
|
return;
|
||||||
} else if snap.value.is_empty() {
|
} else if snap.value.is_empty() {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
snap.op = InputOp::Select(snap.cursor);
|
snap.op = InputOp::Select(snap.cursor);
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::input::{op::InputOp, Input};
|
use crate::input::{op::InputOp, Input};
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn yank(&mut self, _: &Exec) -> bool {
|
pub fn yank(&mut self, _: &Exec) {
|
||||||
match self.snap().op {
|
match self.snap().op {
|
||||||
InputOp::None => {
|
InputOp::None => {
|
||||||
self.snap_mut().op = InputOp::Yank(self.snap().cursor);
|
self.snap_mut().op = InputOp::Yank(self.snap().cursor);
|
||||||
false
|
|
||||||
}
|
}
|
||||||
InputOp::Select(start) => {
|
InputOp::Select(start) => {
|
||||||
self.snap_mut().op = InputOp::Yank(start);
|
self.snap_mut().op = InputOp::Yank(start);
|
||||||
return self.handle_op(self.snap().cursor, true).then(|| self.move_(0)).is_some();
|
render!(self.handle_op(self.snap().cursor, true));
|
||||||
|
self.move_(0);
|
||||||
}
|
}
|
||||||
InputOp::Yank(_) => {
|
InputOp::Yank(_) => {
|
||||||
self.snap_mut().op = InputOp::Yank(0);
|
self.snap_mut().op = InputOp::Yank(0);
|
||||||
self.move_(self.snap().len() as isize);
|
self.move_(self.snap().len() as isize);
|
||||||
false
|
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::ops::Range;
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use yazi_config::{popup::Position, INPUT};
|
use yazi_config::{popup::Position, INPUT};
|
||||||
use yazi_shared::InputError;
|
use yazi_shared::{render, InputError};
|
||||||
|
|
||||||
use super::{mode::InputMode, op::InputOp, InputSnap, InputSnaps};
|
use super::{mode::InputMode, op::InputOp, InputSnap, InputSnaps};
|
||||||
use crate::CLIPBOARD;
|
use crate::CLIPBOARD;
|
||||||
|
|
@ -32,7 +32,7 @@ impl Input {
|
||||||
self.position.offset.width.saturating_sub(INPUT.border()) as usize
|
self.position.offset.width.saturating_sub(INPUT.border()) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_str(&mut self, s: &str) -> bool {
|
pub fn type_str(&mut self, s: &str) {
|
||||||
let snap = self.snaps.current_mut();
|
let snap = self.snaps.current_mut();
|
||||||
if snap.cursor < 1 {
|
if snap.cursor < 1 {
|
||||||
snap.value.insert_str(0, s);
|
snap.value.insert_str(0, s);
|
||||||
|
|
@ -42,7 +42,7 @@ impl Input {
|
||||||
|
|
||||||
self.move_(s.chars().count() as isize);
|
self.move_(s.chars().count() as isize);
|
||||||
self.flush_value();
|
self.flush_value();
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool {
|
pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ use yazi_shared::event::Exec;
|
||||||
use crate::{manager::Manager, tasks::Tasks};
|
use crate::{manager::Manager, tasks::Tasks};
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn close(&mut self, _: &Exec, tasks: &Tasks) -> bool {
|
pub fn close(&mut self, _: &Exec, tasks: &Tasks) {
|
||||||
if self.tabs.len() > 1 {
|
if self.tabs.len() > 1 {
|
||||||
return self.tabs.close(self.tabs.idx);
|
return self.tabs.close(self.tabs.idx);
|
||||||
}
|
}
|
||||||
self.quit((), tasks)
|
self.quit((), tasks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn create(&self, opt: impl Into<Opt>) -> bool {
|
pub fn create(&self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let cwd = self.cwd().to_owned();
|
let cwd = self.cwd().to_owned();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
|
@ -47,6 +47,5 @@ impl Manager {
|
||||||
}
|
}
|
||||||
Ok::<(), anyhow::Error>(())
|
Ok::<(), anyhow::Error>(())
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use yazi_shared::{emit, event::Exec, fs::Url, Layer};
|
use yazi_shared::{emit, event::Exec, fs::Url, render, Layer};
|
||||||
|
|
||||||
use crate::manager::Manager;
|
use crate::manager::Manager;
|
||||||
|
|
||||||
|
|
@ -24,13 +24,13 @@ impl Manager {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hover(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn hover(&mut self, opt: impl Into<Opt>) {
|
||||||
// Hover on the file
|
// Hover on the file
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let mut b = self.current_mut().repos(opt.url);
|
render!(self.current_mut().repos(opt.url));
|
||||||
|
|
||||||
// Re-peek
|
// Re-peek
|
||||||
b |= self.peek(false);
|
self.peek(false);
|
||||||
|
|
||||||
// Refresh watcher
|
// Refresh watcher
|
||||||
let mut to_watch = BTreeSet::new();
|
let mut to_watch = BTreeSet::new();
|
||||||
|
|
@ -44,7 +44,5 @@ impl Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.watcher.watch(to_watch);
|
self.watcher.watch(to_watch);
|
||||||
|
|
||||||
b
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,13 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn link(&mut self, opt: impl Into<Opt>, tasks: &Tasks) -> bool {
|
pub fn link(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
|
||||||
let opt = opt.into() as Opt;
|
|
||||||
let (cut, ref src) = self.yanked;
|
let (cut, ref src) = self.yanked;
|
||||||
!cut && tasks.file_link(src, self.cwd(), opt.relative, opt.force)
|
if cut {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opt = opt.into() as Opt;
|
||||||
|
tasks.file_link(src, self.cwd(), opt.relative, opt.force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn open(&mut self, opt: impl Into<Opt>, tasks: &Tasks) -> bool {
|
pub fn open(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
|
||||||
let selected = self.selected();
|
let selected = self.selected();
|
||||||
if selected.is_empty() {
|
if selected.is_empty() {
|
||||||
return false;
|
return;
|
||||||
} else if Self::quit_with_selected(&selected) {
|
} else if Self::quit_with_selected(&selected) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut done, mut todo) = (Vec::with_capacity(selected.len()), vec![]);
|
let (mut done, mut todo) = (Vec::with_capacity(selected.len()), vec![]);
|
||||||
|
|
@ -53,7 +53,6 @@ impl Manager {
|
||||||
|
|
||||||
Self::_open_do(opt.interactive, done);
|
Self::_open_do(opt.interactive, done);
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -64,10 +63,10 @@ impl Manager {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_do(&mut self, opt: impl Into<Opt>, tasks: &Tasks) -> bool {
|
pub fn open_do(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let Some(targets) = opt.targets else {
|
let Some(targets) = opt.targets else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let targets: Vec<_> = targets
|
let targets: Vec<_> = targets
|
||||||
|
|
@ -76,15 +75,15 @@ impl Manager {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if targets.is_empty() {
|
if targets.is_empty() {
|
||||||
return false;
|
return;
|
||||||
} else if !opt.interactive {
|
} else if !opt.interactive {
|
||||||
tasks.file_open(&targets);
|
tasks.file_open(&targets);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let openers: Vec<_> = OPEN.common_openers(&targets).into_iter().cloned().collect();
|
let openers: Vec<_> = OPEN.common_openers(&targets).into_iter().cloned().collect();
|
||||||
if openers.is_empty() {
|
if openers.is_empty() {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let urls = targets.into_iter().map(|(u, _)| u).collect();
|
let urls = targets.into_iter().map(|(u, _)| u).collect();
|
||||||
|
|
@ -94,7 +93,6 @@ impl Manager {
|
||||||
Tasks::_open(urls, openers[choice].clone());
|
Tasks::_open(urls, openers[choice].clone());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quit_with_selected(selected: &[&File]) -> bool {
|
fn quit_with_selected(selected: &[&File]) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,15 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn paste(&mut self, opt: impl Into<Opt>, tasks: &Tasks) -> bool {
|
pub fn paste(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
|
||||||
let dest = self.cwd();
|
let dest = self.cwd();
|
||||||
let (cut, ref src) = self.yanked;
|
let (cut, ref src) = self.yanked;
|
||||||
|
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if cut {
|
if cut {
|
||||||
tasks.file_cut(src, dest, opt.force)
|
tasks.file_cut(src, dest, opt.force);
|
||||||
} else {
|
} else {
|
||||||
tasks.file_copy(src, dest, opt.force, opt.follow)
|
tasks.file_copy(src, dest, opt.force, opt.follow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::{emit, event::Exec, fs::Url, Layer, MIME_DIR};
|
use yazi_shared::{emit, event::Exec, fs::Url, render, Layer, MIME_DIR};
|
||||||
|
|
||||||
use crate::manager::Manager;
|
use crate::manager::Manager;
|
||||||
|
|
||||||
|
|
@ -30,20 +30,20 @@ impl Manager {
|
||||||
emit!(Call(Exec::call("peek", vec![]).with_bool("force", force).vec(), Layer::Manager));
|
emit!(Call(Exec::call("peek", vec![]).with_bool("force", force).vec(), Layer::Manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn peek(&mut self, opt: impl Into<Opt>) {
|
||||||
let Some(hovered) = self.hovered() else {
|
let Some(hovered) = self.hovered() else {
|
||||||
return self.active_mut().preview.reset();
|
return render!(self.active_mut().preview.reset());
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt = opt.into() as Opt;
|
|
||||||
if matches!(opt.only_if, Some(ref u) if *u != hovered.url) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let hovered = hovered.clone();
|
let hovered = hovered.clone();
|
||||||
if !self.active().preview.same_url(&hovered.url) {
|
if !self.active().preview.same_url(&hovered.url) {
|
||||||
self.active_mut().preview.skip = 0;
|
self.active_mut().preview.skip = 0;
|
||||||
self.active_mut().preview.reset();
|
render!(self.active_mut().preview.reset());
|
||||||
|
}
|
||||||
|
|
||||||
|
let opt = opt.into() as Opt;
|
||||||
|
if matches!(opt.only_if, Some(ref u) if *u != hovered.url) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(skip) = opt.skip {
|
if let Some(skip) = opt.skip {
|
||||||
|
|
@ -61,14 +61,13 @@ impl Manager {
|
||||||
} else {
|
} else {
|
||||||
self.active_mut().preview.go_folder(hovered, opt.force);
|
self.active_mut().preview.go_folder(hovered, opt.force);
|
||||||
}
|
}
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = self.mimetype.get(&hovered.url).cloned() {
|
if let Some(s) = self.mimetype.get(&hovered.url).cloned() {
|
||||||
self.active_mut().preview.go(hovered, &s, opt.force);
|
self.active_mut().preview.go(hovered, &s, opt.force);
|
||||||
} else {
|
} else {
|
||||||
return self.active_mut().preview.reset();
|
render!(self.active_mut().preview.reset());
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,13 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn quit(&self, opt: impl Into<Opt>, tasks: &Tasks) -> bool {
|
pub fn quit(&self, opt: impl Into<Opt>, tasks: &Tasks) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
let tasks = tasks.len();
|
let tasks = tasks.len();
|
||||||
if tasks == 0 {
|
if tasks == 0 {
|
||||||
emit!(Quit(opt.no_cwd_file));
|
emit!(Quit(opt.no_cwd_file));
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
|
@ -32,6 +32,5 @@ impl Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ impl Manager {
|
||||||
emit!(Call(Exec::call("refresh", vec![]).vec(), Layer::Manager));
|
emit!(Call(Exec::call("refresh", vec![]).vec(), Layer::Manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh(&mut self, _: &Exec) -> bool {
|
pub fn refresh(&mut self, _: &Exec) {
|
||||||
env::set_current_dir(self.cwd()).ok();
|
env::set_current_dir(self.cwd()).ok();
|
||||||
env::set_var("PWD", self.cwd());
|
env::set_var("PWD", self.cwd());
|
||||||
|
|
||||||
|
|
@ -22,6 +22,6 @@ impl Manager {
|
||||||
self.watcher.trigger_dirs(&[self.cwd()]);
|
self.watcher.trigger_dirs(&[self.cwd()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.hover(None)
|
self.hover(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn remove(&mut self, opt: impl Into<Opt>, tasks: &Tasks) -> bool {
|
pub fn remove(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let targets = self.selected().into_iter().map(|f| f.url()).collect();
|
let targets = self.selected().into_iter().map(|f| f.url()).collect();
|
||||||
tasks.file_remove(targets, opt.force, opt.permanently)
|
tasks.file_remove(targets, opt.force, opt.permanently);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,13 @@ impl Manager {
|
||||||
Ok(Self::_hover(Some(new)))
|
Ok(Self::_hover(Some(new)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename(&self, opt: impl Into<Opt>) -> bool {
|
pub fn rename(&self, opt: impl Into<Opt>) {
|
||||||
if self.active().in_selecting() {
|
if self.active().in_selecting() {
|
||||||
return self.bulk_rename();
|
return self.bulk_rename();
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(hovered) = self.hovered().map(|h| h.url()) else {
|
let Some(hovered) = self.hovered().map(|h| h.url()) else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
@ -60,10 +60,9 @@ impl Manager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bulk_rename(&self) -> bool {
|
fn bulk_rename(&self) {
|
||||||
let old: Vec<_> = self.selected().into_iter().map(|f| &f.url).collect();
|
let old: Vec<_> = self.selected().into_iter().map(|f| &f.url).collect();
|
||||||
|
|
||||||
let root = max_common_root(&old);
|
let root = max_common_root(&old);
|
||||||
|
|
@ -104,8 +103,6 @@ impl Manager {
|
||||||
let new: Vec<_> = fs::read_to_string(&tmp).await?.lines().map(PathBuf::from).collect();
|
let new: Vec<_> = fs::read_to_string(&tmp).await?.lines().map(PathBuf::from).collect();
|
||||||
Self::bulk_rename_do(root, old, new).await
|
Self::bulk_rename_do(root, old, new).await
|
||||||
});
|
});
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn bulk_rename_do(root: PathBuf, old: Vec<PathBuf>, new: Vec<PathBuf>) -> Result<()> {
|
async fn bulk_rename_do(root: PathBuf, old: Vec<PathBuf>, new: Vec<PathBuf>) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use yazi_config::PLUGIN;
|
use yazi_config::PLUGIN;
|
||||||
use yazi_plugin::isolate;
|
use yazi_plugin::isolate;
|
||||||
use yazi_shared::{event::Exec, MIME_DIR};
|
use yazi_shared::{event::Exec, render, MIME_DIR};
|
||||||
|
|
||||||
use crate::manager::Manager;
|
use crate::manager::Manager;
|
||||||
|
|
||||||
|
|
@ -16,9 +16,9 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn seek(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn seek(&mut self, opt: impl Into<Opt>) {
|
||||||
let Some(hovered) = self.hovered() else {
|
let Some(hovered) = self.hovered() else {
|
||||||
return self.active_mut().preview.reset();
|
return render!(self.active_mut().preview.reset());
|
||||||
};
|
};
|
||||||
|
|
||||||
let mime = if hovered.is_dir() {
|
let mime = if hovered.is_dir() {
|
||||||
|
|
@ -26,15 +26,14 @@ impl Manager {
|
||||||
} else if let Some(s) = self.mimetype.get(&hovered.url) {
|
} else if let Some(s) = self.mimetype.get(&hovered.url) {
|
||||||
s
|
s
|
||||||
} else {
|
} else {
|
||||||
return self.active_mut().preview.reset();
|
return render!(self.active_mut().preview.reset());
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(previewer) = PLUGIN.previewer(&hovered.url, mime) else {
|
let Some(previewer) = PLUGIN.previewer(&hovered.url, mime) else {
|
||||||
return self.active_mut().preview.reset();
|
return render!(self.active_mut().preview.reset());
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
isolate::seek_sync(&previewer.exec, hovered.clone(), opt.units);
|
isolate::seek_sync(&previewer.exec, hovered.clone(), opt.units);
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,11 @@ use yazi_shared::event::Exec;
|
||||||
use crate::manager::Manager;
|
use crate::manager::Manager;
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn suspend(&mut self, _: &Exec) -> bool {
|
pub fn suspend(&mut self, _: &Exec) {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
Scheduler::app_stop().await;
|
Scheduler::app_stop().await;
|
||||||
unsafe { libc::raise(libc::SIGTSTP) };
|
unsafe { libc::raise(libc::SIGTSTP) };
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::manager::Tabs;
|
use crate::manager::Tabs;
|
||||||
|
|
||||||
|
|
@ -17,12 +17,12 @@ impl From<usize> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tabs {
|
impl Tabs {
|
||||||
pub fn close(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn close(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
let len = self.items.len();
|
let len = self.items.len();
|
||||||
if len < 2 || opt.idx >= len {
|
if len < 2 || opt.idx >= len {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.items.remove(opt.idx);
|
self.items.remove(opt.idx);
|
||||||
|
|
@ -30,6 +30,6 @@ impl Tabs {
|
||||||
self.set_idx(self.absolute(1));
|
self.set_idx(self.absolute(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::{event::Exec, fs::Url};
|
use yazi_shared::{event::Exec, fs::Url, render};
|
||||||
|
|
||||||
use crate::{manager::Tabs, tab::Tab};
|
use crate::{manager::Tabs, tab::Tab};
|
||||||
|
|
||||||
|
|
@ -21,9 +21,9 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tabs {
|
impl Tabs {
|
||||||
pub fn create(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn create(&mut self, opt: impl Into<Opt>) {
|
||||||
if self.items.len() >= MAX_TABS {
|
if self.items.len() >= MAX_TABS {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
@ -35,6 +35,6 @@ impl Tabs {
|
||||||
|
|
||||||
self.items.insert(self.idx + 1, tab);
|
self.items.insert(self.idx + 1, tab);
|
||||||
self.set_idx(self.idx + 1);
|
self.set_idx(self.idx + 1);
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::manager::Tabs;
|
use crate::manager::Tabs;
|
||||||
|
|
||||||
|
|
@ -13,14 +13,14 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tabs {
|
impl Tabs {
|
||||||
pub fn swap(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn swap(&mut self, opt: impl Into<Opt>) {
|
||||||
let idx = self.absolute(opt.into().step);
|
let idx = self.absolute(opt.into().step);
|
||||||
if idx == self.idx {
|
if idx == self.idx {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.items.swap(self.idx, idx);
|
self.items.swap(self.idx, idx);
|
||||||
self.set_idx(idx);
|
self.set_idx(idx);
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::manager::Tabs;
|
use crate::manager::Tabs;
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tabs {
|
impl Tabs {
|
||||||
pub fn switch(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn switch(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let idx = if opt.relative {
|
let idx = if opt.relative {
|
||||||
(self.idx as isize + opt.step).rem_euclid(self.items.len() as isize) as usize
|
(self.idx as isize + opt.step).rem_euclid(self.items.len() as isize) as usize
|
||||||
|
|
@ -26,10 +26,10 @@ impl Tabs {
|
||||||
};
|
};
|
||||||
|
|
||||||
if idx == self.idx || idx >= self.items.len() {
|
if idx == self.idx || idx >= self.items.len() {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_idx(idx);
|
self.set_idx(idx);
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::{event::Exec, fs::FilesOp};
|
use yazi_shared::{event::Exec, fs::FilesOp, render};
|
||||||
|
|
||||||
use crate::{folder::Folder, manager::Manager, tasks::Tasks};
|
use crate::{folder::Folder, manager::Manager, tasks::Tasks};
|
||||||
|
|
||||||
|
|
@ -13,51 +13,49 @@ impl TryFrom<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
fn handle_read(&mut self, op: FilesOp) -> bool {
|
// TODO: refactor this
|
||||||
|
fn handle_read(&mut self, op: FilesOp) {
|
||||||
let url = op.url().clone();
|
let url = op.url().clone();
|
||||||
let cwd = self.cwd().to_owned();
|
let cwd = self.cwd().to_owned();
|
||||||
let hovered = self.hovered().map(|h| h.url());
|
let hovered = self.hovered().map(|h| h.url());
|
||||||
|
|
||||||
let mut b = if cwd == url {
|
if cwd == url {
|
||||||
self.current_mut().update(op)
|
render!(self.current_mut().update(op));
|
||||||
} else if matches!(self.parent(), Some(p) if p.cwd == url) {
|
} else if matches!(self.parent(), Some(p) if p.cwd == url) {
|
||||||
self.active_mut().parent.as_mut().unwrap().update(op)
|
render!(self.active_mut().parent.as_mut().unwrap().update(op));
|
||||||
} else if matches!(self.hovered(), Some(h) if h.url == url) {
|
} else if matches!(self.hovered(), Some(h) if h.url == url) {
|
||||||
self.active_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(&url));
|
self.active_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(&url));
|
||||||
self.active_mut().apply_files_attrs(true);
|
self.active_mut().apply_files_attrs(true);
|
||||||
self.active_mut().history.get_mut(&url).unwrap().update(op) | self.peek(true)
|
render!(self.active_mut().history.get_mut(&url).unwrap().update(op));
|
||||||
|
self.peek(true);
|
||||||
} else {
|
} else {
|
||||||
self.active_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(&url)).update(op);
|
self.active_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(&url)).update(op);
|
||||||
false
|
}
|
||||||
};
|
|
||||||
|
|
||||||
b |= self.active_mut().parent.as_mut().is_some_and(|p| p.hover(&cwd));
|
render!(self.active_mut().parent.as_mut().is_some_and(|p| p.hover(&cwd)));
|
||||||
b |= hovered.as_ref().is_some_and(|h| self.current_mut().hover(h));
|
render!(hovered.as_ref().is_some_and(|h| self.current_mut().hover(h)));
|
||||||
|
|
||||||
if hovered.as_ref() != self.hovered().map(|h| &h.url) {
|
if hovered.as_ref() != self.hovered().map(|h| &h.url) {
|
||||||
b |= self.hover(None);
|
self.hover(None);
|
||||||
}
|
}
|
||||||
b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ioerr(&mut self, op: FilesOp) -> bool {
|
fn handle_ioerr(&mut self, op: FilesOp) {
|
||||||
let url = op.url();
|
let url = op.url();
|
||||||
let op = FilesOp::Full(url.clone(), vec![]);
|
let op = FilesOp::Full(url.clone(), vec![]);
|
||||||
|
|
||||||
if url == self.cwd() {
|
if url == self.cwd() {
|
||||||
self.current_mut().update(op);
|
self.current_mut().update(op);
|
||||||
self.active_mut().leave(());
|
self.active_mut().leave(());
|
||||||
true
|
render!();
|
||||||
} else if matches!(self.parent(), Some(p) if &p.cwd == url) {
|
} else if matches!(self.parent(), Some(p) if &p.cwd == url) {
|
||||||
self.active_mut().parent.as_mut().unwrap().update(op)
|
render!(self.active_mut().parent.as_mut().unwrap().update(op));
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_files(&mut self, opt: impl TryInto<Opt>, tasks: &Tasks) -> bool {
|
pub fn update_files(&mut self, opt: impl TryInto<Opt>, tasks: &Tasks) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let Ok(opt) = opt.try_into() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
let calc = !matches!(opt.op, FilesOp::Size(..) | FilesOp::IOErr(_) | FilesOp::Deleting(..));
|
let calc = !matches!(opt.op, FilesOp::Size(..) | FilesOp::IOErr(_) | FilesOp::Deleting(..));
|
||||||
|
|
||||||
|
|
@ -66,9 +64,8 @@ impl Manager {
|
||||||
ops.push(ops[0].chroot(u));
|
ops.push(ops[0].chroot(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut b = false;
|
|
||||||
for op in ops {
|
for op in ops {
|
||||||
b |= match op {
|
match op {
|
||||||
FilesOp::IOErr(..) => self.handle_ioerr(op),
|
FilesOp::IOErr(..) => self.handle_ioerr(op),
|
||||||
_ => self.handle_read(op),
|
_ => self.handle_read(op),
|
||||||
};
|
};
|
||||||
|
|
@ -77,6 +74,5 @@ impl Manager {
|
||||||
if calc {
|
if calc {
|
||||||
tasks.preload_sorted(&self.current().files);
|
tasks.preload_sorted(&self.current().files);
|
||||||
}
|
}
|
||||||
b
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use yazi_plugin::ValueSendable;
|
use yazi_plugin::ValueSendable;
|
||||||
use yazi_shared::{event::Exec, fs::Url};
|
use yazi_shared::{event::Exec, fs::Url, render};
|
||||||
|
|
||||||
use crate::{manager::Manager, tasks::Tasks};
|
use crate::{manager::Manager, tasks::Tasks};
|
||||||
|
|
||||||
|
|
@ -16,9 +16,9 @@ impl TryFrom<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn update_mimetype(&mut self, opt: impl TryInto<Opt>, tasks: &Tasks) -> bool {
|
pub fn update_mimetype(&mut self, opt: impl TryInto<Opt>, tasks: &Tasks) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let Ok(opt) = opt.try_into() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let linked = self.watcher.linked.read();
|
let linked = self.watcher.linked.read();
|
||||||
|
|
@ -38,7 +38,7 @@ impl Manager {
|
||||||
|
|
||||||
drop(linked);
|
drop(linked);
|
||||||
if updates.is_empty() {
|
if updates.is_empty() {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let affected: Vec<_> = self
|
let affected: Vec<_> = self
|
||||||
|
|
@ -53,6 +53,6 @@ impl Manager {
|
||||||
self.peek(false);
|
self.peek(false);
|
||||||
|
|
||||||
tasks.preload_affected(&affected, &self.mimetype);
|
tasks.preload_affected(&affected, &self.mimetype);
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::manager::Manager;
|
use crate::manager::Manager;
|
||||||
|
|
||||||
|
|
@ -11,11 +11,11 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
pub fn yank(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn yank(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
self.yanked.0 = opt.cut;
|
self.yanked.0 = opt.cut;
|
||||||
self.yanked.1 = self.selected().into_iter().map(|f| f.url()).collect();
|
self.yanked.1 = self.selected().into_iter().map(|f| f.url()).collect();
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::select::Select;
|
use crate::select::Select;
|
||||||
|
|
||||||
|
|
@ -13,10 +13,10 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Select {
|
impl Select {
|
||||||
fn next(&mut self, step: usize) -> bool {
|
fn next(&mut self, step: usize) {
|
||||||
let len = self.items.len();
|
let len = self.items.len();
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let old = self.cursor;
|
let old = self.cursor;
|
||||||
|
|
@ -27,10 +27,10 @@ impl Select {
|
||||||
self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old);
|
self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old);
|
||||||
}
|
}
|
||||||
|
|
||||||
old != self.cursor
|
render!(old != self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev(&mut self, step: usize) -> bool {
|
fn prev(&mut self, step: usize) {
|
||||||
let old = self.cursor;
|
let old = self.cursor;
|
||||||
self.cursor = self.cursor.saturating_sub(step);
|
self.cursor = self.cursor.saturating_sub(step);
|
||||||
|
|
||||||
|
|
@ -38,10 +38,10 @@ impl Select {
|
||||||
self.offset = self.offset.saturating_sub(old - self.cursor);
|
self.offset = self.offset.saturating_sub(old - self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
old != self.cursor
|
render!(old != self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arrow(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn arrow(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.step > 0 { self.next(opt.step as usize) } else { self.prev(opt.step.unsigned_abs()) }
|
if opt.step > 0 { self.next(opt.step as usize) } else { self.prev(opt.step.unsigned_abs()) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::select::Select;
|
use crate::select::Select;
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ impl From<bool> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Select {
|
impl Select {
|
||||||
pub fn close(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn close(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if let Some(cb) = self.callback.take() {
|
if let Some(cb) = self.callback.take() {
|
||||||
_ = cb.send(if opt.submit { Ok(self.cursor) } else { Err(anyhow!("canceled")) });
|
_ = cb.send(if opt.submit { Ok(self.cursor) } else { Err(anyhow!("canceled")) });
|
||||||
|
|
@ -24,6 +24,6 @@ impl Select {
|
||||||
self.cursor = 0;
|
self.cursor = 0;
|
||||||
self.offset = 0;
|
self.offset = 0;
|
||||||
self.visible = false;
|
self.visible = false;
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use yazi_config::popup::SelectCfg;
|
use yazi_config::popup::SelectCfg;
|
||||||
use yazi_shared::{emit, event::Exec, term::Term, Layer};
|
use yazi_shared::{emit, event::Exec, render, term::Term, Layer};
|
||||||
|
|
||||||
use crate::select::Select;
|
use crate::select::Select;
|
||||||
|
|
||||||
|
|
@ -25,9 +25,9 @@ impl Select {
|
||||||
rx.await.unwrap_or_else(|_| Term::goodbye(|| false))
|
rx.await.unwrap_or_else(|_| Term::goodbye(|| false))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&mut self, opt: impl TryInto<Opt>) -> bool {
|
pub fn show(&mut self, opt: impl TryInto<Opt>) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let Ok(opt) = opt.try_into() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.close(false);
|
self.close(false);
|
||||||
|
|
@ -37,6 +37,6 @@ impl Select {
|
||||||
|
|
||||||
self.callback = Some(opt.tx);
|
self.callback = Some(opt.tx);
|
||||||
self.visible = true;
|
self.visible = true;
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::{manager::Manager, tab::Tab, Step};
|
use crate::{manager::Manager, tab::Tab, Step};
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn arrow(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn arrow(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let ok = if opt.step.is_positive() {
|
let ok = if opt.step.is_positive() {
|
||||||
self.current.next(opt.step)
|
self.current.next(opt.step)
|
||||||
|
|
@ -28,7 +28,7 @@ impl Tab {
|
||||||
self.current.prev(opt.step)
|
self.current.prev(opt.step)
|
||||||
};
|
};
|
||||||
if !ok {
|
if !ok {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visual selection
|
// Visual selection
|
||||||
|
|
@ -42,6 +42,6 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
Manager::_hover(None);
|
Manager::_hover(None);
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,15 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn back(&mut self, _: impl Into<Opt>) -> bool {
|
pub fn back(&mut self, _: impl Into<Opt>) {
|
||||||
if let Some(url) = self.backstack.shift_backward().cloned() {
|
if let Some(url) = self.backstack.shift_backward().cloned() {
|
||||||
self.cd(url);
|
self.cd(url);
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn forward(&mut self, _: impl Into<Opt>) -> bool {
|
pub fn forward(&mut self, _: impl Into<Opt>) {
|
||||||
if let Some(url) = self.backstack.shift_forward().cloned() {
|
if let Some(url) = self.backstack.shift_forward().cloned() {
|
||||||
self.cd(url);
|
self.cd(url);
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{mem, time::Duration};
|
||||||
use tokio::{fs, pin};
|
use tokio::{fs, pin};
|
||||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_shared::{emit, event::Exec, fs::{expand_path, Url}, Debounce, InputError, Layer};
|
use yazi_shared::{emit, event::Exec, fs::{expand_path, Url}, render, Debounce, InputError, Layer};
|
||||||
|
|
||||||
use crate::{completion::Completion, input::Input, manager::Manager, tab::Tab};
|
use crate::{completion::Completion, input::Input, manager::Manager, tab::Tab};
|
||||||
|
|
||||||
|
|
@ -32,14 +32,14 @@ impl Tab {
|
||||||
emit!(Call(Exec::call("cd", vec![target.to_string()]).vec(), Layer::Manager));
|
emit!(Call(Exec::call("cd", vec![target.to_string()]).vec(), Layer::Manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cd(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn cd(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.interactive {
|
if opt.interactive {
|
||||||
return self.cd_interactive();
|
return self.cd_interactive();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.current.cwd == opt.target {
|
if self.current.cwd == opt.target {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take parent to history
|
// Take parent to history
|
||||||
|
|
@ -65,10 +65,10 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
Manager::_refresh();
|
Manager::_refresh();
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cd_interactive(&mut self) -> bool {
|
fn cd_interactive(&mut self) {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let rx = Input::_show(InputCfg::cd());
|
let rx = Input::_show(InputCfg::cd());
|
||||||
|
|
||||||
|
|
@ -96,6 +96,5 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ impl<'a> From<&'a Exec> for Opt<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn copy<'a>(&self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn copy<'a>(&self, opt: impl Into<Opt<'a>>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
let mut s = OsString::new();
|
let mut s = OsString::new();
|
||||||
|
|
@ -24,7 +24,7 @@ impl Tab {
|
||||||
"dirname" => f.url.parent().map_or(OsStr::new(""), |p| p.as_os_str()),
|
"dirname" => f.url.parent().map_or(OsStr::new(""), |p| p.as_os_str()),
|
||||||
"filename" => f.name().unwrap_or(OsStr::new("")),
|
"filename" => f.name().unwrap_or(OsStr::new("")),
|
||||||
"name_without_ext" => f.stem().unwrap_or(OsStr::new("")),
|
"name_without_ext" => f.stem().unwrap_or(OsStr::new("")),
|
||||||
_ => return false,
|
_ => return,
|
||||||
});
|
});
|
||||||
if it.peek().is_some() {
|
if it.peek().is_some() {
|
||||||
s.push("\n");
|
s.push("\n");
|
||||||
|
|
@ -32,6 +32,5 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
futures::executor::block_on(CLIPBOARD.set(s));
|
futures::executor::block_on(CLIPBOARD.set(s));
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::{manager::Manager, tab::Tab};
|
use crate::{manager::Manager, tab::Tab};
|
||||||
|
|
||||||
|
|
@ -13,9 +13,9 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn enter(&mut self, _: impl Into<Opt>) -> bool {
|
pub fn enter(&mut self, _: impl Into<Opt>) {
|
||||||
let Some(hovered) = self.current.hovered().filter(|h| h.is_dir()).map(|h| h.url()) else {
|
let Some(hovered) = self.current.hovered().filter(|h| h.is_dir()).map(|h| h.url()) else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Current
|
// Current
|
||||||
|
|
@ -35,6 +35,6 @@ impl Tab {
|
||||||
self.backstack.push(hovered);
|
self.backstack.push(hovered);
|
||||||
|
|
||||||
Manager::_refresh();
|
Manager::_refresh();
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::tab::{Mode, Tab};
|
use crate::tab::{Mode, Tab};
|
||||||
|
|
||||||
|
|
@ -42,42 +42,48 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn escape_select(&mut self) -> bool { self.select_all(Some(false)) }
|
fn escape_select(&mut self) -> bool { self.current.files.select_all(Some(false)) }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn escape_filter(&mut self) -> bool {
|
fn escape_filter(&mut self) -> bool {
|
||||||
self.filter_do(super::filter::Opt { query: "", ..Default::default() })
|
let b = self.current.files.filter().is_some();
|
||||||
|
self.filter_do(super::filter::Opt { query: "", ..Default::default() });
|
||||||
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn escape_search(&mut self) -> bool { self.search_stop() }
|
fn escape_search(&mut self) -> bool {
|
||||||
|
let b = self.current.cwd.is_search();
|
||||||
pub fn escape(&mut self, opt: impl Into<Opt>) -> bool {
|
self.search_stop();
|
||||||
let opt = opt.into() as Opt;
|
|
||||||
if opt.is_empty() {
|
|
||||||
return self.escape_find()
|
|
||||||
|| self.escape_visual()
|
|
||||||
|| self.escape_select()
|
|
||||||
|| self.escape_filter()
|
|
||||||
|| self.escape_search();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut b = false;
|
|
||||||
if opt.contains(Opt::FIND) {
|
|
||||||
b |= self.escape_find();
|
|
||||||
}
|
|
||||||
if opt.contains(Opt::VISUAL) {
|
|
||||||
b |= self.escape_visual();
|
|
||||||
}
|
|
||||||
if opt.contains(Opt::SELECT) {
|
|
||||||
b |= self.escape_select();
|
|
||||||
}
|
|
||||||
if opt.contains(Opt::FILTER) {
|
|
||||||
b |= self.escape_filter();
|
|
||||||
}
|
|
||||||
if opt.contains(Opt::SEARCH) {
|
|
||||||
b |= self.escape_search();
|
|
||||||
}
|
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn escape(&mut self, opt: impl Into<Opt>) {
|
||||||
|
let opt = opt.into() as Opt;
|
||||||
|
if opt.is_empty() {
|
||||||
|
return render!(
|
||||||
|
self.escape_find()
|
||||||
|
|| self.escape_visual()
|
||||||
|
|| self.escape_select()
|
||||||
|
|| self.escape_filter()
|
||||||
|
|| self.escape_search()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.contains(Opt::FIND) {
|
||||||
|
render!(self.escape_find());
|
||||||
|
}
|
||||||
|
if opt.contains(Opt::VISUAL) {
|
||||||
|
render!(self.escape_visual());
|
||||||
|
}
|
||||||
|
if opt.contains(Opt::SELECT) {
|
||||||
|
render!(self.escape_select());
|
||||||
|
}
|
||||||
|
if opt.contains(Opt::FILTER) {
|
||||||
|
render!(self.escape_filter());
|
||||||
|
}
|
||||||
|
if opt.contains(Opt::SEARCH) {
|
||||||
|
render!(self.escape_search());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::time::Duration;
|
||||||
use tokio::pin;
|
use tokio::pin;
|
||||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_shared::{emit, event::Exec, Debounce, InputError, Layer};
|
use yazi_shared::{emit, event::Exec, render, Debounce, InputError, Layer};
|
||||||
|
|
||||||
use crate::{folder::{Filter, FilterCase}, input::Input, manager::Manager, tab::Tab};
|
use crate::{folder::{Filter, FilterCase}, input::Input, manager::Manager, tab::Tab};
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ impl<'a> From<&'a Exec> for Opt<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn filter<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn filter<'a>(&mut self, opt: impl Into<Opt<'a>>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let rx = Input::_show(InputCfg::filter());
|
let rx = Input::_show(InputCfg::filter());
|
||||||
|
|
@ -38,10 +38,9 @@ impl Tab {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter_do<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn filter_do<'a>(&mut self, opt: impl Into<Opt<'a>>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
let filter = if opt.query.is_empty() {
|
let filter = if opt.query.is_empty() {
|
||||||
|
|
@ -49,17 +48,17 @@ impl Tab {
|
||||||
} else if let Ok(f) = Filter::new(opt.query, opt.case) {
|
} else if let Ok(f) = Filter::new(opt.query, opt.case) {
|
||||||
Some(f)
|
Some(f)
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let hovered = self.current.hovered().map(|f| f.url());
|
let hovered = self.current.hovered().map(|f| f.url());
|
||||||
if !self.current.files.set_filter(filter) {
|
if !self.current.files.set_filter(filter) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.current.repos(hovered) {
|
if self.current.repos(hovered) {
|
||||||
Manager::_hover(None);
|
Manager::_hover(None);
|
||||||
}
|
}
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::time::Duration;
|
||||||
use tokio::pin;
|
use tokio::pin;
|
||||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_shared::{emit, event::Exec, Debounce, InputError, Layer};
|
use yazi_shared::{emit, event::Exec, render, Debounce, InputError, Layer};
|
||||||
|
|
||||||
use crate::{folder::FilterCase, input::Input, tab::{Finder, Tab}};
|
use crate::{folder::FilterCase, input::Input, tab::{Finder, Tab}};
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ impl From<&Exec> for ArrowOpt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn find<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn find<'a>(&mut self, opt: impl Into<Opt<'a>>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let rx = Input::_show(InputCfg::find(opt.prev));
|
let rx = Input::_show(InputCfg::find(opt.prev));
|
||||||
|
|
@ -51,23 +51,22 @@ impl Tab {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_do<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
pub fn find_do<'a>(&mut self, opt: impl Into<Opt<'a>>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let Some(query) = opt.query else {
|
let Some(query) = opt.query else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
return self.escape(super::escape::Opt::FIND);
|
return self.escape(super::escape::Opt::FIND);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(finder) = Finder::new(query, opt.case) else {
|
let Ok(finder) = Finder::new(query, opt.case) else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
if matches!(&self.finder, Some(f) if f.filter == finder.filter) {
|
if matches!(&self.finder, Some(f) if f.filter == finder.filter) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let step = if opt.prev {
|
let step = if opt.prev {
|
||||||
|
|
@ -81,21 +80,19 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finder = Some(finder);
|
self.finder = Some(finder);
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_arrow(&mut self, opt: impl Into<ArrowOpt>) -> bool {
|
pub fn find_arrow(&mut self, opt: impl Into<ArrowOpt>) {
|
||||||
let Some(finder) = &mut self.finder else {
|
let Some(finder) = &mut self.finder else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let b = finder.catchup(&self.current.files);
|
render!(finder.catchup(&self.current.files));
|
||||||
let step = if opt.into().prev {
|
if opt.into().prev {
|
||||||
finder.prev(&self.current.files, self.current.cursor, false)
|
finder.prev(&self.current.files, self.current.cursor, false).map(|s| self.arrow(s));
|
||||||
} else {
|
} else {
|
||||||
finder.next(&self.current.files, self.current.cursor, false)
|
finder.next(&self.current.files, self.current.cursor, false).map(|s| self.arrow(s));
|
||||||
};
|
}
|
||||||
|
|
||||||
b | step.is_some_and(|s| self.arrow(s))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,14 @@ use yazi_shared::event::Exec;
|
||||||
use crate::{manager::Manager, tab::Tab};
|
use crate::{manager::Manager, tab::Tab};
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn hidden(&mut self, e: &Exec) -> bool {
|
pub fn hidden(&mut self, e: &Exec) {
|
||||||
self.conf.show_hidden = match e.args.first().map(|s| s.as_bytes()) {
|
self.conf.show_hidden = match e.args.first().map(|s| s.as_bytes()) {
|
||||||
Some(b"show") => true,
|
Some(b"show") => true,
|
||||||
Some(b"hide") => false,
|
Some(b"hide") => false,
|
||||||
_ => !self.conf.show_hidden,
|
_ => !self.conf.show_hidden,
|
||||||
};
|
};
|
||||||
if self.apply_files_attrs(false) {
|
|
||||||
Manager::_hover(None);
|
self.apply_files_attrs(false);
|
||||||
return true;
|
Manager::_hover(None);
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,10 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn jump(&self, opt: impl Into<Opt>) -> bool {
|
pub fn jump(&self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.type_ == OptType::None {
|
if opt.type_ == OptType::None {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cwd = self.current.cwd.clone();
|
let cwd = self.current.cwd.clone();
|
||||||
|
|
@ -56,6 +56,5 @@ impl Tab {
|
||||||
Tab::_cd(&url)
|
Tab::_cd(&url)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::{manager::Manager, tab::Tab};
|
use crate::{manager::Manager, tab::Tab};
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn leave(&mut self, _: impl Into<Opt>) -> bool {
|
pub fn leave(&mut self, _: impl Into<Opt>) {
|
||||||
let current = self
|
let current = self
|
||||||
.current
|
.current
|
||||||
.hovered()
|
.hovered()
|
||||||
|
|
@ -22,7 +22,7 @@ impl Tab {
|
||||||
.or_else(|| self.current.cwd.parent_url());
|
.or_else(|| self.current.cwd.parent_url());
|
||||||
|
|
||||||
let Some(current) = current else {
|
let Some(current) = current else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parent
|
// Parent
|
||||||
|
|
@ -44,6 +44,6 @@ impl Tab {
|
||||||
self.backstack.push(current);
|
self.backstack.push(current);
|
||||||
|
|
||||||
Manager::_refresh();
|
Manager::_refresh();
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::tab::Tab;
|
use crate::tab::Tab;
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn linemode(&mut self, e: &Exec) -> bool {
|
pub fn linemode(&mut self, e: &Exec) {
|
||||||
self.conf.patch(|c| {
|
render!(self.conf.patch(|c| {
|
||||||
let Some(mode) = e.args.first() else {
|
let Some(mode) = e.args.first() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !mode.is_empty() && mode.len() <= 20 {
|
if !mode.is_empty() && mode.len() <= 20 {
|
||||||
c.linemode = mode.to_owned();
|
c.linemode = mode.to_owned();
|
||||||
}
|
}
|
||||||
})
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use yazi_plugin::utils::PreviewLock;
|
use yazi_plugin::utils::PreviewLock;
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::tab::Tab;
|
use crate::tab::Tab;
|
||||||
|
|
||||||
|
|
@ -17,20 +17,20 @@ impl TryFrom<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn preview(&mut self, opt: impl TryInto<Opt>) -> bool {
|
pub fn preview(&mut self, opt: impl TryInto<Opt>) {
|
||||||
let Some(hovered) = self.current.hovered().map(|h| &h.url) else {
|
let Some(hovered) = self.current.hovered().map(|h| &h.url) else {
|
||||||
return self.preview.reset();
|
return render!(self.preview.reset());
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(opt) = opt.try_into() else {
|
let Ok(opt) = opt.try_into() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if hovered != &opt.lock.url {
|
if hovered != &opt.lock.url {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.preview.lock = Some(opt.lock);
|
self.preview.lock = Some(opt.lock);
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,16 +26,15 @@ impl Tab {
|
||||||
emit!(Call(Exec::call("reveal", vec![target.to_string()]).vec(), Layer::Manager));
|
emit!(Call(Exec::call("reveal", vec![target.to_string()]).vec(), Layer::Manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reveal(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn reveal(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
let Some(parent) = opt.target.parent_url() else {
|
let Some(parent) = opt.target.parent_url() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let b = self.cd(parent.clone());
|
self.cd(parent.clone());
|
||||||
FilesOp::Creating(parent, vec![File::from_dummy(opt.target.clone())]).emit();
|
FilesOp::Creating(parent, vec![File::from_dummy(opt.target.clone())]).emit();
|
||||||
Manager::_hover(Some(opt.target));
|
Manager::_hover(Some(opt.target));
|
||||||
b
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use tokio::pin;
|
||||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_plugin::external;
|
use yazi_plugin::external;
|
||||||
use yazi_shared::{event::Exec, fs::FilesOp};
|
use yazi_shared::{event::Exec, fs::FilesOp, render};
|
||||||
|
|
||||||
use crate::{input::Input, manager::Manager, tab::Tab};
|
use crate::{input::Input, manager::Manager, tab::Tab};
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn search(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn search(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.type_ == OptType::None {
|
if opt.type_ == OptType::None {
|
||||||
return self.search_stop();
|
return self.search_stop();
|
||||||
|
|
@ -70,10 +70,11 @@ impl Tab {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}));
|
}));
|
||||||
true
|
|
||||||
|
render!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn search_stop(&mut self) -> bool {
|
pub(super) fn search_stop(&mut self) {
|
||||||
if let Some(handle) = self.search.take() {
|
if let Some(handle) = self.search.take() {
|
||||||
handle.abort();
|
handle.abort();
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +83,5 @@ impl Tab {
|
||||||
drop(mem::replace(&mut self.current, rep));
|
drop(mem::replace(&mut self.current, rep));
|
||||||
Manager::_refresh();
|
Manager::_refresh();
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::tab::Tab;
|
use crate::tab::Tab;
|
||||||
|
|
||||||
|
|
@ -9,9 +9,9 @@ pub struct Opt {
|
||||||
impl From<&Exec> for Opt {
|
impl From<&Exec> for Opt {
|
||||||
fn from(e: &Exec) -> Self {
|
fn from(e: &Exec) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: match e.named.get("state").map(|s| s.as_bytes()) {
|
state: match e.named.get("state").map(|s| s.as_str()) {
|
||||||
Some(b"true") => Some(true),
|
Some("true") => Some(true),
|
||||||
Some(b"false") => Some(false),
|
Some("false") => Some(false),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -22,14 +22,13 @@ impl From<Option<bool>> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn select(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn select(&mut self, opt: impl Into<Opt>) {
|
||||||
if let Some(u) = self.current.hovered().map(|h| h.url()) {
|
if let Some(u) = self.current.hovered().map(|h| h.url()) {
|
||||||
return self.current.files.select(&u, opt.into().state);
|
render!(self.current.files.select(&u, opt.into().state));
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_all(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn select_all(&mut self, opt: impl Into<Opt>) {
|
||||||
self.current.files.select_all(opt.into().state)
|
render!(self.current.files.select_all(opt.into().state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ impl<'a> From<&'a Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn shell(&self, opt: impl Into<Opt>) -> bool {
|
pub fn shell(&self, opt: impl Into<Opt>) {
|
||||||
let mut opt = opt.into() as Opt;
|
let mut opt = opt.into() as Opt;
|
||||||
let selected: Vec<_> = self.selected().into_iter().map(|f| f.url()).collect();
|
let selected: Vec<_> = self.selected().into_iter().map(|f| f.url()).collect();
|
||||||
|
|
||||||
|
|
@ -42,6 +42,5 @@ impl Tab {
|
||||||
spread: true,
|
spread: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use yazi_shared::event::Exec;
|
||||||
use crate::tab::Tab;
|
use crate::tab::Tab;
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn sort(&mut self, e: &Exec) -> bool {
|
pub fn sort(&mut self, e: &Exec) {
|
||||||
if let Some(by) = e.args.first() {
|
if let Some(by) = e.args.first() {
|
||||||
self.conf.sort_by = SortBy::from_str(by).unwrap_or_default();
|
self.conf.sort_by = SortBy::from_str(by).unwrap_or_default();
|
||||||
}
|
}
|
||||||
|
|
@ -14,6 +14,6 @@ impl Tab {
|
||||||
self.conf.sort_reverse = e.named.contains_key("reverse");
|
self.conf.sort_reverse = e.named.contains_key("reverse");
|
||||||
self.conf.sort_dir_first = e.named.contains_key("dir-first");
|
self.conf.sort_dir_first = e.named.contains_key("dir-first");
|
||||||
|
|
||||||
self.apply_files_attrs(false)
|
self.apply_files_attrs(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::tab::{Mode, Tab};
|
use crate::tab::{Mode, Tab};
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn visual_mode(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn visual_mode(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
let idx = self.current.cursor;
|
let idx = self.current.cursor;
|
||||||
|
|
||||||
|
|
@ -22,6 +22,6 @@ impl Tab {
|
||||||
} else {
|
} else {
|
||||||
self.mode = Mode::Select(idx, BTreeSet::from([idx]));
|
self.mode = Mode::Select(idx, BTreeSet::from([idx]));
|
||||||
};
|
};
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{borrow::Cow, collections::BTreeMap};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use yazi_shared::fs::{File, Url};
|
use yazi_shared::{fs::{File, Url}, render};
|
||||||
|
|
||||||
use super::{Backstack, Config, Finder, Mode, Preview};
|
use super::{Backstack, Config, Finder, Mode, Preview};
|
||||||
use crate::folder::Folder;
|
use crate::folder::Folder;
|
||||||
|
|
@ -72,30 +72,28 @@ impl Tab {
|
||||||
self.history.remove(url).unwrap_or_else(|| Folder::from(url))
|
self.history.remove(url).unwrap_or_else(|| Folder::from(url))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_files_attrs(&mut self, just_preview: bool) -> bool {
|
pub fn apply_files_attrs(&mut self, just_preview: bool) {
|
||||||
let apply = |f: &mut Folder| {
|
let apply = |f: &mut Folder| {
|
||||||
let hovered = f.hovered().map(|h| h.url());
|
let hovered = f.hovered().map(|h| h.url());
|
||||||
|
|
||||||
f.files.set_show_hidden(self.conf.show_hidden);
|
f.files.set_show_hidden(self.conf.show_hidden);
|
||||||
f.files.set_sorter(self.conf.sorter());
|
f.files.set_sorter(self.conf.sorter());
|
||||||
f.files.catchup_revision() | f.repos(hovered)
|
render!(f.files.catchup_revision());
|
||||||
|
render!(f.repos(hovered));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut b = false;
|
|
||||||
if let Some(f) =
|
if let Some(f) =
|
||||||
self.current.hovered().filter(|h| h.is_dir()).and_then(|h| self.history.get_mut(&h.url))
|
self.current.hovered().filter(|h| h.is_dir()).and_then(|h| self.history.get_mut(&h.url))
|
||||||
{
|
{
|
||||||
b |= apply(f);
|
apply(f);
|
||||||
}
|
}
|
||||||
if just_preview {
|
if just_preview {
|
||||||
return b;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
b |= apply(&mut self.current);
|
apply(&mut self.current);
|
||||||
if let Some(parent) = self.parent.as_mut() {
|
if let Some(parent) = self.parent.as_mut() {
|
||||||
b |= apply(parent);
|
apply(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
b
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::tasks::Tasks;
|
use crate::tasks::Tasks;
|
||||||
|
|
||||||
|
|
@ -14,23 +14,28 @@ impl From<&Exec> for Opt {
|
||||||
|
|
||||||
impl Tasks {
|
impl Tasks {
|
||||||
#[allow(clippy::should_implement_trait)]
|
#[allow(clippy::should_implement_trait)]
|
||||||
fn next(&mut self) -> bool {
|
fn next(&mut self) {
|
||||||
let limit = Self::limit().min(self.len());
|
let limit = Self::limit().min(self.len());
|
||||||
|
|
||||||
let old = self.cursor;
|
let old = self.cursor;
|
||||||
self.cursor = limit.saturating_sub(1).min(self.cursor + 1);
|
self.cursor = limit.saturating_sub(1).min(self.cursor + 1);
|
||||||
|
|
||||||
old != self.cursor
|
render!(old != self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev(&mut self) -> bool {
|
fn prev(&mut self) {
|
||||||
let old = self.cursor;
|
let old = self.cursor;
|
||||||
self.cursor = self.cursor.saturating_sub(1);
|
self.cursor = self.cursor.saturating_sub(1);
|
||||||
old != self.cursor
|
|
||||||
|
render!(old != self.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arrow(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn arrow(&mut self, opt: impl Into<Opt>) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.step > 0 { self.next() } else { self.prev() }
|
if opt.step > 0 {
|
||||||
|
self.next();
|
||||||
|
} else {
|
||||||
|
self.prev();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::tasks::Tasks;
|
use crate::tasks::Tasks;
|
||||||
|
|
||||||
impl Tasks {
|
impl Tasks {
|
||||||
pub fn cancel(&mut self, _: &Exec) -> bool {
|
pub fn cancel(&mut self, _: &Exec) {
|
||||||
let id = self.scheduler.running.read().get_id(self.cursor);
|
let id = self.scheduler.running.read().get_id(self.cursor);
|
||||||
if id.map(|id| self.scheduler.cancel(id)) != Some(true) {
|
if id.map(|id| self.scheduler.cancel(id)) != Some(true) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = self.scheduler.running.read().len();
|
let len = self.scheduler.running.read().len();
|
||||||
self.cursor = self.cursor.min(len.saturating_sub(1));
|
self.cursor = self.cursor.min(len.saturating_sub(1));
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ use yazi_shared::{event::Exec, term::Term, Defer};
|
||||||
use crate::tasks::Tasks;
|
use crate::tasks::Tasks;
|
||||||
|
|
||||||
impl Tasks {
|
impl Tasks {
|
||||||
pub fn inspect(&self, _: &Exec) -> bool {
|
pub fn inspect(&self, _: &Exec) {
|
||||||
let Some(id) = self.scheduler.running.read().get_id(self.cursor) else {
|
let Some(id) = self.scheduler.running.read().get_id(self.cursor) else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let scheduler = self.scheduler.clone();
|
let scheduler = self.scheduler.clone();
|
||||||
|
|
@ -66,6 +66,5 @@ impl Tasks {
|
||||||
stdin.read(&mut quit).await.ok();
|
stdin.read(&mut quit).await.ok();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,9 @@ impl Tasks {
|
||||||
emit!(Call(Exec::call("open", vec![]).with_data(Opt { targets, opener }).vec(), Layer::Tasks));
|
emit!(Call(Exec::call("open", vec![]).with_data(Opt { targets, opener }).vec(), Layer::Tasks));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(&mut self, opt: impl TryInto<Opt>) -> bool {
|
pub fn open(&mut self, opt: impl TryInto<Opt>) {
|
||||||
if let Ok(opt) = opt.try_into() {
|
if let Ok(opt) = opt.try_into() {
|
||||||
self.file_open_with(&opt.opener, &opt.targets);
|
self.file_open_with(&opt.opener, &opt.targets);
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use yazi_shared::event::Exec;
|
use yazi_shared::{event::Exec, render};
|
||||||
|
|
||||||
use crate::tasks::Tasks;
|
use crate::tasks::Tasks;
|
||||||
|
|
||||||
|
|
@ -12,8 +12,8 @@ impl From<()> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tasks {
|
impl Tasks {
|
||||||
pub fn toggle(&mut self, _: impl Into<Opt>) -> bool {
|
pub fn toggle(&mut self, _: impl Into<Opt>) {
|
||||||
self.visible = !self.visible;
|
self.visible = !self.visible;
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use yazi_shared::{emit, event::Exec, Layer};
|
use yazi_shared::{emit, event::Exec, render, Layer};
|
||||||
|
|
||||||
use crate::tasks::{Tasks, TasksProgress};
|
use crate::tasks::{Tasks, TasksProgress};
|
||||||
|
|
||||||
|
|
@ -20,12 +20,12 @@ impl Tasks {
|
||||||
emit!(Call(Exec::call("update", vec![]).with_data(Opt { progress }).vec(), Layer::Tasks));
|
emit!(Call(Exec::call("update", vec![]).with_data(Opt { progress }).vec(), Layer::Tasks));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, opt: impl TryInto<Opt>) -> bool {
|
pub fn update(&mut self, opt: impl TryInto<Opt>) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let Ok(opt) = opt.try_into() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.progress = opt.progress;
|
self.progress = opt.progress;
|
||||||
true
|
render!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ impl Tasks {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_cut(&self, src: &HashSet<Url>, dest: &Url, force: bool) -> bool {
|
pub fn file_cut(&self, src: &HashSet<Url>, dest: &Url, force: bool) {
|
||||||
for u in src {
|
for u in src {
|
||||||
let to = dest.join(u.file_name().unwrap());
|
let to = dest.join(u.file_name().unwrap());
|
||||||
if force && u == &to {
|
if force && u == &to {
|
||||||
|
|
@ -86,10 +86,9 @@ impl Tasks {
|
||||||
self.scheduler.file_cut(u.clone(), to, force);
|
self.scheduler.file_cut(u.clone(), to, force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_copy(&self, src: &HashSet<Url>, dest: &Url, force: bool, follow: bool) -> bool {
|
pub fn file_copy(&self, src: &HashSet<Url>, dest: &Url, force: bool, follow: bool) {
|
||||||
for u in src {
|
for u in src {
|
||||||
let to = dest.join(u.file_name().unwrap());
|
let to = dest.join(u.file_name().unwrap());
|
||||||
if force && u == &to {
|
if force && u == &to {
|
||||||
|
|
@ -98,10 +97,9 @@ impl Tasks {
|
||||||
self.scheduler.file_copy(u.clone(), to, force, follow);
|
self.scheduler.file_copy(u.clone(), to, force, follow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_link(&self, src: &HashSet<Url>, dest: &Url, relative: bool, force: bool) -> bool {
|
pub fn file_link(&self, src: &HashSet<Url>, dest: &Url, relative: bool, force: bool) {
|
||||||
for u in src {
|
for u in src {
|
||||||
let to = dest.join(u.file_name().unwrap());
|
let to = dest.join(u.file_name().unwrap());
|
||||||
if force && *u == to {
|
if force && *u == to {
|
||||||
|
|
@ -110,10 +108,9 @@ impl Tasks {
|
||||||
self.scheduler.file_link(u.clone(), to, relative, force);
|
self.scheduler.file_link(u.clone(), to, relative, force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_remove(&self, targets: Vec<Url>, force: bool, permanently: bool) -> bool {
|
pub fn file_remove(&self, targets: Vec<Url>, force: bool, permanently: bool) {
|
||||||
if force {
|
if force {
|
||||||
for u in targets {
|
for u in targets {
|
||||||
if permanently {
|
if permanently {
|
||||||
|
|
@ -122,7 +119,7 @@ impl Tasks {
|
||||||
self.scheduler.file_trash(u);
|
self.scheduler.file_trash(u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let scheduler = self.scheduler.clone();
|
let scheduler = self.scheduler.clone();
|
||||||
|
|
@ -146,18 +143,11 @@ impl Tasks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plugin_micro(&self, name: &str) -> bool {
|
pub fn plugin_micro(&self, name: &str) { self.scheduler.plugin_micro(name.to_owned()); }
|
||||||
self.scheduler.plugin_micro(name.to_owned());
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn plugin_macro(&self, name: &str) -> bool {
|
pub fn plugin_macro(&self, name: &str) { self.scheduler.plugin_macro(name.to_owned()); }
|
||||||
self.scheduler.plugin_macro(name.to_owned());
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn preload_paged(&self, paged: &[File], mimetype: &HashMap<Url, String>) {
|
pub fn preload_paged(&self, paged: &[File], mimetype: &HashMap<Url, String>) {
|
||||||
let mut single_tasks = Vec::with_capacity(paged.len());
|
let mut single_tasks = Vec::with_capacity(paged.len());
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use anyhow::{Ok, Result};
|
use anyhow::{Ok, Result};
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::KeyEvent;
|
||||||
use yazi_config::{keymap::Key, ARGS};
|
use yazi_config::keymap::Key;
|
||||||
use yazi_core::input::InputMode;
|
use yazi_core::input::InputMode;
|
||||||
use yazi_shared::{emit, event::{Event, Exec}, term::Term, Layer};
|
use yazi_shared::{event::{Event, Exec, NEED_RENDER}, term::Term, Layer};
|
||||||
|
|
||||||
use crate::{lives::Lives, Ctx, Executor, Logs, Panic, Signals};
|
use crate::{lives::Lives, Ctx, Executor, Logs, Panic, Signals};
|
||||||
|
|
||||||
|
|
@ -16,51 +18,44 @@ impl App {
|
||||||
pub(crate) async fn run() -> Result<()> {
|
pub(crate) async fn run() -> Result<()> {
|
||||||
Panic::install();
|
Panic::install();
|
||||||
let _log = Logs::init()?;
|
let _log = Logs::init()?;
|
||||||
|
|
||||||
let term = Term::start()?;
|
let term = Term::start()?;
|
||||||
let signals = Signals::start()?;
|
let signals = Signals::start()?;
|
||||||
|
|
||||||
Lives::register()?;
|
Lives::register()?;
|
||||||
let mut app = Self { cx: Ctx::make(), term: Some(term), signals };
|
let mut app = Self { cx: Ctx::make(), term: Some(term), signals };
|
||||||
|
|
||||||
app.render()?;
|
app.render()?;
|
||||||
while let Some(event) = app.signals.recv().await {
|
|
||||||
match event {
|
let mut events = Vec::with_capacity(10);
|
||||||
Event::Quit(no_cwd_file) => {
|
while app.signals.rx.recv_many(&mut events, 10).await > 0 {
|
||||||
app.dispatch_quit(no_cwd_file);
|
for event in events.drain(..) {
|
||||||
break;
|
match event {
|
||||||
|
Event::Quit(no_cwd_file) => {
|
||||||
|
app.quit(no_cwd_file)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Event::Key(key) => app.dispatch_key(key),
|
||||||
|
Event::Paste(str) => app.dispatch_paste(str),
|
||||||
|
Event::Resize(cols, rows) => app.dispatch_resize(cols, rows)?,
|
||||||
|
Event::Call(exec, layer) => app.dispatch_call(exec, layer),
|
||||||
|
event => app.dispatch_module(event),
|
||||||
}
|
}
|
||||||
Event::Key(key) => app.dispatch_key(key),
|
}
|
||||||
Event::Paste(str) => app.dispatch_paste(str),
|
|
||||||
Event::Render(_) => app.render()?,
|
if NEED_RENDER.swap(false, Ordering::Relaxed) {
|
||||||
Event::Resize(cols, rows) => app.dispatch_resize(cols, rows)?,
|
app.render()?;
|
||||||
Event::Call(exec, layer) => app.dispatch_call(exec, layer),
|
|
||||||
event => app.dispatch_module(event),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_quit(&mut self, no_cwd_file: bool) {
|
#[inline]
|
||||||
if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !no_cwd_file) {
|
fn dispatch_key(&mut self, key: KeyEvent) { Executor::new(self).handle(Key::from(key)); }
|
||||||
let cwd = self.cx.manager.cwd().as_os_str();
|
|
||||||
std::fs::write(p, cwd.as_encoded_bytes()).ok();
|
|
||||||
}
|
|
||||||
Term::goodbye(|| false);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch_key(&mut self, key: KeyEvent) {
|
|
||||||
let key = Key::from(key);
|
|
||||||
if Executor::new(self).handle(key) {
|
|
||||||
emit!(Render);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch_paste(&mut self, str: String) {
|
fn dispatch_paste(&mut self, str: String) {
|
||||||
if self.cx.input.visible {
|
if self.cx.input.visible {
|
||||||
let input = &mut self.cx.input;
|
let input = &mut self.cx.input;
|
||||||
if input.mode() == InputMode::Insert && input.type_str(&str) {
|
if input.mode() == InputMode::Insert {
|
||||||
emit!(Render);
|
input.type_str(&str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -76,9 +71,7 @@ impl App {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn dispatch_call(&mut self, exec: Vec<Exec>, layer: Layer) {
|
fn dispatch_call(&mut self, exec: Vec<Exec>, layer: Layer) {
|
||||||
if Executor::new(self).dispatch(&exec, layer) {
|
Executor::new(self).dispatch(&exec, layer);
|
||||||
emit!(Render);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_module(&mut self, event: Event) {
|
fn dispatch_module(&mut self, event: Event) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
mod plugin;
|
mod plugin;
|
||||||
|
mod quit;
|
||||||
mod render;
|
mod render;
|
||||||
mod stop;
|
mod stop;
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ use yazi_shared::{emit, event::Exec, Layer};
|
||||||
use crate::{app::App, lives::Lives};
|
use crate::{app::App, lives::Lives};
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub(crate) fn plugin(&mut self, opt: impl TryInto<yazi_plugin::Opt>) -> bool {
|
pub(crate) fn plugin(&mut self, opt: impl TryInto<yazi_plugin::Opt>) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let Ok(opt) = opt.try_into() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !opt.sync {
|
if !opt.sync {
|
||||||
|
|
@ -24,12 +24,11 @@ impl App {
|
||||||
emit!(Call(Exec::call("plugin_do", vec![opt.name]).with_data(opt.data).vec(), Layer::App));
|
emit!(Call(Exec::call("plugin_do", vec![opt.name]).with_data(opt.data).vec(), Layer::App));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn plugin_do(&mut self, opt: impl TryInto<yazi_plugin::Opt>) -> bool {
|
pub(crate) fn plugin_do(&mut self, opt: impl TryInto<yazi_plugin::Opt>) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let Ok(opt) = opt.try_into() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let args = Variadic::from_iter(opt.data.args.into_iter().filter_map(|v| v.into_lua(&LUA).ok()));
|
let args = Variadic::from_iter(opt.data.args.into_iter().filter_map(|v| v.into_lua(&LUA).ok()));
|
||||||
|
|
@ -51,16 +50,15 @@ impl App {
|
||||||
|
|
||||||
if let Err(e) = ret {
|
if let Err(e) = ret {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(tx) = opt.data.tx else {
|
let Some(tx) = opt.data.tx else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(v) = ret.and_then(|v| v.try_into().into_lua_err()) {
|
if let Ok(v) = ret.and_then(|v| v.try_into().into_lua_err()) {
|
||||||
tx.send(v).ok();
|
tx.send(v).ok();
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
yazi-fm/src/app/commands/quit.rs
Normal file
26
yazi-fm/src/app/commands/quit.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use yazi_config::ARGS;
|
||||||
|
use yazi_shared::term::Term;
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
|
||||||
|
pub struct Opt {
|
||||||
|
no_cwd_file: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for Opt {
|
||||||
|
fn from(no_cwd_file: bool) -> Self { Self { no_cwd_file } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub(crate) fn quit(&mut self, opt: impl Into<Opt>) -> Result<()> {
|
||||||
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
|
if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !opt.no_cwd_file) {
|
||||||
|
let cwd = self.cx.manager.cwd().as_os_str();
|
||||||
|
std::fs::write(p, cwd.as_encoded_bytes()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Term::goodbye(|| false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,8 @@ use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ratatui::backend::Backend;
|
use ratatui::backend::Backend;
|
||||||
use yazi_shared::COLLISION;
|
|
||||||
|
|
||||||
use crate::{app::App, lives::Lives, root::Root};
|
use crate::{app::App, lives::Lives, root::{Root, COLLISION}};
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub(crate) fn render(&mut self) -> Result<()> {
|
pub(crate) fn render(&mut self) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ impl TryFrom<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub(crate) fn stop(&mut self, opt: impl TryInto<Opt>) -> bool {
|
pub(crate) fn stop(&mut self, opt: impl TryInto<Opt>) {
|
||||||
let Ok(opt) = opt.try_into() else {
|
let Ok(opt) = opt.try_into() else {
|
||||||
return false;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cx.manager.active_mut().preview.reset_image();
|
self.cx.manager.active_mut().preview.reset_image();
|
||||||
|
|
@ -38,6 +38,5 @@ impl App {
|
||||||
if let Some(tx) = opt.tx {
|
if let Some(tx) = opt.tx {
|
||||||
tx.send(()).ok();
|
tx.send(()).ok();
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ impl<'a> Executor<'a> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let b = if cx.completion.visible {
|
if cx.completion.visible {
|
||||||
self.matches(Layer::Completion, key).or_else(|| self.matches(Layer::Input, key))
|
self.matches(Layer::Completion, key) || self.matches(Layer::Input, key)
|
||||||
} else if cx.help.visible {
|
} else if cx.help.visible {
|
||||||
self.matches(Layer::Help, key)
|
self.matches(Layer::Help, key)
|
||||||
} else if cx.input.visible {
|
} else if cx.input.visible {
|
||||||
|
|
@ -37,31 +37,30 @@ impl<'a> Executor<'a> {
|
||||||
self.matches(Layer::Tasks, key)
|
self.matches(Layer::Tasks, key)
|
||||||
} else {
|
} else {
|
||||||
self.matches(Layer::Manager, key)
|
self.matches(Layer::Manager, key)
|
||||||
};
|
}
|
||||||
b == Some(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn matches(&mut self, layer: Layer, key: Key) -> Option<bool> {
|
fn matches(&mut self, layer: Layer, key: Key) -> bool {
|
||||||
for Control { on, exec, .. } in KEYMAP.get(layer) {
|
for Control { on, exec, .. } in KEYMAP.get(layer) {
|
||||||
if on.is_empty() || on[0] != key {
|
if on.is_empty() || on[0] != key {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some(if on.len() > 1 {
|
if on.len() > 1 {
|
||||||
self.app.cx.which.show(&key, layer)
|
self.app.cx.which.show(&key, layer);
|
||||||
} else {
|
} else {
|
||||||
self.dispatch(exec, layer)
|
self.dispatch(exec, layer);
|
||||||
});
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
None
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn dispatch(&mut self, exec: &[Exec], layer: Layer) -> bool {
|
pub(super) fn dispatch(&mut self, exec: &[Exec], layer: Layer) {
|
||||||
let mut render = false;
|
|
||||||
for e in exec {
|
for e in exec {
|
||||||
render |= match layer {
|
match layer {
|
||||||
Layer::App => self.app(e),
|
Layer::App => self.app(e),
|
||||||
Layer::Manager => self.manager(e),
|
Layer::Manager => self.manager(e),
|
||||||
Layer::Tasks => self.tasks(e),
|
Layer::Tasks => self.tasks(e),
|
||||||
|
|
@ -72,10 +71,9 @@ impl<'a> Executor<'a> {
|
||||||
Layer::Which => unreachable!(),
|
Layer::Which => unreachable!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
render
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app(&mut self, exec: &Exec) -> bool {
|
fn app(&mut self, exec: &Exec) {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
|
|
@ -87,11 +85,9 @@ impl<'a> Executor<'a> {
|
||||||
on!(plugin);
|
on!(plugin);
|
||||||
on!(plugin_do);
|
on!(plugin_do);
|
||||||
on!(stop);
|
on!(stop);
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn manager(&mut self, exec: &Exec) -> bool {
|
fn manager(&mut self, exec: &Exec) {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
(MANAGER, $name:ident $(,$args:expr)*) => {
|
(MANAGER, $name:ident $(,$args:expr)*) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
|
|
@ -175,11 +171,11 @@ impl<'a> Executor<'a> {
|
||||||
b"tasks_show" => self.app.cx.tasks.toggle(()),
|
b"tasks_show" => self.app.cx.tasks.toggle(()),
|
||||||
// Help
|
// Help
|
||||||
b"help" => self.app.cx.help.toggle(Layer::Manager),
|
b"help" => self.app.cx.help.toggle(Layer::Manager),
|
||||||
_ => false,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tasks(&mut self, exec: &Exec) -> bool {
|
fn tasks(&mut self, exec: &Exec) {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
|
|
@ -202,11 +198,11 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"help" => self.app.cx.help.toggle(Layer::Tasks),
|
"help" => self.app.cx.help.toggle(Layer::Tasks),
|
||||||
_ => false,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select(&mut self, exec: &Exec) -> bool {
|
fn select(&mut self, exec: &Exec) {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
|
|
@ -221,11 +217,11 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"help" => self.app.cx.help.toggle(Layer::Select),
|
"help" => self.app.cx.help.toggle(Layer::Select),
|
||||||
_ => false,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input(&mut self, exec: &Exec) -> bool {
|
fn input(&mut self, exec: &Exec) {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
|
|
@ -268,19 +264,17 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"help" => self.app.cx.help.toggle(Layer::Input),
|
"help" => self.app.cx.help.toggle(Layer::Input),
|
||||||
_ => false,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputMode::Insert => {
|
InputMode::Insert => {
|
||||||
on!(backspace);
|
on!(backspace);
|
||||||
on!(kill);
|
on!(kill);
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn help(&mut self, exec: &Exec) -> bool {
|
fn help(&mut self, exec: &Exec) {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
|
|
@ -295,11 +289,11 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"close" => self.app.cx.help.toggle(Layer::Help),
|
"close" => self.app.cx.help.toggle(Layer::Help),
|
||||||
_ => false,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn completion(&mut self, exec: &Exec) -> bool {
|
fn completion(&mut self, exec: &Exec) {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
|
|
@ -315,7 +309,7 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"help" => self.app.cx.help.toggle(Layer::Completion),
|
"help" => self.app.cx.help.toggle(Layer::Completion),
|
||||||
_ => false,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
use ratatui::{buffer::Buffer, layout::{Constraint, Direction, Layout, Rect}, widgets::Widget};
|
use ratatui::{buffer::Buffer, layout::{Constraint, Direction, Layout, Rect}, widgets::Widget};
|
||||||
|
|
||||||
use super::{completion, input, select, tasks, which};
|
use super::{completion, input, select, tasks, which};
|
||||||
use crate::{components, help, Ctx};
|
use crate::{components, help, Ctx};
|
||||||
|
|
||||||
|
pub(super) static COLLISION: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
pub(super) struct Root<'a> {
|
pub(super) struct Root<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ use tokio::{select, sync::{mpsc::{self, UnboundedReceiver, UnboundedSender}, one
|
||||||
use yazi_shared::event::Event;
|
use yazi_shared::event::Event;
|
||||||
|
|
||||||
pub(super) struct Signals {
|
pub(super) struct Signals {
|
||||||
tx: UnboundedSender<Event>,
|
tx: UnboundedSender<Event>,
|
||||||
rx: UnboundedReceiver<Event>,
|
pub(super) rx: UnboundedReceiver<Event>,
|
||||||
|
|
||||||
term_stop_tx: Option<oneshot::Sender<()>>,
|
term_stop_tx: Option<oneshot::Sender<()>>,
|
||||||
term_stop_rx: Option<oneshot::Receiver<()>>,
|
term_stop_rx: Option<oneshot::Receiver<()>>,
|
||||||
|
|
@ -27,9 +27,6 @@ impl Signals {
|
||||||
Ok(signals)
|
Ok(signals)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(super) async fn recv(&mut self) -> Option<Event> { self.rx.recv().await }
|
|
||||||
|
|
||||||
pub(super) fn stop_term(&mut self, state: bool) {
|
pub(super) fn stop_term(&mut self, state: bool) {
|
||||||
if state == self.term_stop_tx.is_none() {
|
if state == self.term_stop_tx.is_none() {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use std::sync::atomic::Ordering;
|
||||||
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
||||||
use yazi_adaptor::ADAPTOR;
|
use yazi_adaptor::ADAPTOR;
|
||||||
use yazi_config::LAYOUT;
|
use yazi_config::LAYOUT;
|
||||||
use yazi_shared::COLLISION;
|
|
||||||
|
use crate::root::COLLISION;
|
||||||
|
|
||||||
pub(crate) struct Clear;
|
pub(crate) struct Clear;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ use super::{RectRef, Renderable, Style};
|
||||||
pub struct Bar {
|
pub struct Bar {
|
||||||
area: ratatui::layout::Rect,
|
area: ratatui::layout::Rect,
|
||||||
|
|
||||||
position: ratatui::widgets::Borders,
|
direction: ratatui::widgets::Borders,
|
||||||
symbol: String,
|
symbol: String,
|
||||||
style: Option<ratatui::style::Style>,
|
style: Option<ratatui::style::Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bar {
|
impl Bar {
|
||||||
|
|
@ -18,13 +18,14 @@ impl Bar {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
area: *area,
|
area: *area,
|
||||||
|
|
||||||
position: Borders::from_bits_truncate(direction),
|
direction: Borders::from_bits_truncate(direction),
|
||||||
symbol: Default::default(),
|
symbol: Default::default(),
|
||||||
style: Default::default(),
|
style: Default::default(),
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let bar = lua.create_table_from([
|
let bar = lua.create_table_from([
|
||||||
|
// Direction
|
||||||
("NONE", Borders::NONE.bits().into_lua(lua)?),
|
("NONE", Borders::NONE.bits().into_lua(lua)?),
|
||||||
("TOP", Borders::TOP.bits().into_lua(lua)?),
|
("TOP", Borders::TOP.bits().into_lua(lua)?),
|
||||||
("RIGHT", Borders::RIGHT.bits().into_lua(lua)?),
|
("RIGHT", Borders::RIGHT.bits().into_lua(lua)?),
|
||||||
|
|
@ -68,15 +69,15 @@ impl Renderable for Bar {
|
||||||
|
|
||||||
let symbol = if !self.symbol.is_empty() {
|
let symbol = if !self.symbol.is_empty() {
|
||||||
&self.symbol
|
&self.symbol
|
||||||
} else if self.position.intersects(Borders::TOP | Borders::BOTTOM) {
|
} else if self.direction.intersects(Borders::TOP | Borders::BOTTOM) {
|
||||||
"─"
|
"─"
|
||||||
} else if self.position.intersects(Borders::LEFT | Borders::RIGHT) {
|
} else if self.direction.intersects(Borders::LEFT | Borders::RIGHT) {
|
||||||
"│"
|
"│"
|
||||||
} else {
|
} else {
|
||||||
" "
|
" "
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.position.contains(Borders::LEFT) {
|
if self.direction.contains(Borders::LEFT) {
|
||||||
for y in self.area.top()..self.area.bottom() {
|
for y in self.area.top()..self.area.bottom() {
|
||||||
let cell = buf.get_mut(self.area.left(), y).set_symbol(symbol);
|
let cell = buf.get_mut(self.area.left(), y).set_symbol(symbol);
|
||||||
if let Some(style) = self.style {
|
if let Some(style) = self.style {
|
||||||
|
|
@ -84,7 +85,7 @@ impl Renderable for Bar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.position.contains(Borders::TOP) {
|
if self.direction.contains(Borders::TOP) {
|
||||||
for x in self.area.left()..self.area.right() {
|
for x in self.area.left()..self.area.right() {
|
||||||
let cell = buf.get_mut(x, self.area.top()).set_symbol(symbol);
|
let cell = buf.get_mut(x, self.area.top()).set_symbol(symbol);
|
||||||
if let Some(style) = self.style {
|
if let Some(style) = self.style {
|
||||||
|
|
@ -92,7 +93,7 @@ impl Renderable for Bar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.position.contains(Borders::RIGHT) {
|
if self.direction.contains(Borders::RIGHT) {
|
||||||
let x = self.area.right() - 1;
|
let x = self.area.right() - 1;
|
||||||
for y in self.area.top()..self.area.bottom() {
|
for y in self.area.top()..self.area.bottom() {
|
||||||
let cell = buf.get_mut(x, y).set_symbol(symbol);
|
let cell = buf.get_mut(x, y).set_symbol(symbol);
|
||||||
|
|
@ -101,7 +102,7 @@ impl Renderable for Bar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.position.contains(Borders::BOTTOM) {
|
if self.direction.contains(Borders::BOTTOM) {
|
||||||
let y = self.area.bottom() - 1;
|
let y = self.area.bottom() - 1;
|
||||||
for x in self.area.left()..self.area.right() {
|
for x in self.area.left()..self.area.right() {
|
||||||
let cell = buf.get_mut(x, y).set_symbol(symbol);
|
let cell = buf.get_mut(x, y).set_symbol(symbol);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use mlua::{AnyUserData, ExternalError, IntoLua, Lua, Table, UserData, Value};
|
use mlua::{AnyUserData, ExternalError, IntoLua, Lua, Table, UserData, Value};
|
||||||
use ratatui::widgets::Widget;
|
use ratatui::widgets::{Borders, Widget};
|
||||||
|
|
||||||
use super::{RectRef, Renderable, Style};
|
use super::{RectRef, Renderable, Style};
|
||||||
|
|
||||||
|
// Type
|
||||||
const PLAIN: u8 = 0;
|
const PLAIN: u8 = 0;
|
||||||
const ROUNDED: u8 = 1;
|
const ROUNDED: u8 = 1;
|
||||||
const DOUBLE: u8 = 2;
|
const DOUBLE: u8 = 2;
|
||||||
|
|
@ -30,7 +31,14 @@ impl Border {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let border = lua.create_table_from([
|
let border = lua.create_table_from([
|
||||||
// Border type
|
// Position
|
||||||
|
("NONE", Borders::NONE.bits().into_lua(lua)?),
|
||||||
|
("TOP", Borders::TOP.bits().into_lua(lua)?),
|
||||||
|
("RIGHT", Borders::RIGHT.bits().into_lua(lua)?),
|
||||||
|
("BOTTOM", Borders::BOTTOM.bits().into_lua(lua)?),
|
||||||
|
("LEFT", Borders::LEFT.bits().into_lua(lua)?),
|
||||||
|
("ALL", Borders::ALL.bits().into_lua(lua)?),
|
||||||
|
// Type
|
||||||
("PLAIN", PLAIN.into_lua(lua)?),
|
("PLAIN", PLAIN.into_lua(lua)?),
|
||||||
("ROUNDED", ROUNDED.into_lua(lua)?),
|
("ROUNDED", ROUNDED.into_lua(lua)?),
|
||||||
("DOUBLE", DOUBLE.into_lua(lua)?),
|
("DOUBLE", DOUBLE.into_lua(lua)?),
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ pub enum Event {
|
||||||
Quit(bool), // no-cwd-file
|
Quit(bool), // no-cwd-file
|
||||||
Key(KeyEvent),
|
Key(KeyEvent),
|
||||||
Paste(String),
|
Paste(String),
|
||||||
Render(String),
|
|
||||||
Resize(u16, u16),
|
Resize(u16, u16),
|
||||||
Call(Vec<Exec>, Layer),
|
Call(Vec<Exec>, Layer),
|
||||||
|
|
||||||
|
|
@ -36,9 +35,6 @@ macro_rules! emit {
|
||||||
(Quit($no_cwd_file:expr)) => {
|
(Quit($no_cwd_file:expr)) => {
|
||||||
$crate::event::Event::Quit($no_cwd_file).emit();
|
$crate::event::Event::Quit($no_cwd_file).emit();
|
||||||
};
|
};
|
||||||
(Render) => {
|
|
||||||
$crate::event::Event::Render(format!("{}:{}", file!(), line!())).emit();
|
|
||||||
};
|
|
||||||
(Call($exec:expr, $layer:expr)) => {
|
(Call($exec:expr, $layer:expr)) => {
|
||||||
$crate::event::Event::Call($exec, $layer).emit();
|
$crate::event::Event::Call($exec, $layer).emit();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
mod event;
|
mod event;
|
||||||
mod exec;
|
mod exec;
|
||||||
|
mod render;
|
||||||
|
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
pub use exec::*;
|
pub use exec::*;
|
||||||
|
pub use render::*;
|
||||||
|
|
|
||||||
15
yazi-shared/src/event/render.rs
Normal file
15
yazi-shared/src/event/render.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
|
pub static NEED_RENDER: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! render {
|
||||||
|
() => {
|
||||||
|
$crate::event::NEED_RENDER.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
};
|
||||||
|
($expr:expr) => {
|
||||||
|
if $expr {
|
||||||
|
render!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,3 @@ pub use number::*;
|
||||||
pub use ro_cell::*;
|
pub use ro_cell::*;
|
||||||
pub use throttle::*;
|
pub use throttle::*;
|
||||||
pub use time::*;
|
pub use time::*;
|
||||||
|
|
||||||
// TODO: remove this
|
|
||||||
pub static COLLISION: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue