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