@@ -176,9 +176,12 @@ export const CodeBlockHighlight: ExtensionAuto<CodeBlockHighlightOptions> = (bui
176176 const contentDOM = document . createElement ( 'div' ) ;
177177
178178 let lineNumbersContainer : HTMLDivElement | undefined ;
179+ let prevLineCount = 0 ;
179180
180181 if ( opts . lineNumbers ?. enabled ) {
181- lineNumbersContainer = initializeLineNumbers ( node , code ) ;
182+ const result = initializeLineNumbers ( node , code ) ;
183+ lineNumbersContainer = result . container ;
184+ prevLineCount = result . lineCount ;
182185 }
183186
184187 code . append ( contentDOM ) ;
@@ -207,11 +210,14 @@ export const CodeBlockHighlight: ExtensionAuto<CodeBlockHighlightOptions> = (bui
207210 ) ;
208211
209212 if ( opts . lineNumbers ?. enabled ) {
210- lineNumbersContainer = updateLineNumbers (
213+ const result = updateLineNumbers (
211214 newNode ,
212215 code ,
213216 lineNumbersContainer ,
217+ prevLineCount ,
214218 ) ;
219+ lineNumbersContainer = result . container ;
220+ prevLineCount = result . lineCount ;
215221 }
216222
217223 return true ;
@@ -290,64 +296,102 @@ function updateDomAttribute(elem: Element, attr: string, value: string | null |
290296 elem . removeAttribute ( attr ) ;
291297 }
292298}
293- function initializeLineNumbers ( node : Node , code : HTMLElement ) {
299+ function initializeLineNumbers (
300+ node : Node ,
301+ code : HTMLElement ,
302+ ) : { container ?: HTMLDivElement ; lineCount : number } {
294303 const showLineNumbers = node . attrs [ CodeBlockNodeAttr . ShowLineNumbers ] ;
295304
296305 if ( ! showLineNumbers ) {
297- return undefined ;
306+ return { container : undefined , lineCount : 0 } ;
298307 }
299308
300309 const lineNumbersContainer = document . createElement ( 'div' ) ;
301310 lineNumbersContainer . className = 'yfm-line-numbers' ;
302311 lineNumbersContainer . contentEditable = 'false' ;
303312
304- const lineNumbersContent = createLineNumbersContent ( node . textContent ) ;
305- lineNumbersContainer . innerHTML = lineNumbersContent ;
313+ const lines = node . textContent ? node . textContent . split ( '\n' ) : [ '' ] ;
314+ const lineCount = lines . length ;
315+
316+ appendLineNumbers ( lineNumbersContainer , 1 , lineCount ) ;
306317
307318 code . prepend ( lineNumbersContainer ) ;
308319 code . classList . add ( 'show-line-numbers' ) ;
309320
310- return lineNumbersContainer ;
321+ return { container : lineNumbersContainer , lineCount } ;
311322}
312323
313324function updateLineNumbers (
314325 node : Node ,
315326 code : HTMLElement ,
316327 prevLineNumbersContainer ?: HTMLDivElement ,
317- ) {
328+ prevLineCount = 0 ,
329+ ) : { container ?: HTMLDivElement ; lineCount : number } {
318330 const showLineNumbers = node . attrs [ CodeBlockNodeAttr . ShowLineNumbers ] ;
319331
320332 if ( ! prevLineNumbersContainer && showLineNumbers !== 'true' ) {
321- return undefined ;
333+ return { container : undefined , lineCount : 0 } ;
322334 } else if ( ! prevLineNumbersContainer && showLineNumbers === 'true' ) {
323335 return initializeLineNumbers ( node , code ) ;
324336 } else if ( prevLineNumbersContainer && showLineNumbers !== 'true' ) {
325337 code . removeChild ( prevLineNumbersContainer ) ;
326338 code . classList . remove ( 'show-line-numbers' ) ;
327- return undefined ;
339+ return { container : undefined , lineCount : 0 } ;
328340 }
329341
330342 if ( ! prevLineNumbersContainer ) {
331- return prevLineNumbersContainer ;
343+ return { container : prevLineNumbersContainer , lineCount : prevLineCount } ;
332344 }
333345
334- const lineNumbersContent = createLineNumbersContent ( node . textContent ) ;
335- prevLineNumbersContainer . innerHTML = lineNumbersContent ;
346+ const lines = node . textContent ? node . textContent . split ( '\n' ) : [ '' ] ;
347+ const currentLineCount = lines . length ;
348+
336349 code . classList . add ( 'show-line-numbers' ) ;
337350
338- return prevLineNumbersContainer ;
351+ if ( currentLineCount === prevLineCount ) {
352+ return { container : prevLineNumbersContainer , lineCount : prevLineCount } ;
353+ }
354+
355+ if ( currentLineCount > prevLineCount ) {
356+ appendLineNumbers ( prevLineNumbersContainer , prevLineCount + 1 , currentLineCount ) ;
357+ } else if ( currentLineCount < prevLineCount ) {
358+ removeExcessLineNumbers ( prevLineNumbersContainer , currentLineCount , prevLineCount ) ;
359+ }
360+
361+ return { container : prevLineNumbersContainer , lineCount : currentLineCount } ;
339362}
340363
341- function createLineNumbersContent ( content : string ) {
342- const lines = content ? content . split ( '\n' ) : [ '' ] ;
343- const lineCount = lines . length ;
344- const maxDigits = String ( lineCount ) . length ;
364+ function appendLineNumbers ( container : HTMLDivElement , startLine : number , endLine : number ) {
365+ const maxDigits = String ( endLine ) . length ;
345366
346- let lineNumbersHtml = '' ;
347- for ( let i = 1 ; i <= lineCount ; i ++ ) {
348- const num = String ( i ) . padStart ( maxDigits , ' ' ) ;
349- lineNumbersHtml += `<div class="yfm-line-number">${ num } </div>` ;
367+ for ( let i = startLine ; i <= endLine ; i ++ ) {
368+ const lineNumberElement = document . createElement ( 'div' ) ;
369+ lineNumberElement . className = 'yfm-line-number' ;
370+ lineNumberElement . textContent = String ( i ) . padStart ( maxDigits , ' ' ) ;
371+ container . appendChild ( lineNumberElement ) ;
350372 }
351373
352- return lineNumbersHtml ;
374+ // Update padding on all line numbers if digit count changed
375+ if ( startLine === 1 ) {
376+ updateLineNumberPadding ( container , maxDigits ) ;
377+ }
378+ }
379+
380+ function removeExcessLineNumbers ( container : HTMLDivElement , keepCount : number , prevCount : number ) {
381+ for ( let i = prevCount ; i > keepCount ; i -- ) {
382+ if ( container . lastChild ) {
383+ container . removeChild ( container . lastChild ) ;
384+ }
385+ }
386+
387+ // Update padding on remaining line numbers if digit count changed
388+ const maxDigits = String ( keepCount ) . length ;
389+ updateLineNumberPadding ( container , maxDigits ) ;
390+ }
391+
392+ function updateLineNumberPadding ( container : HTMLDivElement , maxDigits : number ) {
393+ Array . from ( container . children ) . forEach ( ( lineNumber , index ) => {
394+ const lineNum = index + 1 ;
395+ lineNumber . textContent = String ( lineNum ) . padStart ( maxDigits , ' ' ) ;
396+ } ) ;
353397}
0 commit comments