Compare commits

...

12 Commits

Author SHA1 Message Date
Austen Adler
95c394cbaf Print command after output
Some checks failed
continuous-integration/drone/push Build is failing
2023-02-18 16:23:38 -05:00
Austen Adler
3637091341 Fixes
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-29 15:22:32 -05:00
Austen Adler
4b8087bf8d Try using curl
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-29 15:16:32 -05:00
Austen Adler
3471ea3c0b Print output when done 2022-12-29 15:10:28 -05:00
Austen Adler
bdd28da59e Upload artifacts to registry
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-29 15:09:58 -05:00
Austen Adler
6e258ad2a4 Show off combined command
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2022-12-28 14:12:24 -05:00
Austen Adler
9f7fea0cfa Only use shell
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-28 12:36:32 -05:00
Austen Adler
20a62f7be3 Comment minio artifacts
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-28 11:36:46 -05:00
Austen Adler
f5e8bd8e2c Add .cargo
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is failing
2022-12-28 11:21:05 -05:00
Austen Adler
162890f731 Add earthignore
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-28 11:17:04 -05:00
Austen Adler
5807c7cf9f Add artifact uploading
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is failing
2022-12-28 11:05:33 -05:00
Austen Adler
90596e8670 Bump version
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-27 23:45:10 -05:00
6 changed files with 86 additions and 120 deletions

View File

@ -18,7 +18,31 @@ steps:
- 'earthly +all'
- '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
when:
event:

View File

@ -1 +1,5 @@
/target
*
!/Cargo.lock
!/Cargo.toml
!/src
!/.cargo

2
Cargo.lock generated
View File

@ -398,7 +398,7 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "live-cli"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"ansi4tui",
"anyhow",

View File

@ -1,6 +1,6 @@
[package]
name = "live-cli"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -6,7 +6,6 @@ mod command;
mod event;
mod ui;
mod util;
use anyhow::anyhow;
use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
@ -23,7 +22,6 @@ use crossterm::event::KeyModifiers;
use event::EventMessage;
use parking_lot::RwLock;
use ropey::Rope;
use std::str::FromStr;
use std::{
io::{self, Write},
sync::Arc,
@ -81,6 +79,9 @@ pub struct CommandOptions {
/// The position of the cursor
cmdline_position: u16,
/// Command combined
combined_command: String,
}
/// The state of the application
@ -105,53 +106,31 @@ pub struct App {
render_states: RwLock<RenderStates>,
}
impl CommandOptions {
#[must_use]
pub fn from_template(template: &Template) -> Self {
let defaults = Self {
command: template.command(),
impl Default for CommandOptions {
fn default() -> Self {
Self {
command: String::from("sh"),
cmdline_position: 0_u16,
hidden_options: Vec::new(),
hidden_options: vec!["-c"],
cmdline: String::new(),
command_result: None,
autorun: true,
wordsplit: true,
};
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
},
wordsplit: false,
combined_command: String::new(),
}
}
}
// 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 {
/// Constructs a new instance of `App`
///
@ -159,20 +138,18 @@ impl App {
///
/// * `command_request_tx` - The sender for the command request worker
/// * `input` - The stdin to be passed in to each invocation
/// * `template` - The template to use
#[must_use]
pub fn from_template(
pub fn new(
message_rx: Receiver<EventMessage>,
command_request_tx: Sender<CommandRequest>,
input: Option<String>,
template: &Template,
) -> Self {
// let text_orig_rope = Rope::from_str(&input);
let text_orig_rope = input.as_ref().map(|s| Rope::from_str(s));
Self {
text_orig: Arc::new(input),
text_orig_rope,
command_options: Arc::new(RwLock::new(CommandOptions::from_template(template))),
command_options: Arc::new(RwLock::new(CommandOptions::default())),
message_rx,
command_request_tx,
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<()> {
#[cfg(debug_assertions)]
enable_tracing();
@ -254,12 +190,7 @@ fn main() -> Result<()> {
let (message_rx, command_request_tx) = init_message_passing();
// Run the actual application
let app = App::from_template(
message_rx,
command_request_tx,
text_orig,
&Template::from_str(&args.command)?,
);
let mut app = App::new(message_rx, command_request_tx, text_orig);
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(
@ -271,7 +202,7 @@ fn main() -> Result<()> {
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let res = run_app(&mut terminal, app);
let resulting_commandline = run_app(&mut terminal, &mut app);
// Restore terminal
disable_raw_mode()?;
@ -283,11 +214,13 @@ fn main() -> Result<()> {
)?;
terminal.show_cursor()?;
let res = res?;
let resulting_commandline = resulting_commandline?;
if let Some(res) = res {
std::io::stderr().write_all(res.as_bytes())?;
if let Some(resulting_commandline) = resulting_commandline {
// 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(resulting_commandline.as_bytes())?;
}
Ok(())
@ -317,7 +250,7 @@ fn init_message_passing() -> (Receiver<EventMessage>, Sender<CommandRequest>) {
#[allow(clippy::too_many_lines)]
#[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
{
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,
..
}) => {
if command_options.command_result.is_some() {
command_options.append_command();
}
// Move stdout to stdin
if let Some(result) = &command_options.command_result {
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;
}
KeyCode::Enter => {
return Ok(Some(format!(
"{} {}",
command_options.command, command_options.cmdline
)));
if command_options.command_result.is_some() {
command_options.append_command();
}
return Ok(Some(command_options.combined_command.to_string()));
}
KeyCode::Left => {
command_options.cmdline_position =

View File

@ -32,30 +32,31 @@ pub fn draw<B: Backend>(f: &mut Frame<B>, app: &App) {
event!(Level::INFO, "Rendering orig");
// 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(
f,
chunks[0],
render_states.stdin.as_ref(),
text_orig_rope.slice(..),
"Output",
stdin_title.as_str(),
)
} else {
f.render_widget(
Paragraph::new("! No Stdin !")
.alignment(Alignment::Center)
.block(Block::default().title("Output").borders(Borders::ALL)),
chunks[0], // Layout::default()
// .direction(Direction::Vertical)
// .constraints(
// [
// Constraint::Percentage(50),
// Constraint::Length(1),
// Constraint::Min(0),
// ]
// .as_ref(),
// )
// .split(chunks[0])[1],
.block(
Block::default()
.title(stdin_title.as_str())
.borders(Borders::ALL),
),
chunks[0],
);
Some(RenderState {
// TODO
@ -161,7 +162,7 @@ fn lazy_render_rope_slice<'a, B: Backend>(
output: Rect,
previous_state: Option<&RenderState>,
data: RopeSlice<'_>,
block_title: &'static str,
block_title: &str,
) -> Option<RenderState> {
let data = data
.lines()