@@ -342,6 +342,51 @@ impl fmt::Display for IndexItemFunctionType {
342
342
thread_local ! ( static CACHE_KEY : RefCell <Arc <Cache >> = Default :: default ( ) ) ;
343
343
thread_local ! ( pub static CURRENT_LOCATION_KEY : RefCell <Vec <String >> =
344
344
RefCell :: new( Vec :: new( ) ) ) ;
345
+ thread_local ! ( static USED_ID_MAP : RefCell <HashMap <String , usize >> =
346
+ RefCell :: new( init_ids( ) ) ) ;
347
+
348
+ fn init_ids ( ) -> HashMap < String , usize > {
349
+ [
350
+ "main" ,
351
+ "search" ,
352
+ "help" ,
353
+ "TOC" ,
354
+ "render-detail" ,
355
+ "associated-types" ,
356
+ "associated-const" ,
357
+ "required-methods" ,
358
+ "provided-methods" ,
359
+ "implementors" ,
360
+ "implementors-list" ,
361
+ "methods" ,
362
+ "deref-methods" ,
363
+ "implementations" ,
364
+ "derived_implementations"
365
+ ] . into_iter ( ) . map ( |id| ( String :: from ( * id) , 1 ) ) . collect :: < HashMap < _ , _ > > ( )
366
+ }
367
+
368
+ /// This method resets the local table of used ID attributes. This is typically
369
+ /// used at the beginning of rendering an entire HTML page to reset from the
370
+ /// previous state (if any).
371
+ pub fn reset_ids ( ) {
372
+ USED_ID_MAP . with ( |s| * s. borrow_mut ( ) = init_ids ( ) ) ;
373
+ }
374
+
375
+ pub fn derive_id ( candidate : String ) -> String {
376
+ USED_ID_MAP . with ( |map| {
377
+ let id = match map. borrow_mut ( ) . get_mut ( & candidate) {
378
+ None => candidate,
379
+ Some ( a) => {
380
+ let id = format ! ( "{}-{}" , candidate, * a) ;
381
+ * a += 1 ;
382
+ id
383
+ }
384
+ } ;
385
+
386
+ map. borrow_mut ( ) . insert ( id. clone ( ) , 1 ) ;
387
+ id
388
+ } )
389
+ }
345
390
346
391
/// Generates the documentation for `crate` into the directory `dst`
347
392
pub fn run ( mut krate : clean:: Crate ,
@@ -1276,7 +1321,7 @@ impl Context {
1276
1321
keywords : & keywords,
1277
1322
} ;
1278
1323
1279
- markdown :: reset_headers ( ) ;
1324
+ reset_ids ( ) ;
1280
1325
1281
1326
// We have a huge number of calls to write, so try to alleviate some
1282
1327
// of the pain by using a buffered writer instead of invoking the
@@ -1698,10 +1743,9 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
1698
1743
ItemType :: AssociatedType => ( "associated-types" , "Associated Types" ) ,
1699
1744
ItemType :: AssociatedConst => ( "associated-consts" , "Associated Constants" ) ,
1700
1745
} ;
1701
- try!( write ! ( w,
1702
- "<h2 id='{id}' class='section-header'>\
1703
- <a href=\" #{id}\" >{name}</a></h2>\n <table>",
1704
- id = short, name = name) ) ;
1746
+ try!( write ! ( w, "<h2 id='{id}' class='section-header'>\
1747
+ <a href=\" #{id}\" >{name}</a></h2>\n <table>",
1748
+ id = derive_id( short. to_owned( ) ) , name = name) ) ;
1705
1749
}
1706
1750
1707
1751
match myitem. inner {
@@ -1922,10 +1966,11 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
1922
1966
1923
1967
fn trait_item ( w : & mut fmt:: Formatter , cx : & Context , m : & clean:: Item )
1924
1968
-> fmt:: Result {
1925
- try!( write ! ( w, "<h3 id='{ty}.{name}' class='method stab {stab}'><code>" ,
1926
- ty = shortty( m) ,
1927
- name = * m. name. as_ref( ) . unwrap( ) ,
1928
- stab = m. stability_class( ) ) ) ;
1969
+ let name = m. name . as_ref ( ) . unwrap ( ) ;
1970
+ let id = derive_id ( format ! ( "{}.{}" , shortty( m) , name) ) ;
1971
+ try!( write ! ( w, "<h3 id='{id}' class='method stab {stab}'><code>" ,
1972
+ id = id,
1973
+ stab = m. stability_class( ) ) ) ;
1929
1974
try!( render_assoc_item ( w, m, AssocItemLink :: Anchor ) ) ;
1930
1975
try!( write ! ( w, "</code></h3>" ) ) ;
1931
1976
try!( document ( w, cx, m) ) ;
@@ -2420,44 +2465,38 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
2420
2465
2421
2466
fn doctraititem ( w : & mut fmt:: Formatter , cx : & Context , item : & clean:: Item ,
2422
2467
link : AssocItemLink , render_static : bool ) -> fmt:: Result {
2468
+ let name = item. name . as_ref ( ) . unwrap ( ) ;
2423
2469
match item. inner {
2424
2470
clean:: MethodItem ( ..) | clean:: TyMethodItem ( ..) => {
2425
2471
// Only render when the method is not static or we allow static methods
2426
2472
if !is_static_method ( item) || render_static {
2427
- try!( write ! ( w, "<h4 id='method.{}' class='{}'><code>" ,
2428
- * item. name. as_ref( ) . unwrap( ) ,
2429
- shortty( item) ) ) ;
2473
+ let id = derive_id ( format ! ( "method.{}" , name) ) ;
2474
+ try!( write ! ( w, "<h4 id='{}' class='{}'><code>" , id, shortty( item) ) ) ;
2430
2475
try!( render_assoc_item ( w, item, link) ) ;
2431
2476
try!( write ! ( w, "</code></h4>\n " ) ) ;
2432
2477
}
2433
2478
}
2434
2479
clean:: TypedefItem ( ref tydef, _) => {
2435
- let name = item. name . as_ref ( ) . unwrap ( ) ;
2436
- try!( write ! ( w, "<h4 id='assoc_type.{}' class='{}'><code>" ,
2437
- * name,
2438
- shortty( item) ) ) ;
2480
+ let id = derive_id ( format ! ( "assoc_type.{}" , name) ) ;
2481
+ try!( write ! ( w, "<h4 id='{}' class='{}'><code>" , id, shortty( item) ) ) ;
2439
2482
try!( write ! ( w, "type {} = {}" , name, tydef. type_) ) ;
2440
2483
try!( write ! ( w, "</code></h4>\n " ) ) ;
2441
2484
}
2442
2485
clean:: AssociatedConstItem ( ref ty, ref default) => {
2443
- let name = item. name . as_ref ( ) . unwrap ( ) ;
2444
- try!( write ! ( w, "<h4 id='assoc_const.{}' class='{}'><code>" ,
2445
- * name, shortty( item) ) ) ;
2486
+ let id = derive_id ( format ! ( "assoc_const.{}" , name) ) ;
2487
+ try!( write ! ( w, "<h4 id='{}' class='{}'><code>" , id, shortty( item) ) ) ;
2446
2488
try!( assoc_const ( w, item, ty, default. as_ref ( ) ) ) ;
2447
2489
try!( write ! ( w, "</code></h4>\n " ) ) ;
2448
2490
}
2449
2491
clean:: ConstantItem ( ref c) => {
2450
- let name = item. name . as_ref ( ) . unwrap ( ) ;
2451
- try!( write ! ( w, "<h4 id='assoc_const.{}' class='{}'><code>" ,
2452
- * name, shortty( item) ) ) ;
2492
+ let id = derive_id ( format ! ( "assoc_const.{}" , name) ) ;
2493
+ try!( write ! ( w, "<h4 id='{}' class='{}'><code>" , id, shortty( item) ) ) ;
2453
2494
try!( assoc_const ( w, item, & c. type_ , Some ( & c. expr ) ) ) ;
2454
2495
try!( write ! ( w, "</code></h4>\n " ) ) ;
2455
2496
}
2456
2497
clean:: AssociatedTypeItem ( ref bounds, ref default) => {
2457
- let name = item. name . as_ref ( ) . unwrap ( ) ;
2458
- try!( write ! ( w, "<h4 id='assoc_type.{}' class='{}'><code>" ,
2459
- * name,
2460
- shortty( item) ) ) ;
2498
+ let id = derive_id ( format ! ( "assoc_type.{}" , name) ) ;
2499
+ try!( write ! ( w, "<h4 id='{}' class='{}'><code>" , id, shortty( item) ) ) ;
2461
2500
try!( assoc_type ( w, item, bounds, default) ) ;
2462
2501
try!( write ! ( w, "</code></h4>\n " ) ) ;
2463
2502
}
@@ -2671,3 +2710,22 @@ fn get_index_type_name(clean_type: &clean::Type) -> Option<String> {
2671
2710
pub fn cache ( ) -> Arc < Cache > {
2672
2711
CACHE_KEY . with ( |c| c. borrow ( ) . clone ( ) )
2673
2712
}
2713
+
2714
+ #[ cfg( test) ]
2715
+ #[ test]
2716
+ fn test_unique_id ( ) {
2717
+ let input = [ "foo" , "examples" , "examples" , "method.into_iter" , "examples" ,
2718
+ "method.into_iter" , "foo" , "main" , "search" , "methods" ,
2719
+ "examples" , "method.into_iter" , "assoc_type.Item" , "assoc_type.Item" ] ;
2720
+ let expected = [ "foo" , "examples" , "examples-1" , "method.into_iter" , "examples-2" ,
2721
+ "method.into_iter-1" , "foo-1" , "main-1" , "search-1" , "methods-1" ,
2722
+ "examples-3" , "method.into_iter-2" , "assoc_type.Item" , "assoc_type.Item-1" ] ;
2723
+
2724
+ let test = || {
2725
+ let actual: Vec < String > = input. iter ( ) . map ( |s| derive_id ( s. to_string ( ) ) ) . collect ( ) ;
2726
+ assert_eq ! ( & actual[ ..] , expected) ;
2727
+ } ;
2728
+ test ( ) ;
2729
+ reset_ids ( ) ;
2730
+ test ( ) ;
2731
+ }
0 commit comments