Allow to use custom "ellipsis" string

timeout_old
Jakub Jirutka 2 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}`,
`{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 `{<placeholder>:<fmt_str>}` or `{<placeholder>:<fmt_str>…}`. 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.
syntax is `{<placeholder>:<fmt_str><clipped_str>}`. 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 10th
character and the last 3 characters of that substring will be replaced with
`...` (`<clipped_str>`).
swayr v0.12.0
=============

@ -327,14 +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 `{<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
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
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.
padding), if it's longer, it'll be cut after the 10th character and the last
3 characters of that substring will be replaced with `...` (`<clipped_str>`).
It is crucial that during selection (using wofi or some other menu program)
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)];
if let Ok(pf) = ParsedFormat::parse(fmt, args, &NoNamedArguments) {
let mut s = format!("{}", pf);
if ellipsis && !s.contains(arg) {
s.pop();
s.push('…');
if !clipped_str.is_empty() && !s.contains(arg) {
remove_last_n_chars(&mut s, clipped_str.chars().count());
s.push_str(clipped_str);
}
s
} 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]
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), "𝔰𝔴…");
assert_eq!(format("{:.10}", "sway", ""), "sway");
assert_eq!(format("{:.10}", "sway", "…"), "sway");
assert_eq!(format("{:.4}", "𝔰𝔴𝔞𝔶", "……"), "𝔰𝔴𝔞𝔶");
assert_eq!(format("{:.3}", "sway", ""), "swa");
assert_eq!(format("{:.3}", "sway", "…"), "sw…");
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 =
regex::Regex::new("(.+)(-[0-9.]+)").unwrap();
static ref PLACEHOLDER_RX: regex::Regex = regex::Regex::new(
r"\{(?P<name>[^}:]+)(?::(?P<fmtstr>\{[^}]*\})(?P<ellipsis>…)?)?\}"
r"\{(?P<name>[^}:]+)(?::(?P<fmtstr>\{[^}]*\})(?P<clipstr>[^}]*))?\}"
)
.unwrap();
}
@ -531,11 +531,12 @@ impl DisplayFormat for DisplayNode<'_> {
_ => caps[0].to_string(),
};
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(
html_escape,
rtfmt::format(fmt_str, &value, ellipsis),
rtfmt::format(fmt_str, &value, &clipped_str),
)
})
.into()
@ -573,15 +574,20 @@ 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);
assert_eq!(caps.name("clipstr"), 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);
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("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