mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
WIP [no ci]
This commit is contained in:
parent
b749972908
commit
11d8ee6f5b
11 changed files with 124 additions and 116 deletions
|
|
@ -102,9 +102,15 @@ impl Exec {
|
||||||
pub fn vec(self) -> Vec<Self> { vec![self] }
|
pub fn vec(self) -> Vec<Self> { vec![self] }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_bool(mut self, name: &str, state: bool) -> Self {
|
pub fn with(mut self, name: impl ToString, value: impl ToString) -> Self {
|
||||||
|
self.named.insert(name.to_string(), value.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn with_bool(mut self, name: impl ToString, state: bool) -> Self {
|
||||||
if state {
|
if state {
|
||||||
self.named.insert(name.to_string(), "".to_string());
|
self.named.insert(name.to_string(), Default::default());
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
yazi-core/src/completion/commands/mod.rs
Normal file
5
yazi-core/src/completion/commands/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
mod show;
|
||||||
|
mod trigger;
|
||||||
|
|
||||||
|
pub use show::*;
|
||||||
|
pub use trigger::*;
|
||||||
32
yazi-core/src/completion/commands/show.rs
Normal file
32
yazi-core/src/completion/commands/show.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use yazi_config::keymap::Exec;
|
||||||
|
|
||||||
|
use crate::completion::Completion;
|
||||||
|
|
||||||
|
pub struct Opt<'a> {
|
||||||
|
cands: &'a Vec<String>,
|
||||||
|
version: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Exec> for Opt<'a> {
|
||||||
|
fn from(e: &'a Exec) -> Self {
|
||||||
|
Self {
|
||||||
|
cands: &e.args,
|
||||||
|
version: e.named.get("version").and_then(|v| v.parse().ok()).unwrap_or(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Completion {
|
||||||
|
pub fn show<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
||||||
|
let opt = opt.into();
|
||||||
|
if self.version != opt.version {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.close(false);
|
||||||
|
self.items = opt.cands.clone();
|
||||||
|
self.version = opt.version;
|
||||||
|
self.visible = true;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
34
yazi-core/src/completion/commands/trigger.rs
Normal file
34
yazi-core/src/completion/commands/trigger.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use tracing::info;
|
||||||
|
use yazi_config::keymap::Exec;
|
||||||
|
|
||||||
|
use crate::completion::Completion;
|
||||||
|
|
||||||
|
pub struct Opt<'a> {
|
||||||
|
word: &'a str,
|
||||||
|
version: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Exec> for Opt<'a> {
|
||||||
|
fn from(e: &'a Exec) -> Self {
|
||||||
|
Self {
|
||||||
|
word: e.args.get(0).map(|w| w.as_str()).unwrap_or_default(),
|
||||||
|
version: e.named.get("version").and_then(|v| v.parse().ok()).unwrap_or(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Completion {
|
||||||
|
pub fn trigger<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
||||||
|
let opt = opt.into();
|
||||||
|
if self.version >= opt.version {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.close(false);
|
||||||
|
self.version = opt.version;
|
||||||
|
|
||||||
|
info!("trigger completion: {}", opt.word);
|
||||||
|
tokio::spawn(async move {});
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,43 +1,16 @@
|
||||||
use crate::{completion::CompletionOpt, Position};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Completion {
|
pub struct Completion {
|
||||||
pub items: Vec<String>,
|
pub items: Vec<String>,
|
||||||
pub cursor: usize,
|
pub cursor: usize,
|
||||||
|
|
||||||
pub identifier: String,
|
pub version: usize,
|
||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
|
|
||||||
// TODO: remove these
|
|
||||||
pub position: Position,
|
|
||||||
pub column_cnt: u8,
|
|
||||||
pub max_width: u16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Completion {
|
impl Completion {
|
||||||
pub fn show(&mut self, opt: CompletionOpt) {
|
|
||||||
self.close(false);
|
|
||||||
self.items = opt.items;
|
|
||||||
|
|
||||||
self.identifier = format!(
|
|
||||||
"{}",
|
|
||||||
std::time::SystemTime::now()
|
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.as_millis()
|
|
||||||
);
|
|
||||||
self.visible = true;
|
|
||||||
|
|
||||||
// TODO: remove these
|
|
||||||
self.position = opt.position;
|
|
||||||
self.column_cnt = opt.column_cnt;
|
|
||||||
self.max_width = opt.max_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close(&mut self, submit: bool) -> bool {
|
pub fn close(&mut self, submit: bool) -> bool {
|
||||||
self.cursor = 0;
|
self.cursor = 0;
|
||||||
|
|
||||||
self.identifier = String::new();
|
|
||||||
self.visible = false;
|
self.visible = false;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
|
mod commands;
|
||||||
mod completion;
|
mod completion;
|
||||||
mod option;
|
|
||||||
|
|
||||||
pub(super) use completion::Completion;
|
pub(super) use completion::*;
|
||||||
pub use option::CompletionOpt;
|
|
||||||
|
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
use ratatui::layout::Rect;
|
|
||||||
|
|
||||||
use crate::Position;
|
|
||||||
|
|
||||||
pub struct CompletionOpt {
|
|
||||||
pub items: Vec<String>,
|
|
||||||
pub position: Position,
|
|
||||||
pub column_cnt: u8,
|
|
||||||
pub max_width: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CompletionOpt {
|
|
||||||
pub fn top() -> Self {
|
|
||||||
Self {
|
|
||||||
items: vec![],
|
|
||||||
position: Position::Top(
|
|
||||||
// TODO: hardcode
|
|
||||||
Rect { x: 0, y: 5, width: 80, height: 10 },
|
|
||||||
),
|
|
||||||
column_cnt: 4,
|
|
||||||
max_width: 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hover() -> Self {
|
|
||||||
Self {
|
|
||||||
items: vec![],
|
|
||||||
position: Position::Hovered(
|
|
||||||
// TODO: hardcode
|
|
||||||
Rect { x: 0, y: 3, width: 80, height: 10 },
|
|
||||||
),
|
|
||||||
column_cnt: 4,
|
|
||||||
max_width: 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn with_items(mut self, items: Vec<String>) -> Self {
|
|
||||||
self.items = items;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,11 +3,11 @@ use std::ops::Range;
|
||||||
use crossterm::event::KeyCode;
|
use crossterm::event::KeyCode;
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use yazi_config::keymap::Key;
|
use yazi_config::keymap::{Exec, Key, KeymapLayer};
|
||||||
use yazi_shared::{CharKind, InputError};
|
use yazi_shared::{CharKind, InputError};
|
||||||
|
|
||||||
use super::{mode::InputMode, op::InputOp, InputOpt, InputSnap, InputSnaps};
|
use super::{mode::InputMode, op::InputOp, InputOpt, InputSnap, InputSnaps};
|
||||||
use crate::{external, Position};
|
use crate::{emit, external, Position};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
|
|
@ -20,6 +20,7 @@ pub struct Input {
|
||||||
// Typing
|
// Typing
|
||||||
callback: Option<UnboundedSender<Result<String, InputError>>>,
|
callback: Option<UnboundedSender<Result<String, InputError>>>,
|
||||||
realtime: bool,
|
realtime: bool,
|
||||||
|
completion: bool,
|
||||||
|
|
||||||
// Shell
|
// Shell
|
||||||
pub(super) highlight: bool,
|
pub(super) highlight: bool,
|
||||||
|
|
@ -37,6 +38,7 @@ impl Input {
|
||||||
// Typing
|
// Typing
|
||||||
self.callback = Some(tx);
|
self.callback = Some(tx);
|
||||||
self.realtime = opt.realtime;
|
self.realtime = opt.realtime;
|
||||||
|
self.completion = opt.completion;
|
||||||
|
|
||||||
// Shell
|
// Shell
|
||||||
self.highlight = opt.highlight;
|
self.highlight = opt.highlight;
|
||||||
|
|
@ -190,7 +192,8 @@ impl Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(c) = key.plain() {
|
if let Some(c) = key.plain() {
|
||||||
return self.type_char(c);
|
let mut bits = [0; 4];
|
||||||
|
return self.type_str(c.encode_utf8(&mut bits));
|
||||||
}
|
}
|
||||||
|
|
||||||
match key {
|
match key {
|
||||||
|
|
@ -199,12 +202,6 @@ impl Input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn type_char(&mut self, c: char) -> bool {
|
|
||||||
let mut bits = [0; 4];
|
|
||||||
self.type_str(c.encode_utf8(&mut bits))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_str(&mut self, s: &str) -> bool {
|
pub fn type_str(&mut self, s: &str) -> bool {
|
||||||
let snap = self.snaps.current_mut();
|
let snap = self.snaps.current_mut();
|
||||||
if snap.cursor < 1 {
|
if snap.cursor < 1 {
|
||||||
|
|
@ -278,9 +275,7 @@ impl Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.insert(!before);
|
self.insert(!before);
|
||||||
for c in s.to_string_lossy().chars() {
|
self.type_str(&s.to_string_lossy());
|
||||||
self.type_char(c);
|
|
||||||
}
|
|
||||||
self.escape();
|
self.escape();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -305,10 +300,6 @@ impl Input {
|
||||||
snap.op = InputOp::None;
|
snap.op = InputOp::None;
|
||||||
snap.mode = if insert { InputMode::Insert } else { InputMode::Normal };
|
snap.mode = if insert { InputMode::Insert } else { InputMode::Normal };
|
||||||
snap.cursor = range.start;
|
snap.cursor = range.start;
|
||||||
|
|
||||||
if self.realtime {
|
|
||||||
self.callback.as_ref().unwrap().send(Err(InputError::Typed(snap.value.clone()))).ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
InputOp::Yank(_) => {
|
InputOp::Yank(_) => {
|
||||||
let range = snap.op.range(cursor, include).unwrap();
|
let range = snap.op.range(cursor, include).unwrap();
|
||||||
|
|
@ -325,7 +316,7 @@ impl Input {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !matches!(old.op, InputOp::None | InputOp::Select(_)) {
|
if !matches!(old.op, InputOp::None | InputOp::Select(_)) {
|
||||||
self.snaps.tag();
|
self.snaps.tag().then(|| self.flush_value());
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -336,6 +327,12 @@ impl Input {
|
||||||
let value = self.snap().value.clone();
|
let value = self.snap().value.clone();
|
||||||
self.callback.as_ref().unwrap().send(Err(InputError::Typed(value))).ok();
|
self.callback.as_ref().unwrap().send(Err(InputError::Typed(value))).ok();
|
||||||
}
|
}
|
||||||
|
if self.completion {
|
||||||
|
emit!(Call(
|
||||||
|
Exec::call("complete", vec!["".to_string()]).with("version", 1).vec(),
|
||||||
|
KeymapLayer::Input
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ use ratatui::prelude::Rect;
|
||||||
|
|
||||||
use crate::Position;
|
use crate::Position;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct InputOpt {
|
pub struct InputOpt {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
pub realtime: bool,
|
pub realtime: bool,
|
||||||
|
pub completion: bool,
|
||||||
pub highlight: bool,
|
pub highlight: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,23 +16,19 @@ impl InputOpt {
|
||||||
pub fn top(title: impl AsRef<str>) -> Self {
|
pub fn top(title: impl AsRef<str>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
title: title.as_ref().to_owned(),
|
title: title.as_ref().to_owned(),
|
||||||
value: String::new(),
|
|
||||||
position: Position::Top(/* TODO: hardcode */ Rect { x: 0, y: 2, width: 50, height: 3 }),
|
position: Position::Top(/* TODO: hardcode */ Rect { x: 0, y: 2, width: 50, height: 3 }),
|
||||||
realtime: false,
|
..Default::default()
|
||||||
highlight: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hovered(title: impl AsRef<str>) -> Self {
|
pub fn hovered(title: impl AsRef<str>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
title: title.as_ref().to_owned(),
|
title: title.as_ref().to_owned(),
|
||||||
value: String::new(),
|
|
||||||
position: Position::Hovered(
|
position: Position::Hovered(
|
||||||
// TODO: hardcode
|
// TODO: hardcode
|
||||||
Rect { x: 0, y: 1, width: 50, height: 3 },
|
Rect { x: 0, y: 1, width: 50, height: 3 },
|
||||||
),
|
),
|
||||||
realtime: false,
|
..Default::default()
|
||||||
highlight: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,11 +45,14 @@ impl InputOpt {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_highlight(mut self) -> Self {
|
pub fn with_completion(mut self) -> Self {
|
||||||
self.highlight = true;
|
self.completion = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_completion(mut self) -> Self { todo!() }
|
pub fn with_highlight(mut self) -> Self {
|
||||||
|
self.highlight = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,9 @@ impl Tab {
|
||||||
|
|
||||||
pub fn cd_interactive(&mut self, target: Url) -> bool {
|
pub fn cd_interactive(&mut self, target: Url) -> bool {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut result =
|
let mut result = emit!(Input(
|
||||||
emit!(Input(InputOpt::top("Change directory:").with_value(target.to_string_lossy())));
|
InputOpt::top("Change directory:").with_value(target.to_string_lossy()).with_completion()
|
||||||
|
));
|
||||||
|
|
||||||
if let Some(Ok(s)) = result.recv().await {
|
if let Some(Ok(s)) = result.recv().await {
|
||||||
emit!(Cd(Url::from(s.trim())));
|
emit!(Cd(Url::from(s.trim())));
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,7 @@ impl Executor {
|
||||||
return if in_operating { cx.input.move_in_operating(step) } else { cx.input.move_(step) };
|
return if in_operating { cx.input.move_in_operating(step) } else { cx.input.move_(step) };
|
||||||
}
|
}
|
||||||
|
|
||||||
"complete" => todo!(),
|
"complete" => return cx.completion.trigger(exec),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,6 +281,8 @@ impl Executor {
|
||||||
|
|
||||||
fn completion(cx: &mut Ctx, exec: &Exec) -> bool {
|
fn completion(cx: &mut Ctx, exec: &Exec) -> bool {
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
|
"trigger" => cx.completion.trigger(exec),
|
||||||
|
"show" => cx.completion.show(exec),
|
||||||
"close" => cx.completion.close(exec.named.contains_key("submit")),
|
"close" => cx.completion.close(exec.named.contains_key("submit")),
|
||||||
|
|
||||||
"arrow" => {
|
"arrow" => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue