Skip to content

Commit

Permalink
feat: remote control zjstatus with pipes (#48)
Browse files Browse the repository at this point in the history
* chore: update to latest version of zellij

* fix: deprecated functions in chrono

* chore: update nix deps

* chore: remove deprecated chrono functions

* feat: add refresh via pipes with line protocol

* fix: handle multiple lines in pipe

* fix(clippy): char instead of str

* feat: add notification widget

* chore: update nix flake

* chore: update cargo deps

* chore: update cargo deps

* chore: update nix flake

* chore: clean up imports for tracing

* chore: update nix flake

* chore: update cargo deps

* chore: update nix flake

* chore: update cargo deps

* chore: remove pinning of chrono

Since chrono-tz now fixed the deprecations from chrono
we can finally remove the pinning.

* chore: update nix flake

* chore: update cargo deps
  • Loading branch information
dj95 authored Apr 16, 2024
1 parent a584399 commit 0723923
Show file tree
Hide file tree
Showing 12 changed files with 461 additions and 229 deletions.
393 changes: 205 additions & 188 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ bench = []
tracing = []

[dependencies]
zellij-tile = "0.39.0"
chrono = { version = "0.4.0", default-features = false }
zellij-tile = "0.40.0"
chrono = { version = "0.4.35", default-features = false }
regex = "1.10.0"
chrono-tz = "0.8.3"
chrono-tz = "0.9.0"
anyhow = "1.0.75"
anstyle = "1.0.2"
uuid = { version = "1.6.1", features = ["v4"] }
Expand Down
4 changes: 2 additions & 2 deletions benches/widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn bench_widget_command(c: &mut Criterion) {

let wid = widgets::command::CommandWidget::new(&config);

let ts = Local::now().sub(Duration::seconds(1));
let ts = Local::now().sub(Duration::try_seconds(1).unwrap());

let state = ZellijState {
command_results: BTreeMap::from([(
Expand Down Expand Up @@ -56,7 +56,7 @@ fn bench_widget_command_dynamic(c: &mut Criterion) {

let wid = widgets::command::CommandWidget::new(&config);

let ts = Local::now().sub(Duration::seconds(1));
let ts = Local::now().sub(Duration::try_seconds(1).unwrap());

let state = ZellijState {
command_results: BTreeMap::from([(
Expand Down
24 changes: 12 additions & 12 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions plugin-dev-workspace.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ layout {

pane size=2 borderless=true {
plugin location="file:target/wasm32-wasi/debug/zjstatus.wasm" {
format_left "{mode} #[fg=#89B4FA,bg=#181825,bold]{session} {tabs} "
format_left "{mode}#[fg=#89B4FA,bg=#181825,bold] {session} {tabs}"
format_center "{command_0} {command_1} {command_git_branch} {command_3}"
format_right "{tabs} {datetime}"
format_right "{notifications}{datetime}"
format_space "#[bg=#181825]"

// foo
notification_format_unread "#[fg=#89B4FA,bg=#181825,blink]  #[fg=#89B4FA,bg=#181825] {message} "
notification_format_no_notifications "#[fg=#89B4FA,bg=#181825,dim]  "
notification_show_interval "10"

border_enabled "true"
border_char "─"
border_format "#[fg=#6C7086]{char}"
Expand All @@ -36,7 +39,7 @@ layout {
command_0_interval "1"

command_1_command "date"
command_1_format "#[fg=blue,reverse,dashed-underscore,bg=default,us=red,blink,dim,strikethrough] {exit_code} {stdout} "
command_1_format "#[fg=blue,reverse,bg=default,us=red,blink,dim,strikethrough] {exit_code} {stdout} "
command_1_interval "1"

command_git_branch_command "git rev-parse --abbrev-ref HEAD"
Expand Down
59 changes: 43 additions & 16 deletions src/bin/zjstatus.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
use config::ModuleConfig;
use widgets::{
command::{CommandResult, CommandWidget},
datetime::DateTimeWidget,
mode::ModeWidget,
session::SessionWidget,
swap_layout::SwapLayoutWidget,
tabs::TabsWidget,
widget::Widget,
};

use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

use zellij_tile::prelude::*;

use chrono::Local;
use std::{collections::BTreeMap, fs::File, sync::Arc, usize};
use std::{collections::BTreeMap, sync::Arc, usize};
use uuid::Uuid;

use zjstatus::{
config::{self, UpdateEventMask, ZellijState},
frames, widgets,
config::{self, ModuleConfig, UpdateEventMask, ZellijState},
frames, pipe,
widgets::command::{CommandResult, CommandWidget},
widgets::datetime::DateTimeWidget,
widgets::mode::ModeWidget,
widgets::notification::NotificationWidget,
widgets::session::SessionWidget,
widgets::swap_layout::SwapLayoutWidget,
widgets::tabs::TabsWidget,
widgets::widget::Widget,
};

#[derive(Default)]
Expand All @@ -36,6 +31,9 @@ register_plugin!(State);

#[cfg(feature = "tracing")]
fn init_tracing() {
use std::fs::File;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

let file = File::create(".zjstatus.log");
let file = match file {
Ok(file) => file,
Expand Down Expand Up @@ -88,9 +86,34 @@ impl ZellijPlugin for State {
sessions: Vec::new(),
start_time: Local::now(),
cache_mask: 0,
incoming_notification: None,
};
}

fn pipe(&mut self, pipe_message: PipeMessage) -> bool {
let mut should_render = false;

match pipe_message.source {
PipeSource::Cli(_) => {
if let Some(input) = pipe_message.payload {
should_render = pipe::parse_protocol(&mut self.state, &input);
}
}
PipeSource::Plugin(_) => {
if let Some(input) = pipe_message.payload {
should_render = pipe::parse_protocol(&mut self.state, &input);
}
}
PipeSource::Keybind => {
if let Some(input) = pipe_message.payload {
should_render = pipe::parse_protocol(&mut self.state, &input);
}
}
}

should_render
}

#[tracing::instrument(skip_all, fields(event_type))]
fn update(&mut self, event: Event) -> bool {
let mut should_render = false;
Expand Down Expand Up @@ -267,6 +290,10 @@ fn register_widgets(configuration: &BTreeMap<String, String>) -> BTreeMap<String
Arc::new(SessionWidget::new(configuration)),
);
widget_map.insert("tabs".to_owned(), Arc::new(TabsWidget::new(configuration)));
widget_map.insert(
"notifications".to_owned(),
Arc::new(NotificationWidget::new(configuration)),
);

tracing::debug!("registered widgets: {:?}", widget_map.keys());

Expand Down
4 changes: 3 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use zellij_tile::prelude::*;
use crate::{
border::{parse_border_config, BorderConfig, BorderPosition},
render::FormattedPart,
widgets::{command::CommandResult, widget::Widget},
widgets::{command::CommandResult, notification, widget::Widget},
};
use chrono::{DateTime, Local};

Expand All @@ -20,6 +20,7 @@ pub struct ZellijState {
pub tabs: Vec<TabInfo>,
pub sessions: Vec<SessionInfo>,
pub start_time: DateTime<Local>,
pub incoming_notification: Option<notification::Message>,
pub cache_mask: u8,
}

Expand All @@ -37,6 +38,7 @@ pub fn event_mask_from_widget_name(name: &str) -> u8 {
"command" => UpdateEventMask::Always as u8,
"datetime" => UpdateEventMask::Always as u8,
"mode" => UpdateEventMask::Mode as u8,
"notifications" => UpdateEventMask::Always as u8,
"session" => UpdateEventMask::Session as u8,
"swap_layout" => UpdateEventMask::Tab as u8,
"tabs" => UpdateEventMask::Tab as u8,
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod border;
pub mod config;
pub mod frames;
pub mod pipe;
pub mod render;
pub mod widgets;
103 changes: 103 additions & 0 deletions src/pipe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::ops::Sub;

use chrono::{Duration, Local};

use crate::{
config::ZellijState,
widgets::{command::TIMESTAMP_FORMAT, notification},
};

/// Parses the line protocol and updates the state accordingly
///
/// The protocol is as follows:
///
/// zjstatus::command_name::args
///
/// It first starts with `zjstatus` as a prefix to indicate that the line is
/// used for the line protocol and zjstatus should parse it. It is followed
/// by the command name and then the arguments. The following commands are
/// available:
///
/// - `rerun` - Reruns the command with the given name (like in the config) as
/// argument. E.g. `zjstatus::rerun::command_1`
///
/// The function returns a boolean indicating whether the state has been
/// changed and the UI should be re-rendered.
#[tracing::instrument(skip(state))]
pub fn parse_protocol(state: &mut ZellijState, input: &str) -> bool {
tracing::debug!("parsing protocol");
let lines = input.split('\n').collect::<Vec<&str>>();

let mut should_render = false;
for line in lines {
let line_renders = process_line(state, line);

if line_renders {
should_render = true;
}
}

should_render
}

#[tracing::instrument(skip_all)]
fn process_line(state: &mut ZellijState, line: &str) -> bool {
let parts = line.split("::").collect::<Vec<&str>>();

if parts.len() < 3 {
return false;
}

if parts[0] != "zjstatus" {
return false;
}

tracing::debug!("command: {}", parts[1]);

let mut should_render = false;
#[allow(clippy::single_match)]
match parts[1] {
"rerun" => {
rerun_command(state, parts[2]);

should_render = true;
}
"notify" => {
notify(state, parts[2]);

should_render = true;
}
_ => {}
}

should_render
}

fn notify(state: &mut ZellijState, message: &str) {
state.incoming_notification = Some(notification::Message {
body: message.to_string(),
received_at: Local::now(),
});
}

fn rerun_command(state: &mut ZellijState, command_name: &str) {
let command_result = state.command_results.get(command_name);

if command_result.is_none() {
return;
}

let mut command_result = command_result.unwrap().clone();

let ts = Sub::<Duration>::sub(Local::now(), Duration::try_days(1).unwrap());

command_result.context.insert(
"timestamp".to_string(),
ts.format(TIMESTAMP_FORMAT).to_string(),
);

state.command_results.remove(command_name);
state
.command_results
.insert(command_name.to_string(), command_result.clone());
}
6 changes: 3 additions & 3 deletions src/widgets/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,13 @@ fn get_timestamp_from_event_or_default(
return Local::now();
}

return Sub::<Duration>::sub(Local::now(), Duration::days(1));
return Sub::<Duration>::sub(Local::now(), Duration::try_days(1).unwrap());
}
let command_result = command_result.unwrap();

let ts_context = command_result.context.get("timestamp");
if ts_context.is_none() {
return Sub::<Duration>::sub(Local::now(), Duration::days(1));
return Sub::<Duration>::sub(Local::now(), Duration::try_days(1).unwrap());
}
let ts_context = ts_context.unwrap();

Expand All @@ -226,7 +226,7 @@ fn get_timestamp_from_event_or_default(

match DateTime::parse_from_str(ts_context, TIMESTAMP_FORMAT) {
Ok(ts) => ts.into(),
Err(_) => Sub::<Duration>::sub(Local::now(), Duration::days(1)),
Err(_) => Sub::<Duration>::sub(Local::now(), Duration::try_days(1).unwrap()),
}
}

Expand Down
1 change: 1 addition & 0 deletions src/widgets/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod command;
pub mod datetime;
pub mod mode;
pub mod notification;
pub mod swap_layout;
pub mod session;
pub mod tabs;
Expand Down
Loading

0 comments on commit 0723923

Please sign in to comment.