mirror of
https://github.com/clap-rs/clap
synced 2024-11-10 14:54:15 +00:00
refactor(help): Track style via ANSI codes
This commit is contained in:
parent
1040114162
commit
bdbfe6470f
8 changed files with 84 additions and 184 deletions
|
@ -4280,7 +4280,7 @@ impl Arg {
|
|||
styled.literal("-");
|
||||
styled.literal(s);
|
||||
}
|
||||
styled.extend(self.stylize_arg_suffix(required).into_iter());
|
||||
styled.push_styled(&self.stylize_arg_suffix(required));
|
||||
styled
|
||||
}
|
||||
|
||||
|
|
|
@ -2,27 +2,13 @@
|
|||
///
|
||||
/// For now, this is the same as a [`Str`][crate::builder::Str]. This exists to reserve space in
|
||||
/// the API for exposing terminal styling.
|
||||
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
||||
pub struct StyledStr {
|
||||
#[cfg(feature = "color")]
|
||||
pieces: Vec<(Option<Style>, String)>,
|
||||
#[cfg(not(feature = "color"))]
|
||||
pieces: String,
|
||||
}
|
||||
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct StyledStr(String);
|
||||
|
||||
impl StyledStr {
|
||||
/// Create an empty buffer
|
||||
#[cfg(feature = "color")]
|
||||
pub const fn new() -> Self {
|
||||
Self { pieces: Vec::new() }
|
||||
}
|
||||
|
||||
/// Create an empty buffer
|
||||
#[cfg(not(feature = "color"))]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
pieces: String::new(),
|
||||
}
|
||||
Self(String::new())
|
||||
}
|
||||
|
||||
/// Display using [ANSI Escape Code](https://en.wikipedia.org/wiki/ANSI_escape_code) styling
|
||||
|
@ -66,43 +52,22 @@ impl StyledStr {
|
|||
self.stylize_(None, msg.into());
|
||||
}
|
||||
|
||||
pub(crate) fn stylize(&mut self, style: impl Into<Option<Style>>, msg: impl Into<String>) {
|
||||
self.stylize_(style.into(), msg.into());
|
||||
}
|
||||
|
||||
pub(crate) fn trim(&mut self) {
|
||||
self.trim_start();
|
||||
self.trim_end();
|
||||
self.0 = self.0.trim().to_owned()
|
||||
}
|
||||
|
||||
pub(crate) fn trim_start(&mut self) {
|
||||
if let Some((_, item)) = self.iter_mut().next() {
|
||||
*item = item.trim_start().to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
pub(crate) fn trim_end(&mut self) {
|
||||
if let Some((_, item)) = self.pieces.last_mut() {
|
||||
*item = item.trim_end().to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "color"))]
|
||||
pub(crate) fn trim_end(&mut self) {
|
||||
self.pieces = self.pieces.trim_end().to_owned();
|
||||
#[cfg(feature = "help")]
|
||||
pub(crate) fn replace_newline_var(&mut self) {
|
||||
self.0 = self.0.replace("{n}", "\n");
|
||||
}
|
||||
|
||||
#[cfg(feature = "help")]
|
||||
pub(crate) fn indent(&mut self, initial: &str, trailing: &str) {
|
||||
if let Some((_, first)) = self.iter_mut().next() {
|
||||
first.insert_str(0, initial);
|
||||
}
|
||||
self.0.insert_str(0, initial);
|
||||
|
||||
let mut line_sep = "\n".to_owned();
|
||||
line_sep.push_str(trailing);
|
||||
for (_, content) in self.iter_mut() {
|
||||
*content = content.replace('\n', &line_sep);
|
||||
}
|
||||
self.0 = self.0.replace('\n', &line_sep);
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "wrap_help"), feature = "help"))]
|
||||
|
@ -110,42 +75,57 @@ impl StyledStr {
|
|||
|
||||
#[cfg(feature = "wrap_help")]
|
||||
pub(crate) fn wrap(&mut self, hard_width: usize) {
|
||||
let mut new = String::with_capacity(self.0.len());
|
||||
|
||||
let mut last = 0;
|
||||
let mut wrapper = crate::output::textwrap::wrap_algorithms::LineWrapper::new(hard_width);
|
||||
for (_, content) in self.iter_mut() {
|
||||
let mut total = Vec::new();
|
||||
for content in self.iter_text() {
|
||||
// Preserve styling
|
||||
let current = content.as_ptr() as usize - self.0.as_str().as_ptr() as usize;
|
||||
if last != current {
|
||||
new.push_str(&self.0.as_str()[last..current]);
|
||||
}
|
||||
last = current + content.len();
|
||||
|
||||
for (i, line) in content.split_inclusive('\n').enumerate() {
|
||||
if 0 < i {
|
||||
// start of a section does not imply newline
|
||||
// reset char count on newline, skipping the start as we might have carried
|
||||
// over from a prior block of styled text
|
||||
wrapper.reset();
|
||||
}
|
||||
let line = crate::output::textwrap::word_separators::find_words_ascii_space(line)
|
||||
.collect::<Vec<_>>();
|
||||
total.extend(wrapper.wrap(line));
|
||||
new.extend(wrapper.wrap(line));
|
||||
}
|
||||
let total = total.join("");
|
||||
*content = total;
|
||||
}
|
||||
if last != self.0.len() {
|
||||
new.push_str(&self.0.as_str()[last..]);
|
||||
}
|
||||
new = new.trim_end().to_owned();
|
||||
|
||||
self.trim_end();
|
||||
self.0 = new;
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
fn stylize_(&mut self, style: Option<Style>, msg: String) {
|
||||
if !msg.is_empty() {
|
||||
self.pieces.push((style, msg));
|
||||
use std::fmt::Write as _;
|
||||
|
||||
let style = style.map(|s| s.as_style()).unwrap_or_default();
|
||||
let _ = write!(self.0, "{}{}{}", style.render(), msg, style.render_reset());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "color"))]
|
||||
fn stylize_(&mut self, _style: Option<Style>, msg: String) {
|
||||
self.pieces.push_str(&msg);
|
||||
self.0.push_str(&msg);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[cfg(feature = "help")]
|
||||
pub(crate) fn display_width(&self) -> usize {
|
||||
let mut width = 0;
|
||||
for (_, c) in self.iter() {
|
||||
for c in self.iter_text() {
|
||||
width += crate::output::display_width(c);
|
||||
}
|
||||
width
|
||||
|
@ -153,56 +133,30 @@ impl StyledStr {
|
|||
|
||||
#[cfg(feature = "help")]
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.pieces.is_empty()
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
#[cfg(feature = "help")]
|
||||
pub(crate) fn as_styled_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = (Option<Style>, &str)> {
|
||||
self.pieces.iter().map(|(s, c)| (*s, c.as_str()))
|
||||
pub(crate) fn iter_text(&self) -> impl Iterator<Item = &str> {
|
||||
anstream::adapter::strip_str(&self.0)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "color"))]
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = (Option<Style>, &str)> {
|
||||
[(None, self.pieces.as_str())].into_iter()
|
||||
pub(crate) fn iter_text(&self) -> impl Iterator<Item = &str> {
|
||||
[self.0.as_str()].into_iter()
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = (Option<Style>, &mut String)> {
|
||||
self.pieces.iter_mut().map(|(s, c)| (*s, c))
|
||||
pub(crate) fn push_styled(&mut self, other: &Self) {
|
||||
self.0.push_str(&other.0);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "color"))]
|
||||
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = (Option<Style>, &mut String)> {
|
||||
[(None, &mut self.pieces)].into_iter()
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Option<Style>, String)> {
|
||||
self.pieces.into_iter()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "color"))]
|
||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Option<Style>, String)> {
|
||||
[(None, self.pieces)].into_iter()
|
||||
}
|
||||
|
||||
pub(crate) fn extend(
|
||||
&mut self,
|
||||
other: impl IntoIterator<Item = (impl Into<Option<Style>>, impl Into<String>)>,
|
||||
) {
|
||||
for (style, content) in other {
|
||||
self.stylize(style.into(), content.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
pub(crate) fn write_colored(&self, buffer: &mut dyn std::io::Write) -> std::io::Result<()> {
|
||||
for (style, content) in &self.pieces {
|
||||
let style = style.map(|s| s.as_style()).unwrap_or_default();
|
||||
ok!(style.write_to(buffer));
|
||||
ok!(buffer.write_all(content.as_bytes()));
|
||||
ok!(style.write_reset_to(buffer));
|
||||
}
|
||||
pub(crate) fn write_to(&self, buffer: &mut dyn std::io::Write) -> std::io::Result<()> {
|
||||
ok!(buffer.write_all(self.0.as_bytes()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -245,29 +199,11 @@ impl From<&'_ &'static str> for StyledStr {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for StyledStr {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for StyledStr {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.iter().map(cmp_key).cmp(other.iter().map(cmp_key))
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_key(c: (Option<Style>, &str)) -> (Option<usize>, &str) {
|
||||
let style = c.0.map(|s| s.as_usize());
|
||||
let content = c.1;
|
||||
(style, content)
|
||||
}
|
||||
|
||||
/// Color-unaware printing. Never uses coloring.
|
||||
impl std::fmt::Display for StyledStr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for (_, content) in self.iter() {
|
||||
ok!(std::fmt::Display::fmt(content, f));
|
||||
for part in self.iter_text() {
|
||||
part.fmt(f)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -282,14 +218,7 @@ struct AnsiDisplay<'s> {
|
|||
#[cfg(feature = "color")]
|
||||
impl std::fmt::Display for AnsiDisplay<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for (style, content) in &self.styled.pieces {
|
||||
let style = style.map(|s| s.as_style()).unwrap_or_default();
|
||||
ok!(style.render().fmt(f));
|
||||
ok!(content.fmt(f));
|
||||
ok!(style.render_reset().fmt(f));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
self.styled.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,18 +234,6 @@ pub(crate) enum Style {
|
|||
}
|
||||
|
||||
impl Style {
|
||||
fn as_usize(&self) -> usize {
|
||||
match self {
|
||||
Self::Header => 0,
|
||||
Self::Literal => 1,
|
||||
Self::Placeholder => 2,
|
||||
Self::Good => 3,
|
||||
Self::Warning => 4,
|
||||
Self::Error => 5,
|
||||
Self::Hint => 6,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
fn as_style(&self) -> anstyle::Style {
|
||||
match self {
|
||||
|
|
|
@ -100,13 +100,13 @@ impl ErrorFormatter for RichFormatter {
|
|||
styled.none("\n");
|
||||
styled.none(TAB);
|
||||
styled.good("note: ");
|
||||
styled.extend(suggestion.iter());
|
||||
styled.push_styled(suggestion);
|
||||
}
|
||||
}
|
||||
|
||||
let usage = error.get(ContextKind::Usage);
|
||||
if let Some(ContextValue::StyledStr(usage)) = usage {
|
||||
put_usage(&mut styled, usage.clone());
|
||||
put_usage(&mut styled, usage);
|
||||
}
|
||||
|
||||
try_help(&mut styled, error.inner.help_flag);
|
||||
|
@ -377,7 +377,7 @@ fn write_dynamic_context(error: &crate::error::Error, styled: &mut StyledStr) ->
|
|||
pub(crate) fn format_error_message(
|
||||
message: &str,
|
||||
cmd: Option<&Command>,
|
||||
usage: Option<StyledStr>,
|
||||
usage: Option<&StyledStr>,
|
||||
) -> StyledStr {
|
||||
let mut styled = StyledStr::new();
|
||||
start_error(&mut styled);
|
||||
|
@ -400,9 +400,9 @@ fn singular_or_plural(n: usize) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
fn put_usage(styled: &mut StyledStr, usage: StyledStr) {
|
||||
fn put_usage(styled: &mut StyledStr, usage: &StyledStr) {
|
||||
styled.none("\n\n");
|
||||
styled.extend(usage.into_iter());
|
||||
styled.push_styled(usage);
|
||||
}
|
||||
|
||||
pub(crate) fn get_help_flag(cmd: &Command) -> Option<&'static str> {
|
||||
|
|
|
@ -806,7 +806,7 @@ impl Message {
|
|||
let mut message = String::new();
|
||||
std::mem::swap(s, &mut message);
|
||||
|
||||
let styled = format::format_error_message(&message, Some(cmd), usage);
|
||||
let styled = format::format_error_message(&message, Some(cmd), usage.as_ref());
|
||||
|
||||
*self = Self::Formatted(styled);
|
||||
}
|
||||
|
|
|
@ -53,25 +53,23 @@ impl Colorizer {
|
|||
}
|
||||
};
|
||||
|
||||
self.content.write_colored(writer)
|
||||
self.content.write_to(writer)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "color"))]
|
||||
pub(crate) fn print(&self) -> std::io::Result<()> {
|
||||
use std::io::Write;
|
||||
|
||||
// [e]println can't be used here because it panics
|
||||
// if something went wrong. We don't want that.
|
||||
match self.stream {
|
||||
Stream::Stdout => {
|
||||
let stdout = std::io::stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
write!(stdout, "{}", self)
|
||||
self.content.write_to(&mut stdout)
|
||||
}
|
||||
Stream::Stderr => {
|
||||
let stderr = std::io::stderr();
|
||||
let mut stderr = stderr.lock();
|
||||
write!(stderr, "{}", self)
|
||||
self.content.write_to(&mut stderr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,21 +10,15 @@ pub(crate) fn write_help(writer: &mut StyledStr, cmd: &Command, usage: &Usage<'_
|
|||
debug!("write_help");
|
||||
|
||||
if let Some(h) = cmd.get_override_help() {
|
||||
writer.extend(h.iter());
|
||||
writer.push_styled(h);
|
||||
} else {
|
||||
#[cfg(feature = "help")]
|
||||
{
|
||||
use super::AutoHelp;
|
||||
use super::HelpTemplate;
|
||||
if let Some(tmpl) = cmd.get_help_template() {
|
||||
for (style, content) in tmpl.iter() {
|
||||
if style.is_none() {
|
||||
HelpTemplate::new(writer, cmd, usage, use_long)
|
||||
.write_templated_help(content);
|
||||
} else {
|
||||
writer.stylize(style, content);
|
||||
}
|
||||
}
|
||||
HelpTemplate::new(writer, cmd, usage, use_long)
|
||||
.write_templated_help(tmpl.as_styled_str());
|
||||
} else {
|
||||
AutoHelp::new(writer, cmd, usage, use_long).write_help();
|
||||
}
|
||||
|
|
|
@ -166,11 +166,8 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
|
|||
self.header("Usage:");
|
||||
}
|
||||
"usage" => {
|
||||
self.writer.extend(
|
||||
self.usage
|
||||
.create_usage_no_title(&[])
|
||||
.unwrap_or_default()
|
||||
.into_iter(),
|
||||
self.writer.push_styled(
|
||||
&self.usage.create_usage_no_title(&[]).unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
"all-args" => {
|
||||
|
@ -284,9 +281,9 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
|
|||
self.none("\n");
|
||||
}
|
||||
let mut output = output.clone();
|
||||
replace_newline_var(&mut output);
|
||||
output.replace_newline_var();
|
||||
output.wrap(self.term_w);
|
||||
self.writer.extend(output.into_iter());
|
||||
self.writer.push_styled(&output);
|
||||
if after_new_line {
|
||||
self.none("\n");
|
||||
}
|
||||
|
@ -304,9 +301,9 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
|
|||
};
|
||||
if let Some(output) = before_help {
|
||||
let mut output = output.clone();
|
||||
replace_newline_var(&mut output);
|
||||
output.replace_newline_var();
|
||||
output.wrap(self.term_w);
|
||||
self.writer.extend(output.into_iter());
|
||||
self.writer.push_styled(&output);
|
||||
self.none("\n\n");
|
||||
}
|
||||
}
|
||||
|
@ -323,9 +320,9 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
|
|||
if let Some(output) = after_help {
|
||||
self.none("\n\n");
|
||||
let mut output = output.clone();
|
||||
replace_newline_var(&mut output);
|
||||
output.replace_newline_var();
|
||||
output.wrap(self.term_w);
|
||||
self.writer.extend(output.into_iter());
|
||||
self.writer.push_styled(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -470,7 +467,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
|
|||
self.none(TAB);
|
||||
self.short(arg);
|
||||
self.long(arg);
|
||||
self.writer.extend(arg.stylize_arg_suffix(None).into_iter());
|
||||
self.writer.push_styled(&arg.stylize_arg_suffix(None));
|
||||
self.align_to_about(arg, next_line_help, longest);
|
||||
|
||||
let about = if self.use_long {
|
||||
|
@ -580,7 +577,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
|
|||
let trailing_indent = self.get_spaces(trailing_indent);
|
||||
|
||||
let mut help = about.clone();
|
||||
replace_newline_var(&mut help);
|
||||
help.replace_newline_var();
|
||||
if !spec_vals.is_empty() {
|
||||
if !help.is_empty() {
|
||||
let sep = if self.use_long && arg.is_some() {
|
||||
|
@ -602,7 +599,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
|
|||
help.wrap(avail_chars);
|
||||
help.indent("", &trailing_indent);
|
||||
let help_is_empty = help.is_empty();
|
||||
self.writer.extend(help.into_iter());
|
||||
self.writer.push_styled(&help);
|
||||
if let Some(arg) = arg {
|
||||
const DASH_SPACE: usize = "- ".len();
|
||||
const COLON_SPACE: usize = ": ".len();
|
||||
|
@ -668,10 +665,10 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
|
|||
};
|
||||
|
||||
let mut help = help.clone();
|
||||
replace_newline_var(&mut help);
|
||||
help.replace_newline_var();
|
||||
help.wrap(avail_chars);
|
||||
help.indent("", &trailing_indent);
|
||||
self.writer.extend(help.into_iter());
|
||||
self.writer.push_styled(&help);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -948,7 +945,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
|
|||
let width = sc_str.display_width();
|
||||
|
||||
self.none(TAB);
|
||||
self.writer.extend(sc_str.into_iter());
|
||||
self.writer.push_styled(&sc_str);
|
||||
if !next_line_help {
|
||||
self.spaces(longest + TAB_WIDTH - width);
|
||||
}
|
||||
|
@ -1021,12 +1018,6 @@ fn should_show_subcommand(subcommand: &Command) -> bool {
|
|||
!subcommand.is_hide_set()
|
||||
}
|
||||
|
||||
fn replace_newline_var(styled: &mut StyledStr) {
|
||||
for (_, content) in styled.iter_mut() {
|
||||
*content = content.replace("{n}", "\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn longest_filter(arg: &Arg) -> bool {
|
||||
arg.is_takes_value_set() || arg.get_long().is_some() || arg.get_short().is_none()
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ impl<'cmd> Usage<'cmd> {
|
|||
let mut styled = StyledStr::new();
|
||||
styled.header("Usage:");
|
||||
styled.none(" ");
|
||||
styled.extend(usage.into_iter());
|
||||
styled.push_styled(&usage);
|
||||
Some(styled)
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ impl<'cmd> Usage<'cmd> {
|
|||
// Short-circuit full usage creation since no args will be relevant
|
||||
styled.literal(name);
|
||||
} else {
|
||||
styled.extend(self.create_help_usage(false).into_iter());
|
||||
styled.push_styled(&self.create_help_usage(false));
|
||||
}
|
||||
styled.placeholder(" <");
|
||||
styled.placeholder(placeholder);
|
||||
|
@ -190,7 +190,7 @@ impl<'cmd> Usage<'cmd> {
|
|||
pub(crate) fn write_args(&self, incls: &[Id], force_optional: bool, styled: &mut StyledStr) {
|
||||
for required in self.get_args(incls, force_optional) {
|
||||
styled.none(" ");
|
||||
styled.extend(required.into_iter());
|
||||
styled.push_styled(&required);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,7 +280,7 @@ impl<'cmd> Usage<'cmd> {
|
|||
let styled = required_positionals[index].take().unwrap();
|
||||
let mut new = StyledStr::new();
|
||||
new.literal("-- ");
|
||||
new.extend(styled.into_iter());
|
||||
new.push_styled(&styled);
|
||||
required_positionals[index] = Some(new);
|
||||
}
|
||||
} else {
|
||||
|
@ -288,7 +288,7 @@ impl<'cmd> Usage<'cmd> {
|
|||
if pos.is_last_set() {
|
||||
styled = StyledStr::new();
|
||||
styled.literal("[-- ");
|
||||
styled.extend(pos.stylized(Some(true)).into_iter());
|
||||
styled.push_styled(&pos.stylized(Some(true)));
|
||||
styled.literal("]");
|
||||
} else {
|
||||
styled = pos.stylized(Some(false));
|
||||
|
|
Loading…
Reference in a new issue