This commit is contained in:
sxyazi 2023-07-24 07:25:12 +08:00
parent 5c69beb104
commit 83cbef649d
No known key found for this signature in database
11 changed files with 103 additions and 43 deletions

24
Cargo.lock generated
View file

@ -382,9 +382,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]] [[package]]
name = "either" name = "either"
version = "1.8.1" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]] [[package]]
name = "equivalent" name = "equivalent"
@ -665,15 +665,6 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "home"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
dependencies = [
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.9" version = "0.2.9"
@ -1334,9 +1325,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.31" version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -2358,12 +2349,9 @@ dependencies = [
[[package]] [[package]]
name = "xdg" name = "xdg"
version = "2.5.0" version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688597db5a750e9cad4511cb94729a078e274308099a0382b5b8203bbc767fee" checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
dependencies = [
"home",
]
[[package]] [[package]]
name = "yaml-rust" name = "yaml-rust"

View file

@ -28,6 +28,25 @@ impl From<&str> for Exec {
} }
} }
impl ToString for Exec {
fn to_string(&self) -> String {
let mut s = self.cmd.clone();
for arg in &self.args {
s.push(' ');
s.push_str(arg);
}
for (name, value) in &self.named {
s.push_str(" --");
s.push_str(name);
if !value.is_empty() {
s.push('=');
s.push_str(value);
}
}
s
}
}
impl Exec { impl Exec {
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Exec>, D::Error> pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Exec>, D::Error>
where where

View file

@ -106,7 +106,8 @@ impl TryFrom<String> for Key {
impl ToString for Key { impl ToString for Key {
fn to_string(&self) -> String { fn to_string(&self) -> String {
if let Some(c) = self.plain() { if let Some(c) = self.plain() {
return if self.shift { c.to_ascii_uppercase() } else { c }.to_string(); let c = if self.shift { c.to_ascii_uppercase() } else { c };
return if c == ' ' { "<Space>".to_string() } else { c.to_string() };
} }
let mut s = "<".to_string(); let mut s = "<".to_string();
@ -149,6 +150,7 @@ impl ToString for Key {
KeyCode::F(12) => "F12", KeyCode::F(12) => "F12",
KeyCode::Esc => "Esc", KeyCode::Esc => "Esc",
KeyCode::Char(c) if c == ' ' => "Space",
KeyCode::Char(c) => { KeyCode::Char(c) => {
s.push(if self.shift { c.to_ascii_uppercase() } else { c }); s.push(if self.shift { c.to_ascii_uppercase() } else { c });
"" ""

View file

@ -64,7 +64,8 @@ pub struct Theme {
impl Theme { impl Theme {
pub fn new() -> Self { pub fn new() -> Self {
let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap(); let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap();
theme.preview.syntect_theme = absolute_path(&theme.preview.syntect_theme); theme.preview.syntect_theme =
futures::executor::block_on(absolute_path(&theme.preview.syntect_theme));
theme theme
} }
} }

View file

@ -1,4 +1,4 @@
use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, mem, path::PathBuf}; use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, mem, path::PathBuf};
use tokio::fs; use tokio::fs;
@ -25,6 +25,8 @@ impl Manager {
} }
pub fn refresh(&mut self) { pub fn refresh(&mut self) {
env::set_current_dir(&self.current().cwd).ok();
self.watcher.trigger(&self.current().cwd); self.watcher.trigger(&self.current().cwd);
if let Some(p) = self.parent() { if let Some(p) = self.parent() {
self.watcher.trigger(&p.cwd); self.watcher.trigger(&p.cwd);

View file

@ -41,6 +41,9 @@ impl Which {
} else if self.cands.len() == 1 { } else if self.cands.len() == 1 {
self.visible = false; self.visible = false;
emit!(Ctrl(self.cands.remove(0), self.layer)); emit!(Ctrl(self.cands.remove(0), self.layer));
} else if let Some(i) = self.cands.iter().position(|c| c.on.len() == self.times + 1) {
emit!(Ctrl(self.cands.remove(i), self.layer));
self.visible = false;
} }
self.times += 1; self.times += 1;

View file

@ -2,7 +2,8 @@ use std::{env, path::{Path, PathBuf}};
use tokio::fs; use tokio::fs;
pub fn absolute_path(p: &Path) -> PathBuf { pub async fn absolute_path(p: impl AsRef<Path>) -> PathBuf {
let p = p.as_ref();
if p.starts_with("~") { if p.starts_with("~") {
if let Ok(home) = env::var("HOME") { if let Ok(home) = env::var("HOME") {
let mut expanded = PathBuf::new(); let mut expanded = PathBuf::new();
@ -11,7 +12,7 @@ pub fn absolute_path(p: &Path) -> PathBuf {
return expanded; return expanded;
} }
} }
p.to_path_buf() fs::canonicalize(p).await.unwrap_or_else(|_| p.to_path_buf())
} }
pub fn readable_path(p: &Path, base: &Path) -> String { pub fn readable_path(p: &Path, base: &Path) -> String {

View file

@ -3,7 +3,7 @@ use crossterm::event::KeyEvent;
use tokio::sync::oneshot::{self}; use tokio::sync::oneshot::{self};
use super::{root::Root, Ctx, Executor, Logs, Signals, Term}; use super::{root::Root, Ctx, Executor, Logs, Signals, Term};
use crate::{config::keymap::{Control, Key, KeymapLayer}, core::{files::FilesOp, Event}, emit}; use crate::{config::keymap::{Control, Key, KeymapLayer}, core::{files::FilesOp, Event}, emit, misc::absolute_path};
pub struct App { pub struct App {
cx: Ctx, cx: Ctx,
@ -84,7 +84,7 @@ impl App {
let tasks = &mut self.cx.tasks; let tasks = &mut self.cx.tasks;
match event { match event {
Event::Cd(path) => { Event::Cd(path) => {
manager.active_mut().cd(path).await; manager.active_mut().cd(absolute_path(path).await).await;
} }
Event::Refresh => { Event::Refresh => {
manager.refresh(); manager.refresh();

View file

@ -1,5 +1,7 @@
use std::path::PathBuf;
use super::Ctx; use super::Ctx;
use crate::{config::{keymap::{Control, Exec, Key, KeymapLayer}, KEYMAP}, core::input::InputMode, misc::optional_bool}; use crate::{config::{keymap::{Control, Exec, Key, KeymapLayer}, KEYMAP}, core::input::InputMode, emit, misc::optional_bool};
pub struct Executor; pub struct Executor;
@ -60,6 +62,11 @@ impl Executor {
"enter" => cx.manager.active_mut().enter(), "enter" => cx.manager.active_mut().enter(),
"back" => cx.manager.active_mut().back(), "back" => cx.manager.active_mut().back(),
"forward" => cx.manager.active_mut().forward(), "forward" => cx.manager.active_mut().forward(),
"cd" => {
let path = exec.args.get(0).map(|s| PathBuf::from(s)).unwrap_or_default();
emit!(Cd(path));
false
}
// Selection // Selection
"select" => { "select" => {

View file

@ -1,4 +1,4 @@
use ratatui::{layout, prelude::{Buffer, Constraint, Direction, Rect}, widgets::Widget}; use ratatui::{layout, prelude::{Buffer, Constraint, Direction, Rect}, widgets::{Clear, Widget}};
use super::Side; use super::Side;
use crate::ui::Ctx; use crate::ui::Ctx;
@ -13,20 +13,35 @@ impl<'a> Which<'a> {
impl Widget for Which<'_> { impl Widget for Which<'_> {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
let chunks = layout::Layout::default() let which = &self.cx.which;
.direction(Direction::Vertical) let mut cands: (Vec<_>, Vec<_>, Vec<_>) = Default::default();
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) for (i, c) in which.cands.iter().enumerate() {
.split(area); match i % 3 {
0 => cands.0.push(c),
1 => cands.1.push(c),
2 => cands.2.push(c),
_ => unreachable!(),
}
}
let height = cands.0.len() as u16 + 2;
let area = Rect {
x: 1,
y: area.height.saturating_sub(height + 2),
width: area.width.saturating_sub(2),
height,
};
let chunks = layout::Layout::default() let chunks = layout::Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .constraints(
.split(chunks[1]); [Constraint::Ratio(1, 3), Constraint::Ratio(1, 3), Constraint::Ratio(1, 3)].as_ref(),
)
let which = &self.cx.which; .split(area);
let cands: (Vec<_>, Vec<_>) = which.cands.iter().enumerate().partition(|(i, _)| i % 2 == 0);
Clear.render(area, buf);
Side::new(which.times, cands.0).render(chunks[0], buf); Side::new(which.times, cands.0).render(chunks[0], buf);
Side::new(which.times, cands.1).render(chunks[1], buf); Side::new(which.times, cands.1).render(chunks[1], buf);
Side::new(which.times, cands.2).render(chunks[2], buf);
} }
} }

View file

@ -1,14 +1,14 @@
use ratatui::{prelude::{Buffer, Rect}, widgets::{List, ListItem, Widget}}; use ratatui::{prelude::{Buffer, Rect}, style::{Color, Style, Stylize}, text::{Line, Span}, widgets::{Block, List, ListItem, Padding, Widget}};
use crate::config::keymap::Control; use crate::config::keymap::Control;
pub struct Side<'a> { pub struct Side<'a> {
times: usize, times: usize,
cands: Vec<(usize, &'a Control)>, cands: Vec<&'a Control>,
} }
impl<'a> Side<'a> { impl<'a> Side<'a> {
pub fn new(times: usize, cands: Vec<(usize, &'a Control)>) -> Self { Self { times, cands } } pub fn new(times: usize, cands: Vec<&'a Control>) -> Self { Self { times, cands } }
} }
impl Widget for Side<'_> { impl Widget for Side<'_> {
@ -16,12 +16,34 @@ impl Widget for Side<'_> {
let items = self let items = self
.cands .cands
.into_iter() .into_iter()
.map(|(_, c)| { .map(|c| {
let s = c.on[self.times..].into_iter().map(ToString::to_string).collect::<String>(); let mut spans = vec![];
ListItem::new(format!("{:?}", s))
// Keys
let keys = c.on[self.times..].iter().map(ToString::to_string).collect::<Vec<_>>();
spans.push(Span::raw(" ".repeat(10usize.saturating_sub(keys.join("").len()))));
spans.push(Span::styled(keys[0].clone(), Style::default().fg(Color::LightCyan)));
spans.extend(
keys
.iter()
.skip(1)
.map(|k| Span::styled(k.to_string(), Style::default().fg(Color::DarkGray))),
);
// Separator
spans.push(Span::styled("".to_string(), Style::default().fg(Color::DarkGray)));
// Exec
let exec = c.exec.iter().map(ToString::to_string).collect::<Vec<_>>().join("; ");
spans.push(Span::styled(exec, Style::default().fg(Color::Magenta)));
ListItem::new(Line::from(spans))
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
List::new(items).render(area, buf); List::new(items)
.block(Block::new().padding(Padding::new(0, 1, 1, 1)))
.bg(Color::Rgb(47, 51, 73))
.render(area, buf);
} }
} }