Allow to use custom "ellipsis" string

timeout_old
Jakub Jirutka 3 years ago
parent befb2aa7d3
commit 7a57f6e541
  1. 17
      NEWS.md
  2. 8
      README.md
  3. 31
      src/rtfmt.rs
  4. 18
      src/tree.rs

@ -4,14 +4,15 @@ swayr v0.13.0
- The placeholders `{id}`, `{app_name}`, `{name}`/`{title}`, `{output_name}`, - The placeholders `{id}`, `{app_name}`, `{name}`/`{title}`, `{output_name}`,
`{workspace_name}`, and `{marks}` may optionally provide a format string as `{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 specified by [Rust's std::fmt](https://doc.rust-lang.org/std/fmt/). The
syntax is `{<placeholder>:<fmt_str>}` or `{<placeholder>:<fmt_str>…}`. For syntax is `{<placeholder>:<fmt_str><clipped_str>}`. For example,
example, `{app_name:{:>10.10}}` would mean that the application name is `{app_name:{:>10.10}}` would mean that the application name is printed with
printed with exactly 10 characters. If it's shorter, it will be exactly 10 characters. If it's shorter, it will be right-aligned (the `>`)
right-aligned (the `>`) and padded with spaces, if it's longer, it'll be cut and padded with spaces, if it's longer, it'll be cut after the 10th
after the 10th character. Another example, `{app_name:{:.10}…}` would mean character. Another example, `{app_name:{:.10}...}` would mean that the
that the application name is truncated at 10 characters. If it's shorter, it application name is truncated at 10 characters. If it's shorter, it will be
will be printed as-is (no padding), if it's longer, it'll be cut after the printed as-is (no padding), if it's longer, it'll be cut after the 10th
9th character and the ellipsis (…) character will be added after it. character and the last 3 characters of that substring will be replaced with
`...` (`<clipped_str>`).
swayr v0.12.0 swayr v0.12.0
============= =============

@ -327,14 +327,14 @@ right now.
The placeholders `{id}`, `{app_name}`, `{name}`/`{title}`, `{output_name}`, The placeholders `{id}`, `{app_name}`, `{name}`/`{title}`, `{output_name}`,
`{workspace_name}`, and `{marks}` may optionally provide a format string as `{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 specified by [Rust's std::fmt](https://doc.rust-lang.org/std/fmt/). The syntax
is `{<placeholder>:<fmt_str>}` or `{<placeholder>:<fmt_str>…}`. For example, is `{<placeholder>:<fmt_str><clipped_str>}`. For example,
`{app_name:{:>10.10}}` would mean that the application name is printed with `{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 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. 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 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 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 padding), if it's longer, it'll be cut after the 10th character and the last
(…) character will be added after it. 3 characters of that substring will be replaced with `...` (`<clipped_str>`).
It is crucial that during selection (using wofi or some other menu program) It is crucial that during selection (using wofi or some other menu program)
each window has a different display string. Therefore, it is highly each window has a different display string. Therefore, it is highly

@ -73,15 +73,15 @@ impl<'a> std::convert::TryInto<usize> for &FmtArg<'a> {
} }
} }
pub fn format(fmt: &str, arg: &str, ellipsis: bool) -> String { pub fn format(fmt: &str, arg: &str, clipped_str: &str) -> String {
let args = &[FmtArg::Str(arg)]; let args = &[FmtArg::Str(arg)];
if let Ok(pf) = ParsedFormat::parse(fmt, args, &NoNamedArguments) { if let Ok(pf) = ParsedFormat::parse(fmt, args, &NoNamedArguments) {
let mut s = format!("{}", pf); let mut s = format!("{}", pf);
if ellipsis && !s.contains(arg) { if !clipped_str.is_empty() && !s.contains(arg) {
s.pop(); remove_last_n_chars(&mut s, clipped_str.chars().count());
s.push('…'); s.push_str(clipped_str);
} }
s s
} else { } else {
@ -89,13 +89,22 @@ pub fn format(fmt: &str, arg: &str, ellipsis: bool) -> String {
} }
} }
fn remove_last_n_chars(s: &mut String, n: usize) -> () {
match s.char_indices().nth_back(n) {
Some((pos, ch)) => s.truncate(pos + ch.len_utf8()),
None => s.clear(),
}
}
#[test] #[test]
fn test_format() { fn test_format() {
assert_eq!(format("{:.10}", "sway", false), "sway"); assert_eq!(format("{:.10}", "sway", ""), "sway");
assert_eq!(format("{:.10}", "sway", true), "sway"); assert_eq!(format("{:.10}", "sway", "…"), "sway");
assert_eq!(format("{:.4}", "𝔰𝔴𝔞𝔶", true), "𝔰𝔴𝔞𝔶"); assert_eq!(format("{:.4}", "𝔰𝔴𝔞𝔶", "……"), "𝔰𝔴𝔞𝔶");
assert_eq!(format("{:.3}", "sway", false), "swa"); assert_eq!(format("{:.3}", "sway", ""), "swa");
assert_eq!(format("{:.3}", "sway", true), "sw…"); assert_eq!(format("{:.3}", "sway", "…"), "sw…");
assert_eq!(format("{:.3}", "𝔰𝔴𝔞𝔶", true), "𝔰𝔴…"); assert_eq!(format("{:.5}", "𝔰𝔴𝔞𝔶 𝔴𝔦𝔫𝔡𝔬𝔴", "…?"), "𝔰𝔴𝔞…?");
assert_eq!(format("{:.5}", "sway window", "..."), "sw...");
assert_eq!(format("{:.2}", "sway", "..."), "...");
} }

@ -429,7 +429,7 @@ lazy_static! {
static ref APP_NAME_AND_VERSION_RX: regex::Regex = static ref APP_NAME_AND_VERSION_RX: regex::Regex =
regex::Regex::new("(.+)(-[0-9.]+)").unwrap(); regex::Regex::new("(.+)(-[0-9.]+)").unwrap();
static ref PLACEHOLDER_RX: regex::Regex = regex::Regex::new( static ref PLACEHOLDER_RX: regex::Regex = regex::Regex::new(
r"\{(?P<name>[^}:]+)(?::(?P<fmtstr>\{[^}]*\})(?P<ellipsis>…)?)?\}" r"\{(?P<name>[^}:]+)(?::(?P<fmtstr>\{[^}]*\})(?P<clipstr>[^}]*))?\}"
) )
.unwrap(); .unwrap();
} }
@ -531,11 +531,12 @@ impl DisplayFormat for DisplayNode<'_> {
_ => caps[0].to_string(), _ => caps[0].to_string(),
}; };
let fmt_str = caps.name("fmtstr").map_or("{}", |m| m.as_str()); let fmt_str = caps.name("fmtstr").map_or("{}", |m| m.as_str());
let ellipsis = caps.name("ellipsis").is_some(); let clipped_str =
caps.name("clipstr").map_or("", |m| m.as_str());
maybe_html_escape( maybe_html_escape(
html_escape, html_escape,
rtfmt::format(fmt_str, &value, ellipsis), rtfmt::format(fmt_str, &value, &clipped_str),
) )
}) })
.into() .into()
@ -573,15 +574,20 @@ fn test_placeholder_rx() {
let caps = PLACEHOLDER_RX.captures("Hello, {place}!").unwrap(); let caps = PLACEHOLDER_RX.captures("Hello, {place}!").unwrap();
assert_eq!(caps.name("name").unwrap().as_str(), "place"); assert_eq!(caps.name("name").unwrap().as_str(), "place");
assert_eq!(caps.name("fmtstr"), None); assert_eq!(caps.name("fmtstr"), None);
assert_eq!(caps.name("ellipsis"), None); assert_eq!(caps.name("clipstr"), None);
let caps = PLACEHOLDER_RX.captures("Hi, {place:{:>10.10}}!").unwrap(); let caps = PLACEHOLDER_RX.captures("Hi, {place:{:>10.10}}!").unwrap();
assert_eq!(caps.name("name").unwrap().as_str(), "place"); assert_eq!(caps.name("name").unwrap().as_str(), "place");
assert_eq!(caps.name("fmtstr").unwrap().as_str(), "{:>10.10}"); assert_eq!(caps.name("fmtstr").unwrap().as_str(), "{:>10.10}");
assert_eq!(caps.name("ellipsis"), None); assert_eq!(caps.name("clipstr").unwrap().as_str(), "");
let caps = PLACEHOLDER_RX.captures("Hello, {place:{:.5}…}!").unwrap(); let caps = PLACEHOLDER_RX.captures("Hello, {place:{:.5}…}!").unwrap();
assert_eq!(caps.name("name").unwrap().as_str(), "place"); assert_eq!(caps.name("name").unwrap().as_str(), "place");
assert_eq!(caps.name("fmtstr").unwrap().as_str(), "{:.5}"); assert_eq!(caps.name("fmtstr").unwrap().as_str(), "{:.5}");
assert_eq!(caps.name("ellipsis").unwrap().as_str(), "…"); assert_eq!(caps.name("clipstr").unwrap().as_str(), "…");
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("clipstr").unwrap().as_str(), "...");
} }

Loading…
Cancel
Save