Allow placeholders to specify their own format string

timeout_old
Tassilo Horn 3 years ago
parent 784df6582e
commit 053becd176
  1. 11
      Cargo.lock
  2. 1
      Cargo.toml
  3. 12
      NEWS.md
  4. 12
      README.md
  5. 2
      src/bin/swayr.rs
  6. 2
      src/bin/swayrd.rs
  7. 2
      src/client.rs
  8. 2
      src/cmds.rs
  9. 2
      src/config.rs
  10. 2
      src/demon.rs
  11. 2
      src/layout.rs
  12. 3
      src/lib.rs
  13. 83
      src/rtfmt.rs
  14. 51
      src/tree.rs
  15. 2
      src/util.rs

11
Cargo.lock generated

@ -289,6 +289,16 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rt-format"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93323b19fb32e6df2d1d3d9e86ae0f3ad72ec0d4c23f6ef7d3fcb898d888b0b0"
dependencies = [
"lazy_static",
"regex",
]
[[package]]
name = "ryu"
version = "1.0.9"
@ -363,6 +373,7 @@ dependencies = [
"lazy_static",
"rand",
"regex",
"rt-format",
"serde",
"serde_json",
"swayipc",

@ -19,3 +19,4 @@ directories = "4.0"
regex = "1.5.4"
lazy_static = "1.4.0"
rand = "0.8.4"
rt-format = "0.2.0"

@ -1,11 +1,13 @@
swayr v0.13.0
=============
- The placeholders `{app_name}`, `{name}`, `{output_name}`, and
`{workspace_name}` allow to specify the maximum string length using format
`{<name>:<len>}` (e.g. `{app_name:10}`). If the string is longer than the
specified length, it will be truncated and an ellipsis ("…") will be inserted
at the end.
- 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>}`. 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.
swayr v0.12.0
=============

@ -324,11 +324,13 @@ right now.
* `fallback_icon` is a path to some PNG/SVG icon which will be used as
`{app_icon}` if no application-specific icon can be determined.
The placeholders `{app_name}`, `{name}`, `{output_name}`, and
`{workspace_name}` allow to specify the maximum string length using format
`{<name>:<len>}` (e.g. `{app_name:10}`). If the string is longer than the
specified length, it will be truncated and an ellipsis ("…") will be inserted
at the end.
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>}`. 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.
It is crucial that during selection (using wofi or some other menu program)
each window has a different display string. Therefore, it is highly

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
@ -28,5 +28,6 @@ pub mod cmds;
pub mod config;
pub mod demon;
pub mod layout;
pub mod rtfmt;
pub mod tree;
pub mod util;

@ -0,0 +1,83 @@
// Copyright (C) 2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.
//! Provides runtime formatting of strings since our format strings are read
//! from the swayr config, not specified as literals, e.g., `{name:{:.30}}` in
//! `format.window_format`.
use rt_format::{
Format, FormatArgument, NoNamedArguments, ParsedFormat, Specifier,
};
use std::fmt;
enum FmtArg<'a> {
Str(&'a str),
}
impl<'a> FormatArgument for FmtArg<'a> {
fn supports_format(&self, spec: &Specifier) -> bool {
spec.format == Format::Display
}
fn fmt_display(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Str(val) => fmt::Display::fmt(&val, f),
}
}
fn fmt_debug(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Err(fmt::Error)
}
fn fmt_octal(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Err(fmt::Error)
}
fn fmt_lower_hex(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Err(fmt::Error)
}
fn fmt_upper_hex(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Err(fmt::Error)
}
fn fmt_binary(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Err(fmt::Error)
}
fn fmt_lower_exp(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Err(fmt::Error)
}
fn fmt_upper_exp(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Err(fmt::Error)
}
}
impl<'a> std::convert::TryInto<usize> for &FmtArg<'a> {
type Error = ();
fn try_into(self) -> Result<usize, Self::Error> {
Err(())
}
}
pub fn format(fmt: &str, arg: &str) -> String {
let args = &[FmtArg::Str(arg)];
if let Ok(pf) = ParsedFormat::parse(fmt, args, &NoNamedArguments) {
format!("{}", pf)
} else {
format!("Invalid format string: {}", fmt)
}
}

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
@ -16,6 +16,7 @@
//! Convenience data structures built from the IPC structs.
use crate::config;
use crate::rtfmt;
use crate::util;
use crate::util::DisplayFormat;
use lazy_static::lazy_static;
@ -427,20 +428,18 @@ pub fn get_tree<'a>(
lazy_static! {
static ref APP_NAME_AND_VERSION_RX: regex::Regex =
regex::Regex::new("(.+)(-[0-9.]+)").unwrap();
}
lazy_static! {
static ref PLACEHOLDER_RX: regex::Regex =
regex::Regex::new(r"\{(?P<name>[^}:]+)(?::(?P<width>\d+))?\}").unwrap();
regex::Regex::new(r"\{(?P<name>[^}:]+)(?::(?P<fmtstr>\{[^}]*\}))?\}")
.unwrap();
}
fn maybe_html_escape(do_it: bool, text: &str) -> String {
fn maybe_html_escape(do_it: bool, text: String) -> String {
if do_it {
text.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("&", "&amp;")
} else {
text.to_string()
text
}
}
@ -477,14 +476,6 @@ impl DisplayFormat for DisplayNode<'_> {
};
let fmt = fmt
.replace("{indent}", &indent.repeat(self.get_indent_level()))
.replace("{id}", format!("{}", self.node.id).as_str())
.replace(
"{marks}",
&maybe_html_escape(
html_escape,
&format_marks(&self.node.marks),
),
)
.replace(
"{urgency_start}",
if self.node.urgent {
@ -522,32 +513,24 @@ impl DisplayFormat for DisplayNode<'_> {
PLACEHOLDER_RX
.replace_all(&fmt, |caps: &regex::Captures| {
let value = match &caps["name"] {
"app_name" => self.node.get_app_name(),
"name" | "title" => self.node.get_name(),
"id" => self.node.id.to_string(),
"app_name" => self.node.get_app_name().to_string(),
"name" | "title" => self.node.get_name().to_string(),
"output_name" => self
.tree
.get_parent_node_of_type(self.node.id, Type::Output)
.map_or("<no_output>", |w| w.get_name()),
.map_or("<no_output>", |w| w.get_name())
.to_string(),
"workspace_name" => self
.tree
.get_parent_node_of_type(self.node.id, Type::Workspace)
.map_or("<no_workspace>", |w| w.get_name()),
_ => &caps[0],
.map_or("<no_workspace>", |w| w.get_name())
.to_string(),
"marks" => format_marks(&self.node.marks),
_ => caps[0].to_string(),
};
let width = caps
.name("width")
.map_or("0", |m| m.as_str())
.parse::<usize>()
.unwrap();
if width > 0 && value.len() > width {
maybe_html_escape(
html_escape,
&format!("{}…", &value[..width - 1]),
)
} else {
maybe_html_escape(html_escape, value)
}
let fmt_str = caps.name("fmtstr").map_or("{}", |m| m.as_str());
maybe_html_escape(html_escape, rtfmt::format(fmt_str, &value))
})
.into()
}

@ -1,4 +1,4 @@
// Copyright (C) 2021 Tassilo Horn <tsdh@gnu.org>
// Copyright (C) 2021-2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free

Loading…
Cancel
Save