]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use std::collections::BTreeMap; |
3dfed10e | 2 | use std::path::Path; |
60c5eb7d | 3 | |
3dfed10e XL |
4 | use rustc_data_structures::fx::FxHashMap; |
5 | use rustc_span::symbol::sym; | |
60c5eb7d | 6 | use serde::Serialize; |
e74abb32 | 7 | |
3dfed10e XL |
8 | use crate::clean::types::GetDefId; |
9 | use crate::clean::{self, AttributesExt}; | |
10 | use crate::formats::cache::Cache; | |
11 | use crate::formats::item_type::ItemType; | |
1b1a35ee | 12 | use crate::html::render::{plain_text_summary, shorten}; |
3dfed10e | 13 | use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind}; |
e74abb32 XL |
14 | |
15 | /// Indicates where an external crate can be found. | |
16 | pub enum ExternalLocation { | |
17 | /// Remote URL root of the external crate | |
18 | Remote(String), | |
19 | /// This external crate can be found in the local doc/ folder | |
20 | Local, | |
21 | /// The external crate could not be found. | |
22 | Unknown, | |
23 | } | |
24 | ||
e74abb32 XL |
25 | /// Attempts to find where an external crate is located, given that we're |
26 | /// rendering in to the specified source destination. | |
3dfed10e | 27 | pub fn extern_location( |
dfeec247 XL |
28 | e: &clean::ExternalCrate, |
29 | extern_url: Option<&str>, | |
30 | dst: &Path, | |
31 | ) -> ExternalLocation { | |
e74abb32 XL |
32 | use ExternalLocation::*; |
33 | // See if there's documentation generated into the local directory | |
34 | let local_location = dst.join(&e.name); | |
35 | if local_location.is_dir() { | |
36 | return Local; | |
37 | } | |
38 | ||
39 | if let Some(url) = extern_url { | |
40 | let mut url = url.to_string(); | |
74b04a01 | 41 | if !url.ends_with('/') { |
e74abb32 XL |
42 | url.push('/'); |
43 | } | |
44 | return Remote(url); | |
45 | } | |
46 | ||
47 | // Failing that, see if there's an attribute specifying where to find this | |
48 | // external crate | |
dfeec247 XL |
49 | e.attrs |
50 | .lists(sym::doc) | |
3dfed10e | 51 | .filter(|a| a.has_name(sym::html_root_url)) |
dfeec247 XL |
52 | .filter_map(|a| a.value_str()) |
53 | .map(|url| { | |
54 | let mut url = url.to_string(); | |
74b04a01 | 55 | if !url.ends_with('/') { |
dfeec247 XL |
56 | url.push('/') |
57 | } | |
58 | Remote(url) | |
59 | }) | |
60 | .next() | |
61 | .unwrap_or(Unknown) // Well, at least we tried. | |
e74abb32 XL |
62 | } |
63 | ||
64 | /// Builds the search index from the collected metadata | |
3dfed10e | 65 | pub fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { |
74b04a01 | 66 | let mut defid_to_pathid = FxHashMap::default(); |
e74abb32 | 67 | let mut crate_items = Vec::with_capacity(cache.search_index.len()); |
60c5eb7d | 68 | let mut crate_paths = vec![]; |
e74abb32 | 69 | |
f9f354fc XL |
70 | let Cache { ref mut search_index, ref orphan_impl_items, ref paths, ref mut aliases, .. } = |
71 | *cache; | |
e74abb32 XL |
72 | |
73 | // Attach all orphan items to the type's definition if the type | |
74 | // has since been learned. | |
75 | for &(did, ref item) in orphan_impl_items { | |
76 | if let Some(&(ref fqp, _)) = paths.get(&did) { | |
77 | search_index.push(IndexItem { | |
78 | ty: item.type_(), | |
79 | name: item.name.clone().unwrap(), | |
80 | path: fqp[..fqp.len() - 1].join("::"), | |
1b1a35ee | 81 | desc: shorten(plain_text_summary(item.doc_value())), |
e74abb32 XL |
82 | parent: Some(did), |
83 | parent_idx: None, | |
84 | search_type: get_index_search_type(&item), | |
85 | }); | |
f9f354fc XL |
86 | for alias in item.attrs.get_doc_aliases() { |
87 | aliases | |
88 | .entry(alias.to_lowercase()) | |
89 | .or_insert(Vec::new()) | |
90 | .push(search_index.len() - 1); | |
91 | } | |
e74abb32 XL |
92 | } |
93 | } | |
94 | ||
74b04a01 | 95 | // Reduce `DefId` in paths into smaller sequential numbers, |
e74abb32 XL |
96 | // and prune the paths that do not appear in the index. |
97 | let mut lastpath = String::new(); | |
98 | let mut lastpathid = 0usize; | |
99 | ||
100 | for item in search_index { | |
ba9703b0 | 101 | item.parent_idx = item.parent.and_then(|defid| { |
74b04a01 | 102 | if defid_to_pathid.contains_key(&defid) { |
ba9703b0 | 103 | defid_to_pathid.get(&defid).copied() |
e74abb32 XL |
104 | } else { |
105 | let pathid = lastpathid; | |
74b04a01 | 106 | defid_to_pathid.insert(defid, pathid); |
e74abb32 XL |
107 | lastpathid += 1; |
108 | ||
ba9703b0 XL |
109 | if let Some(&(ref fqp, short)) = paths.get(&defid) { |
110 | crate_paths.push((short, fqp.last().unwrap().clone())); | |
111 | Some(pathid) | |
112 | } else { | |
113 | None | |
114 | } | |
e74abb32 XL |
115 | } |
116 | }); | |
117 | ||
118 | // Omit the parent path if it is same to that of the prior item. | |
119 | if lastpath == item.path { | |
120 | item.path.clear(); | |
121 | } else { | |
122 | lastpath = item.path.clone(); | |
123 | } | |
60c5eb7d | 124 | crate_items.push(&*item); |
e74abb32 XL |
125 | } |
126 | ||
dfeec247 XL |
127 | let crate_doc = krate |
128 | .module | |
129 | .as_ref() | |
1b1a35ee | 130 | .map(|module| shorten(plain_text_summary(module.doc_value()))) |
29967ef6 | 131 | .unwrap_or_default(); |
e74abb32 | 132 | |
60c5eb7d XL |
133 | #[derive(Serialize)] |
134 | struct CrateData<'a> { | |
135 | doc: String, | |
136 | #[serde(rename = "i")] | |
137 | items: Vec<&'a IndexItem>, | |
138 | #[serde(rename = "p")] | |
139 | paths: Vec<(ItemType, String)>, | |
f9f354fc XL |
140 | // The String is alias name and the vec is the list of the elements with this alias. |
141 | // | |
142 | // To be noted: the `usize` elements are indexes to `items`. | |
143 | #[serde(rename = "a")] | |
144 | #[serde(skip_serializing_if = "BTreeMap::is_empty")] | |
145 | aliases: &'a BTreeMap<String, Vec<usize>>, | |
60c5eb7d | 146 | } |
e74abb32 XL |
147 | |
148 | // Collect the index into a string | |
60c5eb7d | 149 | format!( |
ba9703b0 | 150 | r#""{}":{}"#, |
60c5eb7d XL |
151 | krate.name, |
152 | serde_json::to_string(&CrateData { | |
153 | doc: crate_doc, | |
154 | items: crate_items, | |
155 | paths: crate_paths, | |
f9f354fc | 156 | aliases, |
60c5eb7d | 157 | }) |
dfeec247 | 158 | .expect("failed serde conversion") |
ba9703b0 XL |
159 | // All these `replace` calls are because we have to go through JS string for JSON content. |
160 | .replace(r"\", r"\\") | |
161 | .replace("'", r"\'") | |
162 | // We need to escape double quotes for the JSON. | |
163 | .replace("\\\"", "\\\\\"") | |
60c5eb7d | 164 | ) |
e74abb32 XL |
165 | } |
166 | ||
3dfed10e | 167 | crate fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> { |
e74abb32 XL |
168 | let (all_types, ret_types) = match item.inner { |
169 | clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types), | |
170 | clean::MethodItem(ref m) => (&m.all_types, &m.ret_types), | |
171 | clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types), | |
172 | _ => return None, | |
173 | }; | |
174 | ||
ba9703b0 XL |
175 | let inputs = all_types |
176 | .iter() | |
177 | .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind))) | |
178 | .filter(|a| a.ty.name.is_some()) | |
179 | .collect(); | |
dfeec247 XL |
180 | let output = ret_types |
181 | .iter() | |
ba9703b0 XL |
182 | .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind))) |
183 | .filter(|a| a.ty.name.is_some()) | |
dfeec247 XL |
184 | .collect::<Vec<_>>(); |
185 | let output = if output.is_empty() { None } else { Some(output) }; | |
e74abb32 XL |
186 | |
187 | Some(IndexItemFunctionType { inputs, output }) | |
188 | } | |
189 | ||
ba9703b0 XL |
190 | fn get_index_type(clean_type: &clean::Type) -> RenderType { |
191 | RenderType { | |
192 | ty: clean_type.def_id(), | |
193 | idx: None, | |
e74abb32 XL |
194 | name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()), |
195 | generics: get_generics(clean_type), | |
ba9703b0 | 196 | } |
e74abb32 XL |
197 | } |
198 | ||
199 | fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> { | |
200 | match *clean_type { | |
201 | clean::ResolvedPath { ref path, .. } => { | |
202 | let segments = &path.segments; | |
3dfed10e XL |
203 | let path_segment = segments.iter().last().unwrap_or_else(|| { |
204 | panic!( | |
e74abb32 XL |
205 | "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path", |
206 | clean_type, accept_generic | |
3dfed10e XL |
207 | ) |
208 | }); | |
e74abb32 XL |
209 | Some(path_segment.name.clone()) |
210 | } | |
211 | clean::Generic(ref s) if accept_generic => Some(s.clone()), | |
212 | clean::Primitive(ref p) => Some(format!("{:?}", p)), | |
213 | clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic), | |
214 | // FIXME: add all from clean::Type. | |
dfeec247 | 215 | _ => None, |
e74abb32 XL |
216 | } |
217 | } | |
218 | ||
ba9703b0 | 219 | fn get_generics(clean_type: &clean::Type) -> Option<Vec<Generic>> { |
dfeec247 XL |
220 | clean_type.generics().and_then(|types| { |
221 | let r = types | |
222 | .iter() | |
ba9703b0 XL |
223 | .filter_map(|t| { |
224 | get_index_type_name(t, false).map(|name| Generic { | |
225 | name: name.to_ascii_lowercase(), | |
226 | defid: t.def_id(), | |
227 | idx: None, | |
228 | }) | |
229 | }) | |
dfeec247 XL |
230 | .collect::<Vec<_>>(); |
231 | if r.is_empty() { None } else { Some(r) } | |
232 | }) | |
e74abb32 | 233 | } |