Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cfb7841
implement checks for tail calls
WaffleLapkin May 11, 2023
3208b86
use `expect(incomplete_feature)` instead of `allow` in tail call tests
WaffleLapkin Nov 29, 2024
d93ea6b
simplify things by using `tcx.fn_trait_kind_from_def_id`
WaffleLapkin Nov 29, 2024
ef5808a
add a fixme for tailcalls with opaque types
WaffleLapkin Nov 29, 2024
c6454dd
don't polymorphize without a reason to
WaffleLapkin Nov 29, 2024
144d6cc
simplify things using `tcx.as_lang_item`
WaffleLapkin Nov 29, 2024
b81391e
CI: use free runners for i686-gnu jobs
marcoieni Dec 3, 2024
7338710
Fix typo
Kobzol Dec 3, 2024
f676ed2
Fix copy-pasted tool name
Kobzol Dec 3, 2024
b24d608
Replace `black` with `ruff` for formatting Python code
Kobzol Dec 3, 2024
d5eb93b
Remove dependency on `black`
Kobzol Dec 3, 2024
7cc6f4d
CI: rfl: move job forward to Linux v6.13-rc1
ojeda Dec 3, 2024
0b737a1
Exclude additional subtrees in `ruff` config
Kobzol Dec 4, 2024
536516f
Reformat Python code with `ruff`
Kobzol Dec 4, 2024
62c7ce4
Avoid fetching the anon const hir node that is already available
oli-obk Dec 5, 2024
9aa4be0
Normalize target-cpus.rs stdout test for LLVM changes
TimNN Dec 5, 2024
8f0ea9a
Adapt codegen tests for NUW inference
TimNN Dec 5, 2024
fad5f51
Always display first line of impl blocks even when collapsed
GuillaumeGomez Oct 25, 2024
32e6826
Update browser-ui-test version to 0.18.2
GuillaumeGomez Oct 25, 2024
5d26acc
Add GUI test for impl block doc display
GuillaumeGomez Oct 25, 2024
abcd094
Update GUI tests
GuillaumeGomez Oct 25, 2024
6e0dabd
Turn `markdown_split_summary_and_content` into a method of `Markdown`
GuillaumeGomez Oct 28, 2024
448d9ad
Use text ellipsis instead of bottom blurring
GuillaumeGomez Oct 29, 2024
90feb9a
Improve positioning of "..." in collapsed impl block
GuillaumeGomez Nov 19, 2024
854ebe7
Update GUI test after rebase
GuillaumeGomez Dec 2, 2024
a424813
Rollup merge of #132155 - GuillaumeGomez:impl-block-doc, r=rustdoc
GuillaumeGomez Dec 5, 2024
7416c33
Rollup merge of #133256 - MarcoIeni:use-linux-free-runners, r=Kobzol
GuillaumeGomez Dec 5, 2024
e941e73
Rollup merge of #133607 - WaffleLapkin:tail-call-checks, r=compiler-e…
GuillaumeGomez Dec 5, 2024
5a9c9ef
Rollup merge of #133821 - Kobzol:replace-black-with-ruff, r=onur-ozkan
GuillaumeGomez Dec 5, 2024
9765a4e
Rollup merge of #133827 - ojeda:ci-rfl, r=lqd
GuillaumeGomez Dec 5, 2024
e82ee96
Rollup merge of #133910 - TimNN:llvm-target-cpus, r=jieyouxu
GuillaumeGomez Dec 5, 2024
beb9b24
Rollup merge of #133921 - TimNN:nuw-infer, r=nikic
GuillaumeGomez Dec 5, 2024
5dc05a8
Rollup merge of #133936 - oli-obk:push-qmvqsmwqrtqr, r=lqd
GuillaumeGomez Dec 5, 2024
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
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.18.1
0.18.2
96 changes: 76 additions & 20 deletions src/librustdoc/html/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ use std::iter::Peekable;
use std::ops::{ControlFlow, Range};
use std::path::PathBuf;
use std::str::{self, CharIndices};
use std::sync::atomic::AtomicUsize;
use std::sync::{Arc, Weak};

use pulldown_cmark::{
BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
Expand Down Expand Up @@ -1301,8 +1303,20 @@ impl LangString {
}
}

impl Markdown<'_> {
impl<'a> Markdown<'a> {
pub fn into_string(self) -> String {
// This is actually common enough to special-case
if self.content.is_empty() {
return String::new();
}

let mut s = String::with_capacity(self.content.len() * 3 / 2);
html::push_html(&mut s, self.into_iter());

s
}

fn into_iter(self) -> CodeBlocks<'a, 'a, impl Iterator<Item = Event<'a>>> {
let Markdown {
content: md,
links,
Expand All @@ -1313,32 +1327,72 @@ impl Markdown<'_> {
heading_offset,
} = self;

// This is actually common enough to special-case
if md.is_empty() {
return String::new();
}
let mut replacer = |broken_link: BrokenLink<'_>| {
let replacer = move |broken_link: BrokenLink<'_>| {
links
.iter()
.find(|link| *link.original_text == *broken_link.reference)
.map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
};

let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut replacer));
let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(replacer));
let p = p.into_offset_iter();

let mut s = String::with_capacity(md.len() * 3 / 2);

ids.handle_footnotes(|ids, existing_footnotes| {
let p = HeadingLinks::new(p, None, ids, heading_offset);
let p = footnotes::Footnotes::new(p, existing_footnotes);
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
let p = TableWrapper::new(p);
let p = CodeBlocks::new(p, codes, edition, playground);
html::push_html(&mut s, p);
});
CodeBlocks::new(p, codes, edition, playground)
})
}

s
/// Convert markdown to (summary, remaining) HTML.
///
/// - The summary is the first top-level Markdown element (usually a paragraph, but potentially
/// any block).
/// - The remaining docs contain everything after the summary.
pub(crate) fn split_summary_and_content(self) -> (Option<String>, Option<String>) {
if self.content.is_empty() {
return (None, None);
}
let mut p = self.into_iter();

let mut event_level = 0;
let mut summary_events = Vec::new();
let mut get_next_tag = false;

let mut end_of_summary = false;
while let Some(event) = p.next() {
match event {
Event::Start(_) => event_level += 1,
Event::End(kind) => {
event_level -= 1;
if event_level == 0 {
// We're back at the "top" so it means we're done with the summary.
end_of_summary = true;
// We surround tables with `<div>` HTML tags so this is a special case.
get_next_tag = kind == TagEnd::Table;
}
}
_ => {}
}
summary_events.push(event);
if end_of_summary {
if get_next_tag && let Some(event) = p.next() {
summary_events.push(event);
}
break;
}
}
let mut summary = String::new();
html::push_html(&mut summary, summary_events.into_iter());
if summary.is_empty() {
return (None, None);
}
let mut content = String::new();
html::push_html(&mut content, p);

if content.is_empty() { (Some(summary), None) } else { (Some(summary), Some(content)) }
}
}

Expand Down Expand Up @@ -1882,7 +1936,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
#[derive(Clone, Default, Debug)]
pub struct IdMap {
map: FxHashMap<String, usize>,
existing_footnotes: usize,
existing_footnotes: Arc<AtomicUsize>,
}

fn is_default_id(id: &str) -> bool {
Expand Down Expand Up @@ -1942,7 +1996,7 @@ fn is_default_id(id: &str) -> bool {

impl IdMap {
pub fn new() -> Self {
IdMap { map: FxHashMap::default(), existing_footnotes: 0 }
IdMap { map: FxHashMap::default(), existing_footnotes: Arc::new(AtomicUsize::new(0)) }
}

pub(crate) fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
Expand Down Expand Up @@ -1970,15 +2024,17 @@ impl IdMap {

/// Method to handle `existing_footnotes` increment automatically (to prevent forgetting
/// about it).
pub(crate) fn handle_footnotes<F: FnOnce(&mut Self, &mut usize)>(&mut self, closure: F) {
let mut existing_footnotes = self.existing_footnotes;
pub(crate) fn handle_footnotes<'a, T, F: FnOnce(&'a mut Self, Weak<AtomicUsize>) -> T>(
&'a mut self,
closure: F,
) -> T {
let existing_footnotes = Arc::downgrade(&self.existing_footnotes);

closure(self, &mut existing_footnotes);
self.existing_footnotes = existing_footnotes;
closure(self, existing_footnotes)
}

pub(crate) fn clear(&mut self) {
self.map.clear();
self.existing_footnotes = 0;
self.existing_footnotes = Arc::new(AtomicUsize::new(0));
}
}
25 changes: 16 additions & 9 deletions src/librustdoc/html/markdown/footnotes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Markdown footnote handling.

use std::fmt::Write as _;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Weak};

use pulldown_cmark::{CowStr, Event, Tag, TagEnd, html};
use rustc_data_structures::fx::FxIndexMap;
Expand All @@ -8,10 +11,11 @@ use super::SpannedEvent;

/// Moves all footnote definitions to the end and add back links to the
/// references.
pub(super) struct Footnotes<'a, 'b, I> {
pub(super) struct Footnotes<'a, I> {
inner: I,
footnotes: FxIndexMap<String, FootnoteDef<'a>>,
existing_footnotes: &'b mut usize,
existing_footnotes: Arc<AtomicUsize>,
start_id: usize,
}

/// The definition of a single footnote.
Expand All @@ -21,13 +25,16 @@ struct FootnoteDef<'a> {
id: usize,
}

impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> {
pub(super) fn new(iter: I, existing_footnotes: &'b mut usize) -> Self {
Footnotes { inner: iter, footnotes: FxIndexMap::default(), existing_footnotes }
impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, I> {
pub(super) fn new(iter: I, existing_footnotes: Weak<AtomicUsize>) -> Self {
let existing_footnotes =
existing_footnotes.upgrade().expect("`existing_footnotes` was dropped");
let start_id = existing_footnotes.load(Ordering::Relaxed);
Footnotes { inner: iter, footnotes: FxIndexMap::default(), existing_footnotes, start_id }
}

fn get_entry(&mut self, key: &str) -> (&mut Vec<Event<'a>>, usize) {
let new_id = self.footnotes.len() + 1 + *self.existing_footnotes;
let new_id = self.footnotes.len() + 1 + self.start_id;
let key = key.to_owned();
let FootnoteDef { content, id } =
self.footnotes.entry(key).or_insert(FootnoteDef { content: Vec::new(), id: new_id });
Expand All @@ -44,7 +51,7 @@ impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> {
id,
// Although the ID count is for the whole page, the footnote reference
// are local to the item so we make this ID "local" when displayed.
id - *self.existing_footnotes
id - self.start_id
);
Event::Html(reference.into())
}
Expand All @@ -64,7 +71,7 @@ impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Footnotes<'a, 'b, I> {
}
}

impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, '_, I> {
impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
type Item = SpannedEvent<'a>;

fn next(&mut self) -> Option<Self::Item> {
Expand All @@ -87,7 +94,7 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, '_, I>
// After all the markdown is emmited, emit an <hr> then all the footnotes
// in a list.
let defs: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
*self.existing_footnotes += defs.len();
self.existing_footnotes.fetch_add(defs.len(), Ordering::Relaxed);
let defs_html = render_footnotes_defs(defs);
return Some((Event::Html(defs_html.into()), 0..0));
} else {
Expand Down
45 changes: 28 additions & 17 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1904,7 +1904,6 @@ fn render_impl(
}
}

let trait_is_none = trait_.is_none();
// If we've implemented a trait, then also emit documentation for all
// default items which weren't overridden in the implementation block.
// We don't emit documentation for default items if they appear in the
Expand Down Expand Up @@ -1936,6 +1935,23 @@ fn render_impl(
if rendering_params.toggle_open_by_default { " open" } else { "" }
);
}

let (before_dox, after_dox) = i
.impl_item
.opt_doc_value()
.map(|dox| {
Markdown {
content: &*dox,
links: &i.impl_item.links(cx),
ids: &mut cx.id_map.borrow_mut(),
error_codes: cx.shared.codes,
edition: cx.shared.edition(),
playground: &cx.shared.playground,
heading_offset: HeadingOffset::H4,
}
.split_summary_and_content()
})
.unwrap_or((None, None));
render_impl_summary(
w,
cx,
Expand All @@ -1944,33 +1960,23 @@ fn render_impl(
rendering_params.show_def_docs,
use_absolute,
aliases,
&before_dox,
);
if toggled {
w.write_str("</summary>");
}

if let Some(ref dox) = i.impl_item.opt_doc_value() {
if trait_is_none && impl_.items.is_empty() {
if before_dox.is_some() {
if trait_.is_none() && impl_.items.is_empty() {
w.write_str(
"<div class=\"item-info\">\
<div class=\"stab empty-impl\">This impl block contains no items.</div>\
</div>",
);
}
write!(
w,
"<div class=\"docblock\">{}</div>",
Markdown {
content: dox,
links: &i.impl_item.links(cx),
ids: &mut cx.id_map.borrow_mut(),
error_codes: cx.shared.codes,
edition: cx.shared.edition(),
playground: &cx.shared.playground,
heading_offset: HeadingOffset::H4,
}
.into_string()
);
if let Some(after_dox) = after_dox {
write!(w, "<div class=\"docblock\">{after_dox}</div>");
}
}
if !default_impl_items.is_empty() || !impl_items.is_empty() {
w.write_str("<div class=\"impl-items\">");
Expand Down Expand Up @@ -2031,6 +2037,7 @@ pub(crate) fn render_impl_summary(
// This argument is used to reference same type with different paths to avoid duplication
// in documentation pages for trait with automatic implementations like "Send" and "Sync".
aliases: &[String],
doc: &Option<String>,
) {
let inner_impl = i.inner_impl();
let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
Expand Down Expand Up @@ -2082,6 +2089,10 @@ pub(crate) fn render_impl_summary(
);
}

if let Some(doc) = doc {
write!(w, "<div class=\"docblock\">{doc}</div>");
}

w.write_str("</section>");
}

Expand Down
33 changes: 33 additions & 0 deletions src/librustdoc/html/static/css/rustdoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -2210,6 +2210,39 @@ details.toggle[open] > summary::after {
content: "Collapse";
}

details.toggle:not([open]) > summary .docblock {
max-height: calc(1.5em + 0.75em);
overflow-y: hidden;
}
details.toggle:not([open]) > summary .docblock > :first-child {
max-width: 100%;
overflow: hidden;
width: fit-content;
white-space: nowrap;
position: relative;
padding-right: 1em;
}
details.toggle:not([open]) > summary .docblock > :first-child::after {
content: "…";
position: absolute;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
background-color: var(--main-background-color);
font: 1rem/1.5 "Source Serif 4", NanumBarunGothic, serif;
/* To make it look a bit better and not have it stuck to the preceding element. */
padding-left: 0.2em;
}
details.toggle:not([open]) > summary .docblock > div:first-child::after {
/* This is to make the "..." always appear at the bottom. */
padding-top: calc(1.5em + 0.75em - 1.2rem);
}

details.toggle > summary .docblock {
margin-top: 0.75em;
}

/* This is needed in docblocks to have the "▶" element to be on the same line. */
.docblock summary > * {
display: inline-block;
Expand Down
10 changes: 3 additions & 7 deletions tests/rustdoc-gui/docblock-table-overflow.goml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@ assert-property: (".top-doc .docblock table", {"scrollWidth": "1572"})

// Checking it works on other doc blocks as well...

// Logically, the ".docblock" and the "<p>" should have the same scroll width.
compare-elements-property: (
"#implementations-list > details .docblock",
"#implementations-list > details .docblock > p",
["scrollWidth"],
)
assert-property: ("#implementations-list > details .docblock", {"scrollWidth": "835"})
// Logically, the ".docblock" and the "<p>" should have the same scroll width (if we exclude the margin).
assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 816})
assert-property: ("#implementations-list > details .docblock > p", {"scrollWidth": 835})
// However, since there is overflow in the <table>, its scroll width is bigger.
assert-property: ("#implementations-list > details .docblock table", {"scrollWidth": "1572"})
Loading