1 // Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Rustdoc's HTML Rendering module
13 //! This modules contains the bulk of the logic necessary for rendering a
14 //! rustdoc `clean::Crate` instance to a set of static HTML pages. This
15 //! rendering process is largely driven by the `format!` syntax extension to
16 //! perform all I/O into files and streams.
18 //! The rendering process is largely driven by the `Context` and `Cache`
19 //! structures. The cache is pre-populated by crawling the crate in question,
20 //! and then it is shared among the various rendering threads. The cache is meant
21 //! to be a fairly large structure not implementing `Clone` (because it's shared
22 //! among threads). The context, however, should be a lightweight structure. This
23 //! is cloned per-thread and contains information about what is currently being
26 //! In order to speed up rendering (mostly because of markdown rendering), the
27 //! rendering process has been parallelized. This parallelization is only
28 //! exposed through the `crate` method on the context, and then also from the
29 //! fact that the shared cache is stored in TLS (and must be accessed as such).
31 //! In addition to rendering the crate itself, this module is also responsible
32 //! for creating the corresponding search index and source file renderings.
33 //! These threads are not parallelized (they haven't been a bottleneck yet), and
34 //! both occur before the crate is rendered.
35 pub use self::ExternalLocation
::*;
37 use std
::ascii
::AsciiExt
;
38 use std
::cell
::RefCell
;
39 use std
::cmp
::Ordering
;
40 use std
::collections
::BTreeMap
;
41 use std
::default::Default
;
43 use std
::fmt
::{self, Display, Formatter}
;
44 use std
::fs
::{self, File, OpenOptions}
;
45 use std
::io
::prelude
::*;
46 use std
::io
::{self, BufWriter, BufReader}
;
47 use std
::iter
::repeat
;
49 use std
::path
::{PathBuf, Path, Component}
;
53 use externalfiles
::ExternalHtml
;
55 use serialize
::json
::{ToJson, Json, as_json}
;
57 use syntax
::feature_gate
::UnstableFeatures
;
58 use rustc
::hir
::def_id
::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE}
;
59 use rustc
::middle
::privacy
::AccessLevels
;
60 use rustc
::middle
::stability
;
62 use rustc
::util
::nodemap
::{FnvHashMap, FnvHashSet}
;
63 use rustc_data_structures
::flock
;
65 use clean
::{self, Attributes, GetDefId, SelfTy, Mutability}
;
68 use html
::escape
::Escape
;
69 use html
::format
::{ConstnessSpace}
;
70 use html
::format
::{TyParamBounds, WhereClause, href, AbiSpace}
;
71 use html
::format
::{VisSpace, Method, UnsafetySpace, MutableSpace}
;
72 use html
::format
::fmt_impl_for_trait_page
;
73 use html
::item_type
::ItemType
;
74 use html
::markdown
::{self, Markdown}
;
75 use html
::{highlight, layout}
;
77 /// A pair of name and its optional document.
78 pub type NameDoc
= (String
, Option
<String
>);
80 /// Major driving force in all rustdoc rendering. This contains information
81 /// about where in the tree-like hierarchy rendering is occurring and controls
82 /// how the current page is being rendered.
84 /// It is intended that this context is a lightweight object which can be fairly
85 /// easily cloned because it is cloned per work-job (about once per item in the
89 /// Current hierarchy of components leading down to what's currently being
91 pub current
: Vec
<String
>,
92 /// String representation of how to get back to the root path of the 'doc/'
93 /// folder in terms of a relative URL.
94 pub root_path
: String
,
95 /// The current destination folder of where HTML artifacts should be placed.
96 /// This changes as the context descends into the module hierarchy.
98 /// A flag, which when `true`, will render pages which redirect to the
99 /// real location of an item. This is used to allow external links to
100 /// publicly reused items to redirect to the right location.
101 pub render_redirect_pages
: bool
,
102 pub shared
: Arc
<SharedContext
>,
105 pub struct SharedContext
{
106 /// The path to the crate root source minus the file name.
107 /// Used for simplifying paths to the highlighted source code files.
108 pub src_root
: PathBuf
,
109 /// This describes the layout of each page, and is not modified after
110 /// creation of the context (contains info like the favicon and added html).
111 pub layout
: layout
::Layout
,
112 /// This flag indicates whether [src] links should be generated or not. If
113 /// the source files are present in the html rendering, then this will be
115 pub include_sources
: bool
,
116 /// The local file sources we've emitted and their respective url-paths.
117 pub local_sources
: FnvHashMap
<PathBuf
, String
>,
118 /// All the passes that were run on this crate.
119 pub passes
: FnvHashSet
<String
>,
120 /// The base-URL of the issue tracker for when an item has been tagged with
122 pub issue_tracker_base_url
: Option
<String
>,
123 /// The given user css file which allow to customize the generated
124 /// documentation theme.
125 pub css_file_extension
: Option
<PathBuf
>,
128 /// Indicates where an external crate can be found.
129 pub enum ExternalLocation
{
130 /// Remote URL root of the external crate
132 /// This external crate can be found in the local doc/ folder
134 /// The external crate could not be found.
138 /// Metadata about an implementor of a trait.
139 pub struct Implementor
{
141 pub stability
: Option
<clean
::Stability
>,
142 pub impl_
: clean
::Impl
,
145 /// Metadata about implementations for a type.
148 pub impl_item
: clean
::Item
,
152 fn inner_impl(&self) -> &clean
::Impl
{
153 match self.impl_item
.inner
{
154 clean
::ImplItem(ref impl_
) => impl_
,
155 _
=> panic
!("non-impl item found in impl")
159 fn trait_did(&self) -> Option
<DefId
> {
160 self.inner_impl().trait_
.def_id()
170 impl error
::Error
for Error
{
171 fn description(&self) -> &str {
172 self.error
.description()
176 impl Display
for Error
{
177 fn fmt(&self, f
: &mut Formatter
) -> fmt
::Result
{
178 write
!(f
, "\"{}\": {}", self.file
.display(), self.error
)
183 pub fn new(e
: io
::Error
, file
: &Path
) -> Error
{
185 file
: file
.to_path_buf(),
191 macro_rules
! try_err
{
192 ($e
:expr
, $file
:expr
) => ({
195 Err(e
) => return Err(Error
::new(e
, $file
)),
200 /// This cache is used to store information about the `clean::Crate` being
201 /// rendered in order to provide more useful documentation. This contains
202 /// information like all implementors of a trait, all traits a type implements,
203 /// documentation for all known traits, etc.
205 /// This structure purposefully does not implement `Clone` because it's intended
206 /// to be a fairly large and expensive structure to clone. Instead this adheres
207 /// to `Send` so it may be stored in a `Arc` instance and shared among the various
208 /// rendering threads.
211 /// Mapping of typaram ids to the name of the type parameter. This is used
212 /// when pretty-printing a type (so pretty printing doesn't have to
213 /// painfully maintain a context like this)
214 pub typarams
: FnvHashMap
<DefId
, String
>,
216 /// Maps a type id to all known implementations for that type. This is only
217 /// recognized for intra-crate `ResolvedPath` types, and is used to print
218 /// out extra documentation on the page of an enum/struct.
220 /// The values of the map are a list of implementations and documentation
221 /// found on that implementation.
222 pub impls
: FnvHashMap
<DefId
, Vec
<Impl
>>,
224 /// Maintains a mapping of local crate node ids to the fully qualified name
225 /// and "short type description" of that node. This is used when generating
226 /// URLs when a type is being linked to. External paths are not located in
227 /// this map because the `External` type itself has all the information
229 pub paths
: FnvHashMap
<DefId
, (Vec
<String
>, ItemType
)>,
231 /// Similar to `paths`, but only holds external paths. This is only used for
232 /// generating explicit hyperlinks to other crates.
233 pub external_paths
: FnvHashMap
<DefId
, (Vec
<String
>, ItemType
)>,
235 /// This map contains information about all known traits of this crate.
236 /// Implementations of a crate should inherit the documentation of the
237 /// parent trait if no extra documentation is specified, and default methods
238 /// should show up in documentation about trait implementations.
239 pub traits
: FnvHashMap
<DefId
, clean
::Trait
>,
241 /// When rendering traits, it's often useful to be able to list all
242 /// implementors of the trait, and this mapping is exactly, that: a mapping
243 /// of trait ids to the list of known implementors of the trait
244 pub implementors
: FnvHashMap
<DefId
, Vec
<Implementor
>>,
246 /// Cache of where external crate documentation can be found.
247 pub extern_locations
: FnvHashMap
<CrateNum
, (String
, ExternalLocation
)>,
249 /// Cache of where documentation for primitives can be found.
250 pub primitive_locations
: FnvHashMap
<clean
::PrimitiveType
, CrateNum
>,
252 // Note that external items for which `doc(hidden)` applies to are shown as
253 // non-reachable while local items aren't. This is because we're reusing
254 // the access levels from crateanalysis.
255 pub access_levels
: Arc
<AccessLevels
<DefId
>>,
257 // Private fields only used when initially crawling a crate to build a cache
260 parent_stack
: Vec
<DefId
>,
261 parent_is_trait_impl
: bool
,
262 search_index
: Vec
<IndexItem
>,
263 seen_modules
: FnvHashSet
<DefId
>,
266 deref_trait_did
: Option
<DefId
>,
267 deref_mut_trait_did
: Option
<DefId
>,
269 // In rare case where a structure is defined in one module but implemented
270 // in another, if the implementing module is parsed before defining module,
271 // then the fully qualified name of the structure isn't presented in `paths`
272 // yet when its implementation methods are being indexed. Caches such methods
273 // and their parent id here and indexes them at the end of crate parsing.
274 orphan_impl_items
: Vec
<(DefId
, clean
::Item
)>,
277 /// Temporary storage for data obtained during `RustdocVisitor::clean()`.
278 /// Later on moved into `CACHE_KEY`.
280 pub struct RenderInfo
{
281 pub inlined
: FnvHashSet
<DefId
>,
282 pub external_paths
: ::core
::ExternalPaths
,
283 pub external_typarams
: FnvHashMap
<DefId
, String
>,
284 pub deref_trait_did
: Option
<DefId
>,
285 pub deref_mut_trait_did
: Option
<DefId
>,
288 /// Helper struct to render all source code to HTML pages
289 struct SourceCollector
<'a
> {
290 scx
: &'a
mut SharedContext
,
292 /// Root destination to place all HTML output into
296 /// Wrapper struct to render the source code of a file. This will do things like
297 /// adding line numbers to the left-hand side.
298 struct Source
<'a
>(&'a
str);
300 // Helper structs for rendering items/sidebars and carrying along contextual
303 #[derive(Copy, Clone)]
306 item
: &'a clean
::Item
,
309 struct Sidebar
<'a
> { cx: &'a Context, item: &'a clean::Item, }
311 /// Struct representing one entry in the JS search index. These are all emitted
312 /// by hand to a large JS file at the end of cache-creation.
318 parent
: Option
<DefId
>,
319 parent_idx
: Option
<usize>,
320 search_type
: Option
<IndexItemFunctionType
>,
323 impl ToJson
for IndexItem
{
324 fn to_json(&self) -> Json
{
325 assert_eq
!(self.parent
.is_some(), self.parent_idx
.is_some());
327 let mut data
= Vec
::with_capacity(6);
328 data
.push((self.ty
as usize).to_json());
329 data
.push(self.name
.to_json());
330 data
.push(self.path
.to_json());
331 data
.push(self.desc
.to_json());
332 data
.push(self.parent_idx
.to_json());
333 data
.push(self.search_type
.to_json());
339 /// A type used for the search index.
341 name
: Option
<String
>,
344 impl ToJson
for Type
{
345 fn to_json(&self) -> Json
{
348 let mut data
= BTreeMap
::new();
349 data
.insert("name".to_owned(), name
.to_json());
357 /// Full type of functions/methods in the search index.
358 struct IndexItemFunctionType
{
363 impl ToJson
for IndexItemFunctionType
{
364 fn to_json(&self) -> Json
{
365 // If we couldn't figure out a type, just write `null`.
366 if self.inputs
.iter().chain(self.output
.iter()).any(|ref i
| i
.name
.is_none()) {
369 let mut data
= BTreeMap
::new();
370 data
.insert("inputs".to_owned(), self.inputs
.to_json());
371 data
.insert("output".to_owned(), self.output
.to_json());
377 // TLS keys used to carry information around during rendering.
379 thread_local
!(static CACHE_KEY
: RefCell
<Arc
<Cache
>> = Default
::default());
380 thread_local
!(pub static CURRENT_LOCATION_KEY
: RefCell
<Vec
<String
>> =
381 RefCell
::new(Vec
::new()));
382 thread_local
!(static USED_ID_MAP
: RefCell
<FnvHashMap
<String
, usize>> =
383 RefCell
::new(init_ids()));
385 fn init_ids() -> FnvHashMap
<String
, usize> {
401 ].into_iter().map(|id
| (String
::from(*id
), 1)).collect()
404 /// This method resets the local table of used ID attributes. This is typically
405 /// used at the beginning of rendering an entire HTML page to reset from the
406 /// previous state (if any).
407 pub fn reset_ids(embedded
: bool
) {
408 USED_ID_MAP
.with(|s
| {
409 *s
.borrow_mut() = if embedded
{
417 pub fn derive_id(candidate
: String
) -> String
{
418 USED_ID_MAP
.with(|map
| {
419 let id
= match map
.borrow_mut().get_mut(&candidate
) {
422 let id
= format
!("{}-{}", candidate
, *a
);
428 map
.borrow_mut().insert(id
.clone(), 1);
433 /// Generates the documentation for `crate` into the directory `dst`
434 pub fn run(mut krate
: clean
::Crate
,
435 external_html
: &ExternalHtml
,
437 passes
: FnvHashSet
<String
>,
438 css_file_extension
: Option
<PathBuf
>,
439 renderinfo
: RenderInfo
) -> Result
<(), Error
> {
440 let src_root
= match krate
.src
.parent() {
441 Some(p
) => p
.to_path_buf(),
442 None
=> PathBuf
::new(),
444 let mut scx
= SharedContext
{
447 include_sources
: true,
448 local_sources
: FnvHashMap(),
449 issue_tracker_base_url
: None
,
450 layout
: layout
::Layout
{
451 logo
: "".to_string(),
452 favicon
: "".to_string(),
453 external_html
: external_html
.clone(),
454 krate
: krate
.name
.clone(),
455 playground_url
: "".to_string(),
457 css_file_extension
: css_file_extension
.clone(),
460 // Crawl the crate attributes looking for attributes which control how we're
461 // going to emit HTML
462 if let Some(attrs
) = krate
.module
.as_ref().map(|m
| m
.attrs
.list("doc")) {
465 clean
::NameValue(ref x
, ref s
)
466 if "html_favicon_url" == *x
=> {
467 scx
.layout
.favicon
= s
.to_string();
469 clean
::NameValue(ref x
, ref s
)
470 if "html_logo_url" == *x
=> {
471 scx
.layout
.logo
= s
.to_string();
473 clean
::NameValue(ref x
, ref s
)
474 if "html_playground_url" == *x
=> {
475 scx
.layout
.playground_url
= s
.to_string();
476 markdown
::PLAYGROUND_KRATE
.with(|slot
| {
477 if slot
.borrow().is_none() {
478 let name
= krate
.name
.clone();
479 *slot
.borrow_mut() = Some(Some(name
));
483 clean
::NameValue(ref x
, ref s
)
484 if "issue_tracker_base_url" == *x
=> {
485 scx
.issue_tracker_base_url
= Some(s
.to_string());
488 if "html_no_source" == *x
=> {
489 scx
.include_sources
= false;
495 try_err
!(mkdir(&dst
), &dst
);
496 krate
= render_sources(&dst
, &mut scx
, krate
)?
;
499 root_path
: String
::new(),
501 render_redirect_pages
: false,
502 shared
: Arc
::new(scx
),
505 // Crawl the crate to build various caches used for the output
514 let external_paths
= external_paths
.into_iter()
515 .map(|(k
, (v
, t
))| (k
, (v
, ItemType
::from(t
))))
518 let mut cache
= Cache
{
520 external_paths
: external_paths
,
522 implementors
: FnvHashMap(),
524 parent_stack
: Vec
::new(),
525 search_index
: Vec
::new(),
526 parent_is_trait_impl
: false,
527 extern_locations
: FnvHashMap(),
528 primitive_locations
: FnvHashMap(),
529 seen_modules
: FnvHashSet(),
532 access_levels
: krate
.access_levels
.clone(),
533 orphan_impl_items
: Vec
::new(),
534 traits
: mem
::replace(&mut krate
.external_traits
, FnvHashMap()),
535 deref_trait_did
: deref_trait_did
,
536 deref_mut_trait_did
: deref_mut_trait_did
,
537 typarams
: external_typarams
,
540 // Cache where all our extern crates are located
541 for &(n
, ref e
) in &krate
.externs
{
542 cache
.extern_locations
.insert(n
, (e
.name
.clone(),
543 extern_location(e
, &cx
.dst
)));
544 let did
= DefId { krate: n, index: CRATE_DEF_INDEX }
;
545 cache
.external_paths
.insert(did
, (vec
![e
.name
.to_string()], ItemType
::Module
));
548 // Cache where all known primitives have their documentation located.
550 // Favor linking to as local extern as possible, so iterate all crates in
551 // reverse topological order.
552 for &(n
, ref e
) in krate
.externs
.iter().rev() {
553 for &prim
in &e
.primitives
{
554 cache
.primitive_locations
.insert(prim
, n
);
557 for &prim
in &krate
.primitives
{
558 cache
.primitive_locations
.insert(prim
, LOCAL_CRATE
);
561 cache
.stack
.push(krate
.name
.clone());
562 krate
= cache
.fold_crate(krate
);
564 // Build our search index
565 let index
= build_index(&krate
, &mut cache
);
567 // Freeze the cache now that the index has been built. Put an Arc into TLS
568 // for future parallelization opportunities
569 let cache
= Arc
::new(cache
);
570 CACHE_KEY
.with(|v
| *v
.borrow_mut() = cache
.clone());
571 CURRENT_LOCATION_KEY
.with(|s
| s
.borrow_mut().clear());
573 write_shared(&cx
, &krate
, &*cache
, index
)?
;
575 // And finally render the whole crate's documentation
579 /// Build the search index from the collected metadata
580 fn build_index(krate
: &clean
::Crate
, cache
: &mut Cache
) -> String
{
581 let mut nodeid_to_pathid
= FnvHashMap();
582 let mut crate_items
= Vec
::with_capacity(cache
.search_index
.len());
583 let mut crate_paths
= Vec
::<Json
>::new();
585 let Cache
{ ref mut search_index
,
586 ref orphan_impl_items
,
587 ref mut paths
, .. } = *cache
;
589 // Attach all orphan items to the type's definition if the type
590 // has since been learned.
591 for &(did
, ref item
) in orphan_impl_items
{
592 if let Some(&(ref fqp
, _
)) = paths
.get(&did
) {
593 search_index
.push(IndexItem
{
595 name
: item
.name
.clone().unwrap(),
596 path
: fqp
[..fqp
.len() - 1].join("::"),
597 desc
: Escape(&shorter(item
.doc_value())).to_string(),
600 search_type
: get_index_search_type(&item
),
605 // Reduce `NodeId` in paths into smaller sequential numbers,
606 // and prune the paths that do not appear in the index.
607 let mut lastpath
= String
::new();
608 let mut lastpathid
= 0usize
;
610 for item
in search_index
{
611 item
.parent_idx
= item
.parent
.map(|nodeid
| {
612 if nodeid_to_pathid
.contains_key(&nodeid
) {
613 *nodeid_to_pathid
.get(&nodeid
).unwrap()
615 let pathid
= lastpathid
;
616 nodeid_to_pathid
.insert(nodeid
, pathid
);
619 let &(ref fqp
, short
) = paths
.get(&nodeid
).unwrap();
620 crate_paths
.push(((short
as usize), fqp
.last().unwrap().clone()).to_json());
625 // Omit the parent path if it is same to that of the prior item.
626 if lastpath
== item
.path
{
629 lastpath
= item
.path
.clone();
631 crate_items
.push(item
.to_json());
634 let crate_doc
= krate
.module
.as_ref().map(|module
| {
635 Escape(&shorter(module
.doc_value())).to_string()
636 }).unwrap_or(String
::new());
638 let mut crate_data
= BTreeMap
::new();
639 crate_data
.insert("doc".to_owned(), Json
::String(crate_doc
));
640 crate_data
.insert("items".to_owned(), Json
::Array(crate_items
));
641 crate_data
.insert("paths".to_owned(), Json
::Array(crate_paths
));
643 // Collect the index into a string
644 format
!("searchIndex[{}] = {};",
645 as_json(&krate
.name
),
646 Json
::Object(crate_data
))
649 fn write_shared(cx
: &Context
,
650 krate
: &clean
::Crate
,
652 search_index
: String
) -> Result
<(), Error
> {
653 // Write out the shared files. Note that these are shared among all rustdoc
654 // docs placed in the output directory, so this needs to be a synchronized
655 // operation with respect to all other rustdocs running around.
656 try_err
!(mkdir(&cx
.dst
), &cx
.dst
);
657 let _lock
= flock
::Lock
::panicking_new(&cx
.dst
.join(".lock"), true, true, true);
659 // Add all the static files. These may already exist, but we just
660 // overwrite them anyway to make sure that they're fresh and up-to-date.
662 write(cx
.dst
.join("jquery.js"),
663 include_bytes
!("static/jquery-2.1.4.min.js"))?
;
664 write(cx
.dst
.join("main.js"),
665 include_bytes
!("static/main.js"))?
;
666 write(cx
.dst
.join("playpen.js"),
667 include_bytes
!("static/playpen.js"))?
;
668 write(cx
.dst
.join("rustdoc.css"),
669 include_bytes
!("static/rustdoc.css"))?
;
670 write(cx
.dst
.join("main.css"),
671 include_bytes
!("static/styles/main.css"))?
;
672 if let Some(ref css
) = cx
.shared
.css_file_extension
{
673 let mut content
= String
::new();
674 let css
= css
.as_path();
675 let mut f
= try_err
!(File
::open(css
), css
);
677 try_err
!(f
.read_to_string(&mut content
), css
);
678 let css
= cx
.dst
.join("theme.css");
679 let css
= css
.as_path();
680 let mut f
= try_err
!(File
::create(css
), css
);
681 try_err
!(write
!(f
, "{}", &content
), css
);
683 write(cx
.dst
.join("normalize.css"),
684 include_bytes
!("static/normalize.css"))?
;
685 write(cx
.dst
.join("FiraSans-Regular.woff"),
686 include_bytes
!("static/FiraSans-Regular.woff"))?
;
687 write(cx
.dst
.join("FiraSans-Medium.woff"),
688 include_bytes
!("static/FiraSans-Medium.woff"))?
;
689 write(cx
.dst
.join("FiraSans-LICENSE.txt"),
690 include_bytes
!("static/FiraSans-LICENSE.txt"))?
;
691 write(cx
.dst
.join("Heuristica-Italic.woff"),
692 include_bytes
!("static/Heuristica-Italic.woff"))?
;
693 write(cx
.dst
.join("Heuristica-LICENSE.txt"),
694 include_bytes
!("static/Heuristica-LICENSE.txt"))?
;
695 write(cx
.dst
.join("SourceSerifPro-Regular.woff"),
696 include_bytes
!("static/SourceSerifPro-Regular.woff"))?
;
697 write(cx
.dst
.join("SourceSerifPro-Bold.woff"),
698 include_bytes
!("static/SourceSerifPro-Bold.woff"))?
;
699 write(cx
.dst
.join("SourceSerifPro-LICENSE.txt"),
700 include_bytes
!("static/SourceSerifPro-LICENSE.txt"))?
;
701 write(cx
.dst
.join("SourceCodePro-Regular.woff"),
702 include_bytes
!("static/SourceCodePro-Regular.woff"))?
;
703 write(cx
.dst
.join("SourceCodePro-Semibold.woff"),
704 include_bytes
!("static/SourceCodePro-Semibold.woff"))?
;
705 write(cx
.dst
.join("SourceCodePro-LICENSE.txt"),
706 include_bytes
!("static/SourceCodePro-LICENSE.txt"))?
;
707 write(cx
.dst
.join("LICENSE-MIT.txt"),
708 include_bytes
!("static/LICENSE-MIT.txt"))?
;
709 write(cx
.dst
.join("LICENSE-APACHE.txt"),
710 include_bytes
!("static/LICENSE-APACHE.txt"))?
;
711 write(cx
.dst
.join("COPYRIGHT.txt"),
712 include_bytes
!("static/COPYRIGHT.txt"))?
;
714 fn collect(path
: &Path
, krate
: &str,
715 key
: &str) -> io
::Result
<Vec
<String
>> {
716 let mut ret
= Vec
::new();
718 for line
in BufReader
::new(File
::open(path
)?
).lines() {
720 if !line
.starts_with(key
) {
723 if line
.starts_with(&format
!(r
#"{}["{}"]"#, key, krate)) {
726 ret
.push(line
.to_string());
732 // Update the search index
733 let dst
= cx
.dst
.join("search-index.js");
734 let all_indexes
= try_err
!(collect(&dst
, &krate
.name
, "searchIndex"), &dst
);
735 let mut w
= try_err
!(File
::create(&dst
), &dst
);
736 try_err
!(writeln
!(&mut w
, "var searchIndex = {{}};"), &dst
);
737 try_err
!(writeln
!(&mut w
, "{}", search_index
), &dst
);
738 for index
in &all_indexes
{
739 try_err
!(writeln
!(&mut w
, "{}", *index
), &dst
);
741 try_err
!(writeln
!(&mut w
, "initSearch(searchIndex);"), &dst
);
743 // Update the list of all implementors for traits
744 let dst
= cx
.dst
.join("implementors");
745 try_err
!(mkdir(&dst
), &dst
);
746 for (&did
, imps
) in &cache
.implementors
{
747 // Private modules can leak through to this phase of rustdoc, which
748 // could contain implementations for otherwise private types. In some
749 // rare cases we could find an implementation for an item which wasn't
750 // indexed, so we just skip this step in that case.
752 // FIXME: this is a vague explanation for why this can't be a `get`, in
753 // theory it should be...
754 let &(ref remote_path
, remote_item_type
) = match cache
.paths
.get(&did
) {
756 None
=> match cache
.external_paths
.get(&did
) {
762 let mut mydst
= dst
.clone();
763 for part
in &remote_path
[..remote_path
.len() - 1] {
765 try_err
!(mkdir(&mydst
), &mydst
);
767 mydst
.push(&format
!("{}.{}.js",
768 remote_item_type
.css_class(),
769 remote_path
[remote_path
.len() - 1]));
770 let all_implementors
= try_err
!(collect(&mydst
, &krate
.name
,
774 try_err
!(mkdir(mydst
.parent().unwrap()),
775 &mydst
.parent().unwrap().to_path_buf());
776 let mut f
= BufWriter
::new(try_err
!(File
::create(&mydst
), &mydst
));
777 try_err
!(writeln
!(&mut f
, "(function() {{var implementors = {{}};"), &mydst
);
779 for implementor
in &all_implementors
{
780 try_err
!(write
!(&mut f
, "{}", *implementor
), &mydst
);
783 try_err
!(write
!(&mut f
, r
#"implementors["{}"] = ["#, krate.name), &mydst);
785 // If the trait and implementation are in the same crate, then
786 // there's no need to emit information about it (there's inlining
787 // going on). If they're in different crates then the crate defining
788 // the trait will be interested in our implementation.
789 if imp
.def_id
.krate
== did
.krate { continue }
790 try_err
!(write
!(&mut f
, r
#""{}","#, imp.impl_), &mydst);
792 try_err
!(writeln
!(&mut f
, r
"];"), &mydst
);
793 try_err
!(writeln
!(&mut f
, "{}", r
"
794 if (window.register_implementors) {
795 window.register_implementors(implementors);
797 window.pending_implementors = implementors;
800 try_err
!(writeln
!(&mut f
, r
"}})()"), &mydst
);
805 fn render_sources(dst
: &Path
, scx
: &mut SharedContext
,
806 krate
: clean
::Crate
) -> Result
<clean
::Crate
, Error
> {
807 info
!("emitting source files");
808 let dst
= dst
.join("src");
809 try_err
!(mkdir(&dst
), &dst
);
810 let dst
= dst
.join(&krate
.name
);
811 try_err
!(mkdir(&dst
), &dst
);
812 let mut folder
= SourceCollector
{
816 Ok(folder
.fold_crate(krate
))
819 /// Writes the entire contents of a string to a destination, not attempting to
820 /// catch any errors.
821 fn write(dst
: PathBuf
, contents
: &[u8]) -> Result
<(), Error
> {
822 Ok(try_err
!(try_err
!(File
::create(&dst
), &dst
).write_all(contents
), &dst
))
825 /// Makes a directory on the filesystem, failing the thread if an error occurs
826 /// and skipping if the directory already exists.
828 /// Note that this also handles races as rustdoc is likely to be run
829 /// concurrently against another invocation.
830 fn mkdir(path
: &Path
) -> io
::Result
<()> {
831 match fs
::create_dir(path
) {
833 Err(ref e
) if e
.kind() == io
::ErrorKind
::AlreadyExists
=> Ok(()),
838 /// Returns a documentation-level item type from the item.
839 fn item_type(item
: &clean
::Item
) -> ItemType
{
843 /// Takes a path to a source file and cleans the path to it. This canonicalizes
844 /// things like ".." to components which preserve the "top down" hierarchy of a
845 /// static HTML tree. Each component in the cleaned path will be passed as an
846 /// argument to `f`. The very last component of the path (ie the file name) will
847 /// be passed to `f` if `keep_filename` is true, and ignored otherwise.
848 // FIXME (#9639): The closure should deal with &[u8] instead of &str
849 // FIXME (#9639): This is too conservative, rejecting non-UTF-8 paths
850 fn clean_srcpath
<F
>(src_root
: &Path
, p
: &Path
, keep_filename
: bool
, mut f
: F
) where
853 // make it relative, if possible
854 let p
= p
.strip_prefix(src_root
).unwrap_or(p
);
856 let mut iter
= p
.components().peekable();
858 while let Some(c
) = iter
.next() {
859 if !keep_filename
&& iter
.peek().is_none() {
864 Component
::ParentDir
=> f("up"),
865 Component
::Normal(c
) => f(c
.to_str().unwrap()),
871 /// Attempts to find where an external crate is located, given that we're
872 /// rendering in to the specified source destination.
873 fn extern_location(e
: &clean
::ExternalCrate
, dst
: &Path
) -> ExternalLocation
{
874 // See if there's documentation generated into the local directory
875 let local_location
= dst
.join(&e
.name
);
876 if local_location
.is_dir() {
880 // Failing that, see if there's an attribute specifying where to find this
882 e
.attrs
.list("doc").value("html_root_url").map(|url
| {
883 let mut url
= url
.to_owned();
884 if !url
.ends_with("/") {
888 }).unwrap_or(Unknown
) // Well, at least we tried.
891 impl<'a
> DocFolder
for SourceCollector
<'a
> {
892 fn fold_item(&mut self, item
: clean
::Item
) -> Option
<clean
::Item
> {
893 // If we're including source files, and we haven't seen this file yet,
894 // then we need to render it out to the filesystem
895 if self.scx
.include_sources
896 // skip all invalid spans
897 && item
.source
.filename
!= ""
898 // macros from other libraries get special filenames which we can
900 && !(item
.source
.filename
.starts_with("<")
901 && item
.source
.filename
.ends_with("macros>")) {
903 // If it turns out that we couldn't read this file, then we probably
904 // can't read any of the files (generating html output from json or
905 // something like that), so just don't include sources for the
906 // entire crate. The other option is maintaining this mapping on a
907 // per-file basis, but that's probably not worth it...
909 .include_sources
= match self.emit_source(&item
.source
.filename
) {
912 println
!("warning: source code was requested to be rendered, \
913 but processing `{}` had an error: {}",
914 item
.source
.filename
, e
);
915 println
!(" skipping rendering of source code");
920 self.fold_item_recur(item
)
924 impl<'a
> SourceCollector
<'a
> {
925 /// Renders the given filename into its corresponding HTML source file.
926 fn emit_source(&mut self, filename
: &str) -> io
::Result
<()> {
927 let p
= PathBuf
::from(filename
);
928 if self.scx
.local_sources
.contains_key(&p
) {
929 // We've already emitted this source
933 let mut contents
= Vec
::new();
934 File
::open(&p
).and_then(|mut f
| f
.read_to_end(&mut contents
))?
;
936 let contents
= str::from_utf8(&contents
).unwrap();
938 // Remove the utf-8 BOM if any
939 let contents
= if contents
.starts_with("\u{feff}") {
945 // Create the intermediate directories
946 let mut cur
= self.dst
.clone();
947 let mut root_path
= String
::from("../../");
948 let mut href
= String
::new();
949 clean_srcpath(&self.scx
.src_root
, &p
, false, |component
| {
951 mkdir(&cur
).unwrap();
952 root_path
.push_str("../");
953 href
.push_str(component
);
956 let mut fname
= p
.file_name().expect("source has no filename")
960 href
.push_str(&fname
.to_string_lossy());
962 let mut w
= BufWriter
::new(File
::create(&cur
)?
);
963 let title
= format
!("{} -- source", cur
.file_name().unwrap()
965 let desc
= format
!("Source to the Rust file `{}`.", filename
);
966 let page
= layout
::Page
{
969 root_path
: &root_path
,
971 keywords
: BASIC_KEYWORDS
,
973 layout
::render(&mut w
, &self.scx
.layout
,
974 &page
, &(""), &Source(contents
),
975 self.scx
.css_file_extension
.is_some())?
;
977 self.scx
.local_sources
.insert(p
, href
);
982 impl DocFolder
for Cache
{
983 fn fold_item(&mut self, item
: clean
::Item
) -> Option
<clean
::Item
> {
984 // If this is a stripped module,
985 // we don't want it or its children in the search index.
986 let orig_stripped_mod
= match item
.inner
{
987 clean
::StrippedItem(box clean
::ModuleItem(..)) => {
988 mem
::replace(&mut self.stripped_mod
, true)
990 _
=> self.stripped_mod
,
993 // Inlining can cause us to visit the same item multiple times.
994 // (i.e. relevant for gathering impls and implementors)
995 let orig_seen_mod
= if item
.is_mod() {
996 let seen_this
= self.seen_mod
|| !self.seen_modules
.insert(item
.def_id
);
997 mem
::replace(&mut self.seen_mod
, seen_this
)
1002 // Register any generics to their corresponding string. This is used
1003 // when pretty-printing types
1004 if let Some(generics
) = item
.inner
.generics() {
1005 self.generics(generics
);
1009 // Propagate a trait methods' documentation to all implementors of the
1011 if let clean
::TraitItem(ref t
) = item
.inner
{
1012 self.traits
.insert(item
.def_id
, t
.clone());
1015 // Collect all the implementors of traits.
1016 if let clean
::ImplItem(ref i
) = item
.inner
{
1017 if let Some(did
) = i
.trait_
.def_id() {
1018 self.implementors
.entry(did
).or_insert(vec
![]).push(Implementor
{
1019 def_id
: item
.def_id
,
1020 stability
: item
.stability
.clone(),
1027 // Index this method for searching later on
1028 if let Some(ref s
) = item
.name
{
1029 let (parent
, is_inherent_impl_item
) = match item
.inner
{
1030 clean
::StrippedItem(..) => ((None
, None
), false),
1031 clean
::AssociatedConstItem(..) |
1032 clean
::TypedefItem(_
, true) if self.parent_is_trait_impl
=> {
1033 // skip associated items in trait impls
1034 ((None
, None
), false)
1036 clean
::AssociatedTypeItem(..) |
1037 clean
::TyMethodItem(..) |
1038 clean
::StructFieldItem(..) |
1039 clean
::VariantItem(..) => {
1040 ((Some(*self.parent_stack
.last().unwrap()),
1041 Some(&self.stack
[..self.stack
.len() - 1])),
1044 clean
::MethodItem(..) | clean
::AssociatedConstItem(..) => {
1045 if self.parent_stack
.is_empty() {
1046 ((None
, None
), false)
1048 let last
= self.parent_stack
.last().unwrap();
1050 let path
= match self.paths
.get(&did
) {
1051 // The current stack not necessarily has correlation
1052 // for where the type was defined. On the other
1053 // hand, `paths` always has the right
1054 // information if present.
1055 Some(&(ref fqp
, ItemType
::Trait
)) |
1056 Some(&(ref fqp
, ItemType
::Struct
)) |
1057 Some(&(ref fqp
, ItemType
::Union
)) |
1058 Some(&(ref fqp
, ItemType
::Enum
)) =>
1059 Some(&fqp
[..fqp
.len() - 1]),
1060 Some(..) => Some(&*self.stack
),
1063 ((Some(*last
), path
), true)
1066 _
=> ((None
, Some(&*self.stack
)), false)
1070 (parent
, Some(path
)) if is_inherent_impl_item
|| (!self.stripped_mod
) => {
1071 debug_assert
!(!item
.is_stripped());
1073 // A crate has a module at its root, containing all items,
1074 // which should not be indexed. The crate-item itself is
1075 // inserted later on when serializing the search-index.
1076 if item
.def_id
.index
!= CRATE_DEF_INDEX
{
1077 self.search_index
.push(IndexItem
{
1078 ty
: item_type(&item
),
1079 name
: s
.to_string(),
1080 path
: path
.join("::").to_string(),
1081 desc
: Escape(&shorter(item
.doc_value())).to_string(),
1084 search_type
: get_index_search_type(&item
),
1088 (Some(parent
), None
) if is_inherent_impl_item
=> {
1089 // We have a parent, but we don't know where they're
1090 // defined yet. Wait for later to index this item.
1091 self.orphan_impl_items
.push((parent
, item
.clone()));
1097 // Keep track of the fully qualified path for this item.
1098 let pushed
= match item
.name
{
1099 Some(ref n
) if !n
.is_empty() => {
1100 self.stack
.push(n
.to_string());
1107 clean
::StructItem(..) | clean
::EnumItem(..) |
1108 clean
::TypedefItem(..) | clean
::TraitItem(..) |
1109 clean
::FunctionItem(..) | clean
::ModuleItem(..) |
1110 clean
::ForeignFunctionItem(..) | clean
::ForeignStaticItem(..) |
1111 clean
::ConstantItem(..) | clean
::StaticItem(..) |
1112 clean
::UnionItem(..)
1113 if !self.stripped_mod
=> {
1114 // Reexported items mean that the same id can show up twice
1115 // in the rustdoc ast that we're looking at. We know,
1116 // however, that a reexported item doesn't show up in the
1117 // `public_items` map, so we can skip inserting into the
1118 // paths map if there was already an entry present and we're
1119 // not a public item.
1121 !self.paths
.contains_key(&item
.def_id
) ||
1122 self.access_levels
.is_public(item
.def_id
)
1124 self.paths
.insert(item
.def_id
,
1125 (self.stack
.clone(), item_type(&item
)));
1128 // link variants to their parent enum because pages aren't emitted
1130 clean
::VariantItem(..) if !self.stripped_mod
=> {
1131 let mut stack
= self.stack
.clone();
1133 self.paths
.insert(item
.def_id
, (stack
, ItemType
::Enum
));
1136 clean
::PrimitiveItem(..) if item
.visibility
.is_some() => {
1137 self.paths
.insert(item
.def_id
, (self.stack
.clone(),
1144 // Maintain the parent stack
1145 let orig_parent_is_trait_impl
= self.parent_is_trait_impl
;
1146 let parent_pushed
= match item
.inner
{
1147 clean
::TraitItem(..) | clean
::EnumItem(..) |
1148 clean
::StructItem(..) | clean
::UnionItem(..) => {
1149 self.parent_stack
.push(item
.def_id
);
1150 self.parent_is_trait_impl
= false;
1153 clean
::ImplItem(ref i
) => {
1154 self.parent_is_trait_impl
= i
.trait_
.is_some();
1156 clean
::ResolvedPath{ did, .. }
=> {
1157 self.parent_stack
.push(did
);
1161 match t
.primitive_type() {
1163 let did
= DefId
::local(prim
.to_def_index());
1164 self.parent_stack
.push(did
);
1175 // Once we've recursively found all the generics, then hoard off all the
1176 // implementations elsewhere
1177 let ret
= self.fold_item_recur(item
).and_then(|item
| {
1178 if let clean
::Item { inner: clean::ImplItem(_), .. }
= item
{
1179 // Figure out the id of this impl. This may map to a
1180 // primitive rather than always to a struct/enum.
1181 // Note: matching twice to restrict the lifetime of the `i` borrow.
1182 let did
= if let clean
::Item { inner: clean::ImplItem(ref i), .. }
= item
{
1184 clean
::ResolvedPath { did, .. }
|
1185 clean
::BorrowedRef
{
1186 type_
: box clean
::ResolvedPath { did, .. }
, ..
1191 t
.primitive_type().and_then(|t
| {
1192 self.primitive_locations
.get(&t
).map(|n
| {
1193 let id
= t
.to_def_index();
1194 DefId { krate: *n, index: id }
1203 if let Some(did
) = did
{
1204 self.impls
.entry(did
).or_insert(vec
![]).push(Impl
{
1215 if pushed { self.stack.pop().unwrap(); }
1216 if parent_pushed { self.parent_stack.pop().unwrap(); }
1217 self.seen_mod
= orig_seen_mod
;
1218 self.stripped_mod
= orig_stripped_mod
;
1219 self.parent_is_trait_impl
= orig_parent_is_trait_impl
;
1225 fn generics(&mut self, generics
: &clean
::Generics
) {
1226 for typ
in &generics
.type_params
{
1227 self.typarams
.insert(typ
.did
, typ
.name
.clone());
1233 /// Recurse in the directory structure and change the "root path" to make
1234 /// sure it always points to the top (relatively)
1235 fn recurse
<T
, F
>(&mut self, s
: String
, f
: F
) -> T
where
1236 F
: FnOnce(&mut Context
) -> T
,
1239 panic
!("Unexpected empty destination: {:?}", self.current
);
1241 let prev
= self.dst
.clone();
1243 self.root_path
.push_str("../");
1244 self.current
.push(s
);
1246 info
!("Recursing into {}", self.dst
.display());
1250 info
!("Recursed; leaving {}", self.dst
.display());
1252 // Go back to where we were at
1254 let len
= self.root_path
.len();
1255 self.root_path
.truncate(len
- 3);
1256 self.current
.pop().unwrap();
1261 /// Main method for rendering a crate.
1263 /// This currently isn't parallelized, but it'd be pretty easy to add
1264 /// parallelization to this function.
1265 fn krate(self, mut krate
: clean
::Crate
) -> Result
<(), Error
> {
1266 let mut item
= match krate
.module
.take() {
1268 None
=> return Ok(())
1270 item
.name
= Some(krate
.name
);
1272 // render the crate documentation
1273 let mut work
= vec
!((self, item
));
1275 while let Some((mut cx
, item
)) = work
.pop() {
1276 cx
.item(item
, |cx
, item
| {
1277 work
.push((cx
.clone(), item
))
1283 fn render_item(&self,
1284 writer
: &mut io
::Write
,
1288 // A little unfortunate that this is done like this, but it sure
1289 // does make formatting *a lot* nicer.
1290 CURRENT_LOCATION_KEY
.with(|slot
| {
1291 *slot
.borrow_mut() = self.current
.clone();
1294 let mut title
= if it
.is_primitive() {
1295 // No need to include the namespace for primitive types
1298 self.current
.join("::")
1301 if !title
.is_empty() {
1302 title
.push_str("::");
1304 title
.push_str(it
.name
.as_ref().unwrap());
1306 title
.push_str(" - Rust");
1307 let tyname
= item_type(it
).css_class();
1308 let desc
= if it
.is_crate() {
1309 format
!("API documentation for the Rust `{}` crate.",
1310 self.shared
.layout
.krate
)
1312 format
!("API documentation for the Rust `{}` {} in crate `{}`.",
1313 it
.name
.as_ref().unwrap(), tyname
, self.shared
.layout
.krate
)
1315 let keywords
= make_item_keywords(it
);
1316 let page
= layout
::Page
{
1318 root_path
: &self.root_path
,
1321 keywords
: &keywords
,
1326 if !self.render_redirect_pages
{
1327 layout
::render(writer
, &self.shared
.layout
, &page
,
1328 &Sidebar{ cx: self, item: it }
,
1329 &Item{ cx: self, item: it }
,
1330 self.shared
.css_file_extension
.is_some())?
;
1332 let mut url
= repeat("../").take(self.current
.len())
1333 .collect
::<String
>();
1334 if let Some(&(ref names
, ty
)) = cache().paths
.get(&it
.def_id
) {
1335 for name
in &names
[..names
.len() - 1] {
1339 url
.push_str(&item_path(ty
, names
.last().unwrap()));
1340 layout
::redirect(writer
, &url
)?
;
1346 /// Non-parallelized version of rendering an item. This will take the input
1347 /// item, render its contents, and then invoke the specified closure with
1348 /// all sub-items which need to be rendered.
1350 /// The rendering driver uses this closure to queue up more work.
1351 fn item
<F
>(&mut self, item
: clean
::Item
, mut f
: F
) -> Result
<(), Error
> where
1352 F
: FnMut(&mut Context
, clean
::Item
),
1354 // Stripped modules survive the rustdoc passes (i.e. `strip-private`)
1355 // if they contain impls for public types. These modules can also
1356 // contain items such as publicly reexported structures.
1358 // External crates will provide links to these structures, so
1359 // these modules are recursed into, but not rendered normally
1360 // (a flag on the context).
1361 if !self.render_redirect_pages
{
1362 self.render_redirect_pages
= maybe_ignore_item(&item
);
1366 // modules are special because they add a namespace. We also need to
1367 // recurse into the items of the module as well.
1368 let name
= item
.name
.as_ref().unwrap().to_string();
1369 let mut item
= Some(item
);
1370 self.recurse(name
, |this
| {
1371 let item
= item
.take().unwrap();
1373 let mut buf
= Vec
::new();
1374 this
.render_item(&mut buf
, &item
, false).unwrap();
1375 // buf will be empty if the module is stripped and there is no redirect for it
1376 if !buf
.is_empty() {
1377 let joint_dst
= this
.dst
.join("index.html");
1378 try_err
!(fs
::create_dir_all(&this
.dst
), &this
.dst
);
1379 let mut dst
= try_err
!(File
::create(&joint_dst
), &joint_dst
);
1380 try_err
!(dst
.write_all(&buf
), &joint_dst
);
1383 let m
= match item
.inner
{
1384 clean
::StrippedItem(box clean
::ModuleItem(m
)) |
1385 clean
::ModuleItem(m
) => m
,
1389 // Render sidebar-items.js used throughout this module.
1390 if !this
.render_redirect_pages
{
1391 let items
= this
.build_sidebar_items(&m
);
1392 let js_dst
= this
.dst
.join("sidebar-items.js");
1393 let mut js_out
= BufWriter
::new(try_err
!(File
::create(&js_dst
), &js_dst
));
1394 try_err
!(write
!(&mut js_out
, "initSidebarItems({});",
1395 as_json(&items
)), &js_dst
);
1398 for item
in m
.items
{
1404 } else if item
.name
.is_some() {
1405 let mut buf
= Vec
::new();
1406 self.render_item(&mut buf
, &item
, true).unwrap();
1407 // buf will be empty if the item is stripped and there is no redirect for it
1408 if !buf
.is_empty() {
1409 let name
= item
.name
.as_ref().unwrap();
1410 let item_type
= item_type(&item
);
1411 let file_name
= &item_path(item_type
, name
);
1412 let joint_dst
= self.dst
.join(file_name
);
1413 try_err
!(fs
::create_dir_all(&self.dst
), &self.dst
);
1414 let mut dst
= try_err
!(File
::create(&joint_dst
), &joint_dst
);
1415 try_err
!(dst
.write_all(&buf
), &joint_dst
);
1417 // Redirect from a sane URL using the namespace to Rustdoc's
1418 // URL for the page.
1419 let redir_name
= format
!("{}.{}.html", name
, item_type
.name_space());
1420 let redir_dst
= self.dst
.join(redir_name
);
1421 if let Ok(mut redirect_out
) = OpenOptions
::new().create_new(true)
1424 try_err
!(layout
::redirect(&mut redirect_out
, file_name
), &redir_dst
);
1427 // If the item is a macro, redirect from the old macro URL (with !)
1428 // to the new one (without).
1429 // FIXME(#35705) remove this redirect.
1430 if item_type
== ItemType
::Macro
{
1431 let redir_name
= format
!("{}.{}!.html", item_type
, name
);
1432 let redir_dst
= self.dst
.join(redir_name
);
1433 let mut redirect_out
= try_err
!(File
::create(&redir_dst
), &redir_dst
);
1434 try_err
!(layout
::redirect(&mut redirect_out
, file_name
), &redir_dst
);
1441 fn build_sidebar_items(&self, m
: &clean
::Module
) -> BTreeMap
<String
, Vec
<NameDoc
>> {
1442 // BTreeMap instead of HashMap to get a sorted output
1443 let mut map
= BTreeMap
::new();
1444 for item
in &m
.items
{
1445 if maybe_ignore_item(item
) { continue }
1447 let short
= item_type(item
).css_class();
1448 let myname
= match item
.name
{
1450 Some(ref s
) => s
.to_string(),
1452 let short
= short
.to_string();
1453 map
.entry(short
).or_insert(vec
![])
1454 .push((myname
, Some(plain_summary_line(item
.doc_value()))));
1457 for (_
, items
) in &mut map
{
1465 /// Generate a url appropriate for an `href` attribute back to the source of
1468 /// The url generated, when clicked, will redirect the browser back to the
1469 /// original source code.
1471 /// If `None` is returned, then a source link couldn't be generated. This
1472 /// may happen, for example, with externally inlined items where the source
1473 /// of their crate documentation isn't known.
1474 fn href(&self) -> Option
<String
> {
1475 let href
= if self.item
.source
.loline
== self.item
.source
.hiline
{
1476 format
!("{}", self.item
.source
.loline
)
1478 format
!("{}-{}", self.item
.source
.loline
, self.item
.source
.hiline
)
1481 // First check to see if this is an imported macro source. In this case
1482 // we need to handle it specially as cross-crate inlined macros have...
1484 let imported_macro_from
= match self.item
.inner
{
1485 clean
::MacroItem(ref m
) => m
.imported_from
.as_ref(),
1488 if let Some(krate
) = imported_macro_from
{
1489 let cache
= cache();
1490 let root
= cache
.extern_locations
.values().find(|&&(ref n
, _
)| {
1493 let root
= match root
{
1494 Some(&Remote(ref s
)) => s
.to_string(),
1495 Some(&Local
) => self.cx
.root_path
.clone(),
1496 None
| Some(&Unknown
) => return None
,
1498 Some(format
!("{root}/{krate}/macro.{name}.html?gotomacrosrc=1",
1501 name
= self.item
.name
.as_ref().unwrap()))
1503 // If this item is part of the local crate, then we're guaranteed to
1504 // know the span, so we plow forward and generate a proper url. The url
1505 // has anchors for the line numbers that we're linking to.
1506 } else if self.item
.def_id
.is_local() {
1507 let path
= PathBuf
::from(&self.item
.source
.filename
);
1508 self.cx
.shared
.local_sources
.get(&path
).map(|path
| {
1509 format
!("{root}src/{krate}/{path}#{href}",
1510 root
= self.cx
.root_path
,
1511 krate
= self.cx
.shared
.layout
.krate
,
1515 // If this item is not part of the local crate, then things get a little
1516 // trickier. We don't actually know the span of the external item, but
1517 // we know that the documentation on the other end knows the span!
1519 // In this case, we generate a link to the *documentation* for this type
1520 // in the original crate. There's an extra URL parameter which says that
1521 // we want to go somewhere else, and the JS on the destination page will
1522 // pick it up and instantly redirect the browser to the source code.
1524 // If we don't know where the external documentation for this crate is
1525 // located, then we return `None`.
1527 let cache
= cache();
1528 let external_path
= match cache
.external_paths
.get(&self.item
.def_id
) {
1529 Some(&(ref path
, _
)) => path
,
1530 None
=> return None
,
1532 let mut path
= match cache
.extern_locations
.get(&self.item
.def_id
.krate
) {
1533 Some(&(_
, Remote(ref s
))) => s
.to_string(),
1534 Some(&(_
, Local
)) => self.cx
.root_path
.clone(),
1535 Some(&(_
, Unknown
)) => return None
,
1536 None
=> return None
,
1538 for item
in &external_path
[..external_path
.len() - 1] {
1539 path
.push_str(item
);
1542 Some(format
!("{path}{file}?gotosrc={goto}",
1544 file
= item_path(item_type(self.item
), external_path
.last().unwrap()),
1545 goto
= self.item
.def_id
.index
.as_usize()))
1550 impl<'a
> fmt
::Display
for Item
<'a
> {
1551 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
1552 debug_assert
!(!self.item
.is_stripped());
1553 // Write the breadcrumb trail header for the top
1554 write
!(fmt
, "\n<h1 class='fqn'><span class='in-band'>")?
;
1555 match self.item
.inner
{
1556 clean
::ModuleItem(ref m
) => if m
.is_crate
{
1557 write
!(fmt
, "Crate ")?
;
1559 write
!(fmt
, "Module ")?
;
1561 clean
::FunctionItem(..) | clean
::ForeignFunctionItem(..) =>
1562 write
!(fmt
, "Function ")?
,
1563 clean
::TraitItem(..) => write
!(fmt
, "Trait ")?
,
1564 clean
::StructItem(..) => write
!(fmt
, "Struct ")?
,
1565 clean
::UnionItem(..) => write
!(fmt
, "Union ")?
,
1566 clean
::EnumItem(..) => write
!(fmt
, "Enum ")?
,
1567 clean
::TypedefItem(..) => write
!(fmt
, "Type Definition ")?
,
1568 clean
::MacroItem(..) => write
!(fmt
, "Macro ")?
,
1569 clean
::PrimitiveItem(..) => write
!(fmt
, "Primitive Type ")?
,
1570 clean
::StaticItem(..) | clean
::ForeignStaticItem(..) =>
1571 write
!(fmt
, "Static ")?
,
1572 clean
::ConstantItem(..) => write
!(fmt
, "Constant ")?
,
1574 // We don't generate pages for any other type.
1578 if !self.item
.is_primitive() {
1579 let cur
= &self.cx
.current
;
1580 let amt
= if self.item
.is_mod() { cur.len() - 1 }
else { cur.len() }
;
1581 for (i
, component
) in cur
.iter().enumerate().take(amt
) {
1582 write
!(fmt
, "<a href='{}index.html'>{}</a>::<wbr>",
1583 repeat("../").take(cur
.len() - i
- 1)
1584 .collect
::<String
>(),
1588 write
!(fmt
, "<a class='{}' href=''>{}</a>",
1589 item_type(self.item
), self.item
.name
.as_ref().unwrap())?
;
1591 write
!(fmt
, "</span>")?
; // in-band
1592 write
!(fmt
, "<span class='out-of-band'>")?
;
1593 if let Some(version
) = self.item
.stable_since() {
1594 write
!(fmt
, "<span class='since' title='Stable since Rust version {0}'>{0}</span>",
1598 r
##"<span id='render-detail'>
1599 <a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">
1600 [<span class='inner'>−</span>]
1606 // When this item is part of a `pub use` in a downstream crate, the
1607 // [src] link in the downstream documentation will actually come back to
1608 // this page, and this link will be auto-clicked. The `id` attribute is
1609 // used to find the link to auto-click.
1610 if self.cx
.shared
.include_sources
&& !self.item
.is_primitive() {
1611 if let Some(l
) = self.href() {
1612 write
!(fmt
, "<a id='src-{}' class='srclink' \
1613 href='{}' title='{}'>[src]</a>",
1614 self.item
.def_id
.index
.as_usize(), l
, "goto source code")?
;
1618 write
!(fmt
, "</span>")?
; // out-of-band
1620 write
!(fmt
, "</h1>\n")?
;
1622 match self.item
.inner
{
1623 clean
::ModuleItem(ref m
) => {
1624 item_module(fmt
, self.cx
, self.item
, &m
.items
)
1626 clean
::FunctionItem(ref f
) | clean
::ForeignFunctionItem(ref f
) =>
1627 item_function(fmt
, self.cx
, self.item
, f
),
1628 clean
::TraitItem(ref t
) => item_trait(fmt
, self.cx
, self.item
, t
),
1629 clean
::StructItem(ref s
) => item_struct(fmt
, self.cx
, self.item
, s
),
1630 clean
::UnionItem(ref s
) => item_union(fmt
, self.cx
, self.item
, s
),
1631 clean
::EnumItem(ref e
) => item_enum(fmt
, self.cx
, self.item
, e
),
1632 clean
::TypedefItem(ref t
, _
) => item_typedef(fmt
, self.cx
, self.item
, t
),
1633 clean
::MacroItem(ref m
) => item_macro(fmt
, self.cx
, self.item
, m
),
1634 clean
::PrimitiveItem(ref p
) => item_primitive(fmt
, self.cx
, self.item
, p
),
1635 clean
::StaticItem(ref i
) | clean
::ForeignStaticItem(ref i
) =>
1636 item_static(fmt
, self.cx
, self.item
, i
),
1637 clean
::ConstantItem(ref c
) => item_constant(fmt
, self.cx
, self.item
, c
),
1639 // We don't generate pages for any other type.
1646 fn item_path(ty
: ItemType
, name
: &str) -> String
{
1648 ItemType
::Module
=> format
!("{}/index.html", name
),
1649 _
=> format
!("{}.{}.html", ty
.css_class(), name
),
1653 fn full_path(cx
: &Context
, item
: &clean
::Item
) -> String
{
1654 let mut s
= cx
.current
.join("::");
1656 s
.push_str(item
.name
.as_ref().unwrap());
1660 fn shorter
<'a
>(s
: Option
<&'a
str>) -> String
{
1662 Some(s
) => s
.lines().take_while(|line
|{
1663 (*line
).chars().any(|chr
|{
1664 !chr
.is_whitespace()
1666 }).collect
::<Vec
<_
>>().join("\n"),
1667 None
=> "".to_string()
1672 fn plain_summary_line(s
: Option
<&str>) -> String
{
1673 let line
= shorter(s
).replace("\n", " ");
1674 markdown
::plain_summary_line(&line
[..])
1677 fn document(w
: &mut fmt
::Formatter
, cx
: &Context
, item
: &clean
::Item
) -> fmt
::Result
{
1678 document_stability(w
, cx
, item
)?
;
1679 document_full(w
, item
)?
;
1683 fn document_short(w
: &mut fmt
::Formatter
, item
: &clean
::Item
, link
: AssocItemLink
) -> fmt
::Result
{
1684 if let Some(s
) = item
.doc_value() {
1685 let markdown
= if s
.contains('
\n'
) {
1686 format
!("{} [Read more]({})",
1687 &plain_summary_line(Some(s
)), naive_assoc_href(item
, link
))
1689 format
!("{}", &plain_summary_line(Some(s
)))
1691 write
!(w
, "<div class='docblock'>{}</div>", Markdown(&markdown
))?
;
1696 fn document_full(w
: &mut fmt
::Formatter
, item
: &clean
::Item
) -> fmt
::Result
{
1697 if let Some(s
) = item
.doc_value() {
1698 write
!(w
, "<div class='docblock'>{}</div>", Markdown(s
))?
;
1703 fn document_stability(w
: &mut fmt
::Formatter
, cx
: &Context
, item
: &clean
::Item
) -> fmt
::Result
{
1704 for stability
in short_stability(item
, cx
, true) {
1705 write
!(w
, "<div class='stability'>{}</div>", stability
)?
;
1710 fn item_module(w
: &mut fmt
::Formatter
, cx
: &Context
,
1711 item
: &clean
::Item
, items
: &[clean
::Item
]) -> fmt
::Result
{
1712 document(w
, cx
, item
)?
;
1714 let mut indices
= (0..items
.len()).filter(|i
| {
1715 if let clean
::DefaultImplItem(..) = items
[*i
].inner
{
1718 !maybe_ignore_item(&items
[*i
])
1719 }).collect
::<Vec
<usize>>();
1721 // the order of item types in the listing
1722 fn reorder(ty
: ItemType
) -> u8 {
1724 ItemType
::ExternCrate
=> 0,
1725 ItemType
::Import
=> 1,
1726 ItemType
::Primitive
=> 2,
1727 ItemType
::Module
=> 3,
1728 ItemType
::Macro
=> 4,
1729 ItemType
::Struct
=> 5,
1730 ItemType
::Enum
=> 6,
1731 ItemType
::Constant
=> 7,
1732 ItemType
::Static
=> 8,
1733 ItemType
::Trait
=> 9,
1734 ItemType
::Function
=> 10,
1735 ItemType
::Typedef
=> 12,
1736 ItemType
::Union
=> 13,
1741 fn cmp(i1
: &clean
::Item
, i2
: &clean
::Item
, idx1
: usize, idx2
: usize) -> Ordering
{
1742 let ty1
= item_type(i1
);
1743 let ty2
= item_type(i2
);
1745 return (reorder(ty1
), idx1
).cmp(&(reorder(ty2
), idx2
))
1747 let s1
= i1
.stability
.as_ref().map(|s
| s
.level
);
1748 let s2
= i2
.stability
.as_ref().map(|s
| s
.level
);
1750 (Some(stability
::Unstable
), Some(stability
::Stable
)) => return Ordering
::Greater
,
1751 (Some(stability
::Stable
), Some(stability
::Unstable
)) => return Ordering
::Less
,
1754 i1
.name
.cmp(&i2
.name
)
1757 indices
.sort_by(|&i1
, &i2
| cmp(&items
[i1
], &items
[i2
], i1
, i2
));
1759 debug
!("{:?}", indices
);
1760 let mut curty
= None
;
1761 for &idx
in &indices
{
1762 let myitem
= &items
[idx
];
1763 if myitem
.is_stripped() {
1767 let myty
= Some(item_type(myitem
));
1768 if curty
== Some(ItemType
::ExternCrate
) && myty
== Some(ItemType
::Import
) {
1769 // Put `extern crate` and `use` re-exports in the same section.
1771 } else if myty
!= curty
{
1772 if curty
.is_some() {
1773 write
!(w
, "</table>")?
;
1776 let (short
, name
) = match myty
.unwrap() {
1777 ItemType
::ExternCrate
|
1778 ItemType
::Import
=> ("reexports", "Reexports"),
1779 ItemType
::Module
=> ("modules", "Modules"),
1780 ItemType
::Struct
=> ("structs", "Structs"),
1781 ItemType
::Union
=> ("unions", "Unions"),
1782 ItemType
::Enum
=> ("enums", "Enums"),
1783 ItemType
::Function
=> ("functions", "Functions"),
1784 ItemType
::Typedef
=> ("types", "Type Definitions"),
1785 ItemType
::Static
=> ("statics", "Statics"),
1786 ItemType
::Constant
=> ("constants", "Constants"),
1787 ItemType
::Trait
=> ("traits", "Traits"),
1788 ItemType
::Impl
=> ("impls", "Implementations"),
1789 ItemType
::TyMethod
=> ("tymethods", "Type Methods"),
1790 ItemType
::Method
=> ("methods", "Methods"),
1791 ItemType
::StructField
=> ("fields", "Struct Fields"),
1792 ItemType
::Variant
=> ("variants", "Variants"),
1793 ItemType
::Macro
=> ("macros", "Macros"),
1794 ItemType
::Primitive
=> ("primitives", "Primitive Types"),
1795 ItemType
::AssociatedType
=> ("associated-types", "Associated Types"),
1796 ItemType
::AssociatedConst
=> ("associated-consts", "Associated Constants"),
1798 write
!(w
, "<h2 id='{id}' class='section-header'>\
1799 <a href=\"#{id}\">{name}</a></h2>\n<table>",
1800 id
= derive_id(short
.to_owned()), name
= name
)?
;
1803 match myitem
.inner
{
1804 clean
::ExternCrateItem(ref name
, ref src
) => {
1805 use html
::format
::HRef
;
1809 write
!(w
, "<tr><td><code>{}extern crate {} as {};",
1810 VisSpace(&myitem
.visibility
),
1811 HRef
::new(myitem
.def_id
, src
),
1815 write
!(w
, "<tr><td><code>{}extern crate {};",
1816 VisSpace(&myitem
.visibility
),
1817 HRef
::new(myitem
.def_id
, name
))?
1820 write
!(w
, "</code></td></tr>")?
;
1823 clean
::ImportItem(ref import
) => {
1824 write
!(w
, "<tr><td><code>{}{}</code></td></tr>",
1825 VisSpace(&myitem
.visibility
), *import
)?
;
1829 if myitem
.name
.is_none() { continue }
1831 let stabilities
= short_stability(myitem
, cx
, false);
1833 let stab_docs
= if !stabilities
.is_empty() {
1835 .map(|s
| format
!("[{}]", s
))
1836 .collect
::<Vec
<_
>>()
1842 let doc_value
= myitem
.doc_value().unwrap_or("");
1844 <tr class='{stab} module-item'>
1845 <td><a class='{class}' href='{href}'
1846 title='{title}'>{name}</a></td>
1847 <td class='docblock-short'>
1851 name
= *myitem
.name
.as_ref().unwrap(),
1852 stab_docs
= stab_docs
,
1853 docs
= shorter(Some(&Markdown(doc_value
).to_string())),
1854 class
= item_type(myitem
),
1855 stab
= myitem
.stability_class(),
1856 href
= item_path(item_type(myitem
), myitem
.name
.as_ref().unwrap()),
1857 title
= full_path(cx
, myitem
))?
;
1862 if curty
.is_some() {
1863 write
!(w
, "</table>")?
;
1868 fn maybe_ignore_item(it
: &clean
::Item
) -> bool
{
1870 clean
::StrippedItem(..) => true,
1871 clean
::ModuleItem(ref m
) => {
1872 it
.doc_value().is_none() && m
.items
.is_empty()
1873 && it
.visibility
!= Some(clean
::Public
)
1879 fn short_stability(item
: &clean
::Item
, cx
: &Context
, show_reason
: bool
) -> Vec
<String
> {
1880 let mut stability
= vec
![];
1882 if let Some(stab
) = item
.stability
.as_ref() {
1883 let reason
= if show_reason
&& !stab
.reason
.is_empty() {
1884 format
!(": {}", stab
.reason
)
1888 if !stab
.deprecated_since
.is_empty() {
1889 let since
= if show_reason
{
1890 format
!(" since {}", Escape(&stab
.deprecated_since
))
1894 let text
= format
!("Deprecated{}{}", since
, Markdown(&reason
));
1895 stability
.push(format
!("<em class='stab deprecated'>{}</em>", text
))
1898 if stab
.level
== stability
::Unstable
{
1899 let unstable_extra
= if show_reason
{
1900 match (!stab
.feature
.is_empty(), &cx
.shared
.issue_tracker_base_url
, stab
.issue
) {
1901 (true, &Some(ref tracker_url
), Some(issue_no
)) if issue_no
> 0 =>
1902 format
!(" (<code>{}</code> <a href=\"{}{}\">#{}</a>)",
1903 Escape(&stab
.feature
), tracker_url
, issue_no
, issue_no
),
1904 (false, &Some(ref tracker_url
), Some(issue_no
)) if issue_no
> 0 =>
1905 format
!(" (<a href=\"{}{}\">#{}</a>)", Escape(&tracker_url
), issue_no
,
1908 format
!(" (<code>{}</code>)", Escape(&stab
.feature
)),
1914 let text
= format
!("Unstable{}{}", unstable_extra
, Markdown(&reason
));
1915 stability
.push(format
!("<em class='stab unstable'>{}</em>", text
))
1917 } else if let Some(depr
) = item
.deprecation
.as_ref() {
1918 let note
= if show_reason
&& !depr
.note
.is_empty() {
1919 format
!(": {}", depr
.note
)
1923 let since
= if show_reason
&& !depr
.since
.is_empty() {
1924 format
!(" since {}", Escape(&depr
.since
))
1929 let text
= format
!("Deprecated{}{}", since
, Markdown(¬e
));
1930 stability
.push(format
!("<em class='stab deprecated'>{}</em>", text
))
1936 struct Initializer
<'a
>(&'a
str);
1938 impl<'a
> fmt
::Display
for Initializer
<'a
> {
1939 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1940 let Initializer(s
) = *self;
1941 if s
.is_empty() { return Ok(()); }
1942 write
!(f
, "<code> = </code>")?
;
1943 write
!(f
, "<code>{}</code>", Escape(s
))
1947 fn item_constant(w
: &mut fmt
::Formatter
, cx
: &Context
, it
: &clean
::Item
,
1948 c
: &clean
::Constant
) -> fmt
::Result
{
1949 write
!(w
, "<pre class='rust const'>{vis}const \
1950 {name}: {typ}{init}</pre>",
1951 vis
= VisSpace(&it
.visibility
),
1952 name
= it
.name
.as_ref().unwrap(),
1954 init
= Initializer(&c
.expr
))?
;
1958 fn item_static(w
: &mut fmt
::Formatter
, cx
: &Context
, it
: &clean
::Item
,
1959 s
: &clean
::Static
) -> fmt
::Result
{
1960 write
!(w
, "<pre class='rust static'>{vis}static {mutability}\
1961 {name}: {typ}{init}</pre>",
1962 vis
= VisSpace(&it
.visibility
),
1963 mutability
= MutableSpace(s
.mutability
),
1964 name
= it
.name
.as_ref().unwrap(),
1966 init
= Initializer(&s
.expr
))?
;
1970 fn item_function(w
: &mut fmt
::Formatter
, cx
: &Context
, it
: &clean
::Item
,
1971 f
: &clean
::Function
) -> fmt
::Result
{
1972 // FIXME(#24111): remove when `const_fn` is stabilized
1973 let vis_constness
= match UnstableFeatures
::from_environment() {
1974 UnstableFeatures
::Allow
=> f
.constness
,
1975 _
=> hir
::Constness
::NotConst
1977 write
!(w
, "<pre class='rust fn'>{vis}{constness}{unsafety}{abi}fn \
1978 {name}{generics}{decl}{where_clause}</pre>",
1979 vis
= VisSpace(&it
.visibility
),
1980 constness
= ConstnessSpace(vis_constness
),
1981 unsafety
= UnsafetySpace(f
.unsafety
),
1982 abi
= AbiSpace(f
.abi
),
1983 name
= it
.name
.as_ref().unwrap(),
1984 generics
= f
.generics
,
1985 where_clause
= WhereClause(&f
.generics
),
1990 fn item_trait(w
: &mut fmt
::Formatter
, cx
: &Context
, it
: &clean
::Item
,
1991 t
: &clean
::Trait
) -> fmt
::Result
{
1992 let mut bounds
= String
::new();
1993 if !t
.bounds
.is_empty() {
1994 if !bounds
.is_empty() {
1997 bounds
.push_str(": ");
1998 for (i
, p
) in t
.bounds
.iter().enumerate() {
1999 if i
> 0 { bounds.push_str(" + "); }
2000 bounds
.push_str(&format
!("{}", *p
));
2004 // Output the trait definition
2005 write
!(w
, "<pre class='rust trait'>{}{}trait {}{}{}{} ",
2006 VisSpace(&it
.visibility
),
2007 UnsafetySpace(t
.unsafety
),
2008 it
.name
.as_ref().unwrap(),
2011 WhereClause(&t
.generics
))?
;
2013 let types
= t
.items
.iter().filter(|m
| m
.is_associated_type()).collect
::<Vec
<_
>>();
2014 let consts
= t
.items
.iter().filter(|m
| m
.is_associated_const()).collect
::<Vec
<_
>>();
2015 let required
= t
.items
.iter().filter(|m
| m
.is_ty_method()).collect
::<Vec
<_
>>();
2016 let provided
= t
.items
.iter().filter(|m
| m
.is_method()).collect
::<Vec
<_
>>();
2018 if t
.items
.is_empty() {
2019 write
!(w
, "{{ }}")?
;
2021 // FIXME: we should be using a derived_id for the Anchors here
2025 render_assoc_item(w
, t
, AssocItemLink
::Anchor(None
))?
;
2028 if !types
.is_empty() && !consts
.is_empty() {
2033 render_assoc_item(w
, t
, AssocItemLink
::Anchor(None
))?
;
2036 if !consts
.is_empty() && !required
.is_empty() {
2039 for m
in &required
{
2041 render_assoc_item(w
, m
, AssocItemLink
::Anchor(None
))?
;
2044 if !required
.is_empty() && !provided
.is_empty() {
2047 for m
in &provided
{
2049 render_assoc_item(w
, m
, AssocItemLink
::Anchor(None
))?
;
2050 write
!(w
, " {{ ... }}\n")?
;
2054 write
!(w
, "</pre>")?
;
2056 // Trait documentation
2057 document(w
, cx
, it
)?
;
2059 fn trait_item(w
: &mut fmt
::Formatter
, cx
: &Context
, m
: &clean
::Item
, t
: &clean
::Item
)
2061 let name
= m
.name
.as_ref().unwrap();
2062 let item_type
= item_type(m
);
2063 let id
= derive_id(format
!("{}.{}", item_type
, name
));
2064 let ns_id
= derive_id(format
!("{}.{}", name
, item_type
.name_space()));
2065 write
!(w
, "<h3 id='{id}' class='method stab {stab}'>\
2066 <span id='{ns_id}' class='invisible'><code>",
2068 stab
= m
.stability_class(),
2070 render_assoc_item(w
, m
, AssocItemLink
::Anchor(Some(&id
)))?
;
2071 write
!(w
, "</code>")?
;
2072 render_stability_since(w
, m
, t
)?
;
2073 write
!(w
, "</span></h3>")?
;
2074 document(w
, cx
, m
)?
;
2078 if !types
.is_empty() {
2080 <h2 id='associated-types'>Associated Types</h2>
2081 <div class='methods'>
2084 trait_item(w
, cx
, *t
, it
)?
;
2086 write
!(w
, "</div>")?
;
2089 if !consts
.is_empty() {
2091 <h2 id='associated-const'>Associated Constants</h2>
2092 <div class='methods'>
2095 trait_item(w
, cx
, *t
, it
)?
;
2097 write
!(w
, "</div>")?
;
2100 // Output the documentation for each function individually
2101 if !required
.is_empty() {
2103 <h2 id='required-methods'>Required Methods</h2>
2104 <div class='methods'>
2106 for m
in &required
{
2107 trait_item(w
, cx
, *m
, it
)?
;
2109 write
!(w
, "</div>")?
;
2111 if !provided
.is_empty() {
2113 <h2 id='provided-methods'>Provided Methods</h2>
2114 <div class='methods'>
2116 for m
in &provided
{
2117 trait_item(w
, cx
, *m
, it
)?
;
2119 write
!(w
, "</div>")?
;
2122 // If there are methods directly on this trait object, render them here.
2123 render_assoc_items(w
, cx
, it
, it
.def_id
, AssocItemRender
::All
)?
;
2125 let cache
= cache();
2127 <h2 id='implementors'>Implementors</h2>
2128 <ul class='item-list' id='implementors-list'>
2130 if let Some(implementors
) = cache
.implementors
.get(&it
.def_id
) {
2131 for i
in implementors
{
2132 write
!(w
, "<li><code>")?
;
2133 fmt_impl_for_trait_page(&i
.impl_
, w
)?
;
2134 writeln
!(w
, "</code></li>")?
;
2137 write
!(w
, "</ul>")?
;
2138 write
!(w
, r
#"<script type="text/javascript" async
2139 src="{root_path}/implementors/{path}/{ty}.{name}.js">
2141 root_path
= vec
![".."; cx
.current
.len()].join("/"),
2142 path
= if it
.def_id
.is_local() {
2143 cx
.current
.join("/")
2145 let (ref path
, _
) = cache
.external_paths
[&it
.def_id
];
2146 path
[..path
.len() - 1].join("/")
2148 ty
= item_type(it
).css_class(),
2149 name
= *it
.name
.as_ref().unwrap())?
;
2153 fn naive_assoc_href(it
: &clean
::Item
, link
: AssocItemLink
) -> String
{
2154 use html
::item_type
::ItemType
::*;
2156 let name
= it
.name
.as_ref().unwrap();
2157 let ty
= match item_type(it
) {
2158 Typedef
| AssociatedType
=> AssociatedType
,
2162 let anchor
= format
!("#{}.{}", ty
, name
);
2164 AssocItemLink
::Anchor(Some(ref id
)) => format
!("#{}", id
),
2165 AssocItemLink
::Anchor(None
) => anchor
,
2166 AssocItemLink
::GotoSource(did
, _
) => {
2167 href(did
).map(|p
| format
!("{}{}", p
.0, anchor
)).unwrap_or(anchor
)
2172 fn assoc_const(w
: &mut fmt
::Formatter
,
2175 default: Option
<&String
>,
2176 link
: AssocItemLink
) -> fmt
::Result
{
2177 write
!(w
, "const <a href='{}' class='constant'>{}</a>",
2178 naive_assoc_href(it
, link
),
2179 it
.name
.as_ref().unwrap())?
;
2181 write
!(w
, ": {}", ty
)?
;
2182 if let Some(default) = default {
2183 write
!(w
, " = {}", Escape(default))?
;
2188 fn assoc_type(w
: &mut fmt
::Formatter
, it
: &clean
::Item
,
2189 bounds
: &Vec
<clean
::TyParamBound
>,
2190 default: Option
<&clean
::Type
>,
2191 link
: AssocItemLink
) -> fmt
::Result
{
2192 write
!(w
, "type <a href='{}' class='type'>{}</a>",
2193 naive_assoc_href(it
, link
),
2194 it
.name
.as_ref().unwrap())?
;
2195 if !bounds
.is_empty() {
2196 write
!(w
, ": {}", TyParamBounds(bounds
))?
2198 if let Some(default) = default {
2199 write
!(w
, " = {}", default)?
;
2204 fn render_stability_since_raw
<'a
>(w
: &mut fmt
::Formatter
,
2205 ver
: Option
<&'a
str>,
2206 containing_ver
: Option
<&'a
str>) -> fmt
::Result
{
2207 if let Some(v
) = ver
{
2208 if containing_ver
!= ver
&& v
.len() > 0 {
2209 write
!(w
, "<div class='since' title='Stable since Rust version {0}'>{0}</div>",
2216 fn render_stability_since(w
: &mut fmt
::Formatter
,
2218 containing_item
: &clean
::Item
) -> fmt
::Result
{
2219 render_stability_since_raw(w
, item
.stable_since(), containing_item
.stable_since())
2222 fn render_assoc_item(w
: &mut fmt
::Formatter
,
2224 link
: AssocItemLink
) -> fmt
::Result
{
2225 fn method(w
: &mut fmt
::Formatter
,
2227 unsafety
: hir
::Unsafety
,
2228 constness
: hir
::Constness
,
2230 g
: &clean
::Generics
,
2232 link
: AssocItemLink
)
2234 let name
= meth
.name
.as_ref().unwrap();
2235 let anchor
= format
!("#{}.{}", item_type(meth
), name
);
2236 let href
= match link
{
2237 AssocItemLink
::Anchor(Some(ref id
)) => format
!("#{}", id
),
2238 AssocItemLink
::Anchor(None
) => anchor
,
2239 AssocItemLink
::GotoSource(did
, provided_methods
) => {
2240 // We're creating a link from an impl-item to the corresponding
2241 // trait-item and need to map the anchored type accordingly.
2242 let ty
= if provided_methods
.contains(name
) {
2248 href(did
).map(|p
| format
!("{}#{}.{}", p
.0, ty
, name
)).unwrap_or(anchor
)
2251 // FIXME(#24111): remove when `const_fn` is stabilized
2252 let vis_constness
= match UnstableFeatures
::from_environment() {
2253 UnstableFeatures
::Allow
=> constness
,
2254 _
=> hir
::Constness
::NotConst
2256 write
!(w
, "{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\
2257 {generics}{decl}{where_clause}",
2258 ConstnessSpace(vis_constness
),
2259 UnsafetySpace(unsafety
),
2265 where_clause
= WhereClause(g
))
2268 clean
::StrippedItem(..) => Ok(()),
2269 clean
::TyMethodItem(ref m
) => {
2270 method(w
, item
, m
.unsafety
, hir
::Constness
::NotConst
,
2271 m
.abi
, &m
.generics
, &m
.decl
, link
)
2273 clean
::MethodItem(ref m
) => {
2274 method(w
, item
, m
.unsafety
, m
.constness
,
2275 m
.abi
, &m
.generics
, &m
.decl
,
2278 clean
::AssociatedConstItem(ref ty
, ref default) => {
2279 assoc_const(w
, item
, ty
, default.as_ref(), link
)
2281 clean
::AssociatedTypeItem(ref bounds
, ref default) => {
2282 assoc_type(w
, item
, bounds
, default.as_ref(), link
)
2284 _
=> panic
!("render_assoc_item called on non-associated-item")
2288 fn item_struct(w
: &mut fmt
::Formatter
, cx
: &Context
, it
: &clean
::Item
,
2289 s
: &clean
::Struct
) -> fmt
::Result
{
2290 write
!(w
, "<pre class='rust struct'>")?
;
2291 render_attributes(w
, it
)?
;
2299 write
!(w
, "</pre>")?
;
2301 document(w
, cx
, it
)?
;
2302 let mut fields
= s
.fields
.iter().filter_map(|f
| {
2304 clean
::StructFieldItem(ref ty
) => Some((f
, ty
)),
2308 if let doctree
::Plain
= s
.struct_type
{
2309 if fields
.peek().is_some() {
2310 write
!(w
, "<h2 class='fields'>Fields</h2>")?
;
2311 for (field
, ty
) in fields
{
2312 let id
= derive_id(format
!("{}.{}",
2313 ItemType
::StructField
,
2314 field
.name
.as_ref().unwrap()));
2315 let ns_id
= derive_id(format
!("{}.{}",
2316 field
.name
.as_ref().unwrap(),
2317 ItemType
::StructField
.name_space()));
2318 write
!(w
, "<span id='{id}' class='{item_type}'>
2319 <span id='{ns_id}' class='invisible'>
2320 <code>{name}: {ty}</code>
2321 </span></span><span class='stab {stab}'></span>",
2322 item_type
= ItemType
::StructField
,
2325 stab
= field
.stability_class(),
2326 name
= field
.name
.as_ref().unwrap(),
2328 document(w
, cx
, field
)?
;
2332 render_assoc_items(w
, cx
, it
, it
.def_id
, AssocItemRender
::All
)
2335 fn item_union(w
: &mut fmt
::Formatter
, cx
: &Context
, it
: &clean
::Item
,
2336 s
: &clean
::Union
) -> fmt
::Result
{
2337 write
!(w
, "<pre class='rust union'>")?
;
2338 render_attributes(w
, it
)?
;
2345 write
!(w
, "</pre>")?
;
2347 document(w
, cx
, it
)?
;
2348 let mut fields
= s
.fields
.iter().filter_map(|f
| {
2350 clean
::StructFieldItem(ref ty
) => Some((f
, ty
)),
2354 if fields
.peek().is_some() {
2355 write
!(w
, "<h2 class='fields'>Fields</h2>")?
;
2356 for (field
, ty
) in fields
{
2357 write
!(w
, "<span id='{shortty}.{name}' class='{shortty}'><code>{name}: {ty}</code>
2358 </span><span class='stab {stab}'></span>",
2359 shortty
= ItemType
::StructField
,
2360 stab
= field
.stability_class(),
2361 name
= field
.name
.as_ref().unwrap(),
2363 document(w
, cx
, field
)?
;
2366 render_assoc_items(w
, cx
, it
, it
.def_id
, AssocItemRender
::All
)
2369 fn item_enum(w
: &mut fmt
::Formatter
, cx
: &Context
, it
: &clean
::Item
,
2370 e
: &clean
::Enum
) -> fmt
::Result
{
2371 write
!(w
, "<pre class='rust enum'>")?
;
2372 render_attributes(w
, it
)?
;
2373 write
!(w
, "{}enum {}{}{}",
2374 VisSpace(&it
.visibility
),
2375 it
.name
.as_ref().unwrap(),
2377 WhereClause(&e
.generics
))?
;
2378 if e
.variants
.is_empty() && !e
.variants_stripped
{
2379 write
!(w
, " {{}}")?
;
2381 write
!(w
, " {{\n")?
;
2382 for v
in &e
.variants
{
2384 let name
= v
.name
.as_ref().unwrap();
2386 clean
::VariantItem(ref var
) => {
2388 clean
::CLikeVariant
=> write
!(w
, "{}", name
)?
,
2389 clean
::TupleVariant(ref tys
) => {
2390 write
!(w
, "{}(", name
)?
;
2391 for (i
, ty
) in tys
.iter().enumerate() {
2393 write
!(w
, ", ")?
2395 write
!(w
, "{}", *ty
)?
;
2399 clean
::StructVariant(ref s
) => {
2415 if e
.variants_stripped
{
2416 write
!(w
, " // some variants omitted\n")?
;
2420 write
!(w
, "</pre>")?
;
2421 render_stability_since_raw(w
, it
.stable_since(), None
)?
;
2423 document(w
, cx
, it
)?
;
2424 if !e
.variants
.is_empty() {
2425 write
!(w
, "<h2 class='variants'>Variants</h2>\n")?
;
2426 for variant
in &e
.variants
{
2427 let id
= derive_id(format
!("{}.{}",
2429 variant
.name
.as_ref().unwrap()));
2430 let ns_id
= derive_id(format
!("{}.{}",
2431 variant
.name
.as_ref().unwrap(),
2432 ItemType
::Variant
.name_space()));
2433 write
!(w
, "<span id='{id}' class='variant'>\
2434 <span id='{ns_id}' class='invisible'><code>{name}",
2437 name
= variant
.name
.as_ref().unwrap())?
;
2438 if let clean
::VariantItem(ref var
) = variant
.inner
{
2439 if let clean
::TupleVariant(ref tys
) = var
.kind
{
2441 for (i
, ty
) in tys
.iter().enumerate() {
2443 write
!(w
, ", ")?
;
2445 write
!(w
, "{}", *ty
)?
;
2450 write
!(w
, "</code></span></span>")?
;
2451 document(w
, cx
, variant
)?
;
2453 use clean
::{Variant, StructVariant}
;
2454 if let clean
::VariantItem( Variant { kind: StructVariant(ref s) }
) = variant
.inner
{
2455 write
!(w
, "<h3 class='fields'>Fields</h3>\n
2457 for field
in &s
.fields
{
2458 use clean
::StructFieldItem
;
2459 if let StructFieldItem(ref ty
) = field
.inner
{
2460 let id
= derive_id(format
!("variant.{}.field.{}",
2461 variant
.name
.as_ref().unwrap(),
2462 field
.name
.as_ref().unwrap()));
2463 let ns_id
= derive_id(format
!("{}.{}.{}.{}",
2464 variant
.name
.as_ref().unwrap(),
2465 ItemType
::Variant
.name_space(),
2466 field
.name
.as_ref().unwrap(),
2467 ItemType
::StructField
.name_space()));
2468 write
!(w
, "<tr><td \
2470 <span id='{ns_id}' class='invisible'>\
2471 <code>{f}: {t}</code></span></td><td>",
2474 f
= field
.name
.as_ref().unwrap(),
2476 document(w
, cx
, field
)?
;
2477 write
!(w
, "</td></tr>")?
;
2480 write
!(w
, "</table>")?
;
2482 render_stability_since(w
, variant
, it
)?
;
2485 render_assoc_items(w
, cx
, it
, it
.def_id
, AssocItemRender
::All
)?
;
2489 fn render_attributes(w
: &mut fmt
::Formatter
, it
: &clean
::Item
) -> fmt
::Result
{
2490 for attr
in &it
.attrs
{
2492 clean
::Word(ref s
) if *s
== "must_use" => {
2493 write
!(w
, "#[{}]\n", s
)?
;
2495 clean
::NameValue(ref k
, ref v
) if *k
== "must_use" => {
2496 write
!(w
, "#[{} = \"{}\"]\n", k
, v
)?
;
2504 fn render_struct(w
: &mut fmt
::Formatter
, it
: &clean
::Item
,
2505 g
: Option
<&clean
::Generics
>,
2506 ty
: doctree
::StructType
,
2507 fields
: &[clean
::Item
],
2509 structhead
: bool
) -> fmt
::Result
{
2511 VisSpace(&it
.visibility
),
2512 if structhead {"struct "}
else {""}
,
2513 it
.name
.as_ref().unwrap())?
;
2514 if let Some(g
) = g
{
2519 if let Some(g
) = g
{
2520 write
!(w
, "{}", WhereClause(g
))?
2522 let mut has_visible_fields
= false;
2524 for field
in fields
{
2525 if let clean
::StructFieldItem(ref ty
) = field
.inner
{
2526 write
!(w
, "\n{} {}{}: {},",
2528 VisSpace(&field
.visibility
),
2529 field
.name
.as_ref().unwrap(),
2531 has_visible_fields
= true;
2535 if has_visible_fields
{
2536 if it
.has_stripped_fields().unwrap() {
2537 write
!(w
, "\n{} // some fields omitted", tab
)?
;
2539 write
!(w
, "\n{}", tab
)?
;
2540 } else if it
.has_stripped_fields().unwrap() {
2541 // If there are no visible fields we can just display
2542 // `{ /* fields omitted */ }` to save space.
2543 write
!(w
, " /* fields omitted */ ")?
;
2549 for (i
, field
) in fields
.iter().enumerate() {
2554 clean
::StrippedItem(box clean
::StructFieldItem(..)) => {
2557 clean
::StructFieldItem(ref ty
) => {
2558 write
!(w
, "{}{}", VisSpace(&field
.visibility
), *ty
)?
2564 if let Some(g
) = g
{
2565 write
!(w
, "{}", WhereClause(g
))?
2570 // Needed for PhantomData.
2571 if let Some(g
) = g
{
2572 write
!(w
, "{}", WhereClause(g
))?
2580 fn render_union(w
: &mut fmt
::Formatter
, it
: &clean
::Item
,
2581 g
: Option
<&clean
::Generics
>,
2582 fields
: &[clean
::Item
],
2584 structhead
: bool
) -> fmt
::Result
{
2586 VisSpace(&it
.visibility
),
2587 if structhead {"union "}
else {""}
,
2588 it
.name
.as_ref().unwrap())?
;
2589 if let Some(g
) = g
{
2590 write
!(w
, "{}", g
)?
;
2591 write
!(w
, "{}", WhereClause(g
))?
;
2594 write
!(w
, " {{\n{}", tab
)?
;
2595 for field
in fields
{
2596 if let clean
::StructFieldItem(ref ty
) = field
.inner
{
2597 write
!(w
, " {}{}: {},\n{}",
2598 VisSpace(&field
.visibility
),
2599 field
.name
.as_ref().unwrap(),
2605 if it
.has_stripped_fields().unwrap() {
2606 write
!(w
, " // some fields omitted\n{}", tab
)?
;
2612 #[derive(Copy, Clone)]
2613 enum AssocItemLink
<'a
> {
2614 Anchor(Option
<&'a
str>),
2615 GotoSource(DefId
, &'a FnvHashSet
<String
>),
2618 impl<'a
> AssocItemLink
<'a
> {
2619 fn anchor(&self, id
: &'a String
) -> Self {
2621 AssocItemLink
::Anchor(_
) => { AssocItemLink::Anchor(Some(&id)) }
,
2622 ref other
=> *other
,
2627 enum AssocItemRender
<'a
> {
2629 DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type, deref_mut_: bool }
2632 #[derive(Copy, Clone, PartialEq)]
2635 ForDeref { mut_: bool }
,
2638 fn render_assoc_items(w
: &mut fmt
::Formatter
,
2640 containing_item
: &clean
::Item
,
2642 what
: AssocItemRender
) -> fmt
::Result
{
2644 let v
= match c
.impls
.get(&it
) {
2646 None
=> return Ok(()),
2648 let (non_trait
, traits
): (Vec
<_
>, _
) = v
.iter().partition(|i
| {
2649 i
.inner_impl().trait_
.is_none()
2651 if !non_trait
.is_empty() {
2652 let render_mode
= match what
{
2653 AssocItemRender
::All
=> {
2654 write
!(w
, "<h2 id='methods'>Methods</h2>")?
;
2657 AssocItemRender
::DerefFor { trait_, type_, deref_mut_ }
=> {
2658 write
!(w
, "<h2 id='deref-methods'>Methods from \
2659 {}<Target={}></h2>", trait_
, type_
)?
;
2660 RenderMode
::ForDeref { mut_: deref_mut_ }
2663 for i
in &non_trait
{
2664 render_impl(w
, cx
, i
, AssocItemLink
::Anchor(None
), render_mode
,
2665 containing_item
.stable_since())?
;
2668 if let AssocItemRender
::DerefFor { .. }
= what
{
2671 if !traits
.is_empty() {
2672 let deref_impl
= traits
.iter().find(|t
| {
2673 t
.inner_impl().trait_
.def_id() == c
.deref_trait_did
2675 if let Some(impl_
) = deref_impl
{
2676 let has_deref_mut
= traits
.iter().find(|t
| {
2677 t
.inner_impl().trait_
.def_id() == c
.deref_mut_trait_did
2679 render_deref_methods(w
, cx
, impl_
, containing_item
, has_deref_mut
)?
;
2681 write
!(w
, "<h2 id='implementations'>Trait \
2682 Implementations</h2>")?
;
2684 let did
= i
.trait_did().unwrap();
2685 let assoc_link
= AssocItemLink
::GotoSource(did
, &i
.inner_impl().provided_trait_methods
);
2686 render_impl(w
, cx
, i
, assoc_link
,
2687 RenderMode
::Normal
, containing_item
.stable_since())?
;
2693 fn render_deref_methods(w
: &mut fmt
::Formatter
, cx
: &Context
, impl_
: &Impl
,
2694 container_item
: &clean
::Item
, deref_mut
: bool
) -> fmt
::Result
{
2695 let deref_type
= impl_
.inner_impl().trait_
.as_ref().unwrap();
2696 let target
= impl_
.inner_impl().items
.iter().filter_map(|item
| {
2698 clean
::TypedefItem(ref t
, true) => Some(&t
.type_
),
2701 }).next().expect("Expected associated type binding");
2702 let what
= AssocItemRender
::DerefFor
{ trait_
: deref_type
, type_
: target
,
2703 deref_mut_
: deref_mut
};
2704 if let Some(did
) = target
.def_id() {
2705 render_assoc_items(w
, cx
, container_item
, did
, what
)
2707 if let Some(prim
) = target
.primitive_type() {
2708 if let Some(c
) = cache().primitive_locations
.get(&prim
) {
2709 let did
= DefId { krate: *c, index: prim.to_def_index() }
;
2710 render_assoc_items(w
, cx
, container_item
, did
, what
)?
;
2717 fn render_impl(w
: &mut fmt
::Formatter
, cx
: &Context
, i
: &Impl
, link
: AssocItemLink
,
2718 render_mode
: RenderMode
, outer_version
: Option
<&str>) -> fmt
::Result
{
2719 if render_mode
== RenderMode
::Normal
{
2720 write
!(w
, "<h3 class='impl'><span class='in-band'><code>{}</code>", i
.inner_impl())?
;
2721 write
!(w
, "</span><span class='out-of-band'>")?
;
2722 let since
= i
.impl_item
.stability
.as_ref().map(|s
| &s
.since
[..]);
2723 if let Some(l
) = (Item { item: &i.impl_item, cx: cx }
).href() {
2724 write
!(w
, "<div class='ghost'></div>")?
;
2725 render_stability_since_raw(w
, since
, outer_version
)?
;
2726 write
!(w
, "<a id='src-{}' class='srclink' \
2727 href='{}' title='{}'>[src]</a>",
2728 i
.impl_item
.def_id
.index
.as_usize(), l
, "goto source code")?
;
2730 render_stability_since_raw(w
, since
, outer_version
)?
;
2732 write
!(w
, "</span>")?
;
2733 write
!(w
, "</h3>\n")?
;
2734 if let Some(ref dox
) = i
.impl_item
.attrs
.value("doc") {
2735 write
!(w
, "<div class='docblock'>{}</div>", Markdown(dox
))?
;
2739 fn doc_impl_item(w
: &mut fmt
::Formatter
, cx
: &Context
, item
: &clean
::Item
,
2740 link
: AssocItemLink
, render_mode
: RenderMode
,
2741 is_default_item
: bool
, outer_version
: Option
<&str>,
2742 trait_
: Option
<&clean
::Trait
>) -> fmt
::Result
{
2743 let item_type
= item_type(item
);
2744 let name
= item
.name
.as_ref().unwrap();
2746 let render_method_item
: bool
= match render_mode
{
2747 RenderMode
::Normal
=> true,
2748 RenderMode
::ForDeref { mut_: deref_mut_ }
=> {
2749 let self_type_opt
= match item
.inner
{
2750 clean
::MethodItem(ref method
) => method
.decl
.self_type(),
2751 clean
::TyMethodItem(ref method
) => method
.decl
.self_type(),
2755 if let Some(self_ty
) = self_type_opt
{
2756 let by_mut_ref
= match self_ty
{
2757 SelfTy
::SelfBorrowed(_lifetime
, mutability
) => {
2758 mutability
== Mutability
::Mutable
2760 SelfTy
::SelfExplicit(clean
::BorrowedRef { mutability, .. }
) => {
2761 mutability
== Mutability
::Mutable
2766 deref_mut_
|| !by_mut_ref
2774 clean
::MethodItem(..) | clean
::TyMethodItem(..) => {
2775 // Only render when the method is not static or we allow static methods
2776 if render_method_item
{
2777 let id
= derive_id(format
!("{}.{}", item_type
, name
));
2778 let ns_id
= derive_id(format
!("{}.{}", name
, item_type
.name_space()));
2779 write
!(w
, "<h4 id='{}' class='{}'>", id
, item_type
)?
;
2780 write
!(w
, "<span id='{}' class='invisible'>", ns_id
)?
;
2781 write
!(w
, "<code>")?
;
2782 render_assoc_item(w
, item
, link
.anchor(&id
))?
;
2783 write
!(w
, "</code>")?
;
2784 render_stability_since_raw(w
, item
.stable_since(), outer_version
)?
;
2785 write
!(w
, "</span></h4>\n")?
;
2788 clean
::TypedefItem(ref tydef
, _
) => {
2789 let id
= derive_id(format
!("{}.{}", ItemType
::AssociatedType
, name
));
2790 let ns_id
= derive_id(format
!("{}.{}", name
, item_type
.name_space()));
2791 write
!(w
, "<h4 id='{}' class='{}'>", id
, item_type
)?
;
2792 write
!(w
, "<span id='{}' class='invisible'><code>", ns_id
)?
;
2793 assoc_type(w
, item
, &Vec
::new(), Some(&tydef
.type_
), link
.anchor(&id
))?
;
2794 write
!(w
, "</code></span></h4>\n")?
;
2796 clean
::AssociatedConstItem(ref ty
, ref default) => {
2797 let id
= derive_id(format
!("{}.{}", item_type
, name
));
2798 let ns_id
= derive_id(format
!("{}.{}", name
, item_type
.name_space()));
2799 write
!(w
, "<h4 id='{}' class='{}'>", id
, item_type
)?
;
2800 write
!(w
, "<span id='{}' class='invisible'><code>", ns_id
)?
;
2801 assoc_const(w
, item
, ty
, default.as_ref(), link
.anchor(&id
))?
;
2802 write
!(w
, "</code></span></h4>\n")?
;
2804 clean
::ConstantItem(ref c
) => {
2805 let id
= derive_id(format
!("{}.{}", item_type
, name
));
2806 let ns_id
= derive_id(format
!("{}.{}", name
, item_type
.name_space()));
2807 write
!(w
, "<h4 id='{}' class='{}'>", id
, item_type
)?
;
2808 write
!(w
, "<span id='{}' class='invisible'><code>", ns_id
)?
;
2809 assoc_const(w
, item
, &c
.type_
, Some(&c
.expr
), link
.anchor(&id
))?
;
2810 write
!(w
, "</code></span></h4>\n")?
;
2812 clean
::AssociatedTypeItem(ref bounds
, ref default) => {
2813 let id
= derive_id(format
!("{}.{}", item_type
, name
));
2814 let ns_id
= derive_id(format
!("{}.{}", name
, item_type
.name_space()));
2815 write
!(w
, "<h4 id='{}' class='{}'>", id
, item_type
)?
;
2816 write
!(w
, "<span id='{}' class='invisible'><code>", ns_id
)?
;
2817 assoc_type(w
, item
, bounds
, default.as_ref(), link
.anchor(&id
))?
;
2818 write
!(w
, "</code></span></h4>\n")?
;
2820 clean
::StrippedItem(..) => return Ok(()),
2821 _
=> panic
!("can't make docs for trait item with name {:?}", item
.name
)
2824 if render_method_item
|| render_mode
== RenderMode
::Normal
{
2825 if !is_default_item
{
2826 if let Some(t
) = trait_
{
2827 // The trait item may have been stripped so we might not
2828 // find any documentation or stability for it.
2829 if let Some(it
) = t
.items
.iter().find(|i
| i
.name
== item
.name
) {
2830 // We need the stability of the item from the trait
2831 // because impls can't have a stability.
2832 document_stability(w
, cx
, it
)?
;
2833 if item
.doc_value().is_some() {
2834 document_full(w
, item
)?
;
2836 // In case the item isn't documented,
2837 // provide short documentation from the trait.
2838 document_short(w
, it
, link
)?
;
2842 document(w
, cx
, item
)?
;
2845 document_stability(w
, cx
, item
)?
;
2846 document_short(w
, item
, link
)?
;
2852 let traits
= &cache().traits
;
2853 let trait_
= i
.trait_did().and_then(|did
| traits
.get(&did
));
2855 write
!(w
, "<div class='impl-items'>")?
;
2856 for trait_item
in &i
.inner_impl().items
{
2857 doc_impl_item(w
, cx
, trait_item
, link
, render_mode
,
2858 false, outer_version
, trait_
)?
;
2861 fn render_default_items(w
: &mut fmt
::Formatter
,
2865 render_mode
: RenderMode
,
2866 outer_version
: Option
<&str>) -> fmt
::Result
{
2867 for trait_item
in &t
.items
{
2868 let n
= trait_item
.name
.clone();
2869 if i
.items
.iter().find(|m
| m
.name
== n
).is_some() {
2872 let did
= i
.trait_
.as_ref().unwrap().def_id().unwrap();
2873 let assoc_link
= AssocItemLink
::GotoSource(did
, &i
.provided_trait_methods
);
2875 doc_impl_item(w
, cx
, trait_item
, assoc_link
, render_mode
, true,
2876 outer_version
, None
)?
;
2881 // If we've implemented a trait, then also emit documentation for all
2882 // default items which weren't overridden in the implementation block.
2883 if let Some(t
) = trait_
{
2884 render_default_items(w
, cx
, t
, &i
.inner_impl(), render_mode
, outer_version
)?
;
2886 write
!(w
, "</div>")?
;
2890 fn item_typedef(w
: &mut fmt
::Formatter
, cx
: &Context
, it
: &clean
::Item
,
2891 t
: &clean
::Typedef
) -> fmt
::Result
{
2892 write
!(w
, "<pre class='rust typedef'>type {}{}{where_clause} = {type_};</pre>",
2893 it
.name
.as_ref().unwrap(),
2895 where_clause
= WhereClause(&t
.generics
),
2901 impl<'a
> fmt
::Display
for Sidebar
<'a
> {
2902 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
2905 let parentlen
= cx
.current
.len() - if it
.is_mod() {1}
else {0}
;
2907 // the sidebar is designed to display sibling functions, modules and
2908 // other miscellaneous information. since there are lots of sibling
2909 // items (and that causes quadratic growth in large modules),
2910 // we refactor common parts into a shared JavaScript file per module.
2911 // still, we don't move everything into JS because we want to preserve
2912 // as much HTML as possible in order to allow non-JS-enabled browsers
2913 // to navigate the documentation (though slightly inefficiently).
2915 write
!(fmt
, "<p class='location'>")?
;
2916 for (i
, name
) in cx
.current
.iter().take(parentlen
).enumerate() {
2918 write
!(fmt
, "::<wbr>")?
;
2920 write
!(fmt
, "<a href='{}index.html'>{}</a>",
2921 &cx
.root_path
[..(cx
.current
.len() - i
- 1) * 3],
2924 write
!(fmt
, "</p>")?
;
2926 // sidebar refers to the enclosing module, not this module
2927 let relpath
= if it
.is_mod() { "../" }
else { "" }
;
2929 "<script>window.sidebarCurrent = {{\
2934 name
= it
.name
.as_ref().map(|x
| &x
[..]).unwrap_or(""),
2935 ty
= item_type(it
).css_class(),
2938 // there is no sidebar-items.js beyond the crate root path
2939 // FIXME maybe dynamic crate loading can be merged here
2941 write
!(fmt
, "<script defer src=\"{path}sidebar-items.js\"></script>",
2949 impl<'a
> fmt
::Display
for Source
<'a
> {
2950 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
2951 let Source(s
) = *self;
2952 let lines
= s
.lines().count();
2954 let mut tmp
= lines
;
2959 write
!(fmt
, "<pre class=\"line-numbers\">")?
;
2960 for i
in 1..lines
+ 1 {
2961 write
!(fmt
, "<span id=\"{0}\">{0:1$}</span>\n", i
, cols
)?
;
2963 write
!(fmt
, "</pre>")?
;
2964 write
!(fmt
, "{}", highlight
::render_with_highlighting(s
, None
, None
, None
))?
;
2969 fn item_macro(w
: &mut fmt
::Formatter
, cx
: &Context
, it
: &clean
::Item
,
2970 t
: &clean
::Macro
) -> fmt
::Result
{
2971 w
.write_str(&highlight
::render_with_highlighting(&t
.source
,
2975 render_stability_since_raw(w
, it
.stable_since(), None
)?
;
2979 fn item_primitive(w
: &mut fmt
::Formatter
, cx
: &Context
,
2981 _p
: &clean
::PrimitiveType
) -> fmt
::Result
{
2982 document(w
, cx
, it
)?
;
2983 render_assoc_items(w
, cx
, it
, it
.def_id
, AssocItemRender
::All
)
2986 const BASIC_KEYWORDS
: &'
static str = "rust, rustlang, rust-lang";
2988 fn make_item_keywords(it
: &clean
::Item
) -> String
{
2989 format
!("{}, {}", BASIC_KEYWORDS
, it
.name
.as_ref().unwrap())
2992 fn get_index_search_type(item
: &clean
::Item
) -> Option
<IndexItemFunctionType
> {
2993 let decl
= match item
.inner
{
2994 clean
::FunctionItem(ref f
) => &f
.decl
,
2995 clean
::MethodItem(ref m
) => &m
.decl
,
2996 clean
::TyMethodItem(ref m
) => &m
.decl
,
3000 let inputs
= decl
.inputs
.values
.iter().map(|arg
| get_index_type(&arg
.type_
)).collect();
3001 let output
= match decl
.output
{
3002 clean
::FunctionRetTy
::Return(ref return_type
) => Some(get_index_type(return_type
)),
3006 Some(IndexItemFunctionType { inputs: inputs, output: output }
)
3009 fn get_index_type(clean_type
: &clean
::Type
) -> Type
{
3010 Type { name: get_index_type_name(clean_type).map(|s| s.to_ascii_lowercase()) }
3013 fn get_index_type_name(clean_type
: &clean
::Type
) -> Option
<String
> {
3015 clean
::ResolvedPath { ref path, .. }
=> {
3016 let segments
= &path
.segments
;
3017 Some(segments
[segments
.len() - 1].name
.clone())
3019 clean
::Generic(ref s
) => Some(s
.clone()),
3020 clean
::Primitive(ref p
) => Some(format
!("{:?}", p
)),
3021 clean
::BorrowedRef { ref type_, .. }
=> get_index_type_name(type_
),
3022 // FIXME: add all from clean::Type.
3027 pub fn cache() -> Arc
<Cache
> {
3028 CACHE_KEY
.with(|c
| c
.borrow().clone())
3033 fn test_unique_id() {
3034 let input
= ["foo", "examples", "examples", "method.into_iter","examples",
3035 "method.into_iter", "foo", "main", "search", "methods",
3036 "examples", "method.into_iter", "assoc_type.Item", "assoc_type.Item"];
3037 let expected
= ["foo", "examples", "examples-1", "method.into_iter", "examples-2",
3038 "method.into_iter-1", "foo-1", "main-1", "search-1", "methods-1",
3039 "examples-3", "method.into_iter-2", "assoc_type.Item", "assoc_type.Item-1"];
3042 let actual
: Vec
<String
> = input
.iter().map(|s
| derive_id(s
.to_string())).collect();
3043 assert_eq
!(&actual
[..], expected
);