@@ -28,13 +28,13 @@ export type HighlightLinesOptions = {
28
28
* will be removed in the next versions
29
29
*/
30
30
lineContainerTagName ?: "div" | "span" ;
31
- trimBlankLines ?: boolean ;
31
+ keepOuterBlankLine ?: boolean ;
32
32
} ;
33
33
34
34
const DEFAULT_SETTINGS : HighlightLinesOptions = {
35
35
showLineNumbers : false ,
36
36
lineContainerTagName : "span" ,
37
- trimBlankLines : false ,
37
+ keepOuterBlankLine : false ,
38
38
} ;
39
39
40
40
type PartiallyRequiredHighlightLinesOptions = Prettify <
@@ -44,7 +44,7 @@ type PartiallyRequiredHighlightLinesOptions = Prettify<
44
44
type GutterOptions = {
45
45
directiveLineNumbering : boolean | number ;
46
46
directiveLineHighlighting : number [ ] ;
47
- directiveLineTrimming : boolean ;
47
+ directiveKeepOuterBlankLine : boolean ;
48
48
} ;
49
49
50
50
// a simple util for our use case, like clsx package
@@ -244,54 +244,36 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
244
244
245
245
/**
246
246
*
247
- * handle trimming blank line for the last text node
248
- *
249
- */
250
- function handleTrimmingBlankLines ( code : Element , directiveLineTrimming : boolean ) {
251
- if ( ! directiveLineTrimming ) return ;
252
-
253
- const lastChild = code . children [ code . children . length - 1 ] ;
254
-
255
- if ( lastChild . type === "text" ) {
256
- if ( / ( \r ? \n | \r ) ( \1) $ / . test ( lastChild . value ) ) {
257
- lastChild . value = lastChild . value . replace ( / ( \r ? \n | \r ) ( \1) $ / , "$1" ) ;
258
- }
259
- }
260
- }
261
-
262
- /**
263
- *
264
- * extract the lines from HAST of code element
247
+ * finds the code lines from HAST of code element and costructs the lines
265
248
* mutates the code
266
249
*
267
250
*/
268
251
function gutter (
269
252
code : Element ,
270
- { directiveLineNumbering, directiveLineHighlighting, directiveLineTrimming } : GutterOptions ,
253
+ {
254
+ directiveLineNumbering,
255
+ directiveLineHighlighting,
256
+ directiveKeepOuterBlankLine,
257
+ } : GutterOptions ,
271
258
) {
272
- hasFlatteningNeed ( code ) && flattenCodeTree ( code ) ; // mutates the code
273
-
274
- handleFirstElementContent ( code ) ; // mutates the code
275
-
276
- handleMultiLineComments ( code ) ; // mutates the code
259
+ hasFlatteningNeed ( code ) && flattenCodeTree ( code ) ;
277
260
278
- handleTrimmingBlankLines ( code , directiveLineTrimming ) ;
261
+ handleFirstElementContent ( code ) ;
262
+ handleMultiLineComments ( code ) ;
279
263
280
264
const replacement : ElementContent [ ] = [ ] ;
281
-
282
265
let start = 0 ;
283
266
let startTextRemainder = "" ;
284
267
let lineNumber = 0 ;
285
268
286
269
for ( let index = 0 ; index < code . children . length ; index ++ ) {
287
270
const child = code . children [ index ] ;
288
-
289
271
if ( child . type !== "text" ) continue ;
290
272
291
273
let textStart = 0 ;
292
-
293
274
const matches = Array . from ( child . value . matchAll ( REGEX_LINE_BREAKS ) ) ;
294
- matches . forEach ( ( match , iteration ) => {
275
+
276
+ for ( const [ iteration , match ] of matches . entries ( ) ) {
295
277
// Nodes in this line. (current child (index) is exclusive)
296
278
const line = code . children . slice ( start , index ) ;
297
279
@@ -303,46 +285,57 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
303
285
304
286
// Append text from this text.
305
287
if ( match . index > textStart ) {
306
- const value = child . value . slice ( textStart , match . index ) ;
307
- line . push ( { type : "text" , value } ) ;
288
+ line . push ( { type : "text" , value : child . value . slice ( textStart , match . index ) } ) ;
308
289
}
309
290
310
291
const isFirstIteration = index === 0 && iteration === 0 ;
292
+ const isLastIteration =
293
+ index === code . children . length - 1 && iteration === matches . length - 1 ;
311
294
312
- if ( isFirstIteration && directiveLineTrimming && line . length === 0 ) {
295
+ if ( isFirstIteration && ! directiveKeepOuterBlankLine && line . length === 0 ) {
313
296
replacement . push ( { type : "text" , value : match [ 0 ] } ) ; // eol
297
+ } else if ( isLastIteration && ! directiveKeepOuterBlankLine && line . length === 0 ) {
298
+ const lastReplacement = replacement [ replacement . length - 1 ] ;
299
+ if ( ! ( lastReplacement . type === "text" && lastReplacement . value === match [ 0 ] ) )
300
+ /* v8 ignore next */
301
+ replacement . push ( { type : "text" , value : match [ 0 ] } ) ; // eol
314
302
} else {
315
- lineNumber += 1 ;
316
303
replacement . push (
317
- createLine ( line , lineNumber , directiveLineNumbering , directiveLineHighlighting ) ,
304
+ createLine ( line , ++ lineNumber , directiveLineNumbering , directiveLineHighlighting ) ,
318
305
{ type : "text" , value : match [ 0 ] } , // eol
319
306
) ;
320
307
}
321
308
322
309
start = index + 1 ;
323
310
textStart = match . index + match [ 0 ] . length ;
324
- } ) ;
311
+ }
325
312
326
313
// If we matched, make sure to not drop the text after the last line ending.
327
314
if ( start === index + 1 ) {
328
315
startTextRemainder = child . value . slice ( textStart ) ;
329
316
}
330
317
}
331
318
332
- const line = code . children . slice ( start ) ;
319
+ const remainingLine = code . children . slice ( start ) ;
333
320
334
321
// Prepend text from a partial matched earlier text.
335
322
if ( startTextRemainder ) {
336
- line . unshift ( { type : "text" , value : startTextRemainder } ) ;
337
- startTextRemainder = "" ;
323
+ remainingLine . unshift ( { type : "text" , value : startTextRemainder } ) ;
338
324
}
339
325
340
- if ( line . length > 0 ) {
341
- directiveLineTrimming
342
- ? replacement . push ( ...line )
343
- : replacement . push (
344
- createLine ( line , ++ lineNumber , directiveLineNumbering , directiveLineHighlighting ) ,
345
- ) ;
326
+ if ( remainingLine . length > 0 ) {
327
+ if ( remainingLine [ 0 ] . type === "text" && remainingLine [ 0 ] . value . trim ( ) === "" ) {
328
+ replacement . push ( ...remainingLine ) ;
329
+ } else {
330
+ replacement . push (
331
+ createLine (
332
+ remainingLine ,
333
+ ++ lineNumber ,
334
+ directiveLineNumbering ,
335
+ directiveLineHighlighting ,
336
+ ) ,
337
+ ) ;
338
+ }
346
339
}
347
340
348
341
// Replace children with new array.
@@ -407,7 +400,7 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
407
400
( language . startsWith ( "{" ) ||
408
401
language . startsWith ( "showlinenumbers" ) ||
409
402
language . startsWith ( "nolinenumbers" ) ||
410
- language . startsWith ( "trimblanklines " ) )
403
+ language . startsWith ( "keepouterblankline " ) )
411
404
) {
412
405
// add specifiers to meta
413
406
meta = ( language + " " + meta ) . trim ( ) ;
@@ -419,7 +412,7 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
419
412
code . properties . className = undefined ;
420
413
}
421
414
422
- const keywords = [ "showlinenumbers" , "trimblanklines " , "nolinenumbers " ] ;
415
+ const keywords = [ "showlinenumbers" , "nolinenumbers " , "keepouterblankline " ] ;
423
416
424
417
const classNames = code . properties . className
425
418
?. map ( ( cls ) => cls . toLowerCase ( ) . replaceAll ( "-" , "" ) )
@@ -487,10 +480,10 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
487
480
/** the part of defining the directive for line trimming */
488
481
489
482
// find the directive for trimming blank lines
490
- const directiveLineTrimming =
491
- settings . trimBlankLines ||
492
- / t r i m b l a n k l i n e s / . test ( meta ) ||
493
- Boolean ( classNames ?. includes ( "trimblanklines " ) ) ;
483
+ const directiveKeepOuterBlankLine =
484
+ settings . keepOuterBlankLine ||
485
+ / k e e p o u t e r b l a n k l i n e / . test ( meta ) ||
486
+ Boolean ( classNames ?. includes ( "keepouterblankline " ) ) ;
494
487
495
488
/** the part of cleaning of code properties */
496
489
@@ -505,8 +498,8 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
505
498
// if nothing to do for numbering, highlihting or trimming, just return;
506
499
if (
507
500
directiveLineNumbering === false &&
508
- directiveLineHighlighting . length === 0 &&
509
- directiveLineTrimming === false
501
+ directiveLineHighlighting . length === 0
502
+ // directiveKeepOuterBlankLine === false
510
503
) {
511
504
return ;
512
505
}
@@ -515,7 +508,7 @@ const plugin: Plugin<[HighlightLinesOptions?], Root> = (options) => {
515
508
gutter ( code , {
516
509
directiveLineNumbering,
517
510
directiveLineHighlighting,
518
- directiveLineTrimming ,
511
+ directiveKeepOuterBlankLine ,
519
512
} ) ;
520
513
} ) ;
521
514
} ;
0 commit comments