Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 30 additions & 48 deletions src/librustdoc/clean/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
// FIXME: Once the portability lint RFC is implemented (see tracking issue #41619),
// switch to use those structures instead.

use std::fmt::{self, Write};
use std::{mem, ops};
use std::{fmt, mem, ops};

use itertools::Either;
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
use rustc_data_structures::fx::FxHashSet;
use rustc_session::parse::ParseSess;
use rustc_span::Span;
use rustc_span::symbol::{Symbol, sym};

use crate::display::Joined as _;
use crate::display::{Joined as _, MaybeDisplay, Wrapped};
use crate::html::escape::Escape;

#[cfg(test)]
Expand Down Expand Up @@ -376,27 +376,20 @@ impl Format {
Format::LongPlain => false,
}
}

fn escape(self, s: &str) -> impl fmt::Display {
if self.is_html() { Either::Left(Escape(s)) } else { Either::Right(s) }
}
}

/// Pretty-print wrapper for a `Cfg`. Also indicates what form of rendering should be used.
struct Display<'a>(&'a Cfg, Format);

fn write_with_opt_paren<T: fmt::Display>(
fmt: &mut fmt::Formatter<'_>,
has_paren: bool,
obj: T,
) -> fmt::Result {
if has_paren {
fmt.write_char('(')?;
}
obj.fmt(fmt)?;
if has_paren {
fmt.write_char(')')?;
impl Display<'_> {
fn code_wrappers(&self) -> Wrapped<&'static str> {
if self.1.is_html() { Wrapped::with("<code>", "</code>") } else { Wrapped::with("`", "`") }
}
Ok(())
}

impl Display<'_> {
fn display_sub_cfgs(
&self,
fmt: &mut fmt::Formatter<'_>,
Expand Down Expand Up @@ -427,20 +420,17 @@ impl Display<'_> {
sub_cfgs
.iter()
.map(|sub_cfg| {
fmt::from_fn(move |fmt| {
if let Cfg::Cfg(_, Some(feat)) = sub_cfg
&& short_longhand
{
if self.1.is_html() {
write!(fmt, "<code>{feat}</code>")?;
} else {
write!(fmt, "`{feat}`")?;
}
} else {
write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
}
Ok(())
})
if let Cfg::Cfg(_, Some(feat)) = sub_cfg
&& short_longhand
{
Either::Left(self.code_wrappers().wrap(feat))
} else {
Either::Right(
Wrapped::with_parens()
.when(!sub_cfg.is_all())
.wrap(Display(sub_cfg, self.1)),
)
}
})
.joined(separator, f)
})
Expand All @@ -461,9 +451,9 @@ impl fmt::Display for Display<'_> {
sub_cfgs
.iter()
.map(|sub_cfg| {
fmt::from_fn(|fmt| {
write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))
})
Wrapped::with_parens()
.when(!sub_cfg.is_all())
.wrap(Display(sub_cfg, self.1))
})
.joined(separator, fmt)
}
Expand Down Expand Up @@ -568,21 +558,13 @@ impl fmt::Display for Display<'_> {
};
if !human_readable.is_empty() {
fmt.write_str(human_readable)
} else if let Some(v) = value {
if self.1.is_html() {
write!(
fmt,
r#"<code>{}="{}"</code>"#,
Escape(name.as_str()),
Escape(v.as_str())
)
} else {
write!(fmt, r#"`{name}="{v}"`"#)
}
} else if self.1.is_html() {
write!(fmt, "<code>{}</code>", Escape(name.as_str()))
} else {
write!(fmt, "`{name}`")
let value = value
.map(|v| fmt::from_fn(move |f| write!(f, "={}", self.1.escape(v.as_str()))))
.maybe_display();
self.code_wrappers()
.wrap(format_args!("{}{value}", self.1.escape(name.as_str())))
.fmt(fmt)
}
}
}
Expand Down
86 changes: 85 additions & 1 deletion src/librustdoc/display.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Various utilities for working with [`fmt::Display`] implementations.

use std::fmt::{self, Display, Formatter};
use std::fmt::{self, Display, Formatter, FormattingOptions};

pub(crate) trait Joined: IntoIterator {
/// Takes an iterator over elements that implement [`Display`], and format them into `f`, separated by `sep`.
Expand Down Expand Up @@ -45,3 +45,87 @@ impl<T: Display> MaybeDisplay for Option<T> {
})
}
}

#[derive(Clone, Copy)]
pub(crate) struct Wrapped<T> {
prefix: T,
suffix: T,
}

pub(crate) enum AngleBracket {
Open,
Close,
}

impl Display for AngleBracket {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match (self, f.alternate()) {
(Self::Open, true) => "<",
(Self::Open, false) => "&lt;",
(Self::Close, true) => ">",
(Self::Close, false) => "&gt;",
})
}
}

impl Wrapped<AngleBracket> {
pub(crate) fn with_angle_brackets() -> Self {
Self { prefix: AngleBracket::Open, suffix: AngleBracket::Close }
}
}

impl Wrapped<char> {
pub(crate) fn with_parens() -> Self {
Self { prefix: '(', suffix: ')' }
}

pub(crate) fn with_square_brackets() -> Self {
Self { prefix: '[', suffix: ']' }
}
}

impl<T: Display> Wrapped<T> {
pub(crate) fn with(prefix: T, suffix: T) -> Self {
Self { prefix, suffix }
}

pub(crate) fn when(self, if_: bool) -> Wrapped<impl Display> {
Wrapped {
prefix: if_.then_some(self.prefix).maybe_display(),
suffix: if_.then_some(self.suffix).maybe_display(),
}
}

pub(crate) fn wrap_fn(
self,
content: impl Fn(&mut Formatter<'_>) -> fmt::Result,
) -> impl Display {
fmt::from_fn(move |f| {
self.prefix.fmt(f)?;
content(f)?;
self.suffix.fmt(f)
})
}

pub(crate) fn wrap<C: Display>(self, content: C) -> impl Display {
self.wrap_fn(move |f| content.fmt(f))
}
}

#[derive(Clone, Copy)]
pub(crate) struct WithOpts {
opts: FormattingOptions,
}

impl WithOpts {
pub(crate) fn from(f: &Formatter<'_>) -> Self {
Self { opts: f.options() }
}

pub(crate) fn display(self, t: impl Display) -> impl Display {
fmt::from_fn(move |f| {
let mut f = f.with_options(self.opts);
t.fmt(&mut f)
})
}
}
Loading
Loading