@@ -16,6 +16,7 @@ pub struct Shortcode {
16
16
// In practice, span.len() is always equal to SHORTCODE_PLACEHOLDER.len()
17
17
pub ( crate ) span : Range < usize > ,
18
18
pub ( crate ) body : Option < String > ,
19
+ pub ( crate ) indent : String ,
19
20
pub ( crate ) nth : usize ,
20
21
pub ( crate ) inner : Vec < Shortcode > ,
21
22
// set later down the line, for quick access without needing the definitions
@@ -88,9 +89,20 @@ impl Shortcode {
88
89
new_context. insert ( "nth" , & self . nth ) ;
89
90
new_context. extend ( context. clone ( ) ) ;
90
91
91
- let res = utils:: templates:: render_template ( & tpl_name, tera, new_context, & None )
92
- . with_context ( || format ! ( "Failed to render {} shortcode" , name) ) ?
93
- . replace ( "\r \n " , "\n " ) ;
92
+ let rendered = utils:: templates:: render_template ( & tpl_name, tera, new_context, & None )
93
+ . with_context ( || format ! ( "Failed to render {} shortcode" , name) ) ?;
94
+ // Append the rendered text, but indenting each line after the first one as much as the line in which the shortcode was called.
95
+ let mut res = String :: with_capacity ( rendered. len ( ) ) ;
96
+ let mut lines = rendered. split_terminator ( '\n' ) ;
97
+ if let Some ( first_line) = lines. next ( ) {
98
+ res. push_str ( first_line. trim_end_matches ( '\r' ) ) ;
99
+ res. push ( '\n' ) ;
100
+ for line in lines {
101
+ res. push_str ( & self . indent ) ;
102
+ res. push_str ( line. trim_end_matches ( '\r' ) ) ;
103
+ res. push ( '\n' ) ;
104
+ }
105
+ }
94
106
95
107
Ok ( res)
96
108
}
@@ -246,17 +258,31 @@ pub fn parse_for_shortcodes(
246
258
247
259
// We have at least a `page` pair
248
260
for p in pairs. next ( ) . unwrap ( ) . into_inner ( ) {
261
+ fn current_indent ( text : & str ) -> & str {
262
+ let current_line = match text. rsplit_once ( '\n' ) {
263
+ Some ( ( _, line) ) => line,
264
+ None => text,
265
+ } ;
266
+ // Stop at the first character that is not considered indentation by the CommonMark spec.
267
+ match current_line. split_once ( |ch| ch != ' ' && ch != '\t' ) {
268
+ Some ( ( whitespace, _) ) => whitespace,
269
+ None => current_line,
270
+ }
271
+ }
272
+
249
273
match p. as_rule ( ) {
250
274
Rule :: text => output. push_str ( p. as_span ( ) . as_str ( ) ) ,
251
275
Rule :: inline_shortcode => {
252
276
let start = output. len ( ) ;
277
+ let indent = current_indent ( & output) . into ( ) ;
253
278
let ( name, args) = parse_shortcode_call ( p) ;
254
279
let nth = invocation_counter. get ( & name) ;
255
280
shortcodes. push ( Shortcode {
256
281
name,
257
282
args,
258
283
span : start..( start + SHORTCODE_PLACEHOLDER . len ( ) ) ,
259
284
body : None ,
285
+ indent,
260
286
nth,
261
287
inner : Vec :: new ( ) ,
262
288
tera_name : String :: new ( ) ,
@@ -265,6 +291,7 @@ pub fn parse_for_shortcodes(
265
291
}
266
292
Rule :: shortcode_with_body => {
267
293
let start = output. len ( ) ;
294
+ let indent = current_indent ( & output) . into ( ) ;
268
295
let mut inner = p. into_inner ( ) ;
269
296
// 3 items in inner: call, body, end
270
297
// we don't care about the closing tag
@@ -279,6 +306,7 @@ pub fn parse_for_shortcodes(
279
306
args,
280
307
span : start..( start + SHORTCODE_PLACEHOLDER . len ( ) ) ,
281
308
body : Some ( body) ,
309
+ indent,
282
310
nth,
283
311
inner,
284
312
tera_name : String :: new ( ) ,
@@ -422,6 +450,7 @@ mod tests {
422
450
args : Value :: Null ,
423
451
span : 10 ..20 ,
424
452
body : None ,
453
+ indent : String :: new ( ) ,
425
454
nth : 0 ,
426
455
inner : Vec :: new ( ) ,
427
456
tera_name : String :: new ( ) ,
@@ -442,6 +471,7 @@ mod tests {
442
471
args : Value :: Null ,
443
472
span : 42 ..65 ,
444
473
body : None ,
474
+ indent : String :: new ( ) ,
445
475
nth : 0 ,
446
476
inner : Vec :: new ( ) ,
447
477
tera_name : String :: new ( ) ,
0 commit comments