@@ -89,6 +89,9 @@ pub struct InjectionsQuery {
8989    injection_language_capture :  Option < Capture > , 
9090    injection_filename_capture :  Option < Capture > , 
9191    injection_shebang_capture :  Option < Capture > , 
92+     /// 1. The list of matches to compare the parent layer's language 
93+      /// 1. Whether it is negated: `#any-of` or `#not-any-of?` 
94+      injection_parent_layer_langs_predicate :  Option < ( Vec < String > ,  bool ) > , 
9295    // Note that the injections query is concatenated with the locals query. 
9396    pub ( crate )  local_query :  Query , 
9497    // TODO: Use a Vec<bool> instead? 
@@ -108,6 +111,8 @@ impl InjectionsQuery {
108111        query_source. push_str ( injection_query_text) ; 
109112        query_source. push_str ( local_query_text) ; 
110113
114+         let  mut  injection_parent_layer_langs_predicate = None ; 
115+ 
111116        let  mut  injection_properties:  HashMap < Pattern ,  InjectionProperties >  = HashMap :: new ( ) ; 
112117        let  mut  not_scope_inherits = HashSet :: new ( ) ; 
113118        let  injection_query = Query :: new ( grammar,  injection_query_text,  |pattern,  predicate| { 
@@ -122,6 +127,16 @@ impl InjectionsQuery {
122127                        . or_default ( ) 
123128                        . include_children  = IncludedChildren :: Unnamed 
124129                } 
130+                 // Allow filtering for specific languages in 
131+                 // `#set! injection.languae injection.parent-layer` 
132+                 UserPredicate :: IsAnyOf  { 
133+                     negated, 
134+                     value :  INJECTION_PARENT_LAYER , 
135+                     values, 
136+                 }  => { 
137+                     injection_parent_layer_langs_predicate =
138+                         Some ( ( values. into_iter ( ) . map ( ToOwned :: to_owned) . collect ( ) ,  negated) ) ; 
139+                 } 
125140                UserPredicate :: SetProperty  { 
126141                    key :  "injection.include-children" , 
127142                    val :  None , 
@@ -167,6 +182,7 @@ impl InjectionsQuery {
167182        local_query. disable_capture ( "local.reference" ) ; 
168183
169184        Ok ( InjectionsQuery  { 
185+             injection_parent_layer_langs_predicate, 
170186            injection_properties, 
171187            injection_content_capture :  injection_query. get_capture ( "injection.content" ) , 
172188            injection_language_capture :  injection_query. get_capture ( "injection.language" ) , 
@@ -195,6 +211,7 @@ impl InjectionsQuery {
195211
196212    fn  process_match < ' a ,  ' tree > ( 
197213        & self , 
214+         injection_parent_language :  Language , 
198215        query_match :  & QueryMatch < ' a ,  ' tree > , 
199216        node_idx :  MatchedNodeIdx , 
200217        source :  RopeSlice < ' a > , 
@@ -242,11 +259,41 @@ impl InjectionsQuery {
242259                last_content_node = i as  u32 ; 
243260            } 
244261        } 
245-         let  marker = marker. or ( properties
246-             . and_then ( |p| p. language . as_deref ( ) ) 
247-             . map ( InjectionLanguageMarker :: Name ) ) ?; 
248262
249-         let  language = loader. language_for_marker ( marker) ?; 
263+         let  language = marker
264+             . and_then ( |m| loader. language_for_marker ( m) ) 
265+             . or_else ( || { 
266+                 properties
267+                     . and_then ( |p| p. language . as_deref ( ) ) 
268+                     . and_then ( |name| { 
269+                         let  matches_predicate = || { 
270+                             self . injection_parent_layer_langs_predicate 
271+                                 . as_ref ( ) 
272+                                 . is_none_or ( |( predicate,  is_negated) | { 
273+                                     predicate. iter ( ) . any ( |capture| { 
274+                                         let  Some ( marker)  = loader. language_for_marker ( 
275+                                             InjectionLanguageMarker :: Name ( capture) , 
276+                                         )  else  { 
277+                                             return  false ; 
278+                                         } ; 
279+ 
280+                                         if  * is_negated { 
281+                                             marker != injection_parent_language
282+                                         }  else  { 
283+                                             marker == injection_parent_language
284+                                         } 
285+                                     } ) 
286+                                 } ) 
287+                         } ; 
288+ 
289+                         if  name == INJECTION_PARENT_LAYER  && matches_predicate ( )  { 
290+                             Some ( injection_parent_language) 
291+                         }  else  { 
292+                             loader. language_for_marker ( InjectionLanguageMarker :: Name ( name) ) 
293+                         } 
294+                     } ) 
295+             } ) ?; 
296+ 
250297        let  scope = if  properties. is_some_and ( |p| p. combined )  { 
251298            Some ( InjectionScope :: Pattern  { 
252299                pattern :  query_match. pattern ( ) , 
@@ -286,6 +333,7 @@ impl InjectionsQuery {
286333     /// This case should be handled by the calling function 
287334     fn  execute < ' a > ( 
288335        & ' a  self , 
336+         injection_parent_language :  Language , 
289337        node :  & Node < ' a > , 
290338        source :  RopeSlice < ' a > , 
291339        loader :  & ' a  impl  LanguageLoader , 
@@ -298,7 +346,14 @@ impl InjectionsQuery {
298346            if  query_match. matched_node ( node_idx) . capture  != injection_content_capture { 
299347                continue ; 
300348            } 
301-             let  Some ( mat)  = self . process_match ( & query_match,  node_idx,  source,  loader)  else  { 
349+ 
350+             let  Some ( mat)  = self . process_match ( 
351+                 injection_parent_language, 
352+                 & query_match, 
353+                 node_idx, 
354+                 source, 
355+                 loader, 
356+             )  else  { 
302357                query_match. remove ( ) ; 
303358                continue ; 
304359            } ; 
@@ -384,7 +439,18 @@ impl Syntax {
384439        let  mut  injections:  Vec < Injection >  = Vec :: with_capacity ( layer_data. injections . len ( ) ) ; 
385440        let  mut  old_injections = take ( & mut  layer_data. injections ) . into_iter ( ) . peekable ( ) ; 
386441
387-         let  injection_query = injections_query. execute ( & parse_tree. root_node ( ) ,  source,  loader) ; 
442+         // The language to inject if `(#set! injection.language injection.parent-layer)` is set 
443+         let  injection_parent_language = layer_data
444+             . parent 
445+             . map ( |layer| self . layer ( layer) . language ) 
446+             . unwrap_or_else ( || self . layer ( self . root ) . language ) ; 
447+ 
448+         let  injection_query = injections_query. execute ( 
449+             injection_parent_language, 
450+             & parse_tree. root_node ( ) , 
451+             source, 
452+             loader, 
453+         ) ; 
388454
389455        let  mut  combined_injections:  HashMap < InjectionScope ,  Layer >  = HashMap :: with_capacity ( 32 ) ; 
390456        for  mat in  injection_query { 
@@ -713,3 +779,62 @@ fn ranges_intersect(a: &Range, b: &Range) -> bool {
713779    // Adapted from <https://github.com/helix-editor/helix/blob/8df58b2e1779dcf0046fb51ae1893c1eebf01e7c/helix-core/src/selection.rs#L156-L163> 
714780    a. start  == b. start  || ( a. end  > b. start  && b. end  > a. start ) 
715781} 
782+ 
783+ /// When the language is injected, this value will be set to the 
784+ /// language of the parent layer. 
785+ /// 
786+ /// This is useful e.g. when injecting markdown into documentation 
787+ /// comments for a language such as Rust, and we want the default 
788+ /// code block without any info string to be the same as the parent layer. 
789+ /// 
790+ /// In the next two examples, the language injected into the inner 
791+ /// code block in the documentation comments will be the same as the parent 
792+ /// layer 
793+ /// 
794+ /// ````gleam 
795+ /// /// This code block will have the "gleam" language when 
796+ /// /// no info string is supplied: 
797+ /// /// 
798+ /// /// ``` 
799+ /// /// let foo: Int = example() 
800+ /// /// ``` 
801+ /// fn example() -> Int { todo } 
802+ /// ```` 
803+ /// 
804+ /// ````rust 
805+ /// /// This code block will have the "rust" language when 
806+ /// /// no info string is supplied: 
807+ /// /// 
808+ /// /// ``` 
809+ /// /// let foo: i32 = example(); 
810+ /// /// ``` 
811+ /// fn example() -> i32 { todo!() } 
812+ /// ```` 
813+ /// 
814+ /// In the above example, we have two layers: 
815+ /// 
816+ /// ```text 
817+ ///  <--     rust     --> 
818+ ///    <-- markdown --> 
819+ /// ``` 
820+ /// 
821+ /// In the `markdown` layer, by default there will be no injection for a 
822+ /// code block with no `(info_string)` node. 
823+ /// 
824+ /// By using `injection.parent-layer`, when markdown is injected into a 
825+ /// language the code block's default value will be the parent layer. 
826+ /// 
827+ /// # Example 
828+ /// 
829+ /// The following injection will have the effect described above for the 
830+ /// specified languages `gleam` and `rust`. All other languages are treated 
831+ /// normally. 
832+ /// 
833+ /// ```scheme 
834+ /// (fenced_code_block 
835+ ///   (code_fence_content) @injection.content 
836+ ///   (#set! injection.include-unnamed-children) 
837+ ///   (#set! injection.language injection.parent-layer) 
838+ ///   (#any-of? injection.parent-layer "gleam" "rust")) 
839+ /// ``` 
840+ const  INJECTION_PARENT_LAYER :  & str  = "injection.parent-layer" ; 
0 commit comments