mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-24 22:14:28 +00:00
Merge pull request #82 from JustLinuxUser/main
Add `. filename` support
This commit is contained in:
commit
007311b0c7
83
Cargo.lock
generated
83
Cargo.lock
generated
|
@ -280,6 +280,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
|
@ -319,6 +325,25 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
|
||||||
|
dependencies = [
|
||||||
|
"include_dir_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir_macros"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ioctl-rs"
|
name = "ioctl-rs"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -525,6 +550,34 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||||
|
dependencies = [
|
||||||
|
"fuchsia-cprng",
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
"rdrand",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ratatui"
|
name = "ratatui"
|
||||||
version = "0.27.0"
|
version = "0.27.0"
|
||||||
|
@ -546,6 +599,15 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -555,6 +617,15 @@ dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "remove_dir_all"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
@ -722,6 +793,16 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempdir"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
"remove_dir_all",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termios"
|
name = "termios"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -759,9 +840,11 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"ego-tree",
|
"ego-tree",
|
||||||
|
"include_dir",
|
||||||
"oneshot",
|
"oneshot",
|
||||||
"portable-pty",
|
"portable-pty",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"tempdir",
|
||||||
"tui-term",
|
"tui-term",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ oneshot = "0.1.8"
|
||||||
portable-pty = "0.8.1"
|
portable-pty = "0.8.1"
|
||||||
ratatui = "0.27.0"
|
ratatui = "0.27.0"
|
||||||
tui-term = "0.1.12"
|
tui-term = "0.1.12"
|
||||||
|
include_dir = "0.7.4"
|
||||||
|
tempdir = "0.3.7"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "linutil"
|
name = "linutil"
|
||||||
|
|
Binary file not shown.
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
setupAlacritty() {
|
setupAlacritty() {
|
||||||
echo "Install Alacritty if not already installed..."
|
echo "Install Alacritty if not already installed..."
|
||||||
if ! command_exists alacritty; then
|
if ! command_exists alacritty; then
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
setupKitty() {
|
setupKitty() {
|
||||||
echo "Install Kitty if not already installed..."
|
echo "Install Kitty if not already installed..."
|
||||||
if ! command_exists kitty; then
|
if ! command_exists kitty; then
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
setupRofi() {
|
setupRofi() {
|
||||||
echo "Install Rofi if not already installed..."
|
echo "Install Rofi if not already installed..."
|
||||||
if ! command_exists rofi; then
|
if ! command_exists rofi; then
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
||||||
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
installDepend() {
|
installDepend() {
|
||||||
## Check for dependencies.
|
## Check for dependencies.
|
||||||
echo -e "${YELLOW}Installing dependencies...${RC}"
|
echo -e "${YELLOW}Installing dependencies...${RC}"
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
# Check if the home directory and linuxtoolbox folder exist, create them if they don't
|
||||||
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
LINUXTOOLBOXDIR="$HOME/linuxtoolbox"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
. ./common-script.sh
|
||||||
|
|
||||||
fastUpdate() {
|
fastUpdate() {
|
||||||
case ${PACKAGER} in
|
case ${PACKAGER} in
|
||||||
pacman)
|
pacman)
|
||||||
|
|
5
src/commands/test/lib.sh
Executable file
5
src/commands/test/lib.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
say_hello () {
|
||||||
|
echo Hi
|
||||||
|
}
|
6
src/commands/test/main.sh
Executable file
6
src/commands/test/main.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# The current working directory will always be inside "commands"
|
||||||
|
. test/lib.sh
|
||||||
|
|
||||||
|
say_hello
|
126
src/list.rs
126
src/list.rs
|
@ -1,4 +1,4 @@
|
||||||
use crate::{float::floating_window, theme::*};
|
use crate::{float::floating_window, running_command::Command, state::AppState};
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
|
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
|
||||||
use ego_tree::{tree, NodeId};
|
use ego_tree::{tree, NodeId};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
|
@ -9,18 +9,9 @@ use ratatui::{
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! with_common_script {
|
|
||||||
($command:expr) => {
|
|
||||||
concat!(
|
|
||||||
include_str!("commands/common-script.sh"),
|
|
||||||
include_str!($command)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ListNode {
|
struct ListNode {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
command: &'static str,
|
command: Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a data structure that has everything necessary to draw and manage a menu of commands
|
/// This is a data structure that has everything necessary to draw and manage a menu of commands
|
||||||
|
@ -60,60 +51,74 @@ impl CustomList {
|
||||||
// case the tree! macro expands to `ego-tree::tree` data structure
|
// case the tree! macro expands to `ego-tree::tree` data structure
|
||||||
let tree = tree!(ListNode {
|
let tree = tree!(ListNode {
|
||||||
name: "root",
|
name: "root",
|
||||||
command: ""
|
command: Command::None,
|
||||||
} => {
|
} => {
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Full System Update",
|
name: "Full System Update",
|
||||||
command: with_common_script!("commands/system-update.sh"),
|
command: Command::LocalFile("system-update.sh"),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Setup Bash Prompt",
|
name: "Setup Bash Prompt",
|
||||||
command: "bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""
|
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Setup Neovim",
|
name: "Setup Neovim",
|
||||||
command: "bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""
|
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""),
|
||||||
},
|
},
|
||||||
// ListNode {
|
|
||||||
// name: "Just ls, nothing special, trust me",
|
|
||||||
// command: include_str!("commands/special_ls.sh"),
|
|
||||||
// },
|
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "System Setup",
|
name: "System Setup",
|
||||||
command: ""
|
command: Command::None,
|
||||||
} => {
|
} => {
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Build Prerequisites",
|
name: "Build Prerequisites",
|
||||||
command: with_common_script!("commands/system-setup/1-compile-setup.sh"),
|
command: Command::LocalFile("system-setup/1-compile-setup.sh"),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Gaming Dependencies",
|
name: "Gaming Dependencies",
|
||||||
command: with_common_script!("commands/system-setup/2-gaming-setup.sh"),
|
command: Command::LocalFile("system-setup/2-gaming-setup.sh"),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Global Theme",
|
name: "Global Theme",
|
||||||
command: with_common_script!("commands/system-setup/3-global-theme.sh"),
|
command: Command::LocalFile("system-setup/3-global-theme.sh"),
|
||||||
},
|
},
|
||||||
ListNode {
|
|
||||||
name: "Recursion?",
|
|
||||||
command: "cargo run"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Titus Dotfiles",
|
name: "Titus Dotfiles",
|
||||||
command: ""
|
command: Command::None
|
||||||
} => {
|
} => {
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Alacritty Setup",
|
name: "Alacritty Setup",
|
||||||
command: with_common_script!("commands/dotfiles/alacritty-setup.sh"),
|
command: Command::LocalFile("dotfiles/alacritty-setup.sh"),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Kitty Setup",
|
name: "Kitty Setup",
|
||||||
command: with_common_script!("commands/dotfiles/kitty-setup.sh"),
|
command: Command::LocalFile("dotfiles/kitty-setup.sh"),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Rofi Setup",
|
name: "Rofi Setup",
|
||||||
command: with_common_script!("commands/dotfiles/rofi-setup.sh"),
|
command: Command::LocalFile("dotfiles/rofi-setup.sh"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
ListNode {
|
||||||
|
name: "Testing category",
|
||||||
|
command: Command::None,
|
||||||
|
} => {
|
||||||
|
ListNode {
|
||||||
|
name: "Complex command",
|
||||||
|
command: Command::Raw("sleep 1 && ls -la && sleep 1 && ls -la && echo Bonus eza comming... && sleep 1 && ls -la"),
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "Neovim",
|
||||||
|
command: Command::Raw("nvim"),
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "Full bash",
|
||||||
|
command: Command::Raw("bash"),
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "Running file with `source`",
|
||||||
|
command: Command::LocalFile("test/main.sh"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -130,9 +135,8 @@ impl CustomList {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw our custom widget to the frame
|
/// Draw our custom widget to the frame
|
||||||
pub fn draw(&mut self, frame: &mut Frame, area: Rect) {
|
pub fn draw(&mut self, frame: &mut Frame, area: Rect, state: &AppState) {
|
||||||
// Get the last element in the `visit_stack` vec
|
// Get the last element in the `visit_stack` vec
|
||||||
let theme = get_theme();
|
|
||||||
let curr = self
|
let curr = self
|
||||||
.inner_tree
|
.inner_tree
|
||||||
.get(*self.visit_stack.last().unwrap())
|
.get(*self.visit_stack.last().unwrap())
|
||||||
|
@ -141,9 +145,10 @@ impl CustomList {
|
||||||
|
|
||||||
// If we are not at the root of our filesystem tree, we need to add `..` path, to be able
|
// If we are not at the root of our filesystem tree, we need to add `..` path, to be able
|
||||||
// to go up the tree
|
// to go up the tree
|
||||||
// icons:
|
|
||||||
if !self.at_root() {
|
if !self.at_root() {
|
||||||
items.push(Line::from(format!("{} ..", theme.dir_icon)).style(theme.dir_color));
|
items.push(
|
||||||
|
Line::from(format!("{} ..", state.theme.dir_icon)).style(state.theme.dir_color),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through all the children
|
// Iterate through all the children
|
||||||
|
@ -152,13 +157,13 @@ impl CustomList {
|
||||||
// it's a directory and will be handled as such
|
// it's a directory and will be handled as such
|
||||||
if node.has_children() {
|
if node.has_children() {
|
||||||
items.push(
|
items.push(
|
||||||
Line::from(format!("{} {}", theme.dir_icon, node.value().name))
|
Line::from(format!("{} {}", state.theme.dir_icon, node.value().name))
|
||||||
.style(theme.dir_color),
|
.style(state.theme.dir_color),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
items.push(
|
items.push(
|
||||||
Line::from(format!("{} {}", theme.cmd_icon, node.value().name))
|
Line::from(format!("{} {}", state.theme.cmd_icon, node.value().name))
|
||||||
.style(theme.cmd_color),
|
.style(state.theme.cmd_color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +210,7 @@ impl CustomList {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle key events, we are only interested in `Press` and `Repeat` events
|
/// Handle key events, we are only interested in `Press` and `Repeat` events
|
||||||
pub fn handle_key(&mut self, event: KeyEvent) -> Option<&'static str> {
|
pub fn handle_key(&mut self, event: KeyEvent, state: &AppState) -> Option<Command> {
|
||||||
if event.kind == KeyEventKind::Release {
|
if event.kind == KeyEventKind::Release {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -235,14 +240,14 @@ impl CustomList {
|
||||||
}
|
}
|
||||||
// The 'p' key toggles the preview on and off
|
// The 'p' key toggles the preview on and off
|
||||||
KeyCode::Char('p') => {
|
KeyCode::Char('p') => {
|
||||||
self.toggle_preview_window();
|
self.toggle_preview_window(state);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
KeyCode::Enter => self.handle_enter(),
|
KeyCode::Enter => self.handle_enter(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn toggle_preview_window(&mut self) {
|
fn toggle_preview_window(&mut self, state: &AppState) {
|
||||||
// If the preview window is active, disable it
|
// If the preview window is active, disable it
|
||||||
if self.preview_window_state.is_some() {
|
if self.preview_window_state.is_some() {
|
||||||
self.preview_window_state = None;
|
self.preview_window_state = None;
|
||||||
|
@ -251,17 +256,23 @@ impl CustomList {
|
||||||
|
|
||||||
// Get the selected command
|
// Get the selected command
|
||||||
if let Some(selected_command) = self.get_selected_command() {
|
if let Some(selected_command) = self.get_selected_command() {
|
||||||
// If command is a folder, we don't display a preview
|
let lines = match selected_command {
|
||||||
if selected_command.is_empty() {
|
Command::Raw(cmd) => {
|
||||||
return;
|
// Reconstruct the line breaks and file formatting after the
|
||||||
}
|
// 'include_str!()' call in the node
|
||||||
|
cmd.lines().map(|line| line.to_string()).collect()
|
||||||
// Reconstruct the line breaks and file formatting after the
|
}
|
||||||
// 'include_str!()' call in the node
|
Command::LocalFile(file_path) => {
|
||||||
let lines: Vec<String> = selected_command
|
let mut full_path = state.temp_path.clone();
|
||||||
.lines()
|
full_path.push(file_path);
|
||||||
.map(|line| line.to_string())
|
let file_contents = std::fs::read_to_string(&full_path)
|
||||||
.collect();
|
.map_err(|_| format!("File not found: {:?}", &full_path))
|
||||||
|
.unwrap();
|
||||||
|
file_contents.lines().map(|line| line.to_string()).collect()
|
||||||
|
}
|
||||||
|
// If command is a folder, we don't display a preview
|
||||||
|
Command::None => return,
|
||||||
|
};
|
||||||
|
|
||||||
// Show the preview window with the text lines
|
// Show the preview window with the text lines
|
||||||
self.preview_window_state = Some(PreviewWindowState::new(lines));
|
self.preview_window_state = Some(PreviewWindowState::new(lines));
|
||||||
|
@ -315,7 +326,7 @@ impl CustomList {
|
||||||
///
|
///
|
||||||
/// This could probably be integrated into the 'handle_enter()' method as to avoid code
|
/// This could probably be integrated into the 'handle_enter()' method as to avoid code
|
||||||
/// duplication, but I don't want to make too major changes to the codebase.
|
/// duplication, but I don't want to make too major changes to the codebase.
|
||||||
fn get_selected_command(&self) -> Option<&'static str> {
|
fn get_selected_command(&self) -> Option<Command> {
|
||||||
let curr = self
|
let curr = self
|
||||||
.inner_tree
|
.inner_tree
|
||||||
.get(*self.visit_stack.last().unwrap())
|
.get(*self.visit_stack.last().unwrap())
|
||||||
|
@ -332,7 +343,7 @@ impl CustomList {
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
if idx == selected {
|
if idx == selected {
|
||||||
return Some(node.value().command);
|
return Some(node.value().command.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -342,8 +353,9 @@ impl CustomList {
|
||||||
/// - Run a command, if it is the currently selected item,
|
/// - Run a command, if it is the currently selected item,
|
||||||
/// - Go up a directory
|
/// - Go up a directory
|
||||||
/// - Go down into a directory
|
/// - Go down into a directory
|
||||||
|
///
|
||||||
/// Returns `Some(command)` when command is selected, othervise we returns `None`
|
/// Returns `Some(command)` when command is selected, othervise we returns `None`
|
||||||
fn handle_enter(&mut self) -> Option<&'static str> {
|
fn handle_enter(&mut self) -> Option<Command> {
|
||||||
// Get the current node (current directory)
|
// Get the current node (current directory)
|
||||||
let curr = self
|
let curr = self
|
||||||
.inner_tree
|
.inner_tree
|
||||||
|
@ -371,7 +383,7 @@ impl CustomList {
|
||||||
self.list_state.select(Some(0));
|
self.list_state.select(Some(0));
|
||||||
return None;
|
return None;
|
||||||
} else {
|
} else {
|
||||||
return Some(node.value().command);
|
return Some(node.value().command.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -1,6 +1,7 @@
|
||||||
mod float;
|
mod float;
|
||||||
mod list;
|
mod list;
|
||||||
mod running_command;
|
mod running_command;
|
||||||
|
pub mod state;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -16,13 +17,16 @@ use crossterm::{
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
ExecutableCommand,
|
ExecutableCommand,
|
||||||
};
|
};
|
||||||
|
use include_dir::include_dir;
|
||||||
use list::CustomList;
|
use list::CustomList;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::{Backend, CrosstermBackend},
|
backend::{Backend, CrosstermBackend},
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
use running_command::RunningCommand;
|
use running_command::RunningCommand;
|
||||||
use theme::set_theme;
|
use state::AppState;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
use theme::THEMES;
|
||||||
|
|
||||||
/// This is a binary :), Chris, change this to update the documentation on -h
|
/// This is a binary :), Chris, change this to update the documentation on -h
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
@ -34,16 +38,29 @@ struct Args {
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
if args.compat {
|
|
||||||
set_theme(0);
|
let theme = if args.compat {
|
||||||
}
|
THEMES[0].clone()
|
||||||
|
} else {
|
||||||
|
THEMES[1].clone()
|
||||||
|
};
|
||||||
|
let commands_dir = include_dir!("src/commands");
|
||||||
|
let temp_dir: TempDir = TempDir::new("linutil_scripts").unwrap();
|
||||||
|
commands_dir
|
||||||
|
.extract(temp_dir.path())
|
||||||
|
.expect("Failed to extract the saved directory");
|
||||||
|
|
||||||
|
let state = AppState {
|
||||||
|
theme,
|
||||||
|
temp_path: temp_dir.path().to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
stdout().execute(EnterAlternateScreen)?;
|
stdout().execute(EnterAlternateScreen)?;
|
||||||
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()?;
|
||||||
|
|
||||||
run(&mut terminal)?;
|
run(&mut terminal, &state)?;
|
||||||
|
|
||||||
// restore terminal
|
// restore terminal
|
||||||
disable_raw_mode()?;
|
disable_raw_mode()?;
|
||||||
|
@ -55,7 +72,7 @@ fn main() -> std::io::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run<B: Backend>(terminal: &mut Terminal<B>) -> io::Result<()> {
|
fn run<B: Backend>(terminal: &mut Terminal<B>, state: &AppState) -> io::Result<()> {
|
||||||
let mut command_opt: Option<RunningCommand> = None;
|
let mut command_opt: Option<RunningCommand> = None;
|
||||||
|
|
||||||
let mut custom_list = CustomList::new();
|
let mut custom_list = CustomList::new();
|
||||||
|
@ -63,9 +80,9 @@ fn run<B: Backend>(terminal: &mut Terminal<B>) -> io::Result<()> {
|
||||||
// Always redraw
|
// Always redraw
|
||||||
terminal
|
terminal
|
||||||
.draw(|frame| {
|
.draw(|frame| {
|
||||||
custom_list.draw(frame, frame.size());
|
custom_list.draw(frame, frame.size(), state);
|
||||||
if let Some(ref mut command) = &mut command_opt {
|
if let Some(ref mut command) = &mut command_opt {
|
||||||
command.draw(frame);
|
command.draw(frame, state);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -90,8 +107,8 @@ fn run<B: Backend>(terminal: &mut Terminal<B>) -> io::Result<()> {
|
||||||
if key.code == KeyCode::Char('q') {
|
if key.code == KeyCode::Char('q') {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if let Some(cmd) = custom_list.handle_key(key) {
|
if let Some(cmd) = custom_list.handle_key(key, state) {
|
||||||
command_opt = Some(RunningCommand::new(cmd));
|
command_opt = Some(RunningCommand::new(cmd, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use portable_pty::{
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::Size,
|
layout::Size,
|
||||||
style::{Color, Style, Stylize},
|
style::{Style, Stylize},
|
||||||
text::{Line, Span},
|
text::{Line, Span},
|
||||||
widgets::{Block, Borders},
|
widgets::{Block, Borders},
|
||||||
Frame,
|
Frame,
|
||||||
|
@ -21,7 +21,14 @@ use tui_term::{
|
||||||
widget::PseudoTerminal,
|
widget::PseudoTerminal,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{float::floating_window, theme::get_theme};
|
use crate::{float::floating_window, state::AppState};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Command {
|
||||||
|
Raw(&'static str),
|
||||||
|
LocalFile(&'static str),
|
||||||
|
None, // Directory
|
||||||
|
}
|
||||||
|
|
||||||
/// This is a struct for storing everything connected to a running command
|
/// This is a struct for storing everything connected to a running command
|
||||||
// Create a new instance on every new command you want to run
|
// Create a new instance on every new command you want to run
|
||||||
|
@ -50,14 +57,22 @@ pub struct RunningCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunningCommand {
|
impl RunningCommand {
|
||||||
pub fn new(command: &str) -> Self {
|
pub fn new(command: Command, state: &AppState) -> Self {
|
||||||
let pty_system = NativePtySystem::default();
|
let pty_system = NativePtySystem::default();
|
||||||
let mut cmd = CommandBuilder::new("sh");
|
|
||||||
cmd.arg("-c");
|
|
||||||
cmd.arg(command);
|
|
||||||
|
|
||||||
let cwd = std::env::current_dir().unwrap();
|
let mut cmd = CommandBuilder::new("sh");
|
||||||
cmd.cwd(cwd);
|
match command {
|
||||||
|
Command::Raw(prompt) => {
|
||||||
|
cmd.arg("-c");
|
||||||
|
cmd.arg(prompt);
|
||||||
|
}
|
||||||
|
Command::LocalFile(file) => {
|
||||||
|
cmd.arg(file);
|
||||||
|
}
|
||||||
|
Command::None => panic!("Command::None was treated as a command"),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.cwd(&state.temp_path);
|
||||||
|
|
||||||
let pair = pty_system
|
let pair = pty_system
|
||||||
.openpty(PtySize {
|
.openpty(PtySize {
|
||||||
|
@ -153,59 +168,57 @@ impl RunningCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self, frame: &mut Frame) {
|
pub fn draw(&mut self, frame: &mut Frame, state: &AppState) {
|
||||||
{
|
// Funny name
|
||||||
let theme = get_theme();
|
let floater = floating_window(frame.size());
|
||||||
// Funny name
|
|
||||||
let floater = floating_window(frame.size());
|
|
||||||
|
|
||||||
let inner_size = Size {
|
let inner_size = Size {
|
||||||
width: floater.width - 2, // Because we add a `Block` with a border
|
width: floater.width - 2, // Because we add a `Block` with a border
|
||||||
height: floater.height - 2,
|
height: floater.height - 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
// When the command is running
|
// When the command is running
|
||||||
let term_border = if !self.is_finished() {
|
let term_border = if !self.is_finished() {
|
||||||
Block::default()
|
Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.title_top(Line::from("Running the command....").centered())
|
.title_top(Line::from("Running the command....").centered())
|
||||||
.title_style(Style::default().reversed())
|
.title_style(Style::default().reversed())
|
||||||
.title_bottom(Line::from("Press Ctrl-C to KILL the command"))
|
.title_bottom(Line::from("Press Ctrl-C to KILL the command"))
|
||||||
} else {
|
} else {
|
||||||
// This portion is just for pretty colors.
|
// This portion is just for pretty colors.
|
||||||
// You can use multiple `Span`s with different styles each, to construct a line,
|
// You can use multiple `Span`s with different styles each, to construct a line,
|
||||||
// which can be used as a list item, or in this case a `Block` title
|
// which can be used as a list item, or in this case a `Block` title
|
||||||
|
|
||||||
let mut title_line = if self.get_exit_status().success() {
|
let mut title_line = if self.get_exit_status().success() {
|
||||||
Line::from(
|
Line::from(
|
||||||
Span::default()
|
|
||||||
.content("SUCCESS!")
|
|
||||||
.style(Style::default().fg(theme.success_color).reversed()),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Line::from(
|
|
||||||
Span::default()
|
|
||||||
.content("FAILED!")
|
|
||||||
.style(Style::default().fg(theme.fail_color).reversed()),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
title_line.push_span(
|
|
||||||
Span::default()
|
Span::default()
|
||||||
.content(" press <ENTER> to close this window ")
|
.content("SUCCESS!")
|
||||||
.style(Style::default()),
|
.style(Style::default().fg(state.theme.success_color).reversed()),
|
||||||
);
|
)
|
||||||
|
} else {
|
||||||
Block::default()
|
Line::from(
|
||||||
.borders(Borders::ALL)
|
Span::default()
|
||||||
.title_top(title_line.centered())
|
.content("FAILED!")
|
||||||
|
.style(Style::default().fg(state.theme.fail_color).reversed()),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let screen = self.screen(inner_size); // when the terminal is changing a lot, there
|
|
||||||
// will be 1 frame of lag on resizing
|
title_line.push_span(
|
||||||
let pseudo_term = PseudoTerminal::new(&screen).block(term_border);
|
Span::default()
|
||||||
frame.render_widget(pseudo_term, floater);
|
.content(" press <ENTER> to close this window ")
|
||||||
}
|
.style(Style::default()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title_top(title_line.centered())
|
||||||
|
};
|
||||||
|
let screen = self.screen(inner_size); // when the terminal is changing a lot, there
|
||||||
|
// will be 1 frame of lag on resizing
|
||||||
|
let pseudo_term = PseudoTerminal::new(&screen).block(term_border);
|
||||||
|
frame.render_widget(pseudo_term, floater);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send SIGHUB signal, *not* SIGKILL or SIGTERM, to the child process
|
/// Send SIGHUB signal, *not* SIGKILL or SIGTERM, to the child process
|
||||||
pub fn kill_child(&mut self) {
|
pub fn kill_child(&mut self) {
|
||||||
if !self.is_finished() {
|
if !self.is_finished() {
|
||||||
|
|
9
src/state.rs
Normal file
9
src/state.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use crate::theme::Theme;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub struct AppState {
|
||||||
|
/// Selected theme
|
||||||
|
pub theme: Theme,
|
||||||
|
/// Path to the root of the unpacked files in /tmp
|
||||||
|
pub temp_path: PathBuf,
|
||||||
|
}
|
11
src/theme.rs
11
src/theme.rs
|
@ -1,7 +1,6 @@
|
||||||
use ratatui::style::Color;
|
use ratatui::style::Color;
|
||||||
|
|
||||||
pub static mut THEME_IDX: usize = 1;
|
#[derive(Clone)]
|
||||||
|
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub dir_color: Color,
|
pub dir_color: Color,
|
||||||
pub cmd_color: Color,
|
pub cmd_color: Color,
|
||||||
|
@ -29,11 +28,3 @@ pub const THEMES: [Theme; 2] = [
|
||||||
success_color: Color::Rgb(5, 255, 55),
|
success_color: Color::Rgb(5, 255, 55),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn get_theme() -> &'static Theme {
|
|
||||||
&THEMES[unsafe { THEME_IDX }]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_theme(idx: usize) {
|
|
||||||
unsafe { THEME_IDX = idx };
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user