Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
95c394cbaf | ||
|
3637091341 | ||
|
4b8087bf8d | ||
|
3471ea3c0b | ||
|
bdd28da59e | ||
|
6e258ad2a4 | ||
|
9f7fea0cfa | ||
|
20a62f7be3 | ||
|
f5e8bd8e2c | ||
|
162890f731 | ||
|
5807c7cf9f | ||
|
90596e8670 |
26
.drone.yml
26
.drone.yml
@ -18,7 +18,31 @@ steps:
|
|||||||
- 'earthly +all'
|
- 'earthly +all'
|
||||||
- 'cp -v ./dist/* /dist/'
|
- 'cp -v ./dist/* /dist/'
|
||||||
|
|
||||||
- name: Gitea Artifacts
|
# - name: Minio Artifacts
|
||||||
|
# image: plugins/s3
|
||||||
|
# settings:
|
||||||
|
# bucket: artifacts
|
||||||
|
# access_key:
|
||||||
|
# from_secret: MINIO_ARTIFACTS_ACCESS_KEY
|
||||||
|
# secret_key:
|
||||||
|
# from_secret: MINIO_ARTIFACTS_SECRET_KEY
|
||||||
|
# source: dist/**/*
|
||||||
|
# target: live-cli/${DRONE_BUILD_CREATED}-${DRONE_COMMIT}
|
||||||
|
|
||||||
|
- name: Publish Artifacts
|
||||||
|
image: curlimages/curl
|
||||||
|
volumes:
|
||||||
|
- name: dist
|
||||||
|
path: /dist
|
||||||
|
environment:
|
||||||
|
GITEA_SVC_USERNAME:
|
||||||
|
from_secrete: GITEA_SVC_USERNAME
|
||||||
|
GITEA_SVC_PASSWORD:
|
||||||
|
from_secret: GITEA_SVC_PASSWORD
|
||||||
|
commands:
|
||||||
|
- sh -c 'set -ex; cd /dist; for f in *; do curl --user "drone-svc:$${GITEA_SVC_PASSWORD}" -X PUT https://gitea.austen-wares.com.com/api/packages/public/generic/$${DRONE_REPO_NAME}/$$(date -Isecond)-$${DRONE_COMMIT}/$${f}; done'
|
||||||
|
|
||||||
|
- name: Publish release
|
||||||
image: plugins/gitea-release
|
image: plugins/gitea-release
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
|
@ -1 +1,5 @@
|
|||||||
/target
|
*
|
||||||
|
!/Cargo.lock
|
||||||
|
!/Cargo.toml
|
||||||
|
!/src
|
||||||
|
!/.cargo
|
||||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -398,7 +398,7 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "live-cli"
|
name = "live-cli"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi4tui",
|
"ansi4tui",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "live-cli"
|
name = "live-cli"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
139
src/main.rs
139
src/main.rs
@ -6,7 +6,6 @@ mod command;
|
|||||||
mod event;
|
mod event;
|
||||||
mod ui;
|
mod ui;
|
||||||
mod util;
|
mod util;
|
||||||
use anyhow::anyhow;
|
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@ -23,7 +22,6 @@ use crossterm::event::KeyModifiers;
|
|||||||
use event::EventMessage;
|
use event::EventMessage;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use ropey::Rope;
|
use ropey::Rope;
|
||||||
use std::str::FromStr;
|
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
@ -81,6 +79,9 @@ pub struct CommandOptions {
|
|||||||
|
|
||||||
/// The position of the cursor
|
/// The position of the cursor
|
||||||
cmdline_position: u16,
|
cmdline_position: u16,
|
||||||
|
|
||||||
|
/// Command combined
|
||||||
|
combined_command: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state of the application
|
/// The state of the application
|
||||||
@ -105,53 +106,31 @@ pub struct App {
|
|||||||
render_states: RwLock<RenderStates>,
|
render_states: RwLock<RenderStates>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandOptions {
|
impl Default for CommandOptions {
|
||||||
#[must_use]
|
fn default() -> Self {
|
||||||
pub fn from_template(template: &Template) -> Self {
|
Self {
|
||||||
let defaults = Self {
|
command: String::from("sh"),
|
||||||
command: template.command(),
|
|
||||||
cmdline_position: 0_u16,
|
cmdline_position: 0_u16,
|
||||||
hidden_options: Vec::new(),
|
hidden_options: vec!["-c"],
|
||||||
cmdline: String::new(),
|
cmdline: String::new(),
|
||||||
command_result: None,
|
command_result: None,
|
||||||
autorun: true,
|
autorun: true,
|
||||||
wordsplit: true,
|
wordsplit: false,
|
||||||
};
|
combined_command: String::new(),
|
||||||
|
|
||||||
match template {
|
|
||||||
Template::Awk => Self { ..defaults },
|
|
||||||
Template::Sh(_) => Self {
|
|
||||||
hidden_options: vec!["-c"],
|
|
||||||
wordsplit: false,
|
|
||||||
..defaults
|
|
||||||
},
|
|
||||||
Template::Jq => Self {
|
|
||||||
cmdline_position: 2,
|
|
||||||
cmdline: String::from("'.'"),
|
|
||||||
hidden_options: vec!["-C"],
|
|
||||||
..defaults
|
|
||||||
},
|
|
||||||
Template::Grep | Template::Rg => Self {
|
|
||||||
cmdline_position: 1,
|
|
||||||
cmdline: String::from("''"),
|
|
||||||
hidden_options: vec!["--color=always"],
|
|
||||||
..defaults
|
|
||||||
},
|
|
||||||
Template::Sed => Self {
|
|
||||||
cmdline_position: 3_u16,
|
|
||||||
cmdline: String::from("'s///g'"),
|
|
||||||
..defaults
|
|
||||||
},
|
|
||||||
Template::Perl => Self {
|
|
||||||
cmdline_position: 9_u16,
|
|
||||||
cmdline: String::from("-p -e 's///'"),
|
|
||||||
..defaults
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl App<'_> {
|
impl CommandOptions {
|
||||||
|
pub fn append_command(&mut self) {
|
||||||
|
if !self.combined_command.is_empty() {
|
||||||
|
self.combined_command += " | ";
|
||||||
|
}
|
||||||
|
|
||||||
|
self.combined_command += self.cmdline.as_ref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
/// Constructs a new instance of `App`
|
/// Constructs a new instance of `App`
|
||||||
///
|
///
|
||||||
@ -159,20 +138,18 @@ impl App {
|
|||||||
///
|
///
|
||||||
/// * `command_request_tx` - The sender for the command request worker
|
/// * `command_request_tx` - The sender for the command request worker
|
||||||
/// * `input` - The stdin to be passed in to each invocation
|
/// * `input` - The stdin to be passed in to each invocation
|
||||||
/// * `template` - The template to use
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_template(
|
pub fn new(
|
||||||
message_rx: Receiver<EventMessage>,
|
message_rx: Receiver<EventMessage>,
|
||||||
command_request_tx: Sender<CommandRequest>,
|
command_request_tx: Sender<CommandRequest>,
|
||||||
input: Option<String>,
|
input: Option<String>,
|
||||||
template: &Template,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// let text_orig_rope = Rope::from_str(&input);
|
// let text_orig_rope = Rope::from_str(&input);
|
||||||
let text_orig_rope = input.as_ref().map(|s| Rope::from_str(s));
|
let text_orig_rope = input.as_ref().map(|s| Rope::from_str(s));
|
||||||
Self {
|
Self {
|
||||||
text_orig: Arc::new(input),
|
text_orig: Arc::new(input),
|
||||||
text_orig_rope,
|
text_orig_rope,
|
||||||
command_options: Arc::new(RwLock::new(CommandOptions::from_template(template))),
|
command_options: Arc::new(RwLock::new(CommandOptions::default())),
|
||||||
message_rx,
|
message_rx,
|
||||||
command_request_tx,
|
command_request_tx,
|
||||||
render_states: RwLock::new(RenderStates::default()),
|
render_states: RwLock::new(RenderStates::default()),
|
||||||
@ -191,47 +168,6 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Template {
|
|
||||||
Sh(String),
|
|
||||||
Jq,
|
|
||||||
Grep,
|
|
||||||
Rg,
|
|
||||||
Sed,
|
|
||||||
Awk,
|
|
||||||
Perl,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Template {
|
|
||||||
type Err = anyhow::Error;
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s.to_lowercase().trim() {
|
|
||||||
"jq" => Ok(Self::Jq),
|
|
||||||
"grep" => Ok(Self::Grep),
|
|
||||||
"rg" => Ok(Self::Rg),
|
|
||||||
"sed" => Ok(Self::Sed),
|
|
||||||
"awk" => Ok(Self::Awk),
|
|
||||||
"perl" => Ok(Self::Perl),
|
|
||||||
s @ ("sh" | "bash" | "zsh" | "dash") => Ok(Self::Sh(s.to_string())),
|
|
||||||
e => Err(anyhow!("{e} is not a supported command")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Template {
|
|
||||||
#[must_use]
|
|
||||||
pub fn command(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::Sh(s) => s.to_string(),
|
|
||||||
Self::Jq => String::from("jq"),
|
|
||||||
Self::Grep => String::from("grep"),
|
|
||||||
Self::Rg => String::from("rg"),
|
|
||||||
Self::Sed => String::from("sed"),
|
|
||||||
Self::Awk => String::from("awk"),
|
|
||||||
Self::Perl => String::from("perl"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
enable_tracing();
|
enable_tracing();
|
||||||
@ -254,12 +190,7 @@ fn main() -> Result<()> {
|
|||||||
let (message_rx, command_request_tx) = init_message_passing();
|
let (message_rx, command_request_tx) = init_message_passing();
|
||||||
|
|
||||||
// Run the actual application
|
// Run the actual application
|
||||||
let app = App::from_template(
|
let mut app = App::new(message_rx, command_request_tx, text_orig);
|
||||||
message_rx,
|
|
||||||
command_request_tx,
|
|
||||||
text_orig,
|
|
||||||
&Template::from_str(&args.command)?,
|
|
||||||
);
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
execute!(
|
execute!(
|
||||||
@ -271,7 +202,7 @@ fn main() -> Result<()> {
|
|||||||
let backend = CrosstermBackend::new(stdout);
|
let backend = CrosstermBackend::new(stdout);
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
let res = run_app(&mut terminal, app);
|
let resulting_commandline = run_app(&mut terminal, &mut app);
|
||||||
|
|
||||||
// Restore terminal
|
// Restore terminal
|
||||||
disable_raw_mode()?;
|
disable_raw_mode()?;
|
||||||
@ -283,11 +214,13 @@ fn main() -> Result<()> {
|
|||||||
)?;
|
)?;
|
||||||
terminal.show_cursor()?;
|
terminal.show_cursor()?;
|
||||||
|
|
||||||
let res = res?;
|
let resulting_commandline = resulting_commandline?;
|
||||||
|
|
||||||
if let Some(res) = res {
|
if let Some(resulting_commandline) = resulting_commandline {
|
||||||
std::io::stderr().write_all(res.as_bytes())?;
|
// TODO: I do not want to collect the whole thing into a vec
|
||||||
|
app.command_options.read().command_result.as_ref().map(|r| std::io::stdout().write_all(&r.stdout.bytes().collect::<Vec<u8>>())).transpose()?;
|
||||||
std::io::stderr().write_all(b"\n")?;
|
std::io::stderr().write_all(b"\n")?;
|
||||||
|
std::io::stderr().write_all(resulting_commandline.as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -317,7 +250,7 @@ fn init_message_passing() -> (Receiver<EventMessage>, Sender<CommandRequest>) {
|
|||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
#[cfg_attr(debug_assertions, instrument(skip(terminal, app)))]
|
#[cfg_attr(debug_assertions, instrument(skip(terminal, app)))]
|
||||||
fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> Result<Option<String>> {
|
fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> Result<Option<String>> {
|
||||||
// When starting the app, ensure the command runs at least once
|
// When starting the app, ensure the command runs at least once
|
||||||
{
|
{
|
||||||
let mut command_options = app.command_options.write();
|
let mut command_options = app.command_options.write();
|
||||||
@ -350,6 +283,10 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> Result<Optio
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
|
if command_options.command_result.is_some() {
|
||||||
|
command_options.append_command();
|
||||||
|
}
|
||||||
|
|
||||||
// Move stdout to stdin
|
// Move stdout to stdin
|
||||||
if let Some(result) = &command_options.command_result {
|
if let Some(result) = &command_options.command_result {
|
||||||
app.text_orig = Arc::new(Some(result.stdout.to_string()));
|
app.text_orig = Arc::new(Some(result.stdout.to_string()));
|
||||||
@ -405,10 +342,10 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> Result<Optio
|
|||||||
command_options.cmdline_position = 0_u16;
|
command_options.cmdline_position = 0_u16;
|
||||||
}
|
}
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
return Ok(Some(format!(
|
if command_options.command_result.is_some() {
|
||||||
"{} {}",
|
command_options.append_command();
|
||||||
command_options.command, command_options.cmdline
|
}
|
||||||
)));
|
return Ok(Some(command_options.combined_command.to_string()));
|
||||||
}
|
}
|
||||||
KeyCode::Left => {
|
KeyCode::Left => {
|
||||||
command_options.cmdline_position =
|
command_options.cmdline_position =
|
||||||
|
31
src/ui.rs
31
src/ui.rs
@ -32,30 +32,31 @@ pub fn draw<B: Backend>(f: &mut Frame<B>, app: &App) {
|
|||||||
|
|
||||||
event!(Level::INFO, "Rendering orig");
|
event!(Level::INFO, "Rendering orig");
|
||||||
// lazy_render_rope_slice(chunks[0], render_states.stdout)
|
// lazy_render_rope_slice(chunks[0], render_states.stdout)
|
||||||
render_states.stdout = if let Some(text_orig_rope) = app.text_orig_rope.as_ref() {
|
|
||||||
|
let stdin_title = if command_options.combined_command.is_empty() {
|
||||||
|
String::from("Input")
|
||||||
|
} else {
|
||||||
|
format!("Input ({})", command_options.combined_command)
|
||||||
|
};
|
||||||
|
|
||||||
|
render_states.stdin = if let Some(text_orig_rope) = app.text_orig_rope.as_ref() {
|
||||||
lazy_render_rope_slice(
|
lazy_render_rope_slice(
|
||||||
f,
|
f,
|
||||||
chunks[0],
|
chunks[0],
|
||||||
render_states.stdin.as_ref(),
|
render_states.stdin.as_ref(),
|
||||||
text_orig_rope.slice(..),
|
text_orig_rope.slice(..),
|
||||||
"Output",
|
stdin_title.as_str(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new("! No Stdin !")
|
Paragraph::new("! No Stdin !")
|
||||||
.alignment(Alignment::Center)
|
.alignment(Alignment::Center)
|
||||||
.block(Block::default().title("Output").borders(Borders::ALL)),
|
.block(
|
||||||
chunks[0], // Layout::default()
|
Block::default()
|
||||||
// .direction(Direction::Vertical)
|
.title(stdin_title.as_str())
|
||||||
// .constraints(
|
.borders(Borders::ALL),
|
||||||
// [
|
),
|
||||||
// Constraint::Percentage(50),
|
chunks[0],
|
||||||
// Constraint::Length(1),
|
|
||||||
// Constraint::Min(0),
|
|
||||||
// ]
|
|
||||||
// .as_ref(),
|
|
||||||
// )
|
|
||||||
// .split(chunks[0])[1],
|
|
||||||
);
|
);
|
||||||
Some(RenderState {
|
Some(RenderState {
|
||||||
// TODO
|
// TODO
|
||||||
@ -161,7 +162,7 @@ fn lazy_render_rope_slice<'a, B: Backend>(
|
|||||||
output: Rect,
|
output: Rect,
|
||||||
previous_state: Option<&RenderState>,
|
previous_state: Option<&RenderState>,
|
||||||
data: RopeSlice<'_>,
|
data: RopeSlice<'_>,
|
||||||
block_title: &'static str,
|
block_title: &str,
|
||||||
) -> Option<RenderState> {
|
) -> Option<RenderState> {
|
||||||
let data = data
|
let data = data
|
||||||
.lines()
|
.lines()
|
||||||
|
Loading…
Reference in New Issue
Block a user