import type { InternalColumnMeta , ColumnMetasArray , ColumnWidth , } from '../types' ; const isPercentPattern = /^\d+%$/ ; const defaultColumnMetas = { width : 'auto' , align : 'left' , contentWidth : 0 , paddingLeft : 0 , paddingRight : 0 , paddingTop : 0 , paddingBottom : 0 , horizontalPadding : 0 , paddingLeftString : '' , paddingRightString : '' , } ; const initColumns = ( columnContentWidths , columnMetas ) => { const columns = [ ] ; for ( let i = 0 ; i < columnContentWidths . length ; i += 1 ) { const columnWidth = columnMetas [ i ] ?? 'auto' ; if ( typeof columnWidth === 'number' || columnWidth === 'auto' || columnWidth === 'content-width' || ( typeof columnWidth === 'string' && isPercentPattern . test ( columnWidth ) ) ) { columns . push ( { ... defaultColumnMetas , width : columnWidth as ColumnWidth , contentWidth : columnContentWidths [ i ] , } ) ; continue ; } if ( columnWidth && ( typeof columnWidth === 'object' ) ) { const column = { ... defaultColumnMetas , ... columnWidth , contentWidth : columnContentWidths [ i ] , } ; column . horizontalPadding = column . paddingLeft + column . paddingRight ; columns . push ( column ) ; continue ; } throw new Error ( `Invalid column width: ${ JSON . stringify ( columnWidth ) }` ) ; } return columns ; } ; function resolveColumnWidths < T extends ColumnWidth > ( columnMetas , stdoutWidth ) { for ( const column of columnMetas ) { const { width } = column ; if ( width === 'content-width' ) { column . width = column . contentWidth as T ; } if ( width === 'auto' ) { const recommendedWidth = Math . min ( 20 , column . contentWidth ) ; column . width = recommendedWidth as T ; column . autoOverflow = column . contentWidth - recommendedWidth ; } if ( typeof width === 'string' && isPercentPattern . test ( width ) ) { const columnWidthPercent = Number . parseFloat ( width . slice ( 0 , - 1 ) ) / 100 ; column . width = ( Math . floor ( stdoutWidth * columnWidthPercent ) - ( column . paddingLeft + column . paddingRight ) as T ) ; } const { horizontalPadding } = column ; const minimumContentWidth = 1 ; const mimiumWidth = minimumContentWidth + horizontalPadding ; if ( mimiumWidth >= stdoutWidth ) { const exceedingBy = mimiumWidth - stdoutWidth ; const paddingLeftReduction = Math . ceil ( ( column . paddingLeft / horizontalPadding ) * exceedingBy ) ; const paddingRightReduction = exceedingBy - paddingLeftReduction ; column . paddingLeft -= paddingLeftReduction ; column . paddingRight -= paddingRightReduction ; column . horizontalPadding = column . paddingLeft + column . paddingRight ; } column . paddingLeftString = column . paddingLeft ? ' ' . repeat ( column . paddingLeft ) : '' ; column . paddingRightString = column . paddingRight ? ' ' . repeat ( column . paddingRight ) : '' ; const availableWidth = stdoutWidth - column . horizontalPadding ; column . width = Math . max ( Math . min ( column . width as number , availableWidth ) , minimumContentWidth ) as T ; } } const makeRow = ( ) => Object . assign ( [ ] as InternalColumnMeta < number > [ ] , { columns : 0 } ) ; function balanceAuto ( columnMetas , stdoutWidth ) { const rows = [ makeRow ( ) ] ; let [ currentRow ] = rows ; for ( const column of columnMetas ) { const outerWidth = column . width + column . horizontalPadding ; if ( ( currentRow . columns + outerWidth ) > stdoutWidth ) { currentRow = makeRow ( ) ; rows . push ( currentRow ) ; } currentRow . push ( column ) ; currentRow . columns += outerWidth ; } for ( const columns of rows ) { const usedWidth = columns . reduce ( ( sum , column ) => ( sum + ( column . width as number ) + column . horizontalPadding ) , 0 ) ; let remainingWidth = stdoutWidth - usedWidth ; if ( remainingWidth === 0 ) { continue ; } const autoColumns = columns . filter ( column => 'autoOverflow' in column ) as ( InternalColumnMeta < number > & { autoOverflow : number ; } ) [ ] ; const widthOverflows = autoColumns . filter ( column => column . autoOverflow > 0 ) ; const widthOverflowTotal = widthOverflows . reduce ( ( sum , column ) => sum + column . autoOverflow , 0 ) ; const overflowRestorationBudget = Math . min ( widthOverflowTotal , remainingWidth ) ; for ( const column of widthOverflows ) { const addBack = Math . floor ( ( column . autoOverflow / widthOverflowTotal ) * overflowRestorationBudget ) ; column . width += addBack ; remainingWidth -= addBack ; } const addEachColumn = Math . floor ( remainingWidth / autoColumns . length ) ; for ( let i = 0 ; i < autoColumns . length ; i += 1 ) { const column = autoColumns [ i ] ; if ( i === autoColumns . length - 1 ) { column . width += remainingWidth ; } else { column . width += addEachColumn ; } remainingWidth -= addEachColumn ; } } return rows ; } export function computeColumnWidths ( stdoutColumns , columnMetas , columnContentWidths ) { const columnWidths = initColumns ( columnContentWidths , columnMetas ) ; resolveColumnWidths ( columnWidths , stdoutColumns ) ; return balanceAuto ( columnWidths , stdoutColumns ) ; }