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]]
name = "either"
version = "1.8.1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
@ -665,15 +665,6 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "http"
version = "0.2.9"
@ -1334,9 +1325,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.31"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0"
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [
"proc-macro2",
]
@ -2358,12 +2349,9 @@ dependencies = [
[[package]]
name = "xdg"
version = "2.5.0"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688597db5a750e9cad4511cb94729a078e274308099a0382b5b8203bbc767fee"
dependencies = [
"home",
]
checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
[[package]]
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 {
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Exec>, D::Error>
where

View file

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

View file

@ -64,7 +64,8 @@ pub struct Theme {
impl Theme {
pub fn new() -> Self {
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
}
}

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;
@ -25,6 +25,8 @@ impl Manager {
}
pub fn refresh(&mut self) {
env::set_current_dir(&self.current().cwd).ok();
self.watcher.trigger(&self.current().cwd);
if let Some(p) = self.parent() {
self.watcher.trigger(&p.cwd);

View file

@ -41,6 +41,9 @@ impl Which {
} else if self.cands.len() == 1 {
self.visible = false;
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;

View file

@ -2,7 +2,8 @@ use std::{env, path::{Path, PathBuf}};
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 let Ok(home) = env::var("HOME") {
let mut expanded = PathBuf::new();
@ -11,7 +12,7 @@ pub fn absolute_path(p: &Path) -> PathBuf {
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 {

View file

@ -3,7 +3,7 @@ use crossterm::event::KeyEvent;
use tokio::sync::oneshot::{self};
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 {
cx: Ctx,
@ -84,7 +84,7 @@ impl App {
let tasks = &mut self.cx.tasks;
match event {
Event::Cd(path) => {
manager.active_mut().cd(path).await;
manager.active_mut().cd(absolute_path(path).await).await;
}
Event::Refresh => {
manager.refresh();

View file

@ -1,5 +1,7 @@
use std::path::PathBuf;
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;
@ -60,6 +62,11 @@ impl Executor {
"enter" => cx.manager.active_mut().enter(),
"back" => cx.manager.active_mut().back(),
"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
"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 crate::ui::Ctx;
@ -13,20 +13,35 @@ impl<'a> Which<'a> {
impl Widget for Which<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let chunks = layout::Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(area);
let which = &self.cx.which;
let mut cands: (Vec<_>, Vec<_>, Vec<_>) = Default::default();
for (i, c) in which.cands.iter().enumerate() {
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()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(chunks[1]);
let which = &self.cx.which;
let cands: (Vec<_>, Vec<_>) = which.cands.iter().enumerate().partition(|(i, _)| i % 2 == 0);
.constraints(
[Constraint::Ratio(1, 3), Constraint::Ratio(1, 3), Constraint::Ratio(1, 3)].as_ref(),
)
.split(area);
Clear.render(area, 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.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;
pub struct Side<'a> {
times: usize,
cands: Vec<(usize, &'a Control)>,
cands: Vec<&'a Control>,
}
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<'_> {
@ -16,12 +16,34 @@ impl Widget for Side<'_> {
let items = self
.cands
.into_iter()
.map(|(_, c)| {
let s = c.on[self.times..].into_iter().map(ToString::to_string).collect::<String>();
ListItem::new(format!("{:?}", s))
.map(|c| {
let mut spans = vec![];
// 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<_>>();
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);
}
}