(index<- ) ./librustdoc/html/toc.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Sat Apr 19 11:22:39 2014
1 // Copyright 2013 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 //! Table-of-contents creation.
12
13 use std::fmt;
14 use std::strbuf::StrBuf;
15
16 /// A (recursive) table of contents
17 #[deriving(Eq)]
18 pub struct Toc {
19 /// The levels are strictly decreasing, i.e.
20 ///
21 /// entries[0].level >= entries[1].level >= ...
22 ///
23 /// Normally they are equal, but can differ in cases like A and B,
24 /// both of which end up in the same `Toc` as they have the same
25 /// parent (Main).
26 ///
27 /// # Main
28 /// ### A
29 /// ## B
30 entries: Vec<TocEntry>
31 }
32
33 impl Toc {
34 fn count_entries_with_level(&self, level: u32) -> uint {
35 self.entries.iter().count(|e| e.level == level)
36 }
37 }
38
39 #[deriving(Eq)]
40 pub struct TocEntry {
41 level: u32,
42 sec_number: ~str,
43 name: ~str,
44 id: ~str,
45 children: Toc,
46 }
47
48 /// Progressive construction of a table of contents.
49 #[deriving(Eq)]
50 pub struct TocBuilder {
51 top_level: Toc,
52 /// The current heirachy of parent headings, the levels are
53 /// strictly increasing (i.e. chain[0].level < chain[1].level <
54 /// ...) with each entry being the most recent occurance of a
55 /// heading with that level (it doesn't include the most recent
56 /// occurences of every level, just, if *is* in `chain` then is is
57 /// the most recent one).
58 ///
59 /// We also have `chain[0].level <= top_level.entries[last]`.
60 chain: Vec<TocEntry>
61 }
62
63 impl TocBuilder {
64 pub fn new() -> TocBuilder {
65 TocBuilder { top_level: Toc { entries: Vec::new() }, chain: Vec::new() }
66 }
67
68
69 /// Convert into a true `Toc` struct.
70 pub fn into_toc(mut self) -> Toc {
71 // we know all levels are >= 1.
72 self.fold_until(0);
73 self.top_level
74 }
75
76 /// Collapse the chain until the first heading more important than
77 /// `level` (i.e. lower level)
78 ///
79 /// Example:
80 ///
81 /// ## A
82 /// # B
83 /// # C
84 /// ## D
85 /// ## E
86 /// ### F
87 /// #### G
88 /// ### H
89 ///
90 /// If we are considering H (i.e. level 3), then A and B are in
91 /// self.top_level, D is in C.children, and C, E, F, G are in
92 /// self.chain.
93 ///
94 /// When we attempt to push H, we realise that first G is not the
95 /// parent (level is too high) so it is popped from chain and put
96 /// into F.children, then F isn't the parent (level is equal, aka
97 /// sibling), so it's also popped and put into E.children.
98 ///
99 /// This leaves us looking at E, which does have a smaller level,
100 /// and, by construction, it's the most recent thing with smaller
101 /// level, i.e. it's the immediate parent of H.
102 fn fold_until(&mut self, level: u32) {
103 let mut this = None;
104 loop {
105 match self.chain.pop() {
106 Some(mut next) => {
107 this.map(|e| next.children.entries.push(e));
108 if next.level < level {
109 // this is the parent we want, so return it to
110 // its rightful place.
111 self.chain.push(next);
112 return
113 } else {
114 this = Some(next);
115 }
116 }
117 None => {
118 this.map(|e| self.top_level.entries.push(e));
119 return
120 }
121 }
122 }
123 }
124
125 /// Push a level `level` heading into the appropriate place in the
126 /// heirarchy, returning a string containing the section number in
127 /// `<num>.<num>.<num>` format.
128 pub fn push<'a>(&'a mut self, level: u32, name: ~str, id: ~str) -> &'a str {
129 assert!(level >= 1);
130
131 // collapse all previous sections into their parents until we
132 // get to relevant heading (i.e. the first one with a smaller
133 // level than us)
134 self.fold_until(level);
135
136 let mut sec_number;
137 {
138 let (toc_level, toc) = match self.chain.last() {
139 None => {
140 sec_number = StrBuf::new();
141 (0, &self.top_level)
142 }
143 Some(entry) => {
144 sec_number = StrBuf::from_str(entry.sec_number.clone());
145 sec_number.push_str(".");
146 (entry.level, &entry.children)
147 }
148 };
149 // fill in any missing zeros, e.g. for
150 // # Foo (1)
151 // ### Bar (1.0.1)
152 for _ in range(toc_level, level - 1) {
153 sec_number.push_str("0.");
154 }
155 let number = toc.count_entries_with_level(level);
156 sec_number.push_str(format!("{}", number + 1))
157 }
158
159 self.chain.push(TocEntry {
160 level: level,
161 name: name,
162 sec_number: sec_number.into_owned(),
163 id: id,
164 children: Toc { entries: Vec::new() }
165 });
166
167 // get the thing we just pushed, so we can borrow the string
168 // out of it with the right lifetime
169 let just_inserted = self.chain.mut_last().unwrap();
170 just_inserted.sec_number.as_slice()
171 }
172 }
173
174 impl fmt::Show for Toc {
175 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
176 try!(write!(fmt.buf, "<ul>"));
177 for entry in self.entries.iter() {
178 // recursively format this table of contents (the
179 // `{children}` is the key).
180 try!(write!(fmt.buf,
181 "\n<li><a href=\"\\#{id}\">{num} {name}</a>{children}</li>",
182 id = entry.id,
183 num = entry.sec_number, name = entry.name,
184 children = entry.children))
185 }
186 write!(fmt.buf, "</ul>")
187 }
188 }
189
190 #[cfg(test)]
191 mod test {
192 use super::{TocBuilder, Toc, TocEntry};
193
194 #[test]
195 fn builder_smoke() {
196 let mut builder = TocBuilder::new();
197
198 // this is purposely not using a fancy macro like below so
199 // that we're sure that this is doing the correct thing, and
200 // there's been no macro mistake.
201 macro_rules! push {
202 ($level: expr, $name: expr) => {
203 assert_eq!(builder.push($level, $name.to_owned(), "".to_owned()), $name);
204 }
205 }
206 push!(2, "0.1");
207 push!(1, "1");
208 {
209 push!(2, "1.1");
210 {
211 push!(3, "1.1.1");
212 push!(3, "1.1.2");
213 }
214 push!(2, "1.2");
215 {
216 push!(3, "1.2.1");
217 push!(3, "1.2.2");
218 }
219 }
220 push!(1, "2");
221 push!(1, "3");
222 {
223 push!(4, "3.0.0.1");
224 {
225 push!(6, "3.0.0.1.0.1");
226 }
227 push!(4, "3.0.0.2");
228 push!(2, "3.1");
229 {
230 push!(4, "3.1.0.1");
231 }
232 }
233
234 macro_rules! toc {
235 ($(($level: expr, $name: expr, $(($sub: tt))* )),*) => {
236 Toc {
237 entries: vec!(
238 $(
239 TocEntry {
240 level: $level,
241 name: $name.to_owned(),
242 sec_number: $name.to_owned(),
243 id: "".to_owned(),
244 children: toc!($($sub),*)
245 }
246 ),*
247 )
248 }
249 }
250 }
251 let expected = toc!(
252 (2, "0.1", ),
253
254 (1, "1",
255 ((2, "1.1", ((3, "1.1.1", )) ((3, "1.1.2", ))))
256 ((2, "1.2", ((3, "1.2.1", )) ((3, "1.2.2", ))))
257 ),
258
259 (1, "2", ),
260
261 (1, "3",
262 ((4, "3.0.0.1", ((6, "3.0.0.1.0.1", ))))
263 ((4, "3.0.0.2", ))
264 ((2, "3.1", ((4, "3.1.0.1", ))))
265 )
266 );
267 assert_eq!(expected, builder.into_toc());
268 }
269 }
librustdoc/html/toc.rs:49:16-49:16 -struct- definition:
pub struct TocBuilder {
top_level: Toc,
/// The current heirachy of parent headings, the levels are
references:- 1164: pub fn new() -> TocBuilder {
65: TocBuilder { top_level: Toc { entries: Vec::new() }, chain: Vec::new() }
66: }
librustdoc/html/markdown.rs:
95: *hoedown_buffer, *mut libc::c_void),
96: toc_builder: Option<TocBuilder>,
97: }
librustdoc/html/toc.rs:
48: /// Progressive construction of a table of contents.
50: pub struct TocBuilder {
librustdoc/html/toc.rs:39:16-39:16 -struct- definition:
pub struct TocEntry {
level: u32,
sec_number: ~str,
references:- 10159: self.chain.push(TocEntry {
160: level: level,
librustdoc/html/toc.rs:17:16-17:16 -struct- definition:
pub struct Toc {
/// The levels are strictly decreasing, i.e.
///
references:- 14163: id: id,
164: children: Toc { entries: Vec::new() }
165: });
--
174: impl fmt::Show for Toc {
175: fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {