zellij_utils/
shared.rs

1//! Some general utility functions.
2
3use std::{iter, str::from_utf8};
4
5use crate::data::{Palette, PaletteColor, PaletteSource, ThemeHue};
6use crate::envs::get_session_name;
7use colorsys::Rgb;
8use strip_ansi_escapes::strip;
9use unicode_width::UnicodeWidthStr;
10
11#[cfg(unix)]
12pub use unix_only::*;
13
14#[cfg(unix)]
15mod unix_only {
16    use std::os::unix::fs::PermissionsExt;
17    use std::path::Path;
18    use std::{fs, io};
19
20    pub fn set_permissions(path: &Path, mode: u32) -> io::Result<()> {
21        let mut permissions = fs::metadata(path)?.permissions();
22        permissions.set_mode(mode);
23        fs::set_permissions(path, permissions)
24    }
25}
26
27#[cfg(not(unix))]
28pub fn set_permissions(_path: &std::path::Path, _mode: u32) -> std::io::Result<()> {
29    Ok(())
30}
31
32pub fn ansi_len(s: &str) -> usize {
33    from_utf8(&strip(s).unwrap()).unwrap().width()
34}
35
36pub fn adjust_to_size(s: &str, rows: usize, columns: usize) -> String {
37    s.lines()
38        .map(|l| {
39            let actual_len = ansi_len(l);
40            if actual_len > columns {
41                let mut line = String::from(l);
42                line.truncate(columns);
43                line
44            } else {
45                [l, &str::repeat(" ", columns - ansi_len(l))].concat()
46            }
47        })
48        .chain(iter::repeat(str::repeat(" ", columns)))
49        .take(rows)
50        .collect::<Vec<_>>()
51        .join("\n\r")
52}
53
54pub fn make_terminal_title(pane_title: &str) -> String {
55    format!(
56        "\u{1b}]0;Zellij {}- {}\u{07}",
57        get_session_name()
58            .map(|n| format!("({}) ", n))
59            .unwrap_or_default(),
60        pane_title,
61    )
62}
63
64// Colors
65pub mod colors {
66    pub const WHITE: u8 = 255;
67    pub const GREEN: u8 = 154;
68    pub const GRAY: u8 = 238;
69    pub const BRIGHT_GRAY: u8 = 245;
70    pub const RED: u8 = 124;
71    pub const ORANGE: u8 = 166;
72    pub const BLACK: u8 = 16;
73    pub const MAGENTA: u8 = 201;
74    pub const CYAN: u8 = 51;
75    pub const YELLOW: u8 = 226;
76    pub const BLUE: u8 = 45;
77    pub const PURPLE: u8 = 99;
78    pub const GOLD: u8 = 136;
79    pub const SILVER: u8 = 245;
80    pub const PINK: u8 = 207;
81    pub const BROWN: u8 = 215;
82}
83
84pub fn _hex_to_rgb(hex: &str) -> (u8, u8, u8) {
85    Rgb::from_hex_str(hex)
86        .expect("The passed argument must be a valid hex color")
87        .into()
88}
89
90pub fn default_palette() -> Palette {
91    Palette {
92        source: PaletteSource::Default,
93        theme_hue: ThemeHue::Dark,
94        fg: PaletteColor::EightBit(colors::BRIGHT_GRAY),
95        bg: PaletteColor::EightBit(colors::GRAY),
96        black: PaletteColor::EightBit(colors::BLACK),
97        red: PaletteColor::EightBit(colors::RED),
98        green: PaletteColor::EightBit(colors::GREEN),
99        yellow: PaletteColor::EightBit(colors::YELLOW),
100        blue: PaletteColor::EightBit(colors::BLUE),
101        magenta: PaletteColor::EightBit(colors::MAGENTA),
102        cyan: PaletteColor::EightBit(colors::CYAN),
103        white: PaletteColor::EightBit(colors::WHITE),
104        orange: PaletteColor::EightBit(colors::ORANGE),
105        gray: PaletteColor::EightBit(colors::GRAY),
106        purple: PaletteColor::EightBit(colors::PURPLE),
107        gold: PaletteColor::EightBit(colors::GOLD),
108        silver: PaletteColor::EightBit(colors::SILVER),
109        pink: PaletteColor::EightBit(colors::PINK),
110        brown: PaletteColor::EightBit(colors::BROWN),
111    }
112}
113
114// Dark magic
115pub fn detect_theme_hue(bg: PaletteColor) -> ThemeHue {
116    match bg {
117        PaletteColor::Rgb((r, g, b)) => {
118            // HSP, P stands for perceived brightness
119            let hsp: f64 = (0.299 * (r as f64 * r as f64)
120                + 0.587 * (g as f64 * g as f64)
121                + 0.114 * (b as f64 * b as f64))
122                .sqrt();
123            match hsp > 127.5 {
124                true => ThemeHue::Light,
125                false => ThemeHue::Dark,
126            }
127        },
128        _ => ThemeHue::Dark,
129    }
130}
131
132// (this was shamelessly copied from alacritty)
133//
134// This returns the current terminal version as a unique number based on the
135// semver version. The different versions are padded to ensure that a higher semver version will
136// always report a higher version number.
137pub fn version_number(mut version: &str) -> usize {
138    if let Some(separator) = version.rfind('-') {
139        version = &version[..separator];
140    }
141
142    let mut version_number = 0;
143
144    let semver_versions = version.split('.');
145    for (i, semver_version) in semver_versions.rev().enumerate() {
146        let semver_number = semver_version.parse::<usize>().unwrap_or(0);
147        version_number += usize::pow(100, i as u32) * semver_number;
148    }
149
150    version_number
151}