@@ -12,12 +12,14 @@ use std::cell::Cell;
12
12
use std:: comm:: { SharedPort , SharedChan } ;
13
13
use std:: comm;
14
14
use std:: fmt;
15
- use std:: hashmap:: HashMap ;
15
+ use std:: hashmap:: { HashMap , HashSet } ;
16
16
use std:: local_data;
17
17
use std:: rt:: io:: buffered:: BufferedWriter ;
18
18
use std:: rt:: io:: file:: { FileInfo , DirectoryInfo } ;
19
19
use std:: rt:: io:: file;
20
20
use std:: rt:: io;
21
+ use std:: rt:: io:: Reader ;
22
+ use std:: str;
21
23
use std:: task;
22
24
use std:: unstable:: finally:: Finally ;
23
25
use std:: util;
@@ -33,6 +35,7 @@ use syntax::attr;
33
35
use clean;
34
36
use doctree;
35
37
use fold:: DocFolder ;
38
+ use html:: escape:: Escape ;
36
39
use html:: format:: { VisSpace , Method , PuritySpace } ;
37
40
use html:: layout;
38
41
use html:: markdown:: Markdown ;
@@ -44,6 +47,7 @@ pub struct Context {
44
47
dst : Path ,
45
48
layout : layout:: Layout ,
46
49
sidebar : HashMap < ~str , ~[ ~str ] > ,
50
+ include_sources : bool ,
47
51
}
48
52
49
53
enum Implementor {
@@ -68,6 +72,12 @@ struct Cache {
68
72
priv search_index : ~[ IndexItem ] ,
69
73
}
70
74
75
+ struct SourceCollector < ' self > {
76
+ seen : HashSet < ~str > ,
77
+ dst : Path ,
78
+ cx : & ' self Context ,
79
+ }
80
+
71
81
struct Item < ' self > { cx : & ' self Context , item : & ' self clean:: Item , }
72
82
struct Sidebar < ' self > { cx : & ' self Context , item : & ' self clean:: Item , }
73
83
@@ -79,6 +89,8 @@ struct IndexItem {
79
89
parent : Option < ast:: NodeId > ,
80
90
}
81
91
92
+ struct Source < ' self > ( & ' self str ) ;
93
+
82
94
local_data_key ! ( pub cache_key: RWArc <Cache >)
83
95
local_data_key ! ( pub current_location_key: ~[ ~str ] )
84
96
@@ -94,6 +106,7 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
94
106
favicon: ~"",
95
107
crate : crate . name. clone ( ) ,
96
108
} ,
109
+ include_sources : true ,
97
110
} ;
98
111
mkdir ( & cx. dst ) ;
99
112
@@ -107,6 +120,9 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
107
120
clean:: NameValue ( ~"html_logo_url", ref s) => {
108
121
cx. layout . logo = s. to_owned ( ) ;
109
122
}
123
+ clean:: Word ( ~"html_no_source") => {
124
+ cx. include_sources = false ;
125
+ }
110
126
_ => { }
111
127
}
112
128
}
@@ -162,6 +178,19 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
162
178
w. flush ( ) ;
163
179
}
164
180
181
+ if cx. include_sources {
182
+ let dst = cx. dst . push ( "src" ) ;
183
+ mkdir ( & dst) ;
184
+ let dst = dst. push ( crate . name) ;
185
+ mkdir ( & dst) ;
186
+ let mut folder = SourceCollector {
187
+ dst : dst,
188
+ seen : HashSet :: new ( ) ,
189
+ cx : & cx,
190
+ } ;
191
+ crate = folder. fold_crate ( crate ) ;
192
+ }
193
+
165
194
// Now render the whole crate.
166
195
cx. crate ( crate , cache) ;
167
196
}
@@ -183,7 +212,80 @@ fn mkdir(path: &Path) {
183
212
}
184
213
}
185
214
186
- impl < ' self > DocFolder for Cache {
215
+ fn clean_srcpath ( src : & str , f : & fn ( & str ) ) {
216
+ let p = Path ( src) ;
217
+ for c in p. components . iter ( ) {
218
+ if "." == * c {
219
+ loop
220
+ }
221
+ if ".." == * c {
222
+ f ( "up" ) ;
223
+ } else {
224
+ f ( c. as_slice ( ) )
225
+ }
226
+ }
227
+ }
228
+
229
+ impl < ' self > DocFolder for SourceCollector < ' self > {
230
+ fn fold_item ( & mut self , item : clean:: Item ) -> Option < clean:: Item > {
231
+ if !self . seen . contains ( & item. source . filename ) {
232
+ self . emit_source ( item. source . filename ) ;
233
+ self . seen . insert ( item. source . filename . clone ( ) ) ;
234
+ }
235
+ self . fold_item_recur ( item)
236
+ }
237
+ }
238
+
239
+ impl < ' self > SourceCollector < ' self > {
240
+ fn emit_source ( & self , filename : & str ) {
241
+ let p = Path ( filename) ;
242
+
243
+ // Read the contents of the file
244
+ let mut contents = ~[ ] ;
245
+ {
246
+ let mut buf = [ 0 , ..1024 ] ;
247
+ let r = do io:: io_error:: cond. trap ( |_| { } ) . inside {
248
+ p. open_reader ( io:: Open )
249
+ } ;
250
+ // If we couldn't open this file, then just returns because it
251
+ // probably means that it's some standard library macro thing and we
252
+ // can't have the source to it anyway.
253
+ let mut r = match r { Some ( r) => r, None => return } ;
254
+
255
+ // read everything
256
+ loop {
257
+ match r. read ( buf) {
258
+ Some ( n) => contents. push_all ( buf. slice_to ( n) ) ,
259
+ None => break
260
+ }
261
+ }
262
+ }
263
+ let contents = str:: from_utf8_owned ( contents) ;
264
+
265
+ // Create the intermediate directories
266
+ let mut cur = self . dst . clone ( ) ;
267
+ let mut root_path = ~"../../";
268
+ do clean_srcpath(p.pop().to_str()) |component| {
269
+ cur = cur.push(component);
270
+ mkdir(&cur);
271
+ root_path.push_str(" ../");
272
+ }
273
+
274
+ let dst = cur.push(*p.components.last() + " . html ") ;
275
+ let mut w = dst. open_writer ( io:: CreateOrTruncate ) ;
276
+
277
+ let title = format ! ( "{} -- source" , * dst. components. last( ) ) ;
278
+ let page = layout:: Page {
279
+ title : title,
280
+ ty : "source" ,
281
+ root_path : root_path,
282
+ } ;
283
+ layout:: render ( & mut w as & mut io:: Writer , & self . cx . layout ,
284
+ & page, & ( "" ) , & Source ( contents. as_slice ( ) ) ) ;
285
+ }
286
+ }
287
+
288
+ impl DocFolder for Cache {
187
289
fn fold_item ( & mut self , item : clean:: Item ) -> Option < clean:: Item > {
188
290
// Register any generics to their corresponding string. This is used
189
291
// when pretty-printing types
@@ -380,7 +482,6 @@ impl Context {
380
482
return ret;
381
483
}
382
484
383
- /// Processes
384
485
fn crate ( self , mut crate : clean:: Crate , cache : Cache ) {
385
486
enum Work {
386
487
Die ,
@@ -565,6 +666,20 @@ impl<'self> fmt::Default for Item<'self> {
565
666
None => { }
566
667
}
567
668
669
+ if it. cx . include_sources {
670
+ let mut path = ~[ ] ;
671
+ do clean_srcpath ( it. item . source . filename ) |component| {
672
+ path. push ( component. to_owned ( ) ) ;
673
+ }
674
+ write ! ( fmt. buf,
675
+ "<a class='source'
676
+ href='{root}src/{crate}/{path}.html\\ #{line}'>[src]</a>" ,
677
+ root = it. cx. root_path,
678
+ crate = it. cx. layout. crate ,
679
+ path = path. connect( "/" ) ,
680
+ line = it. item. source. loline) ;
681
+ }
682
+
568
683
// Write the breadcrumb trail header for the top
569
684
write ! ( fmt. buf, "<h1 class='fqn'>" ) ;
570
685
match it. item . inner {
@@ -1180,3 +1295,23 @@ fn build_sidebar(m: &clean::Module) -> HashMap<~str, ~[~str]> {
1180
1295
}
1181
1296
return map;
1182
1297
}
1298
+
1299
+ impl < ' self > fmt:: Default for Source < ' self > {
1300
+ fn fmt ( s : & Source < ' self > , fmt : & mut fmt:: Formatter ) {
1301
+ let lines = s. line_iter ( ) . len ( ) ;
1302
+ let mut cols = 0 ;
1303
+ let mut tmp = lines;
1304
+ while tmp > 0 {
1305
+ cols += 1 ;
1306
+ tmp /= 10 ;
1307
+ }
1308
+ write ! ( fmt. buf, "<pre class='line-numbers'>" ) ;
1309
+ for i in range ( 1 , lines + 1 ) {
1310
+ write ! ( fmt. buf, "<span id='{0}'>{0:1$u}</span>\n " , i, cols) ;
1311
+ }
1312
+ write ! ( fmt. buf, "</pre>" ) ;
1313
+ write ! ( fmt. buf, "<pre class='rust'>" ) ;
1314
+ write ! ( fmt. buf, "{}" , Escape ( s. as_slice( ) ) ) ;
1315
+ write ! ( fmt. buf, "</pre>" ) ;
1316
+ }
1317
+ }
0 commit comments