feat(status-bar): add multiple tips (#848)
* feat(status-bar): add draft for multiple tips * feat: add TIPS_MAP * Simplified 'tip' function. * chore: update file structure * feat(status-bar): update method of Tip rendering * feat(status-bar): change type of tip in State * refactor(status-bar): related to random tip selection * feat(status-bar): add simple local cache for testing * feat(status-bar): add cache system for tip data * Add mpadir to wasm for plugin to access zellij temp folder. * refactor(status-bar): update cache and utils * fix(status-bar): update file read error * refactor(status-bar): update macros * chore(status-bar): delete test data * chore(status-bar): update missing fixes * feat(status-bar): add detailed error * style: make clippy
This commit is contained in:
parent
2096cafe1d
commit
d79060f69a
11 changed files with 353 additions and 203 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -2082,6 +2082,11 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.12.1",
|
"ansi_term 0.12.1",
|
||||||
"colored",
|
"colored",
|
||||||
|
"lazy_static",
|
||||||
|
"rand 0.8.4",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
"zellij-tile",
|
"zellij-tile",
|
||||||
"zellij-tile-utils",
|
"zellij-tile-utils",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,10 @@ license = "MIT"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
colored = "2"
|
colored = "2"
|
||||||
ansi_term = "0.12"
|
ansi_term = "0.12"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
rand = "0.8.4"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
thiserror = "1.0.30"
|
||||||
zellij-tile = { path = "../../zellij-tile" }
|
zellij-tile = { path = "../../zellij-tile" }
|
||||||
zellij-tile-utils = { path = "../../zellij-tile-utils" }
|
zellij-tile-utils = { path = "../../zellij-tile-utils" }
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
mod first_line;
|
mod first_line;
|
||||||
mod second_line;
|
mod second_line;
|
||||||
|
mod tip;
|
||||||
|
|
||||||
use ansi_term::Style;
|
use ansi_term::Style;
|
||||||
|
|
||||||
|
|
@ -11,6 +12,7 @@ use first_line::{ctrl_keys, superkey};
|
||||||
use second_line::{
|
use second_line::{
|
||||||
fullscreen_panes_to_hide, keybinds, locked_fullscreen_panes_to_hide, text_copied_hint,
|
fullscreen_panes_to_hide, keybinds, locked_fullscreen_panes_to_hide, text_copied_hint,
|
||||||
};
|
};
|
||||||
|
use tip::utils::get_cached_tip_name;
|
||||||
|
|
||||||
// for more of these, copy paste from: https://en.wikipedia.org/wiki/Box-drawing_character
|
// for more of these, copy paste from: https://en.wikipedia.org/wiki/Box-drawing_character
|
||||||
static ARROW_SEPARATOR: &str = "";
|
static ARROW_SEPARATOR: &str = "";
|
||||||
|
|
@ -19,6 +21,7 @@ static MORE_MSG: &str = " ... ";
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct State {
|
struct State {
|
||||||
tabs: Vec<TabInfo>,
|
tabs: Vec<TabInfo>,
|
||||||
|
tip_name: String,
|
||||||
mode_info: ModeInfo,
|
mode_info: ModeInfo,
|
||||||
diplay_text_copied_hint: bool,
|
diplay_text_copied_hint: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -131,6 +134,8 @@ fn color_elements(palette: Palette) -> ColoredElements {
|
||||||
|
|
||||||
impl ZellijPlugin for State {
|
impl ZellijPlugin for State {
|
||||||
fn load(&mut self) {
|
fn load(&mut self) {
|
||||||
|
// TODO: Should be able to choose whether to use the cache through config.
|
||||||
|
self.tip_name = get_cached_tip_name();
|
||||||
set_selectable(false);
|
set_selectable(false);
|
||||||
subscribe(&[
|
subscribe(&[
|
||||||
EventType::ModeUpdate,
|
EventType::ModeUpdate,
|
||||||
|
|
@ -190,7 +195,7 @@ impl ZellijPlugin for State {
|
||||||
second_line = if self.diplay_text_copied_hint {
|
second_line = if self.diplay_text_copied_hint {
|
||||||
text_copied_hint(&self.mode_info.palette)
|
text_copied_hint(&self.mode_info.palette)
|
||||||
} else {
|
} else {
|
||||||
keybinds(&self.mode_info, cols)
|
keybinds(&self.mode_info, &self.tip_name, cols)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +213,7 @@ impl ZellijPlugin for State {
|
||||||
second_line = if self.diplay_text_copied_hint {
|
second_line = if self.diplay_text_copied_hint {
|
||||||
text_copied_hint(&self.mode_info.palette)
|
text_copied_hint(&self.mode_info.palette)
|
||||||
} else {
|
} else {
|
||||||
keybinds(&self.mode_info, cols)
|
keybinds(&self.mode_info, &self.tip_name, cols)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +221,7 @@ impl ZellijPlugin for State {
|
||||||
second_line = if self.diplay_text_copied_hint {
|
second_line = if self.diplay_text_copied_hint {
|
||||||
text_copied_hint(&self.mode_info.palette)
|
text_copied_hint(&self.mode_info.palette)
|
||||||
} else {
|
} else {
|
||||||
keybinds(&self.mode_info, cols)
|
keybinds(&self.mode_info, &self.tip_name, cols)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@ use ansi_term::{
|
||||||
};
|
};
|
||||||
use zellij_tile::prelude::*;
|
use zellij_tile::prelude::*;
|
||||||
|
|
||||||
use crate::{LinePart, MORE_MSG};
|
use crate::{
|
||||||
|
tip::{data::TIPS, TipFn},
|
||||||
|
LinePart, MORE_MSG,
|
||||||
|
};
|
||||||
|
|
||||||
fn full_length_shortcut(
|
fn full_length_shortcut(
|
||||||
is_first_shortcut: bool,
|
is_first_shortcut: bool,
|
||||||
|
|
@ -82,194 +85,6 @@ fn first_word_shortcut(
|
||||||
len,
|
len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn quicknav_full(palette: Palette) -> LinePart {
|
|
||||||
let text_first_part = " Tip: ";
|
|
||||||
let alt = "Alt";
|
|
||||||
let text_second_part = " + ";
|
|
||||||
let new_pane_shortcut = "<n>";
|
|
||||||
let text_third_part = " => open new pane. ";
|
|
||||||
let second_alt = "Alt";
|
|
||||||
let text_fourth_part = " + ";
|
|
||||||
let brackets_navigation = "<[]";
|
|
||||||
let text_fifth_part = " or ";
|
|
||||||
let hjkl_navigation = "hjkl>";
|
|
||||||
let text_sixths_part = " => navigate between panes. ";
|
|
||||||
let third_alt = "Alt";
|
|
||||||
let text_seventh_parth = " + ";
|
|
||||||
let increase_decrease_parth = "<+->";
|
|
||||||
let text_eighth_parth = " => increase/decrease pane size.";
|
|
||||||
let len = text_first_part.chars().count()
|
|
||||||
+ alt.chars().count()
|
|
||||||
+ text_second_part.chars().count()
|
|
||||||
+ new_pane_shortcut.chars().count()
|
|
||||||
+ text_third_part.chars().count()
|
|
||||||
+ second_alt.chars().count()
|
|
||||||
+ text_fourth_part.chars().count()
|
|
||||||
+ brackets_navigation.chars().count()
|
|
||||||
+ text_fifth_part.chars().count()
|
|
||||||
+ hjkl_navigation.chars().count()
|
|
||||||
+ text_sixths_part.chars().count()
|
|
||||||
+ third_alt.chars().count()
|
|
||||||
+ text_seventh_parth.chars().count()
|
|
||||||
+ increase_decrease_parth.chars().count()
|
|
||||||
+ text_eighth_parth.chars().count();
|
|
||||||
let green_color = match palette.green {
|
|
||||||
PaletteColor::Rgb((r, g, b)) => RGB(r, g, b),
|
|
||||||
PaletteColor::EightBit(color) => Fixed(color),
|
|
||||||
};
|
|
||||||
let orange_color = match palette.orange {
|
|
||||||
PaletteColor::Rgb((r, g, b)) => RGB(r, g, b),
|
|
||||||
PaletteColor::EightBit(color) => Fixed(color),
|
|
||||||
};
|
|
||||||
LinePart {
|
|
||||||
part: format!(
|
|
||||||
"{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
|
|
||||||
text_first_part,
|
|
||||||
Style::new().fg(orange_color).bold().paint(alt),
|
|
||||||
text_second_part,
|
|
||||||
Style::new().fg(green_color).bold().paint(new_pane_shortcut),
|
|
||||||
text_third_part,
|
|
||||||
Style::new().fg(orange_color).bold().paint(second_alt),
|
|
||||||
text_fourth_part,
|
|
||||||
Style::new()
|
|
||||||
.fg(green_color)
|
|
||||||
.bold()
|
|
||||||
.paint(brackets_navigation),
|
|
||||||
text_fifth_part,
|
|
||||||
Style::new().fg(green_color).bold().paint(hjkl_navigation),
|
|
||||||
text_sixths_part,
|
|
||||||
Style::new().fg(orange_color).bold().paint(third_alt),
|
|
||||||
text_seventh_parth,
|
|
||||||
Style::new()
|
|
||||||
.fg(green_color)
|
|
||||||
.bold()
|
|
||||||
.paint(increase_decrease_parth),
|
|
||||||
text_eighth_parth,
|
|
||||||
),
|
|
||||||
len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn quicknav_medium(palette: Palette) -> LinePart {
|
|
||||||
let text_first_part = " Tip: ";
|
|
||||||
let alt = "Alt";
|
|
||||||
let text_second_part = " + ";
|
|
||||||
let new_pane_shortcut = "<n>";
|
|
||||||
let text_third_part = " => new pane. ";
|
|
||||||
let second_alt = "Alt";
|
|
||||||
let text_fourth_part = " + ";
|
|
||||||
let brackets_navigation = "<[]";
|
|
||||||
let text_fifth_part = " or ";
|
|
||||||
let hjkl_navigation = "hjkl>";
|
|
||||||
let text_sixths_part = " => navigate. ";
|
|
||||||
let third_alt = "Alt";
|
|
||||||
let text_seventh_parth = " + ";
|
|
||||||
let increase_decrease_parth = "<+->";
|
|
||||||
let text_eighth_parth = " => resize pane. ";
|
|
||||||
let len = text_first_part.chars().count()
|
|
||||||
+ alt.chars().count()
|
|
||||||
+ text_second_part.chars().count()
|
|
||||||
+ new_pane_shortcut.chars().count()
|
|
||||||
+ text_third_part.chars().count()
|
|
||||||
+ second_alt.chars().count()
|
|
||||||
+ text_fourth_part.chars().count()
|
|
||||||
+ brackets_navigation.chars().count()
|
|
||||||
+ text_fifth_part.chars().count()
|
|
||||||
+ hjkl_navigation.chars().count()
|
|
||||||
+ text_sixths_part.chars().count()
|
|
||||||
+ third_alt.chars().count()
|
|
||||||
+ text_seventh_parth.chars().count()
|
|
||||||
+ increase_decrease_parth.chars().count()
|
|
||||||
+ text_eighth_parth.chars().count();
|
|
||||||
let green_color = match palette.green {
|
|
||||||
PaletteColor::Rgb((r, g, b)) => RGB(r, g, b),
|
|
||||||
PaletteColor::EightBit(color) => Fixed(color),
|
|
||||||
};
|
|
||||||
let orange_color = match palette.orange {
|
|
||||||
PaletteColor::Rgb((r, g, b)) => RGB(r, g, b),
|
|
||||||
PaletteColor::EightBit(color) => Fixed(color),
|
|
||||||
};
|
|
||||||
LinePart {
|
|
||||||
part: format!(
|
|
||||||
"{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
|
|
||||||
text_first_part,
|
|
||||||
Style::new().fg(orange_color).bold().paint(alt),
|
|
||||||
text_second_part,
|
|
||||||
Style::new().fg(green_color).bold().paint(new_pane_shortcut),
|
|
||||||
text_third_part,
|
|
||||||
Style::new().fg(orange_color).bold().paint(second_alt),
|
|
||||||
text_fourth_part,
|
|
||||||
Style::new()
|
|
||||||
.fg(green_color)
|
|
||||||
.bold()
|
|
||||||
.paint(brackets_navigation),
|
|
||||||
text_fifth_part,
|
|
||||||
Style::new().fg(green_color).bold().paint(hjkl_navigation),
|
|
||||||
text_sixths_part,
|
|
||||||
Style::new().fg(orange_color).bold().paint(third_alt),
|
|
||||||
text_seventh_parth,
|
|
||||||
Style::new()
|
|
||||||
.fg(green_color)
|
|
||||||
.bold()
|
|
||||||
.paint(increase_decrease_parth),
|
|
||||||
text_eighth_parth,
|
|
||||||
),
|
|
||||||
len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn quicknav_short(palette: Palette) -> LinePart {
|
|
||||||
let text_first_part = " QuickNav: ";
|
|
||||||
let alt = "Alt";
|
|
||||||
let text_second_part = " + ";
|
|
||||||
let new_pane_shortcut = "n";
|
|
||||||
let text_third_part = "/";
|
|
||||||
let brackets_navigation = "[]";
|
|
||||||
let text_fifth_part = "/";
|
|
||||||
let hjkl_navigation = "hjkl";
|
|
||||||
let text_sixth_part = "/";
|
|
||||||
let increase_decrease_part = "+-";
|
|
||||||
let len = text_first_part.chars().count()
|
|
||||||
+ alt.chars().count()
|
|
||||||
+ text_second_part.chars().count()
|
|
||||||
+ new_pane_shortcut.chars().count()
|
|
||||||
+ text_third_part.chars().count()
|
|
||||||
+ brackets_navigation.chars().count()
|
|
||||||
+ text_fifth_part.chars().count()
|
|
||||||
+ hjkl_navigation.chars().count()
|
|
||||||
+ text_sixth_part.chars().count()
|
|
||||||
+ increase_decrease_part.chars().count();
|
|
||||||
let green_color = match palette.green {
|
|
||||||
PaletteColor::Rgb((r, g, b)) => RGB(r, g, b),
|
|
||||||
PaletteColor::EightBit(color) => Fixed(color),
|
|
||||||
};
|
|
||||||
let orange_color = match palette.orange {
|
|
||||||
PaletteColor::Rgb((r, g, b)) => RGB(r, g, b),
|
|
||||||
PaletteColor::EightBit(color) => Fixed(color),
|
|
||||||
};
|
|
||||||
LinePart {
|
|
||||||
part: format!(
|
|
||||||
"{}{}{}{}{}{}{}{}{}{}",
|
|
||||||
text_first_part,
|
|
||||||
Style::new().fg(orange_color).bold().paint(alt),
|
|
||||||
text_second_part,
|
|
||||||
Style::new().fg(green_color).bold().paint(new_pane_shortcut),
|
|
||||||
text_third_part,
|
|
||||||
Style::new()
|
|
||||||
.fg(green_color)
|
|
||||||
.bold()
|
|
||||||
.paint(brackets_navigation),
|
|
||||||
text_fifth_part,
|
|
||||||
Style::new().fg(green_color).bold().paint(hjkl_navigation),
|
|
||||||
text_sixth_part,
|
|
||||||
Style::new()
|
|
||||||
.fg(green_color)
|
|
||||||
.bold()
|
|
||||||
.paint(increase_decrease_part),
|
|
||||||
),
|
|
||||||
len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn locked_interface_indication(palette: Palette) -> LinePart {
|
fn locked_interface_indication(palette: Palette) -> LinePart {
|
||||||
let locked_text = " -- INTERFACE LOCKED -- ";
|
let locked_text = " -- INTERFACE LOCKED -- ";
|
||||||
|
|
@ -318,9 +133,9 @@ fn select_pane_shortcut(is_first_shortcut: bool, palette: Palette) -> LinePart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn full_shortcut_list(help: &ModeInfo) -> LinePart {
|
fn full_shortcut_list(help: &ModeInfo, tip: TipFn) -> LinePart {
|
||||||
match help.mode {
|
match help.mode {
|
||||||
InputMode::Normal => quicknav_full(help.palette),
|
InputMode::Normal => tip(help.palette),
|
||||||
InputMode::Locked => locked_interface_indication(help.palette),
|
InputMode::Locked => locked_interface_indication(help.palette),
|
||||||
_ => {
|
_ => {
|
||||||
let mut line_part = LinePart::default();
|
let mut line_part = LinePart::default();
|
||||||
|
|
@ -337,9 +152,9 @@ fn full_shortcut_list(help: &ModeInfo) -> LinePart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shortened_shortcut_list(help: &ModeInfo) -> LinePart {
|
fn shortened_shortcut_list(help: &ModeInfo, tip: TipFn) -> LinePart {
|
||||||
match help.mode {
|
match help.mode {
|
||||||
InputMode::Normal => quicknav_medium(help.palette),
|
InputMode::Normal => tip(help.palette),
|
||||||
InputMode::Locked => locked_interface_indication(help.palette),
|
InputMode::Locked => locked_interface_indication(help.palette),
|
||||||
_ => {
|
_ => {
|
||||||
let mut line_part = LinePart::default();
|
let mut line_part = LinePart::default();
|
||||||
|
|
@ -356,10 +171,10 @@ fn shortened_shortcut_list(help: &ModeInfo) -> LinePart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart {
|
fn best_effort_shortcut_list(help: &ModeInfo, tip: TipFn, max_len: usize) -> LinePart {
|
||||||
match help.mode {
|
match help.mode {
|
||||||
InputMode::Normal => {
|
InputMode::Normal => {
|
||||||
let line_part = quicknav_short(help.palette);
|
let line_part = tip(help.palette);
|
||||||
if line_part.len <= max_len {
|
if line_part.len <= max_len {
|
||||||
line_part
|
line_part
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -397,16 +212,19 @@ fn best_effort_shortcut_list(help: &ModeInfo, max_len: usize) -> LinePart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keybinds(help: &ModeInfo, max_width: usize) -> LinePart {
|
pub fn keybinds(help: &ModeInfo, tip_name: &str, max_width: usize) -> LinePart {
|
||||||
let full_shortcut_list = full_shortcut_list(help);
|
// It is assumed that there is at least one TIP data in the TIPS HasMap.
|
||||||
|
let tip_body = TIPS.get(tip_name).unwrap();
|
||||||
|
|
||||||
|
let full_shortcut_list = full_shortcut_list(help, tip_body.full);
|
||||||
if full_shortcut_list.len <= max_width {
|
if full_shortcut_list.len <= max_width {
|
||||||
return full_shortcut_list;
|
return full_shortcut_list;
|
||||||
}
|
}
|
||||||
let shortened_shortcut_list = shortened_shortcut_list(help);
|
let shortened_shortcut_list = shortened_shortcut_list(help, tip_body.medium);
|
||||||
if shortened_shortcut_list.len <= max_width {
|
if shortened_shortcut_list.len <= max_width {
|
||||||
return shortened_shortcut_list;
|
return shortened_shortcut_list;
|
||||||
}
|
}
|
||||||
best_effort_shortcut_list(help, max_width)
|
best_effort_shortcut_list(help, tip_body.short, max_width)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_copied_hint(palette: &Palette) -> LinePart {
|
pub fn text_copied_hint(palette: &Palette) -> LinePart {
|
||||||
|
|
|
||||||
119
default-plugins/status-bar/src/tip/cache.rs
Normal file
119
default-plugins/status-bar/src/tip/cache.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use zellij_tile::prelude::get_zellij_version;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Metadata {
|
||||||
|
zellij_version: String,
|
||||||
|
cached_data: HashMap<String, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LocalCache {
|
||||||
|
path: PathBuf,
|
||||||
|
metadata: Metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type LocalCacheResult = Result<LocalCache, LocalCacheError>;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum LocalCacheError {
|
||||||
|
// Io error
|
||||||
|
#[error("IoError: {0}")]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
// Io error with path context
|
||||||
|
#[error("IoError: {0}, File: {1}")]
|
||||||
|
IoPath(io::Error, PathBuf),
|
||||||
|
// Deserialization error
|
||||||
|
#[error("Deserialization error: {0}")]
|
||||||
|
Serde(#[from] serde_json::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalCache {
|
||||||
|
fn from_json(json_cache: &str) -> Result<Metadata, LocalCacheError> {
|
||||||
|
match serde_json::from_str::<Metadata>(json_cache) {
|
||||||
|
Ok(metadata) => Ok(metadata),
|
||||||
|
Err(err) => {
|
||||||
|
if json_cache.is_empty() {
|
||||||
|
return Ok(Metadata {
|
||||||
|
zellij_version: get_zellij_version(),
|
||||||
|
cached_data: HashMap::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(LocalCacheError::Serde(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(path: PathBuf) -> LocalCacheResult {
|
||||||
|
match OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.create(true)
|
||||||
|
.open(path.as_path())
|
||||||
|
{
|
||||||
|
Ok(mut file) => {
|
||||||
|
let mut json_cache = String::new();
|
||||||
|
file.read_to_string(&mut json_cache)
|
||||||
|
.map_err(LocalCacheError::Io)?;
|
||||||
|
|
||||||
|
let metadata = LocalCache::from_json(&json_cache)?;
|
||||||
|
Ok(LocalCache { path, metadata })
|
||||||
|
}
|
||||||
|
Err(e) => Err(LocalCacheError::IoPath(e, path)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&mut self) -> Result<(), LocalCacheError> {
|
||||||
|
match serde_json::to_string(&self.metadata) {
|
||||||
|
Ok(json_cache) => {
|
||||||
|
let mut file = File::create(self.path.as_path())
|
||||||
|
.map_err(|e| LocalCacheError::IoPath(e, self.path.clone()))?;
|
||||||
|
file.write_all(json_cache.as_bytes())
|
||||||
|
.map_err(LocalCacheError::Io)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => Err(LocalCacheError::Serde(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) -> Result<(), LocalCacheError> {
|
||||||
|
self.metadata.cached_data.clear();
|
||||||
|
self.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_version(&self) -> &String {
|
||||||
|
&self.metadata.zellij_version
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_version<S: Into<String>>(&mut self, version: S) {
|
||||||
|
self.metadata.zellij_version = version.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.metadata.cached_data.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cached_data(&self) -> &HashMap<String, usize> {
|
||||||
|
&self.metadata.cached_data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cached_data_set(&self) -> HashSet<String> {
|
||||||
|
self.get_cached_data().keys().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn caching<S: Into<String>>(&mut self, key: S) -> Result<(), LocalCacheError> {
|
||||||
|
let key = key.into();
|
||||||
|
if let Some(item) = self.metadata.cached_data.get_mut(&key) {
|
||||||
|
*item += 1;
|
||||||
|
} else {
|
||||||
|
self.metadata.cached_data.insert(key, 1);
|
||||||
|
}
|
||||||
|
self.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
2
default-plugins/status-bar/src/tip/consts.rs
Normal file
2
default-plugins/status-bar/src/tip/consts.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub const DEFAULT_CACHE_FILE_PATH: &str = "/tmp/status-bar-tips.cache";
|
||||||
|
pub const MAX_CACHE_HITS: usize = 10;
|
||||||
100
default-plugins/status-bar/src/tip/data.rs
Normal file
100
default-plugins/status-bar/src/tip/data.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use ansi_term::{
|
||||||
|
unstyled_len, ANSIString, ANSIStrings,
|
||||||
|
Color::{Fixed, RGB},
|
||||||
|
Style,
|
||||||
|
};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
use crate::{tip::TipBody, LinePart};
|
||||||
|
use zellij_tile::prelude::*;
|
||||||
|
use zellij_tile_utils::palette_match;
|
||||||
|
|
||||||
|
macro_rules! strings {
|
||||||
|
($ANSIStrings:expr) => {{
|
||||||
|
let strings: &[ANSIString<'static>] = $ANSIStrings;
|
||||||
|
|
||||||
|
let ansi_strings = ANSIStrings(strings);
|
||||||
|
|
||||||
|
LinePart {
|
||||||
|
part: format!("{}", ansi_strings),
|
||||||
|
len: unstyled_len(&ansi_strings),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref TIPS: HashMap<&'static str, TipBody> = HashMap::from([(
|
||||||
|
"quicknav",
|
||||||
|
TipBody {
|
||||||
|
short: quicknav_short,
|
||||||
|
medium: quicknav_medium,
|
||||||
|
full: quicknav_full,
|
||||||
|
}
|
||||||
|
)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quicknav_full(palette: Palette) -> LinePart {
|
||||||
|
let green_color = palette_match!(palette.green);
|
||||||
|
let orange_color = palette_match!(palette.orange);
|
||||||
|
|
||||||
|
strings!(&[
|
||||||
|
Style::new().paint(" Tip: "),
|
||||||
|
Style::new().fg(orange_color).bold().paint("Alt"),
|
||||||
|
Style::new().paint(" + "),
|
||||||
|
Style::new().fg(green_color).bold().paint("<n>"),
|
||||||
|
Style::new().paint(" => open new pane. "),
|
||||||
|
Style::new().fg(orange_color).bold().paint("Alt"),
|
||||||
|
Style::new().paint(" + "),
|
||||||
|
Style::new().fg(green_color).bold().paint("<[]"),
|
||||||
|
Style::new().paint(" or "),
|
||||||
|
Style::new().fg(green_color).bold().paint("hjkl>"),
|
||||||
|
Style::new().paint(" => navigate between panes. "),
|
||||||
|
Style::new().fg(orange_color).bold().paint("Alt"),
|
||||||
|
Style::new().paint(" + "),
|
||||||
|
Style::new().fg(green_color).bold().paint("<+->"),
|
||||||
|
Style::new().paint(" => increase/decrease pane size."),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quicknav_medium(palette: Palette) -> LinePart {
|
||||||
|
let green_color = palette_match!(palette.green);
|
||||||
|
let orange_color = palette_match!(palette.orange);
|
||||||
|
|
||||||
|
strings!(&[
|
||||||
|
Style::new().paint(" Tip: "),
|
||||||
|
Style::new().fg(orange_color).bold().paint("Alt"),
|
||||||
|
Style::new().paint(" + "),
|
||||||
|
Style::new().fg(green_color).bold().paint("<n>"),
|
||||||
|
Style::new().paint(" => new pane. "),
|
||||||
|
Style::new().fg(orange_color).bold().paint("Alt"),
|
||||||
|
Style::new().paint(" + "),
|
||||||
|
Style::new().fg(green_color).bold().paint("<[]"),
|
||||||
|
Style::new().paint(" or "),
|
||||||
|
Style::new().fg(green_color).bold().paint("hjkl>"),
|
||||||
|
Style::new().paint(" => navigate. "),
|
||||||
|
Style::new().fg(orange_color).bold().paint("Alt"),
|
||||||
|
Style::new().paint(" + "),
|
||||||
|
Style::new().fg(green_color).bold().paint("<+->"),
|
||||||
|
Style::new().paint(" => resize pane."),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quicknav_short(palette: Palette) -> LinePart {
|
||||||
|
let green_color = palette_match!(palette.green);
|
||||||
|
let orange_color = palette_match!(palette.orange);
|
||||||
|
|
||||||
|
strings!(&[
|
||||||
|
Style::new().paint(" QuickNav: "),
|
||||||
|
Style::new().fg(orange_color).bold().paint("Alt"),
|
||||||
|
Style::new().paint(" + "),
|
||||||
|
Style::new().fg(green_color).bold().paint("n"),
|
||||||
|
Style::new().paint("/"),
|
||||||
|
Style::new().fg(green_color).bold().paint("[]"),
|
||||||
|
Style::new().paint("/"),
|
||||||
|
Style::new().fg(green_color).bold().paint("hjkl"),
|
||||||
|
Style::new().paint("/"),
|
||||||
|
Style::new().fg(green_color).bold().paint("+-"),
|
||||||
|
])
|
||||||
|
}
|
||||||
16
default-plugins/status-bar/src/tip/mod.rs
Normal file
16
default-plugins/status-bar/src/tip/mod.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
pub mod cache;
|
||||||
|
pub mod consts;
|
||||||
|
pub mod data;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
|
use crate::LinePart;
|
||||||
|
use zellij_tile::prelude::*;
|
||||||
|
|
||||||
|
pub type TipFn = fn(Palette) -> LinePart;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TipBody {
|
||||||
|
pub short: TipFn,
|
||||||
|
pub medium: TipFn,
|
||||||
|
pub full: TipFn,
|
||||||
|
}
|
||||||
68
default-plugins/status-bar/src/tip/utils.rs
Normal file
68
default-plugins/status-bar/src/tip/utils.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use rand::prelude::{IteratorRandom, SliceRandom};
|
||||||
|
|
||||||
|
use zellij_tile::prelude::get_zellij_version;
|
||||||
|
|
||||||
|
use super::cache::LocalCache;
|
||||||
|
use super::consts::{DEFAULT_CACHE_FILE_PATH, MAX_CACHE_HITS};
|
||||||
|
use super::data::TIPS;
|
||||||
|
|
||||||
|
macro_rules! get_name_and_caching {
|
||||||
|
($cache:expr) => {{
|
||||||
|
let name = get_random_tip_name();
|
||||||
|
$cache.caching(name.clone()).unwrap();
|
||||||
|
return name;
|
||||||
|
}};
|
||||||
|
($cache:expr, $from:expr) => {{
|
||||||
|
let name = $from.choose(&mut rand::thread_rng()).unwrap().to_string();
|
||||||
|
$cache.caching(name.clone()).unwrap();
|
||||||
|
return name;
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_random_tip_name() -> String {
|
||||||
|
TIPS.keys()
|
||||||
|
.choose(&mut rand::thread_rng())
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cached_tip_name() -> String {
|
||||||
|
let mut local_cache = LocalCache::new(PathBuf::from(DEFAULT_CACHE_FILE_PATH)).unwrap();
|
||||||
|
|
||||||
|
let zellij_version = get_zellij_version();
|
||||||
|
if zellij_version.ne(local_cache.get_version()) {
|
||||||
|
local_cache.set_version(zellij_version);
|
||||||
|
local_cache.clear().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if local_cache.is_empty() {
|
||||||
|
get_name_and_caching!(local_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
let usable_tips = local_cache
|
||||||
|
.get_cached_data()
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, &v)| v < MAX_CACHE_HITS)
|
||||||
|
.map(|(k, _)| k.to_string())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
if usable_tips.is_empty() {
|
||||||
|
let cached_set = local_cache.get_cached_data_set();
|
||||||
|
let diff = TIPS
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.filter(|k| !cached_set.contains(&k.to_string()))
|
||||||
|
.collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
if !diff.is_empty() {
|
||||||
|
get_name_and_caching!(local_cache, diff);
|
||||||
|
} else {
|
||||||
|
local_cache.clear().unwrap();
|
||||||
|
get_name_and_caching!(local_cache);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
get_name_and_caching!(local_cache, usable_tips);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,7 +29,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
consts::{VERSION, ZELLIJ_PROJ_DIR},
|
consts::{VERSION, ZELLIJ_PROJ_DIR, ZELLIJ_TMP_DIR},
|
||||||
errors::{ContextType, PluginContext},
|
errors::{ContextType, PluginContext},
|
||||||
};
|
};
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
|
|
@ -272,6 +272,8 @@ fn start_plugin(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map_dir("/data", &plugin_own_data_dir)
|
.map_dir("/data", &plugin_own_data_dir)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.map_dir("/tmp", ZELLIJ_TMP_DIR.as_path())
|
||||||
|
.unwrap()
|
||||||
.stdin(Box::new(input))
|
.stdin(Box::new(input))
|
||||||
.stdout(Box::new(output))
|
.stdout(Box::new(output))
|
||||||
.stderr(Box::new(stderr))
|
.stderr(Box::new(stderr))
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,16 @@ macro_rules! rgb {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! palette_match {
|
||||||
|
($palette_color:expr) => {
|
||||||
|
match $palette_color {
|
||||||
|
PaletteColor::Rgb((r, g, b)) => RGB(r, g, b),
|
||||||
|
PaletteColor::EightBit(color) => Fixed(color),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! style {
|
macro_rules! style {
|
||||||
($fg:expr, $bg:expr) => {
|
($fg:expr, $bg:expr) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue