(index<- ) ./librustdoc/passes.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
1 // Copyright 2012-2014 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.
4 //
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.
10
11 use collections::HashSet;
12 use rustc::util::nodemap::NodeSet;
13 use std::cmp;
14 use std::strbuf::StrBuf;
15 use std::uint;
16 use syntax::ast;
17
18 use clean;
19 use clean::Item;
20 use plugins;
21 use fold;
22 use fold::DocFolder;
23
24 /// Strip items marked `#[doc(hidden)]`
25 pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
26 let mut stripped = HashSet::new();
27
28 // strip all #[doc(hidden)] items
29 let krate = {
30 struct Stripper<'a> {
31 stripped: &'a mut HashSet<ast::NodeId>
32 };
33 impl<'a> fold::DocFolder for Stripper<'a> {
34 fn fold_item(&mut self, i: Item) -> Option<Item> {
35 if i.is_hidden_from_doc() {
36 debug!("found one in strip_hidden; removing");
37 self.stripped.insert(i.id);
38
39 // use a dedicated hidden item for given item type if any
40 match i.inner {
41 clean::StructFieldItem(..) => {
42 return Some(clean::Item {
43 inner: clean::StructFieldItem(clean::HiddenStructField),
44 ..i
45 });
46 }
47 _ => {
48 return None;
49 }
50 }
51 }
52
53 self.fold_item_recur(i)
54 }
55 }
56 let mut stripper = Stripper{ stripped: &mut stripped };
57 stripper.fold_crate(krate)
58 };
59
60 // strip any traits implemented on stripped items
61 let krate = {
62 struct ImplStripper<'a> {
63 stripped: &'a mut HashSet<ast::NodeId>
64 };
65 impl<'a> fold::DocFolder for ImplStripper<'a> {
66 fn fold_item(&mut self, i: Item) -> Option<Item> {
67 match i.inner {
68 clean::ImplItem(clean::Impl{ for_: clean::ResolvedPath{ id: for_id, .. },
69 .. }) => {
70 if self.stripped.contains(&for_id) {
71 return None;
72 }
73 }
74 _ => {}
75 }
76 self.fold_item_recur(i)
77 }
78 }
79 let mut stripper = ImplStripper{ stripped: &mut stripped };
80 stripper.fold_crate(krate)
81 };
82
83 (krate, None)
84 }
85
86 /// Strip private items from the point of view of a crate or externally from a
87 /// crate, specified by the `xcrate` flag.
88 pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult {
89 // This stripper collects all *retained* nodes.
90 let mut retained = HashSet::new();
91 let analysis = super::analysiskey.get().unwrap();
92 let exported_items = analysis.exported_items.clone();
93
94 // strip all private items
95 {
96 let mut stripper = Stripper {
97 retained: &mut retained,
98 exported_items: &exported_items,
99 };
100 krate = stripper.fold_crate(krate);
101 }
102
103 // strip all private implementations of traits
104 {
105 let mut stripper = ImplStripper(&retained);
106 krate = stripper.fold_crate(krate);
107 }
108 (krate, None)
109 }
110
111 struct Stripper<'a> {
112 retained: &'a mut HashSet<ast::NodeId>,
113 exported_items: &'a NodeSet,
114 }
115
116 impl<'a> fold::DocFolder for Stripper<'a> {
117 fn fold_item(&mut self, i: Item) -> Option<Item> {
118 match i.inner {
119 // These items can all get re-exported
120 clean::TypedefItem(..) | clean::StaticItem(..) |
121 clean::StructItem(..) | clean::EnumItem(..) |
122 clean::TraitItem(..) | clean::FunctionItem(..) |
123 clean::VariantItem(..) | clean::MethodItem(..) |
124 clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) => {
125 if !self.exported_items.contains(&i.id) {
126 return None;
127 }
128 }
129
130 clean::ViewItemItem(..) => {
131 if i.visibility != Some(ast::Public) {
132 return None
133 }
134 }
135
136 clean::StructFieldItem(..) => {
137 if i.visibility != Some(ast::Public) {
138 return Some(clean::Item {
139 inner: clean::StructFieldItem(clean::HiddenStructField),
140 ..i
141 })
142 }
143 }
144
145 // handled below
146 clean::ModuleItem(..) => {}
147
148 // trait impls for private items should be stripped
149 clean::ImplItem(clean::Impl{ for_: clean::ResolvedPath{ id: ref for_id, .. }, .. }) => {
150 if !self.exported_items.contains(for_id) {
151 return None;
152 }
153 }
154 clean::ImplItem(..) => {}
155
156 // tymethods/macros have no control over privacy
157 clean::MacroItem(..) | clean::TyMethodItem(..) => {}
158 }
159
160 let fastreturn = match i.inner {
161 // nothing left to do for traits (don't want to filter their
162 // methods out, visibility controlled by the trait)
163 clean::TraitItem(..) => true,
164
165 // implementations of traits are always public.
166 clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
167
168 _ => false,
169 };
170
171 let i = if fastreturn {
172 self.retained.insert(i.id);
173 return Some(i);
174 } else {
175 self.fold_item_recur(i)
176 };
177
178 match i {
179 Some(i) => {
180 match i.inner {
181 // emptied modules/impls have no need to exist
182 clean::ModuleItem(ref m)
183 if m.items.len() == 0 &&
184 i.doc_value().is_none() => None,
185 clean::ImplItem(ref i) if i.methods.len() == 0 => None,
186 _ => {
187 self.retained.insert(i.id);
188 Some(i)
189 }
190 }
191 }
192 None => None,
193 }
194 }
195 }
196
197 // This stripper discards all private impls of traits
198 struct ImplStripper<'a>(&'a HashSet<ast::NodeId>);
199 impl<'a> fold::DocFolder for ImplStripper<'a> {
200 fn fold_item(&mut self, i: Item) -> Option<Item> {
201 match i.inner {
202 clean::ImplItem(ref imp) => {
203 match imp.trait_ {
204 Some(clean::ResolvedPath{ id, .. }) => {
205 let ImplStripper(s) = *self;
206 if !s.contains(&id) {
207 return None;
208 }
209 }
210 Some(..) | None => {}
211 }
212 }
213 _ => {}
214 }
215 self.fold_item_recur(i)
216 }
217 }
218
219
220 pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
221 struct CommentCleaner;
222 impl fold::DocFolder for CommentCleaner {
223 fn fold_item(&mut self, i: Item) -> Option<Item> {
224 let mut i = i;
225 let mut avec: Vec<clean::Attribute> = Vec::new();
226 for attr in i.attrs.iter() {
227 match attr {
228 &clean::NameValue(ref x, ref s) if "doc" == *x => avec.push(
229 clean::NameValue("doc".to_owned(), unindent(*s))),
230 x => avec.push(x.clone())
231 }
232 }
233 i.attrs = avec;
234 self.fold_item_recur(i)
235 }
236 }
237 let mut cleaner = CommentCleaner;
238 let krate = cleaner.fold_crate(krate);
239 (krate, None)
240 }
241
242 pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
243 struct Collapser;
244 impl fold::DocFolder for Collapser {
245 fn fold_item(&mut self, i: Item) -> Option<Item> {
246 let mut docstr = StrBuf::new();
247 let mut i = i;
248 for attr in i.attrs.iter() {
249 match *attr {
250 clean::NameValue(ref x, ref s) if "doc" == *x => {
251 docstr.push_str(s.clone());
252 docstr.push_char('\n');
253 },
254 _ => ()
255 }
256 }
257 let mut a: Vec<clean::Attribute> = i.attrs.iter().filter(|&a| match a {
258 &clean::NameValue(ref x, _) if "doc" == *x => false,
259 _ => true
260 }).map(|x| x.clone()).collect();
261 if docstr.len() > 0 {
262 a.push(clean::NameValue("doc".to_owned(), docstr.into_owned()));
263 }
264 i.attrs = a;
265 self.fold_item_recur(i)
266 }
267 }
268 let mut collapser = Collapser;
269 let krate = collapser.fold_crate(krate);
270 (krate, None)
271 }
272
273 pub fn unindent(s: &str) -> ~str {
274 let lines = s.lines_any().collect::<Vec<&str> >();
275 let mut saw_first_line = false;
276 let mut saw_second_line = false;
277 let min_indent = lines.iter().fold(uint::MAX, |min_indent, line| {
278
279 // After we see the first non-whitespace line, look at
280 // the line we have. If it is not whitespace, and therefore
281 // part of the first paragraph, then ignore the indentation
282 // level of the first line
283 let ignore_previous_indents =
284 saw_first_line &&
285 !saw_second_line &&
286 !line.is_whitespace();
287
288 let min_indent = if ignore_previous_indents {
289 uint::MAX
290 } else {
291 min_indent
292 };
293
294 if saw_first_line {
295 saw_second_line = true;
296 }
297
298 if line.is_whitespace() {
299 min_indent
300 } else {
301 saw_first_line = true;
302 let mut spaces = 0;
303 line.chars().all(|char| {
304 // Only comparing against space because I wouldn't
305 // know what to do with mixed whitespace chars
306 if char == ' ' {
307 spaces += 1;
308 true
309 } else {
310 false
311 }
312 });
313 cmp::min(min_indent, spaces)
314 }
315 });
316
317 if lines.len() >= 1 {
318 let mut unindented = vec!( lines.get(0).trim() );
319 unindented.push_all(lines.tail().iter().map(|&line| {
320 if line.is_whitespace() {
321 line
322 } else {
323 assert!(line.len() >= min_indent);
324 line.slice_from(min_indent)
325 }
326 }).collect::<Vec<_>>().as_slice());
327 unindented.connect("\n")
328 } else {
329 s.to_owned()
330 }
331 }
332
333 #[cfg(test)]
334 mod unindent_tests {
335 use super::unindent;
336
337 #[test]
338 fn should_unindent() {
339 let s = " line1\n line2".to_owned();
340 let r = unindent(s);
341 assert_eq!(r, "line1\nline2".to_owned());
342 }
343
344 #[test]
345 fn should_unindent_multiple_paragraphs() {
346 let s = " line1\n\n line2".to_owned();
347 let r = unindent(s);
348 assert_eq!(r, "line1\n\nline2".to_owned());
349 }
350
351 #[test]
352 fn should_leave_multiple_indent_levels() {
353 // Line 2 is indented another level beyond the
354 // base indentation and should be preserved
355 let s = " line1\n\n line2".to_owned();
356 let r = unindent(s);
357 assert_eq!(r, "line1\n\n line2".to_owned());
358 }
359
360 #[test]
361 fn should_ignore_first_line_indent() {
362 // Thi first line of the first paragraph may not be indented as
363 // far due to the way the doc string was written:
364 //
365 // #[doc = "Start way over here
366 // and continue here"]
367 let s = "line1\n line2".to_owned();
368 let r = unindent(s);
369 assert_eq!(r, "line1\nline2".to_owned());
370 }
371
372 #[test]
373 fn should_not_ignore_first_line_indent_in_a_single_line_para() {
374 let s = "line1\n\n line2".to_owned();
375 let r = unindent(s);
376 assert_eq!(r, "line1\n\n line2".to_owned());
377 }
378 }
librustdoc/passes.rs:219:1-219:1 -fn- definition:
pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
struct CommentCleaner;
impl fold::DocFolder for CommentCleaner {
references:- 2librustdoc/test.rs:
81: let krate = v.clean();
82: let (krate, _) = passes::unindent_comments(krate);
83: let (krate, _) = passes::collapse_docs(krate);
librustdoc/lib.rs:
66: "strips all doc(hidden) items from the output"),
67: ("unindent-comments", passes::unindent_comments,
68: "removes excess indentation on comments in order for markdown to like it"),
librustdoc/passes.rs:62:8-62:8 -struct- definition:
struct ImplStripper<'a> {
stripped: &'a mut HashSet<ast::NodeId>
};
references:- 264: };
65: impl<'a> fold::DocFolder for ImplStripper<'a> {
66: fn fold_item(&mut self, i: Item) -> Option<Item> {
--
78: }
79: let mut stripper = ImplStripper{ stripped: &mut stripped };
80: stripper.fold_crate(krate)
librustdoc/passes.rs:30:8-30:8 -struct- definition:
struct Stripper<'a> {
stripped: &'a mut HashSet<ast::NodeId>
};
references:- 232: };
33: impl<'a> fold::DocFolder for Stripper<'a> {
34: fn fold_item(&mut self, i: Item) -> Option<Item> {
--
55: }
56: let mut stripper = Stripper{ stripped: &mut stripped };
57: stripper.fold_crate(krate)
librustdoc/passes.rs:110:1-110:1 -struct- definition:
struct Stripper<'a> {
retained: &'a mut HashSet<ast::NodeId>,
exported_items: &'a NodeSet,
references:- 2116: impl<'a> fold::DocFolder for Stripper<'a> {
117: fn fold_item(&mut self, i: Item) -> Option<Item> {
librustdoc/passes.rs:241:1-241:1 -fn- definition:
pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
struct Collapser;
impl fold::DocFolder for Collapser {
references:- 2librustdoc/lib.rs:
68: "removes excess indentation on comments in order for markdown to like it"),
69: ("collapse-docs", passes::collapse_docs,
70: "concatenates all document attributes into one document attribute"),
librustdoc/test.rs:
82: let (krate, _) = passes::unindent_comments(krate);
83: let (krate, _) = passes::collapse_docs(krate);