]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/html/render/context.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / src / librustdoc / html / render / context.rs
CommitLineData
6a06907d
XL
1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::io;
cdc7bbd5 4use std::path::{Path, PathBuf};
6a06907d 5use std::rc::Rc;
cdc7bbd5 6use std::sync::mpsc::{channel, Receiver};
6a06907d 7
cdc7bbd5 8use rustc_data_structures::fx::{FxHashMap, FxHashSet};
3c0e092e 9use rustc_hir::def_id::{DefId, LOCAL_CRATE};
6a06907d
XL
10use rustc_middle::ty::TyCtxt;
11use rustc_session::Session;
12use rustc_span::edition::Edition;
13use rustc_span::source_map::FileName;
5099ac24 14use rustc_span::{sym, Symbol};
6a06907d 15
6a06907d 16use super::print_item::{full_path, item_path, print_item};
a2a8927a 17use super::search_index::build_index;
6a06907d 18use super::write_shared::write_shared;
94222f64 19use super::{
04454e1e
FG
20 collect_spans_and_sources, print_sidebar, scrape_examples_help, AllTypes, LinkFromSrc, NameDoc,
21 StylePath, BASIC_KEYWORDS,
94222f64 22};
6a06907d 23
a2a8927a 24use crate::clean::{self, types::ExternalLocation, ExternalCrate};
064997fb 25use crate::config::{ModuleSorting, RenderOptions};
6a06907d
XL
26use crate::docfs::{DocFS, PathError};
27use crate::error::Error;
28use crate::formats::cache::Cache;
29use crate::formats::item_type::ItemType;
30use crate::formats::FormatRenderer;
31use crate::html::escape::Escape;
5099ac24 32use crate::html::format::{join_with_double_colon, Buffer};
6a06907d
XL
33use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
34use crate::html::{layout, sources};
3c0e092e
XL
35use crate::scrape_examples::AllCallLocations;
36use crate::try_err;
6a06907d
XL
37
38/// Major driving force in all rustdoc rendering. This contains information
39/// about where in the tree-like hierarchy rendering is occurring and controls
40/// how the current page is being rendered.
41///
42/// It is intended that this context is a lightweight object which can be fairly
43/// easily cloned because it is cloned per work-job (about once per item in the
44/// rustdoc tree).
923072b8 45pub(crate) struct Context<'tcx> {
6a06907d
XL
46 /// Current hierarchy of components leading down to what's currently being
47 /// rendered
5099ac24 48 pub(crate) current: Vec<Symbol>,
6a06907d
XL
49 /// The current destination folder of where HTML artifacts should be placed.
50 /// This changes as the context descends into the module hierarchy.
923072b8 51 pub(crate) dst: PathBuf,
6a06907d
XL
52 /// A flag, which when `true`, will render pages which redirect to the
53 /// real location of an item. This is used to allow external links to
54 /// publicly reused items to redirect to the right location.
55 pub(super) render_redirect_pages: bool,
3c0e092e
XL
56 /// Tracks section IDs for `Deref` targets so they match in both the main
57 /// body and the sidebar.
923072b8 58 pub(super) deref_id_map: FxHashMap<DefId, String>,
6a06907d 59 /// The map used to ensure all generated 'id=' attributes are unique.
923072b8 60 pub(super) id_map: IdMap,
6a06907d
XL
61 /// Shared mutable state.
62 ///
63 /// Issue for improving the situation: [#82381][]
64 ///
65 /// [#82381]: https://github.com/rust-lang/rust/issues/82381
923072b8 66 pub(crate) shared: Rc<SharedContext<'tcx>>,
5099ac24 67 /// This flag indicates whether source links should be generated or not. If
94222f64
XL
68 /// the source files are present in the html rendering, then this will be
69 /// `true`.
923072b8 70 pub(crate) include_sources: bool,
6a06907d
XL
71}
72
73// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
c295e0f8 74#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
923072b8 75rustc_data_structures::static_assert_size!(Context<'_>, 128);
6a06907d 76
cdc7bbd5 77/// Shared mutable state used in [`Context`] and elsewhere.
923072b8
FG
78pub(crate) struct SharedContext<'tcx> {
79 pub(crate) tcx: TyCtxt<'tcx>,
cdc7bbd5
XL
80 /// The path to the crate root source minus the file name.
81 /// Used for simplifying paths to the highlighted source code files.
923072b8 82 pub(crate) src_root: PathBuf,
cdc7bbd5
XL
83 /// This describes the layout of each page, and is not modified after
84 /// creation of the context (contains info like the favicon and added html).
923072b8 85 pub(crate) layout: layout::Layout,
cdc7bbd5 86 /// The local file sources we've emitted and their respective url-paths.
923072b8 87 pub(crate) local_sources: FxHashMap<PathBuf, String>,
17df50a5
XL
88 /// Show the memory layout of types in the docs.
89 pub(super) show_type_layout: bool,
cdc7bbd5
XL
90 /// The base-URL of the issue tracker for when an item has been tagged with
91 /// an issue number.
92 pub(super) issue_tracker_base_url: Option<String>,
93 /// The directories that have already been created in this doc run. Used to reduce the number
94 /// of spurious `create_dir_all` calls.
95 created_dirs: RefCell<FxHashSet<PathBuf>>,
96 /// This flag indicates whether listings of modules (in the side bar and documentation itself)
97 /// should be ordered alphabetically or in order of appearance (in the source code).
064997fb 98 pub(super) module_sorting: ModuleSorting,
cdc7bbd5 99 /// Additional CSS files to be added to the generated docs.
923072b8 100 pub(crate) style_files: Vec<StylePath>,
cdc7bbd5
XL
101 /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes
102 /// "light-v2.css").
923072b8 103 pub(crate) resource_suffix: String,
cdc7bbd5
XL
104 /// Optional path string to be used to load static files on output pages. If not set, uses
105 /// combinations of `../` to reach the documentation root.
923072b8 106 pub(crate) static_root_path: Option<String>,
cdc7bbd5 107 /// The fs handle we are working with.
923072b8 108 pub(crate) fs: DocFS,
cdc7bbd5
XL
109 pub(super) codes: ErrorCodes,
110 pub(super) playground: Option<markdown::Playground>,
111 all: RefCell<AllTypes>,
112 /// Storage for the errors produced while generating documentation so they
113 /// can be printed together at the end.
114 errors: Receiver<String>,
115 /// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set
116 /// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of
117 /// the crate.
118 redirections: Option<RefCell<FxHashMap<String, String>>>,
136023e0 119
94222f64
XL
120 /// Correspondance map used to link types used in the source code pages to allow to click on
121 /// links to jump to the type's definition.
923072b8 122 pub(crate) span_correspondance_map: FxHashMap<rustc_span::Span, LinkFromSrc>,
94222f64 123 /// The [`Cache`] used during rendering.
923072b8 124 pub(crate) cache: Cache,
3c0e092e 125
923072b8 126 pub(crate) call_locations: AllCallLocations,
cdc7bbd5
XL
127}
128
129impl SharedContext<'_> {
923072b8 130 pub(crate) fn ensure_dir(&self, dst: &Path) -> Result<(), Error> {
cdc7bbd5
XL
131 let mut dirs = self.created_dirs.borrow_mut();
132 if !dirs.contains(dst) {
133 try_err!(self.fs.create_dir_all(dst), dst);
134 dirs.insert(dst.to_path_buf());
135 }
136
137 Ok(())
138 }
139
923072b8 140 pub(crate) fn edition(&self) -> Edition {
cdc7bbd5
XL
141 self.tcx.sess.edition()
142 }
143}
144
145impl<'tcx> Context<'tcx> {
146 pub(crate) fn tcx(&self) -> TyCtxt<'tcx> {
6a06907d
XL
147 self.shared.tcx
148 }
149
cdc7bbd5 150 pub(crate) fn cache(&self) -> &Cache {
94222f64 151 &self.shared.cache
cdc7bbd5
XL
152 }
153
17df50a5 154 pub(super) fn sess(&self) -> &'tcx Session {
3c0e092e 155 self.shared.tcx.sess
6a06907d
XL
156 }
157
923072b8
FG
158 pub(super) fn derive_id(&mut self, id: String) -> String {
159 self.id_map.derive(id)
6a06907d
XL
160 }
161
162 /// String representation of how to get back to the root path of the 'doc/'
163 /// folder in terms of a relative URL.
164 pub(super) fn root_path(&self) -> String {
165 "../".repeat(self.current.len())
166 }
167
923072b8 168 fn render_item(&mut self, it: &clean::Item, is_module: bool) -> String {
cdc7bbd5
XL
169 let mut title = String::new();
170 if !is_module {
a2a8927a 171 title.push_str(it.name.unwrap().as_str());
6a06907d 172 }
cdc7bbd5
XL
173 if !it.is_primitive() && !it.is_keyword() {
174 if !is_module {
175 title.push_str(" in ");
176 }
177 // No need to include the namespace for primitive types and keywords
5099ac24 178 title.push_str(&join_with_double_colon(&self.current));
cdc7bbd5 179 };
6a06907d
XL
180 title.push_str(" - Rust");
181 let tyname = it.type_();
3c0e092e 182 let desc = it.doc_value().as_ref().map(|doc| plain_text_summary(doc));
6a06907d
XL
183 let desc = if let Some(desc) = desc {
184 desc
185 } else if it.is_crate() {
186 format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate)
187 } else {
188 format!(
189 "API documentation for the Rust `{}` {} in crate `{}`.",
190 it.name.as_ref().unwrap(),
191 tyname,
192 self.shared.layout.krate
193 )
194 };
195 let keywords = make_item_keywords(it);
17df50a5
XL
196 let name;
197 let tyname_s = if it.is_crate() {
198 name = format!("{} crate", tyname);
199 name.as_str()
200 } else {
201 tyname.as_str()
202 };
6a06907d
XL
203
204 if !self.render_redirect_pages {
923072b8 205 let clone_shared = Rc::clone(&self.shared);
5e7ed085
FG
206 let page = layout::Page {
207 css_class: tyname_s,
208 root_path: &self.root_path(),
923072b8 209 static_root_path: clone_shared.static_root_path.as_deref(),
5e7ed085
FG
210 title: &title,
211 description: &desc,
212 keywords: &keywords,
923072b8 213 resource_suffix: &clone_shared.resource_suffix,
5e7ed085 214 };
923072b8
FG
215 let mut page_buffer = Buffer::html();
216 print_item(self, it, &mut page_buffer, &page);
6a06907d 217 layout::render(
923072b8 218 &clone_shared.layout,
6a06907d
XL
219 &page,
220 |buf: &mut _| print_sidebar(self, it, buf),
923072b8
FG
221 move |buf: &mut Buffer| buf.push_buffer(page_buffer),
222 &clone_shared.style_files,
6a06907d
XL
223 )
224 } else {
04454e1e 225 if let Some(&(ref names, ty)) = self.cache().paths.get(&it.item_id.expect_def_id()) {
5e7ed085
FG
226 if self.current.len() + 1 != names.len()
227 || self.current.iter().zip(names.iter()).any(|(a, b)| a != b)
228 {
229 // We checked that the redirection isn't pointing to the current file,
230 // preventing an infinite redirection loop in the generated
231 // documentation.
232
233 let mut path = String::new();
234 for name in &names[..names.len() - 1] {
923072b8 235 path.push_str(name.as_str());
5e7ed085
FG
236 path.push('/');
237 }
923072b8 238 path.push_str(&item_path(ty, names.last().unwrap().as_str()));
5e7ed085
FG
239 match self.shared.redirections {
240 Some(ref redirections) => {
241 let mut current_path = String::new();
242 for name in &self.current {
923072b8 243 current_path.push_str(name.as_str());
5e7ed085
FG
244 current_path.push('/');
245 }
923072b8 246 current_path.push_str(&item_path(ty, names.last().unwrap().as_str()));
5e7ed085 247 redirections.borrow_mut().insert(current_path, path);
6a06907d 248 }
5e7ed085 249 None => return layout::redirect(&format!("{}{}", self.root_path(), path)),
6a06907d 250 }
6a06907d
XL
251 }
252 }
253 String::new()
254 }
255 }
256
257 /// Construct a map of items shown in the sidebar to a plain-text summary of their docs.
258 fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc>> {
259 // BTreeMap instead of HashMap to get a sorted output
260 let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new();
5e7ed085
FG
261 let mut inserted: FxHashMap<ItemType, FxHashSet<Symbol>> = FxHashMap::default();
262
6a06907d
XL
263 for item in &m.items {
264 if item.is_stripped() {
265 continue;
266 }
267
268 let short = item.type_();
269 let myname = match item.name {
270 None => continue,
5e7ed085 271 Some(s) => s,
6a06907d 272 };
5e7ed085
FG
273 if inserted.entry(short).or_default().insert(myname) {
274 let short = short.to_string();
275 let myname = myname.to_string();
276 map.entry(short).or_default().push((
277 myname,
278 Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))),
279 ));
280 }
6a06907d
XL
281 }
282
064997fb
FG
283 match self.shared.module_sorting {
284 ModuleSorting::Alphabetical => {
285 for items in map.values_mut() {
286 items.sort();
287 }
6a06907d 288 }
064997fb 289 ModuleSorting::DeclarationOrder => {}
6a06907d
XL
290 }
291 map
292 }
293
294 /// Generates a url appropriate for an `href` attribute back to the source of
295 /// this item.
296 ///
297 /// The url generated, when clicked, will redirect the browser back to the
298 /// original source code.
299 ///
300 /// If `None` is returned, then a source link couldn't be generated. This
301 /// may happen, for example, with externally inlined items where the source
302 /// of their crate documentation isn't known.
303 pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> {
3c0e092e 304 self.href_from_span(item.span(self.tcx()), true)
94222f64
XL
305 }
306
923072b8 307 pub(crate) fn href_from_span(&self, span: clean::Span, with_lines: bool) -> Option<String> {
94222f64 308 if span.is_dummy() {
6a06907d
XL
309 return None;
310 }
311 let mut root = self.root_path();
312 let mut path = String::new();
94222f64 313 let cnum = span.cnum(self.sess());
6a06907d
XL
314
315 // We can safely ignore synthetic `SourceFile`s.
94222f64 316 let file = match span.filename(self.sess()) {
17df50a5 317 FileName::Real(ref path) => path.local_path_if_available().to_path_buf(),
6a06907d
XL
318 _ => return None,
319 };
320 let file = &file;
321
a2a8927a 322 let krate_sym;
6a06907d
XL
323 let (krate, path) = if cnum == LOCAL_CRATE {
324 if let Some(path) = self.shared.local_sources.get(file) {
325 (self.shared.layout.krate.as_str(), path)
326 } else {
327 return None;
328 }
329 } else {
94222f64 330 let (krate, src_root) = match *self.cache().extern_locations.get(&cnum)? {
17df50a5
XL
331 ExternalLocation::Local => {
332 let e = ExternalCrate { crate_num: cnum };
333 (e.name(self.tcx()), e.src_root(self.tcx()))
334 }
335 ExternalLocation::Remote(ref s) => {
6a06907d 336 root = s.to_string();
17df50a5
XL
337 let e = ExternalCrate { crate_num: cnum };
338 (e.name(self.tcx()), e.src_root(self.tcx()))
6a06907d 339 }
17df50a5 340 ExternalLocation::Unknown => return None,
6a06907d
XL
341 };
342
343 sources::clean_path(&src_root, file, false, |component| {
344 path.push_str(&component.to_string_lossy());
345 path.push('/');
346 });
347 let mut fname = file.file_name().expect("source has no filename").to_os_string();
348 fname.push(".html");
349 path.push_str(&fname.to_string_lossy());
a2a8927a
XL
350 krate_sym = krate;
351 (krate_sym.as_str(), &path)
6a06907d
XL
352 };
353
3c0e092e
XL
354 let anchor = if with_lines {
355 let loline = span.lo(self.sess()).line;
356 let hiline = span.hi(self.sess()).line;
357 format!(
358 "#{}",
359 if loline == hiline {
360 loline.to_string()
361 } else {
362 format!("{}-{}", loline, hiline)
363 }
364 )
365 } else {
366 "".to_string()
367 };
6a06907d 368 Some(format!(
3c0e092e 369 "{root}src/{krate}/{path}{anchor}",
6a06907d
XL
370 root = Escape(&root),
371 krate = krate,
372 path = path,
3c0e092e 373 anchor = anchor
6a06907d
XL
374 ))
375 }
376}
377
378/// Generates the documentation for `crate` into the directory `dst`
379impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
380 fn descr() -> &'static str {
381 "html"
382 }
383
cdc7bbd5
XL
384 const RUN_ON_MODULE: bool = true;
385
6a06907d 386 fn init(
94222f64 387 krate: clean::Crate,
6a06907d 388 options: RenderOptions,
94222f64 389 cache: Cache,
6a06907d
XL
390 tcx: TyCtxt<'tcx>,
391 ) -> Result<(Self, clean::Crate), Error> {
392 // need to save a copy of the options for rendering the index page
393 let md_opts = options.clone();
cdc7bbd5 394 let emit_crate = options.should_emit_crate();
6a06907d
XL
395 let RenderOptions {
396 output,
397 external_html,
398 id_map,
399 playground_url,
064997fb 400 module_sorting,
6a06907d
XL
401 themes: style_files,
402 default_settings,
403 extension_css,
404 resource_suffix,
405 static_root_path,
6a06907d
XL
406 unstable_features,
407 generate_redirect_map,
17df50a5 408 show_type_layout,
94222f64 409 generate_link_to_definition,
3c0e092e 410 call_locations,
a2a8927a 411 no_emit_shared,
6a06907d
XL
412 ..
413 } = options;
414
3c0e092e 415 let src_root = match krate.src(tcx) {
17df50a5 416 FileName::Real(ref p) => match p.local_path_if_available().parent() {
6a06907d
XL
417 Some(p) => p.to_path_buf(),
418 None => PathBuf::new(),
419 },
420 _ => PathBuf::new(),
421 };
422 // If user passed in `--playground-url` arg, we fill in crate name here
423 let mut playground = None;
424 if let Some(url) = playground_url {
425 playground =
3c0e092e 426 Some(markdown::Playground { crate_name: Some(krate.name(tcx).to_string()), url });
6a06907d
XL
427 }
428 let mut layout = layout::Layout {
429 logo: String::new(),
430 favicon: String::new(),
431 external_html,
432 default_settings,
3c0e092e 433 krate: krate.name(tcx).to_string(),
6a06907d 434 css_file_extension: extension_css,
3c0e092e 435 scrape_examples_extension: !call_locations.is_empty(),
6a06907d
XL
436 };
437 let mut issue_tracker_base_url = None;
438 let mut include_sources = true;
136023e0 439
6a06907d
XL
440 // Crawl the crate attributes looking for attributes which control how we're
441 // going to emit HTML
cdc7bbd5
XL
442 for attr in krate.module.attrs.lists(sym::doc) {
443 match (attr.name_or_empty(), attr.value_str()) {
444 (sym::html_favicon_url, Some(s)) => {
445 layout.favicon = s.to_string();
446 }
447 (sym::html_logo_url, Some(s)) => {
448 layout.logo = s.to_string();
449 }
450 (sym::html_playground_url, Some(s)) => {
451 playground = Some(markdown::Playground {
3c0e092e 452 crate_name: Some(krate.name(tcx).to_string()),
cdc7bbd5
XL
453 url: s.to_string(),
454 });
455 }
456 (sym::issue_tracker_base_url, Some(s)) => {
457 issue_tracker_base_url = Some(s.to_string());
458 }
459 (sym::html_no_source, None) if attr.is_word() => {
460 include_sources = false;
6a06907d 461 }
cdc7bbd5 462 _ => {}
6a06907d
XL
463 }
464 }
94222f64 465
3c0e092e 466 let (local_sources, matches) = collect_spans_and_sources(
94222f64 467 tcx,
3c0e092e 468 &krate,
94222f64
XL
469 &src_root,
470 include_sources,
471 generate_link_to_definition,
472 );
473
6a06907d
XL
474 let (sender, receiver) = channel();
475 let mut scx = SharedContext {
476 tcx,
6a06907d 477 src_root,
94222f64 478 local_sources,
6a06907d
XL
479 issue_tracker_base_url,
480 layout,
481 created_dirs: Default::default(),
064997fb 482 module_sorting,
6a06907d
XL
483 style_files,
484 resource_suffix,
485 static_root_path,
486 fs: DocFS::new(sender),
6a06907d
XL
487 codes: ErrorCodes::from(unstable_features.is_nightly_build()),
488 playground,
489 all: RefCell::new(AllTypes::new()),
490 errors: receiver,
491 redirections: if generate_redirect_map { Some(Default::default()) } else { None },
17df50a5 492 show_type_layout,
94222f64
XL
493 span_correspondance_map: matches,
494 cache,
3c0e092e 495 call_locations,
6a06907d
XL
496 };
497
498 // Add the default themes to the `Vec` of stylepaths
499 //
500 // Note that these must be added before `sources::render` is called
501 // so that the resulting source pages are styled
502 //
503 // `light.css` is not disabled because it is the stylesheet that stays loaded
504 // by the browser as the theme stylesheet. The theme system (hackily) works by
505 // changing the href to this stylesheet. All other themes are disabled to
506 // prevent rule conflicts
a2a8927a
XL
507 scx.style_files.push(StylePath { path: PathBuf::from("light.css") });
508 scx.style_files.push(StylePath { path: PathBuf::from("dark.css") });
509 scx.style_files.push(StylePath { path: PathBuf::from("ayu.css") });
6a06907d
XL
510
511 let dst = output;
512 scx.ensure_dir(&dst)?;
6a06907d
XL
513
514 let mut cx = Context {
515 current: Vec::new(),
516 dst,
517 render_redirect_pages: false,
923072b8
FG
518 id_map,
519 deref_id_map: FxHashMap::default(),
6a06907d 520 shared: Rc::new(scx),
94222f64 521 include_sources,
6a06907d
XL
522 };
523
94222f64 524 if emit_crate {
3c0e092e 525 sources::render(&mut cx, &krate)?;
94222f64
XL
526 }
527
a2a8927a
XL
528 if !no_emit_shared {
529 // Build our search index
530 let index = build_index(&krate, &mut Rc::get_mut(&mut cx.shared).unwrap().cache, tcx);
531
532 // Write shared runs within a flock; disable thread dispatching of IO temporarily.
533 Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
923072b8 534 write_shared(&mut cx, &krate, index, &md_opts)?;
a2a8927a
XL
535 Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
536 }
94222f64 537
6a06907d
XL
538 Ok((cx, krate))
539 }
540
541 fn make_child_renderer(&self) -> Self {
6a06907d
XL
542 Self {
543 current: self.current.clone(),
544 dst: self.dst.clone(),
545 render_redirect_pages: self.render_redirect_pages,
923072b8
FG
546 deref_id_map: FxHashMap::default(),
547 id_map: IdMap::new(),
6a06907d 548 shared: Rc::clone(&self.shared),
94222f64 549 include_sources: self.include_sources,
6a06907d
XL
550 }
551 }
552
cdc7bbd5
XL
553 fn after_krate(&mut self) -> Result<(), Error> {
554 let crate_name = self.tcx().crate_name(LOCAL_CRATE);
a2a8927a 555 let final_file = self.dst.join(crate_name.as_str()).join("all.html");
6a06907d 556 let settings_file = self.dst.join("settings.html");
04454e1e 557 let scrape_examples_help_file = self.dst.join("scrape-examples-help.html");
6a06907d
XL
558
559 let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
560 if !root_path.ends_with('/') {
561 root_path.push('/');
562 }
923072b8 563 let shared = Rc::clone(&self.shared);
6a06907d
XL
564 let mut page = layout::Page {
565 title: "List of all items in this crate",
566 css_class: "mod",
567 root_path: "../",
923072b8 568 static_root_path: shared.static_root_path.as_deref(),
6a06907d
XL
569 description: "List of all items in this crate",
570 keywords: BASIC_KEYWORDS,
923072b8 571 resource_suffix: &shared.resource_suffix,
6a06907d 572 };
923072b8 573 let sidebar = if shared.cache.crate_version.is_some() {
5099ac24 574 format!("<h2 class=\"location\">Crate {}</h2>", crate_name)
6a06907d
XL
575 } else {
576 String::new()
577 };
923072b8 578 let all = shared.all.replace(AllTypes::new());
6a06907d 579 let v = layout::render(
923072b8 580 &shared.layout,
6a06907d
XL
581 &page,
582 sidebar,
583 |buf: &mut Buffer| all.print(buf),
923072b8 584 &shared.style_files,
6a06907d 585 );
923072b8 586 shared.fs.write(final_file, v)?;
6a06907d
XL
587
588 // Generating settings page.
589 page.title = "Rustdoc settings";
590 page.description = "Settings of Rustdoc";
591 page.root_path = "./";
592
17df50a5 593 let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
6a06907d 594 let v = layout::render(
923072b8 595 &shared.layout,
6a06907d
XL
596 &page,
597 sidebar,
04454e1e
FG
598 |buf: &mut Buffer| {
599 write!(
600 buf,
923072b8
FG
601 "<div class=\"main-heading\">\
602 <h1 class=\"fqn\">\
603 <span class=\"in-band\">Rustdoc settings</span>\
604 </h1>\
605 <span class=\"out-of-band\">\
606 <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
607 Back\
608 </a>\
609 </span>\
610 </div>\
611 <noscript>\
612 <section>\
613 You need to enable Javascript be able to update your settings.\
614 </section>\
615 </noscript>\
616 <link rel=\"stylesheet\" type=\"text/css\" \
617 href=\"{root_path}settings{suffix}.css\">\
618 <script defer src=\"{root_path}settings{suffix}.js\"></script>",
04454e1e
FG
619 root_path = page.static_root_path.unwrap_or(""),
620 suffix = page.resource_suffix,
621 )
622 },
923072b8 623 &shared.style_files,
6a06907d 624 );
923072b8 625 shared.fs.write(settings_file, v)?;
04454e1e 626
923072b8 627 if shared.layout.scrape_examples_extension {
04454e1e
FG
628 page.title = "About scraped examples";
629 page.description = "How the scraped examples feature works in Rustdoc";
630 let v = layout::render(
923072b8 631 &shared.layout,
04454e1e
FG
632 &page,
633 "",
923072b8
FG
634 scrape_examples_help(&*shared),
635 &shared.style_files,
04454e1e 636 );
923072b8 637 shared.fs.write(scrape_examples_help_file, v)?;
04454e1e
FG
638 }
639
923072b8 640 if let Some(ref redirections) = shared.redirections {
6a06907d
XL
641 if !redirections.borrow().is_empty() {
642 let redirect_map_path =
a2a8927a 643 self.dst.join(crate_name.as_str()).join("redirect-map.json");
6a06907d 644 let paths = serde_json::to_string(&*redirections.borrow()).unwrap();
923072b8
FG
645 shared.ensure_dir(&self.dst.join(crate_name.as_str()))?;
646 shared.fs.write(redirect_map_path, paths)?;
6a06907d
XL
647 }
648 }
649
923072b8
FG
650 // No need for it anymore.
651 drop(shared);
652
6a06907d
XL
653 // Flush pending errors.
654 Rc::get_mut(&mut self.shared).unwrap().fs.close();
cdc7bbd5
XL
655 let nb_errors =
656 self.shared.errors.iter().map(|err| self.tcx().sess.struct_err(&err).emit()).count();
6a06907d
XL
657 if nb_errors > 0 {
658 Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
659 } else {
660 Ok(())
661 }
662 }
663
cdc7bbd5 664 fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error> {
6a06907d
XL
665 // Stripped modules survive the rustdoc passes (i.e., `strip-private`)
666 // if they contain impls for public types. These modules can also
667 // contain items such as publicly re-exported structures.
668 //
669 // External crates will provide links to these structures, so
670 // these modules are recursed into, but not rendered normally
671 // (a flag on the context).
672 if !self.render_redirect_pages {
673 self.render_redirect_pages = item.is_stripped();
674 }
5099ac24
FG
675 let item_name = item.name.unwrap();
676 self.dst.push(&*item_name.as_str());
cdc7bbd5 677 self.current.push(item_name);
6a06907d
XL
678
679 info!("Recursing into {}", self.dst.display());
680
cdc7bbd5 681 let buf = self.render_item(item, true);
6a06907d
XL
682 // buf will be empty if the module is stripped and there is no redirect for it
683 if !buf.is_empty() {
684 self.shared.ensure_dir(&self.dst)?;
685 let joint_dst = self.dst.join("index.html");
923072b8 686 self.shared.fs.write(joint_dst, buf)?;
6a06907d
XL
687 }
688
689 // Render sidebar-items.js used throughout this module.
690 if !self.render_redirect_pages {
5e7ed085
FG
691 let (clean::StrippedItem(box clean::ModuleItem(ref module)) | clean::ModuleItem(ref module)) = *item.kind
692 else { unreachable!() };
6a06907d 693 let items = self.build_sidebar_items(module);
5099ac24 694 let js_dst = self.dst.join(&format!("sidebar-items{}.js", self.shared.resource_suffix));
923072b8
FG
695 let v = format!("window.SIDEBAR_ITEMS = {};", serde_json::to_string(&items).unwrap());
696 self.shared.fs.write(js_dst, v)?;
6a06907d
XL
697 }
698 Ok(())
699 }
700
cdc7bbd5 701 fn mod_item_out(&mut self) -> Result<(), Error> {
6a06907d
XL
702 info!("Recursed; leaving {}", self.dst.display());
703
704 // Go back to where we were at
705 self.dst.pop();
706 self.current.pop();
707 Ok(())
708 }
709
710 fn item(&mut self, item: clean::Item) -> Result<(), Error> {
711 // Stripped modules survive the rustdoc passes (i.e., `strip-private`)
712 // if they contain impls for public types. These modules can also
713 // contain items such as publicly re-exported structures.
714 //
715 // External crates will provide links to these structures, so
716 // these modules are recursed into, but not rendered normally
717 // (a flag on the context).
718 if !self.render_redirect_pages {
719 self.render_redirect_pages = item.is_stripped();
720 }
721
cdc7bbd5 722 let buf = self.render_item(&item, false);
6a06907d
XL
723 // buf will be empty if the item is stripped and there is no redirect for it
724 if !buf.is_empty() {
725 let name = item.name.as_ref().unwrap();
726 let item_type = item.type_();
a2a8927a 727 let file_name = &item_path(item_type, name.as_str());
6a06907d
XL
728 self.shared.ensure_dir(&self.dst)?;
729 let joint_dst = self.dst.join(file_name);
c295e0f8 730 self.shared.fs.write(joint_dst, buf)?;
6a06907d
XL
731
732 if !self.render_redirect_pages {
733 self.shared.all.borrow_mut().append(full_path(self, &item), &item_type);
734 }
735 // If the item is a macro, redirect from the old macro URL (with !)
736 // to the new one (without).
737 if item_type == ItemType::Macro {
738 let redir_name = format!("{}.{}!.html", item_type, name);
739 if let Some(ref redirections) = self.shared.redirections {
740 let crate_name = &self.shared.layout.krate;
741 redirections.borrow_mut().insert(
742 format!("{}/{}", crate_name, redir_name),
743 format!("{}/{}", crate_name, file_name),
744 );
745 } else {
746 let v = layout::redirect(file_name);
747 let redir_dst = self.dst.join(redir_name);
c295e0f8 748 self.shared.fs.write(redir_dst, v)?;
6a06907d
XL
749 }
750 }
751 }
752 Ok(())
753 }
754
755 fn cache(&self) -> &Cache {
94222f64 756 &self.shared.cache
6a06907d
XL
757 }
758}
759
760fn make_item_keywords(it: &clean::Item) -> String {
761 format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap())
762}