From befb2aa7d3277164c91e05b7d935bbc817c8b224 Mon Sep 17 00:00:00 2001 From: Jakub Jirutka Date: Sun, 30 Jan 2022 01:11:49 +0100 Subject: [PATCH] Bring back support for truncation with ellipsis --- NEWS.md | 12 ++++++++---- README.md | 12 ++++++++---- src/rtfmt.rs | 22 ++++++++++++++++++++-- src/tree.rs | 32 ++++++++++++++++++++++++++++---- 4 files changed, 64 insertions(+), 14 deletions(-) diff --git a/NEWS.md b/NEWS.md index 607ad93..19d377f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,10 +4,14 @@ swayr v0.13.0 - The placeholders `{id}`, `{app_name}`, `{name}`/`{title}`, `{output_name}`, `{workspace_name}`, and `{marks}` may optionally provide a format string as specified by [Rust's std::fmt](https://doc.rust-lang.org/std/fmt/). The - syntax is `{:}`. For example, `{app_name:{:>10.10}}` - would mean that the application name is printed with exactly 10 characters. - If it's shorter, it will be right-aligned (the `>`) and padded with spaces, - if it's longer, it'll be cut after the 10th character. + syntax is `{:}` or `{:…}`. For + example, `{app_name:{:>10.10}}` would mean that the application name is + printed with exactly 10 characters. If it's shorter, it will be + right-aligned (the `>`) and padded with spaces, if it's longer, it'll be cut + after the 10th character. Another example, `{app_name:{:.10}…}` would mean + that the application name is truncated at 10 characters. If it's shorter, it + will be printed as-is (no padding), if it's longer, it'll be cut after the + 9th character and the ellipsis (…) character will be added after it. swayr v0.12.0 ============= diff --git a/README.md b/README.md index 086b3f4..323d9b1 100644 --- a/README.md +++ b/README.md @@ -327,10 +327,14 @@ right now. The placeholders `{id}`, `{app_name}`, `{name}`/`{title}`, `{output_name}`, `{workspace_name}`, and `{marks}` may optionally provide a format string as specified by [Rust's std::fmt](https://doc.rust-lang.org/std/fmt/). The syntax -is `{:}`. For example, `{app_name:{:>10.10}}` would mean -that the application name is printed with exactly 10 characters. If it's -shorter, it will be right-aligned (the `>`) and padded with spaces, if it's -longer, it'll be cut after the 10th character. +is `{:}` or `{:…}`. For example, +`{app_name:{:>10.10}}` would mean that the application name is printed with +exactly 10 characters. If it's shorter, it will be right-aligned (the `>`) and +padded with spaces, if it's longer, it'll be cut after the 10th character. +Another example, `{app_name:{:.10}…}` would mean that the application name is +truncated at 10 characters. If it's shorter, it will be printed as-is (no +padding), if it's longer, it'll be cut after the 9th character and the ellipsis +(…) character will be added after it. It is crucial that during selection (using wofi or some other menu program) each window has a different display string. Therefore, it is highly diff --git a/src/rtfmt.rs b/src/rtfmt.rs index 26966a7..e8ac462 100644 --- a/src/rtfmt.rs +++ b/src/rtfmt.rs @@ -73,11 +73,29 @@ impl<'a> std::convert::TryInto for &FmtArg<'a> { } } -pub fn format(fmt: &str, arg: &str) -> String { +pub fn format(fmt: &str, arg: &str, ellipsis: bool) -> String { let args = &[FmtArg::Str(arg)]; + if let Ok(pf) = ParsedFormat::parse(fmt, args, &NoNamedArguments) { - format!("{}", pf) + let mut s = format!("{}", pf); + + if ellipsis && !s.contains(arg) { + s.pop(); + s.push('…'); + } + s } else { format!("Invalid format string: {}", fmt) } } + +#[test] +fn test_format() { + assert_eq!(format("{:.10}", "sway", false), "sway"); + assert_eq!(format("{:.10}", "sway", true), "sway"); + assert_eq!(format("{:.4}", "𝔰𝔴𝔞𝔶", true), "𝔰𝔴𝔞𝔶"); + + assert_eq!(format("{:.3}", "sway", false), "swa"); + assert_eq!(format("{:.3}", "sway", true), "sw…"); + assert_eq!(format("{:.3}", "𝔰𝔴𝔞𝔶", true), "𝔰𝔴…"); +} diff --git a/src/tree.rs b/src/tree.rs index b145552..fde5d8a 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -428,9 +428,10 @@ pub fn get_tree<'a>( lazy_static! { static ref APP_NAME_AND_VERSION_RX: regex::Regex = regex::Regex::new("(.+)(-[0-9.]+)").unwrap(); - static ref PLACEHOLDER_RX: regex::Regex = - regex::Regex::new(r"\{(?P[^}:]+)(?::(?P\{[^}]*\}))?\}") - .unwrap(); + static ref PLACEHOLDER_RX: regex::Regex = regex::Regex::new( + r"\{(?P[^}:]+)(?::(?P\{[^}]*\})(?P…)?)?\}" + ) + .unwrap(); } fn maybe_html_escape(do_it: bool, text: String) -> String { @@ -530,7 +531,12 @@ impl DisplayFormat for DisplayNode<'_> { _ => caps[0].to_string(), }; let fmt_str = caps.name("fmtstr").map_or("{}", |m| m.as_str()); - maybe_html_escape(html_escape, rtfmt::format(fmt_str, &value)) + let ellipsis = caps.name("ellipsis").is_some(); + + maybe_html_escape( + html_escape, + rtfmt::format(fmt_str, &value, ellipsis), + ) }) .into() } @@ -561,3 +567,21 @@ impl DisplayFormat for DisplayNode<'_> { } } } + +#[test] +fn test_placeholder_rx() { + let caps = PLACEHOLDER_RX.captures("Hello, {place}!").unwrap(); + assert_eq!(caps.name("name").unwrap().as_str(), "place"); + assert_eq!(caps.name("fmtstr"), None); + assert_eq!(caps.name("ellipsis"), None); + + let caps = PLACEHOLDER_RX.captures("Hi, {place:{:>10.10}}!").unwrap(); + assert_eq!(caps.name("name").unwrap().as_str(), "place"); + assert_eq!(caps.name("fmtstr").unwrap().as_str(), "{:>10.10}"); + assert_eq!(caps.name("ellipsis"), None); + + let caps = PLACEHOLDER_RX.captures("Hello, {place:{:.5}…}!").unwrap(); + assert_eq!(caps.name("name").unwrap().as_str(), "place"); + assert_eq!(caps.name("fmtstr").unwrap().as_str(), "{:.5}"); + assert_eq!(caps.name("ellipsis").unwrap().as_str(), "…"); +}