55//!
66//! Use the `render_with_highlighting` to highlight some rust code.
77
8- use crate :: clean:: PrimitiveType ;
8+ use crate :: clean:: { ExternalLocation , PrimitiveType } ;
99use crate :: html:: escape:: Escape ;
1010use crate :: html:: render:: Context ;
1111
1212use std:: collections:: VecDeque ;
1313use std:: fmt:: { Display , Write } ;
14+ use std:: iter:: once;
1415
16+ use rustc_ast as ast;
1517use rustc_data_structures:: fx:: FxHashMap ;
18+ use rustc_hir:: def_id:: DefId ;
1619use rustc_lexer:: { LiteralKind , TokenKind } ;
20+ use rustc_metadata:: creader:: { CStore , LoadedMacro } ;
1721use rustc_span:: edition:: Edition ;
1822use rustc_span:: symbol:: Symbol ;
1923use rustc_span:: { BytePos , Span , DUMMY_SP } ;
@@ -99,6 +103,7 @@ fn write_code(
99103) {
100104 // This replace allows to fix how the code source with DOS backline characters is displayed.
101105 let src = src. replace ( "\r \n " , "\n " ) ;
106+ let mut closing_tag = "" ;
102107 Classifier :: new (
103108 & src,
104109 edition,
@@ -108,8 +113,8 @@ fn write_code(
108113 . highlight ( & mut |highlight| {
109114 match highlight {
110115 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 ) ,
113118 } ;
114119 } ) ;
115120}
@@ -129,7 +134,7 @@ enum Class {
129134 RefKeyWord ,
130135 Self_ ( Span ) ,
131136 Op ,
132- Macro ,
137+ Macro ( Span ) ,
133138 MacroNonTerminal ,
134139 String ,
135140 Number ,
@@ -153,7 +158,7 @@ impl Class {
153158 Class :: RefKeyWord => "kw-2" ,
154159 Class :: Self_ ( _) => "self" ,
155160 Class :: Op => "op" ,
156- Class :: Macro => "macro" ,
161+ Class :: Macro ( _ ) => "macro" ,
157162 Class :: MacroNonTerminal => "macro-nonterminal" ,
158163 Class :: String => "string" ,
159164 Class :: Number => "number" ,
@@ -171,8 +176,22 @@ impl Class {
171176 /// a "span" (a tuple representing `(lo, hi)` equivalent of `Span`).
172177 fn get_span ( self ) -> Option < Span > {
173178 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 ,
176195 }
177196 }
178197}
@@ -611,7 +630,7 @@ impl<'a> Classifier<'a> {
611630 } ,
612631 TokenKind :: Ident | TokenKind :: RawIdent if lookahead == Some ( TokenKind :: Bang ) => {
613632 self . in_macro = true ;
614- sink ( Highlight :: EnterSpan { class : Class :: Macro } ) ;
633+ sink ( Highlight :: EnterSpan { class : Class :: Macro ( self . new_span ( before , text ) ) } ) ;
615634 sink ( Highlight :: Token { text, class : None } ) ;
616635 return ;
617636 }
@@ -658,13 +677,18 @@ impl<'a> Classifier<'a> {
658677
659678/// Called when we start processing a span of text that should be highlighted.
660679/// 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..." )
663687}
664688
665689/// 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 ) ;
668692}
669693
670694/// Called for a span of text. If the text should be highlighted differently
@@ -689,13 +713,28 @@ fn string<T: Display>(
689713 klass : Option < Class > ,
690714 context_info : & Option < ContextInfo < ' _ , ' _ , ' _ > > ,
691715) {
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 > {
692727 let Some ( klass) = klass
693- else { return write ! ( out, "{}" , text) } ;
728+ else {
729+ write ! ( out, "{}" , text) ;
730+ return None ;
731+ } ;
694732 let Some ( def_span) = klass. get_span ( )
695733 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>" ) ;
698736 } ;
737+
699738 let mut text_s = text. to_string ( ) ;
700739 if text_s. contains ( "::" ) {
701740 text_s = text_s. split ( "::" ) . intersperse ( "::" ) . fold ( String :: new ( ) , |mut path, t| {
@@ -730,8 +769,17 @@ fn string<T: Display>(
730769 . map ( |s| format ! ( "{}{}" , context_info. root_path, s) ) ,
731770 LinkFromSrc :: External ( def_id) => {
732771 format:: href_with_root_path ( * def_id, context, Some ( context_info. root_path ) )
733- . ok ( )
734772 . 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 ( )
735783 }
736784 LinkFromSrc :: Primitive ( prim) => format:: href_with_root_path (
737785 PrimitiveType :: primitive_locations ( context. tcx ( ) ) [ prim] ,
@@ -743,11 +791,51 @@ fn string<T: Display>(
743791 }
744792 } )
745793 {
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>" ) ;
748796 }
749797 }
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+ }
751839}
752840
753841#[ cfg( test) ]
0 commit comments