mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-21 21:09:42 +00:00
Compare commits
3 Commits
e681c33926
...
85e533fb20
Author | SHA1 | Date | |
---|---|---|---|
|
85e533fb20 | ||
|
fa69885b6c | ||
|
9a18d08dd3 |
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -425,6 +425,7 @@ dependencies = [
|
||||||
"tree-sitter-highlight",
|
"tree-sitter-highlight",
|
||||||
"tui-term",
|
"tui-term",
|
||||||
"unicode-width 0.2.0",
|
"unicode-width 0.2.0",
|
||||||
|
"vt100-ctt",
|
||||||
"zips",
|
"zips",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1096,7 +1097,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72af159125ce32b02ceaced6cffae6394b0e6b6dfd4dc164a6c59a2db9b3c0b0"
|
checksum = "72af159125ce32b02ceaced6cffae6394b0e6b6dfd4dc164a6c59a2db9b3c0b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"vt100",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1147,9 +1147,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vt100"
|
name = "vt100-ctt"
|
||||||
version = "0.15.2"
|
version = "0.15.3"
|
||||||
source = "git+https://github.com/ChrisTitusTech/vt100-rust#e41fb3d8fb5fd01dd2d076c9a25823a31656012f"
|
source = "git+https://github.com/ChrisTitusTech/vt100-rust#39136a6232d043d8447afa7d47805b6f6baa09ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
@ -8,9 +8,6 @@ members = ["tui", "core", "xtask"]
|
||||||
default-members = ["tui", "core"]
|
default-members = ["tui", "core"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
vt100 = { git = "https://github.com/ChrisTitusTech/vt100-rust" }
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
debug = false
|
debug = false
|
||||||
|
|
|
@ -18,7 +18,7 @@ clap = { version = "4.5.20", features = ["derive", "std"], default-features = fa
|
||||||
oneshot = { version = "0.1.8", features = ["std"], default-features = false }
|
oneshot = { version = "0.1.8", features = ["std"], default-features = false }
|
||||||
portable-pty = "0.8.1"
|
portable-pty = "0.8.1"
|
||||||
ratatui = { version = "0.29.0", features = ["crossterm"], default-features = false }
|
ratatui = { version = "0.29.0", features = ["crossterm"], default-features = false }
|
||||||
tui-term = "0.2.0"
|
tui-term = { version = "0.2.0", default-features = false }
|
||||||
temp-dir = "0.1.14"
|
temp-dir = "0.1.14"
|
||||||
time = { version = "0.3.36", features = ["formatting", "local-offset", "macros"], default-features = false }
|
time = { version = "0.3.36", features = ["formatting", "local-offset", "macros"], default-features = false }
|
||||||
unicode-width = { version = "0.2.0", default-features = false }
|
unicode-width = { version = "0.2.0", default-features = false }
|
||||||
|
@ -31,6 +31,7 @@ anstyle = { version = "1.0.8", default-features = false }
|
||||||
ansi-to-tui = { version = "7.0.0", default-features = false }
|
ansi-to-tui = { version = "7.0.0", default-features = false }
|
||||||
zips = "0.1.7"
|
zips = "0.1.7"
|
||||||
nix = { version = "0.29.0", features = [ "user" ] }
|
nix = { version = "0.29.0", features = [ "user" ] }
|
||||||
|
vt100-ctt = { git = "https://github.com/ChrisTitusTech/vt100-rust" }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "linutil"
|
name = "linutil"
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
||||||
use crate::{float::FloatContent, hint::Shortcut};
|
use crate::{float::FloatContent, hint::Shortcut};
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
crossterm::event::{KeyCode, KeyEvent, MouseEvent, MouseEventKind},
|
crossterm::event::{KeyCode, KeyEvent},
|
||||||
layout::Alignment,
|
layout::Alignment,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{Block, Borders, Clear, List},
|
widgets::{Block, Borders, Clear, List},
|
||||||
|
@ -85,19 +85,6 @@ impl FloatContent for ConfirmPrompt {
|
||||||
frame.render_widget(List::new(paths_text), inner_area);
|
frame.render_widget(List::new(paths_text), inner_area);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_mouse_event(&mut self, event: &MouseEvent) -> bool {
|
|
||||||
match event.kind {
|
|
||||||
MouseEventKind::ScrollDown => {
|
|
||||||
self.scroll_down();
|
|
||||||
}
|
|
||||||
MouseEventKind::ScrollUp => {
|
|
||||||
self.scroll_up();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
||||||
use KeyCode::*;
|
use KeyCode::*;
|
||||||
self.status = match key.code {
|
self.status = match key.code {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
crossterm::event::{KeyCode, KeyEvent, MouseEvent},
|
crossterm::event::{KeyCode, KeyEvent},
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,6 @@ use crate::hint::Shortcut;
|
||||||
pub trait FloatContent {
|
pub trait FloatContent {
|
||||||
fn draw(&mut self, frame: &mut Frame, area: Rect);
|
fn draw(&mut self, frame: &mut Frame, area: Rect);
|
||||||
fn handle_key_event(&mut self, key: &KeyEvent) -> bool;
|
fn handle_key_event(&mut self, key: &KeyEvent) -> bool;
|
||||||
fn handle_mouse_event(&mut self, key: &MouseEvent) -> bool;
|
|
||||||
fn is_finished(&self) -> bool;
|
fn is_finished(&self) -> bool;
|
||||||
fn get_shortcut_list(&self) -> (&str, Box<[Shortcut]>);
|
fn get_shortcut_list(&self) -> (&str, Box<[Shortcut]>);
|
||||||
}
|
}
|
||||||
|
@ -54,10 +53,6 @@ impl<Content: FloatContent + ?Sized> Float<Content> {
|
||||||
self.content.draw(frame, popup_area);
|
self.content.draw(frame, popup_area);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_mouse_event(&mut self, event: &MouseEvent) {
|
|
||||||
self.content.handle_mouse_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the floating window is finished.
|
// Returns true if the floating window is finished.
|
||||||
pub fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
pub fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
||||||
match key.code {
|
match key.code {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{float::FloatContent, hint::Shortcut};
|
||||||
use linutil_core::Command;
|
use linutil_core::Command;
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
crossterm::event::{KeyCode, KeyEvent, MouseEvent, MouseEventKind},
|
crossterm::event::{KeyCode, KeyEvent},
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Style, Stylize},
|
style::{Style, Stylize},
|
||||||
text::Line,
|
text::Line,
|
||||||
|
@ -283,17 +283,6 @@ impl FloatContent for FloatingText {
|
||||||
frame.render_widget(list, inner_area);
|
frame.render_widget(list, inner_area);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_mouse_event(&mut self, event: &MouseEvent) -> bool {
|
|
||||||
match event.kind {
|
|
||||||
MouseEventKind::ScrollDown => self.scroll_down(),
|
|
||||||
MouseEventKind::ScrollUp => self.scroll_up(),
|
|
||||||
MouseEventKind::ScrollLeft => self.scroll_left(),
|
|
||||||
MouseEventKind::ScrollRight => self.scroll_right(),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
||||||
use KeyCode::*;
|
use KeyCode::*;
|
||||||
match key.code {
|
match key.code {
|
||||||
|
|
|
@ -20,7 +20,7 @@ use clap::Parser;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
crossterm::{
|
crossterm::{
|
||||||
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyEventKind},
|
event::{self, DisableMouseCapture, Event, KeyEventKind},
|
||||||
style::ResetColor,
|
style::ResetColor,
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
ExecutableCommand,
|
ExecutableCommand,
|
||||||
|
@ -64,8 +64,6 @@ fn main() -> io::Result<()> {
|
||||||
);
|
);
|
||||||
|
|
||||||
stdout().execute(EnterAlternateScreen)?;
|
stdout().execute(EnterAlternateScreen)?;
|
||||||
stdout().execute(EnableMouseCapture)?;
|
|
||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
||||||
terminal.clear()?;
|
terminal.clear()?;
|
||||||
|
@ -95,22 +93,15 @@ fn run(
|
||||||
|
|
||||||
// It's guaranteed that the `read()` won't block when the `poll()`
|
// It's guaranteed that the `read()` won't block when the `poll()`
|
||||||
// function returns `true`
|
// function returns `true`
|
||||||
match event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
Event::Key(key) => {
|
// We are only interested in Press and Repeat events
|
||||||
if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat {
|
if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !state.handle_key(&key) {
|
if !state.handle_key(&key) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Event::Mouse(mouse_event) => {
|
|
||||||
if !state.handle_mouse(&mouse_event) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use portable_pty::{
|
||||||
ChildKiller, CommandBuilder, ExitStatus, MasterPty, NativePtySystem, PtySize, PtySystem,
|
ChildKiller, CommandBuilder, ExitStatus, MasterPty, NativePtySystem, PtySize, PtySystem,
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind},
|
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||||
layout::{Rect, Size},
|
layout::{Rect, Size},
|
||||||
style::{Color, Style, Stylize},
|
style::{Color, Style, Stylize},
|
||||||
text::{Line, Span},
|
text::{Line, Span},
|
||||||
|
@ -18,10 +18,9 @@ use std::{
|
||||||
thread::JoinHandle,
|
thread::JoinHandle,
|
||||||
};
|
};
|
||||||
use time::{macros::format_description, OffsetDateTime};
|
use time::{macros::format_description, OffsetDateTime};
|
||||||
use tui_term::{
|
use tui_term::widget::PseudoTerminal;
|
||||||
vt100::{self, Screen},
|
use vt100_ctt::{Parser, Screen};
|
||||||
widget::PseudoTerminal,
|
|
||||||
};
|
|
||||||
pub struct RunningCommand {
|
pub struct RunningCommand {
|
||||||
/// A buffer to save all the command output (accumulates, until the command exits)
|
/// A buffer to save all the command output (accumulates, until the command exits)
|
||||||
buffer: Arc<Mutex<Vec<u8>>>,
|
buffer: Arc<Mutex<Vec<u8>>>,
|
||||||
|
@ -104,18 +103,6 @@ impl FloatContent for RunningCommand {
|
||||||
frame.render_widget(pseudo_term, area);
|
frame.render_widget(pseudo_term, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_mouse_event(&mut self, event: &MouseEvent) -> bool {
|
|
||||||
match event.kind {
|
|
||||||
MouseEventKind::ScrollUp => {
|
|
||||||
self.scroll_offset = self.scroll_offset.saturating_add(1);
|
|
||||||
}
|
|
||||||
MouseEventKind::ScrollDown => {
|
|
||||||
self.scroll_offset = self.scroll_offset.saturating_sub(1);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
/// Handle key events of the running command "window". Returns true when the "window" should be
|
/// Handle key events of the running command "window". Returns true when the "window" should be
|
||||||
/// closed
|
/// closed
|
||||||
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
||||||
|
@ -285,7 +272,7 @@ impl RunningCommand {
|
||||||
// Process the buffer with a parser with the current screen size
|
// Process the buffer with a parser with the current screen size
|
||||||
// We don't actually need to create a new parser every time, but it is so much easier this
|
// We don't actually need to create a new parser every time, but it is so much easier this
|
||||||
// way, and doesn't cost that much
|
// way, and doesn't cost that much
|
||||||
let mut parser = vt100::Parser::new(size.height, size.width, 1000);
|
let mut parser = Parser::new(size.height, size.width, 1000);
|
||||||
let mutex = self.buffer.lock();
|
let mutex = self.buffer.lock();
|
||||||
let buffer = mutex.as_ref().unwrap();
|
let buffer = mutex.as_ref().unwrap();
|
||||||
parser.process(buffer);
|
parser.process(buffer);
|
||||||
|
|
|
@ -13,8 +13,8 @@ use linutil_core::{ego_tree::NodeId, Config, ListNode, TabList};
|
||||||
#[cfg(feature = "tips")]
|
#[cfg(feature = "tips")]
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseEvent, MouseEventKind},
|
crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
|
||||||
layout::{Alignment, Constraint, Direction, Flex, Layout, Position, Rect},
|
layout::{Alignment, Constraint, Direction, Flex, Layout},
|
||||||
style::{Style, Stylize},
|
style::{Style, Stylize},
|
||||||
text::{Line, Span, Text},
|
text::{Line, Span, Text},
|
||||||
widgets::{Block, Borders, List, ListState, Paragraph},
|
widgets::{Block, Borders, List, ListState, Paragraph},
|
||||||
|
@ -42,8 +42,6 @@ P* - privileged *
|
||||||
";
|
";
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
/// Areas of tabs
|
|
||||||
areas: Option<Areas>,
|
|
||||||
/// Selected theme
|
/// Selected theme
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
/// Currently focused area
|
/// Currently focused area
|
||||||
|
@ -82,11 +80,6 @@ pub struct ListEntry {
|
||||||
pub has_children: bool,
|
pub has_children: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Areas {
|
|
||||||
tab_list: Rect,
|
|
||||||
list: Rect,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SelectedItem {
|
enum SelectedItem {
|
||||||
UpDir,
|
UpDir,
|
||||||
Directory,
|
Directory,
|
||||||
|
@ -108,7 +101,6 @@ impl AppState {
|
||||||
let auto_execute_commands = config_path.map(|path| Config::from_file(&path).auto_execute);
|
let auto_execute_commands = config_path.map(|path| Config::from_file(&path).auto_execute);
|
||||||
|
|
||||||
let mut state = Self {
|
let mut state = Self {
|
||||||
areas: None,
|
|
||||||
theme,
|
theme,
|
||||||
focus: Focus::List,
|
focus: Focus::List,
|
||||||
tabs,
|
tabs,
|
||||||
|
@ -329,11 +321,6 @@ impl AppState {
|
||||||
.split(horizontal[0]);
|
.split(horizontal[0]);
|
||||||
frame.render_widget(label, left_chunks[0]);
|
frame.render_widget(label, left_chunks[0]);
|
||||||
|
|
||||||
self.areas = Some(Areas {
|
|
||||||
tab_list: left_chunks[1],
|
|
||||||
list: horizontal[1],
|
|
||||||
});
|
|
||||||
|
|
||||||
let tabs = self
|
let tabs = self
|
||||||
.tabs
|
.tabs
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -488,59 +475,6 @@ impl AppState {
|
||||||
frame.render_widget(keybind_para, vertical[1]);
|
frame.render_widget(keybind_para, vertical[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_mouse(&mut self, event: &MouseEvent) -> bool {
|
|
||||||
if !self.drawable {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches!(self.focus, Focus::TabList | Focus::List) {
|
|
||||||
let position = Position::new(event.column, event.row);
|
|
||||||
let mouse_in_tab_list = self.areas.as_ref().unwrap().tab_list.contains(position);
|
|
||||||
let mouse_in_list = self.areas.as_ref().unwrap().list.contains(position);
|
|
||||||
|
|
||||||
match event.kind {
|
|
||||||
MouseEventKind::Moved => {
|
|
||||||
if mouse_in_list {
|
|
||||||
self.focus = Focus::List
|
|
||||||
} else if mouse_in_tab_list {
|
|
||||||
self.focus = Focus::TabList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseEventKind::ScrollDown => {
|
|
||||||
if mouse_in_tab_list {
|
|
||||||
if self.current_tab.selected().unwrap() != self.tabs.len() - 1 {
|
|
||||||
self.current_tab.select_next();
|
|
||||||
}
|
|
||||||
self.refresh_tab();
|
|
||||||
} else if mouse_in_list {
|
|
||||||
self.selection.select_next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseEventKind::ScrollUp => {
|
|
||||||
if mouse_in_tab_list {
|
|
||||||
if self.current_tab.selected().unwrap() != 0 {
|
|
||||||
self.current_tab.select_previous();
|
|
||||||
}
|
|
||||||
self.refresh_tab();
|
|
||||||
} else if mouse_in_list {
|
|
||||||
self.selection.select_previous()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match &mut self.focus {
|
|
||||||
Focus::FloatingWindow(float) => {
|
|
||||||
float.content.handle_mouse_event(event);
|
|
||||||
}
|
|
||||||
Focus::ConfirmationPrompt(confirm) => {
|
|
||||||
confirm.content.handle_mouse_event(event);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_key(&mut self, key: &KeyEvent) -> bool {
|
pub fn handle_key(&mut self, key: &KeyEvent) -> bool {
|
||||||
// This should be defined first to allow closing
|
// This should be defined first to allow closing
|
||||||
// the application even when not drawable ( If terminal is small )
|
// the application even when not drawable ( If terminal is small )
|
||||||
|
|
Loading…
Reference in New Issue
Block a user