5
5
//!
6
6
//! Use the `render_with_highlighting` to highlight some rust code.
7
7
8
- use crate :: clean:: PrimitiveType ;
8
+ use crate :: clean:: { ExternalLocation , PrimitiveType } ;
9
9
use crate :: html:: escape:: Escape ;
10
10
use crate :: html:: render:: Context ;
11
11
12
12
use std:: collections:: VecDeque ;
13
13
use std:: fmt:: { Display , Write } ;
14
+ use std:: iter:: once;
14
15
16
+ use rustc_ast as ast;
15
17
use rustc_data_structures:: fx:: FxHashMap ;
18
+ use rustc_hir:: def_id:: DefId ;
16
19
use rustc_lexer:: { LiteralKind , TokenKind } ;
20
+ use rustc_metadata:: creader:: { CStore , LoadedMacro } ;
17
21
use rustc_span:: edition:: Edition ;
18
22
use rustc_span:: symbol:: Symbol ;
19
23
use rustc_span:: { BytePos , Span , DUMMY_SP } ;
@@ -99,6 +103,7 @@ fn write_code(
99
103
) {
100
104
// This replace allows to fix how the code source with DOS backline characters is displayed.
101
105
let src = src. replace ( "\r \n " , "\n " ) ;
106
+ let mut closing_tag = "" ;
102
107
Classifier :: new (
103
108
& src,
104
109
edition,
@@ -108,8 +113,8 @@ fn write_code(
108
113
. highlight ( & mut |highlight| {
109
114
match highlight {
110
115
Highlight :: Token { text, class } => string ( out, Escape ( text) , class, & context_info) ,
111
- Highlight :: EnterSpan { class } => enter_span ( out, class) ,
112
- Highlight :: ExitSpan => exit_span ( out) ,
116
+ Highlight :: EnterSpan { class } => closing_tag = enter_span ( out, class, & context_info ) ,
117
+ Highlight :: ExitSpan => exit_span ( out, & closing_tag ) ,
113
118
} ;
114
119
} ) ;
115
120
}
@@ -129,7 +134,7 @@ enum Class {
129
134
RefKeyWord ,
130
135
Self_ ( Span ) ,
131
136
Op ,
132
- Macro ,
137
+ Macro ( Span ) ,
133
138
MacroNonTerminal ,
134
139
String ,
135
140
Number ,
@@ -153,7 +158,7 @@ impl Class {
153
158
Class :: RefKeyWord => "kw-2" ,
154
159
Class :: Self_ ( _) => "self" ,
155
160
Class :: Op => "op" ,
156
- Class :: Macro => "macro" ,
161
+ Class :: Macro ( _ ) => "macro" ,
157
162
Class :: MacroNonTerminal => "macro-nonterminal" ,
158
163
Class :: String => "string" ,
159
164
Class :: Number => "number" ,
@@ -171,8 +176,22 @@ impl Class {
171
176
/// a "span" (a tuple representing `(lo, hi)` equivalent of `Span`).
172
177
fn get_span ( self ) -> Option < Span > {
173
178
match self {
174
- Self :: Ident ( sp) | Self :: Self_ ( sp) => Some ( sp) ,
175
- _ => None ,
179
+ Self :: Ident ( sp) | Self :: Self_ ( sp) | Self :: Macro ( sp) => Some ( sp) ,
180
+ Self :: Comment
181
+ | Self :: DocComment
182
+ | Self :: Attribute
183
+ | Self :: KeyWord
184
+ | Self :: RefKeyWord
185
+ | Self :: Op
186
+ | Self :: MacroNonTerminal
187
+ | Self :: String
188
+ | Self :: Number
189
+ | Self :: Bool
190
+ | Self :: Lifetime
191
+ | Self :: PreludeTy
192
+ | Self :: PreludeVal
193
+ | Self :: QuestionMark
194
+ | Self :: Decoration ( _) => None ,
176
195
}
177
196
}
178
197
}
@@ -611,7 +630,7 @@ impl<'a> Classifier<'a> {
611
630
} ,
612
631
TokenKind :: Ident | TokenKind :: RawIdent if lookahead == Some ( TokenKind :: Bang ) => {
613
632
self . in_macro = true ;
614
- sink ( Highlight :: EnterSpan { class : Class :: Macro } ) ;
633
+ sink ( Highlight :: EnterSpan { class : Class :: Macro ( self . new_span ( before , text ) ) } ) ;
615
634
sink ( Highlight :: Token { text, class : None } ) ;
616
635
return ;
617
636
}
@@ -658,13 +677,18 @@ impl<'a> Classifier<'a> {
658
677
659
678
/// Called when we start processing a span of text that should be highlighted.
660
679
/// The `Class` argument specifies how it should be highlighted.
661
- fn enter_span ( out : & mut Buffer , klass : Class ) {
662
- write ! ( out, "<span class=\" {}\" >" , klass. as_html( ) ) ;
680
+ fn enter_span (
681
+ out : & mut Buffer ,
682
+ klass : Class ,
683
+ context_info : & Option < ContextInfo < ' _ , ' _ , ' _ > > ,
684
+ ) -> & ' static str {
685
+ string_without_closing_tag ( out, "" , Some ( klass) , context_info)
686
+ . expect ( "no closing tag to close wrapper..." )
663
687
}
664
688
665
689
/// Called at the end of a span of highlighted text.
666
- fn exit_span ( out : & mut Buffer ) {
667
- out. write_str ( "</span>" ) ;
690
+ fn exit_span ( out : & mut Buffer , closing_tag : & str ) {
691
+ out. write_str ( closing_tag ) ;
668
692
}
669
693
670
694
/// Called for a span of text. If the text should be highlighted differently
@@ -689,13 +713,28 @@ fn string<T: Display>(
689
713
klass : Option < Class > ,
690
714
context_info : & Option < ContextInfo < ' _ , ' _ , ' _ > > ,
691
715
) {
716
+ if let Some ( closing_tag) = string_without_closing_tag ( out, text, klass, context_info) {
717
+ out. write_str ( closing_tag) ;
718
+ }
719
+ }
720
+
721
+ fn string_without_closing_tag < T : Display > (
722
+ out : & mut Buffer ,
723
+ text : T ,
724
+ klass : Option < Class > ,
725
+ context_info : & Option < ContextInfo < ' _ , ' _ , ' _ > > ,
726
+ ) -> Option < & ' static str > {
692
727
let Some ( klass) = klass
693
- else { return write ! ( out, "{}" , text) } ;
728
+ else {
729
+ write ! ( out, "{}" , text) ;
730
+ return None ;
731
+ } ;
694
732
let Some ( def_span) = klass. get_span ( )
695
733
else {
696
- write ! ( out, "<span class=\" {}\" >{}</span> " , klass. as_html( ) , text) ;
697
- return ;
734
+ write ! ( out, "<span class=\" {}\" >{}" , klass. as_html( ) , text) ;
735
+ return Some ( "</span>" ) ;
698
736
} ;
737
+
699
738
let mut text_s = text. to_string ( ) ;
700
739
if text_s. contains ( "::" ) {
701
740
text_s = text_s. split ( "::" ) . intersperse ( "::" ) . fold ( String :: new ( ) , |mut path, t| {
@@ -730,8 +769,17 @@ fn string<T: Display>(
730
769
. map ( |s| format ! ( "{}{}" , context_info. root_path, s) ) ,
731
770
LinkFromSrc :: External ( def_id) => {
732
771
format:: href_with_root_path ( * def_id, context, Some ( context_info. root_path ) )
733
- . ok ( )
734
772
. map ( |( url, _, _) | url)
773
+ . or_else ( |e| {
774
+ if e == format:: HrefError :: NotInExternalCache
775
+ && matches ! ( klass, Class :: Macro ( _) )
776
+ {
777
+ Ok ( generate_macro_def_id_path ( context_info, * def_id) )
778
+ } else {
779
+ Err ( e)
780
+ }
781
+ } )
782
+ . ok ( )
735
783
}
736
784
LinkFromSrc :: Primitive ( prim) => format:: href_with_root_path (
737
785
PrimitiveType :: primitive_locations ( context. tcx ( ) ) [ prim] ,
@@ -743,11 +791,51 @@ fn string<T: Display>(
743
791
}
744
792
} )
745
793
{
746
- write ! ( out, "<a class=\" {}\" href=\" {}\" >{}</a> " , klass. as_html( ) , href, text_s) ;
747
- return ;
794
+ write ! ( out, "<a class=\" {}\" href=\" {}\" >{}" , klass. as_html( ) , href, text_s) ;
795
+ return Some ( "</a>" ) ;
748
796
}
749
797
}
750
- write ! ( out, "<span class=\" {}\" >{}</span>" , klass. as_html( ) , text_s) ;
798
+ write ! ( out, "<span class=\" {}\" >{}" , klass. as_html( ) , text_s) ;
799
+ Some ( "</span>" )
800
+ }
801
+
802
+ /// This function is to get the external macro path because they are not in the cache used n
803
+ /// `href_with_root_path`.
804
+ fn generate_macro_def_id_path ( context_info : & ContextInfo < ' _ , ' _ , ' _ > , def_id : DefId ) -> String {
805
+ let tcx = context_info. context . shared . tcx ;
806
+ let crate_name = tcx. crate_name ( def_id. krate ) . to_string ( ) ;
807
+ let cache = & context_info. context . cache ( ) ;
808
+
809
+ let relative = tcx. def_path ( def_id) . data . into_iter ( ) . filter_map ( |elem| {
810
+ // extern blocks have an empty name
811
+ let s = elem. data . to_string ( ) ;
812
+ if !s. is_empty ( ) { Some ( s) } else { None }
813
+ } ) ;
814
+ // Check to see if it is a macro 2.0 or built-in macro
815
+ let mut path = if matches ! (
816
+ CStore :: from_tcx( tcx) . load_macro_untracked( def_id, tcx. sess) ,
817
+ LoadedMacro :: MacroDef ( def, _)
818
+ if matches!( & def. kind, ast:: ItemKind :: MacroDef ( ast_def)
819
+ if !ast_def. macro_rules)
820
+ ) {
821
+ once ( crate_name. clone ( ) ) . chain ( relative) . collect ( )
822
+ } else {
823
+ vec ! [ crate_name. clone( ) , relative. last( ) . expect( "relative was empty" ) ]
824
+ } ;
825
+
826
+ let url_parts = match cache. extern_locations [ & def_id. krate ] {
827
+ ExternalLocation :: Remote ( ref s) => vec ! [ s. trim_end_matches( '/' ) ] ,
828
+ ExternalLocation :: Local => vec ! [ context_info. root_path. trim_end_matches( '/' ) , & crate_name] ,
829
+ ExternalLocation :: Unknown => panic ! ( "unknown crate" ) ,
830
+ } ;
831
+
832
+ let last = path. pop ( ) . unwrap ( ) ;
833
+ let last = format ! ( "macro.{}.html" , last) ;
834
+ if path. is_empty ( ) {
835
+ format ! ( "{}/{}" , url_parts. join( "/" ) , last)
836
+ } else {
837
+ format ! ( "{}/{}/{}" , url_parts. join( "/" ) , path. join( "/" ) , last)
838
+ }
751
839
}
752
840
753
841
#[ cfg( test) ]
0 commit comments