@@ -22,6 +22,7 @@ private sealed class ColumnInfo
2222 internal int startCol = 0 ;
2323 internal int width = 0 ;
2424 internal int alignment = TextAlignment . Left ;
25+ internal bool HeaderMatchesProperty = true ;
2526 }
2627 /// <summary>
2728 /// Class containing information about the tabular layout.
@@ -82,9 +83,10 @@ internal static int ComputeWideViewBestItemsPerRowFit(int stringLen, int screenC
8283 /// <param name="screenColumns">Number of character columns on the screen.</param>
8384 /// <param name="columnWidths">Array of specified column widths.</param>
8485 /// <param name="alignment">Array of alignment flags.</param>
86+ /// <param name="headerMatchesProperty">Array of flags where the header label matches a property name.</param>
8587 /// <param name="suppressHeader">If true, suppress header printing.</param>
8688 /// <param name="screenRows">Number of rows on the screen.</param>
87- internal void Initialize ( int leftMarginIndent , int screenColumns , Span < int > columnWidths , ReadOnlySpan < int > alignment , bool suppressHeader , int screenRows = int . MaxValue )
89+ internal void Initialize ( int leftMarginIndent , int screenColumns , Span < int > columnWidths , ReadOnlySpan < int > alignment , ReadOnlySpan < bool > headerMatchesProperty , bool suppressHeader , int screenRows = int . MaxValue )
8890 {
8991 if ( leftMarginIndent < 0 )
9092 {
@@ -139,6 +141,11 @@ internal void Initialize(int leftMarginIndent, int screenColumns, Span<int> colu
139141 _si . columnInfo [ k ] . startCol = startCol ;
140142 _si . columnInfo [ k ] . width = columnWidths [ k ] ;
141143 _si . columnInfo [ k ] . alignment = alignment [ k ] ;
144+ if ( ! headerMatchesProperty . IsEmpty )
145+ {
146+ _si . columnInfo [ k ] . HeaderMatchesProperty = headerMatchesProperty [ k ] ;
147+ }
148+
142149 startCol += columnWidths [ k ] + ScreenInfo . separatorCharacterCount ;
143150 }
144151 }
@@ -156,7 +163,7 @@ internal int GenerateHeader(string[] values, LineOutput lo)
156163
157164 foreach ( string line in _header )
158165 {
159- lo . WriteLine ( style == string . Empty ? line : style + line + reset ) ;
166+ lo . WriteLine ( line ) ;
160167 }
161168
162169 return _header . Count ;
@@ -234,35 +241,21 @@ internal void GenerateRow(string[] values, LineOutput lo, bool multiLine, ReadOn
234241
235242 if ( multiLine )
236243 {
237- foreach ( string line in GenerateTableRow ( values , currentAlignment , lo . DisplayCells ) )
244+ foreach ( string line in GenerateTableRow ( values , currentAlignment , lo . DisplayCells , isHeader ) )
238245 {
239246 generatedRows ? . Add ( line ) ;
240- if ( isHeader )
241- {
242- lo . WriteLine ( style == string . Empty ? line : style + line + reset ) ;
243- }
244- else
245- {
246- lo . WriteLine ( line ) ;
247- }
247+ lo . WriteLine ( line ) ;
248248 }
249249 }
250250 else
251251 {
252- string line = GenerateRow ( values , currentAlignment , dc ) ;
252+ string line = GenerateRow ( values , currentAlignment , dc , isHeader ) ;
253253 generatedRows ? . Add ( line ) ;
254- if ( isHeader )
255- {
256- lo . WriteLine ( style == string . Empty ? line : style + line + reset ) ;
257- }
258- else
259- {
260- lo . WriteLine ( line ) ;
261- }
254+ lo . WriteLine ( line ) ;
262255 }
263256 }
264257
265- private string [ ] GenerateTableRow ( string [ ] values , ReadOnlySpan < int > alignment , DisplayCells ds )
258+ private string [ ] GenerateTableRow ( string [ ] values , ReadOnlySpan < int > alignment , DisplayCells ds , bool isHeader )
266259 {
267260 // select the active columns (skip hidden ones)
268261 Span < int > validColumnArray = _si . columnInfo . Length <= OutCommandInner . StackAllocThreshold ? stackalloc int [ _si . columnInfo . Length ] : new int [ _si . columnInfo . Length ] ;
@@ -291,8 +284,7 @@ private string[] GenerateTableRow(string[] values, ReadOnlySpan<int> alignment,
291284 }
292285
293286 // obtain a set of tokens for each field
294- scArray [ k ] = GenerateMultiLineRowField ( values [ validColumnArray [ k ] ] , validColumnArray [ k ] ,
295- alignment [ validColumnArray [ k ] ] , ds , addPadding ) ;
287+ scArray [ k ] = GenerateMultiLineRowField ( values [ validColumnArray [ k ] ] , validColumnArray [ k ] , alignment [ validColumnArray [ k ] ] , ds , addPadding ) ;
296288
297289 // NOTE: the following padding operations assume that we
298290 // pad with a blank (or any character that ALWAYS maps to a single screen cell
@@ -323,7 +315,9 @@ private string[] GenerateTableRow(string[] values, ReadOnlySpan<int> alignment,
323315 for ( int k = 0 ; k < scArray . Length ; k ++ )
324316 {
325317 if ( scArray [ k ] . Count > screenRows )
318+ {
326319 screenRows = scArray [ k ] . Count ;
320+ }
327321 }
328322
329323 // column headers can span multiple rows if the width of the column is shorter than the header text like:
@@ -335,7 +329,6 @@ private string[] GenerateTableRow(string[] values, ReadOnlySpan<int> alignment,
335329 // 1 2 3
336330 //
337331 // To ensure we don't add whitespace to the end, we need to determine the last column in each row with content
338-
339332 System . Span < int > lastColWithContent = screenRows <= OutCommandInner . StackAllocThreshold ? stackalloc int [ screenRows ] : new int [ screenRows ] ;
340333 for ( int row = 0 ; row < screenRows ; row ++ )
341334 {
@@ -390,17 +383,36 @@ private string[] GenerateTableRow(string[] values, ReadOnlySpan<int> alignment,
390383 for ( int row = 0 ; row < screenRows ; row ++ )
391384 {
392385 StringBuilder sb = new StringBuilder ( ) ;
386+
393387 // for a given row, walk the columns
394388 for ( int col = 0 ; col < scArray . Length ; col ++ )
395389 {
390+ string value = scArray [ col ] [ row ] ;
391+
396392 // if the column is the last column with content, we need to trim trailing whitespace, unless there is only one row
397393 if ( col == lastColWithContent [ row ] && screenRows > 1 )
398394 {
399- sb . Append ( scArray [ col ] [ row ] . TrimEnd ( ) ) ;
395+ value = value . TrimEnd ( ) ;
400396 }
401- else
397+
398+ if ( isHeader )
402399 {
403- sb . Append ( scArray [ col ] [ row ] ) ;
400+ if ( _si . columnInfo [ col ] . HeaderMatchesProperty )
401+ {
402+ sb . Append ( PSStyle . Instance . Formatting . TableHeader ) ;
403+ }
404+ else if ( value . Length > 0 )
405+ {
406+ // after the first column, each additional column starts with a whitespace for separation
407+ value = value . Insert ( col == 0 ? 0 : 1 , PSStyle . Instance . Formatting . CustomTableHeaderLabel ) ;
408+ }
409+ }
410+
411+ sb . Append ( value ) ;
412+
413+ if ( isHeader )
414+ {
415+ sb . Append ( PSStyle . Instance . Reset ) ;
404416 }
405417 }
406418
@@ -427,7 +439,7 @@ private StringCollection GenerateMultiLineRowField(string val, int k, int alignm
427439 return sc ;
428440 }
429441
430- private string GenerateRow ( string [ ] values , ReadOnlySpan < int > alignment , DisplayCells dc )
442+ private string GenerateRow ( string [ ] values , ReadOnlySpan < int > alignment , DisplayCells dc , bool isHeader )
431443 {
432444 StringBuilder sb = new StringBuilder ( ) ;
433445
@@ -462,9 +474,14 @@ private string GenerateRow(string[] values, ReadOnlySpan<int> alignment, Display
462474 }
463475
464476 string rowField = GenerateRowField ( values [ k ] , _si . columnInfo [ k ] . width , alignment [ k ] , dc , addPadding ) ;
477+ if ( isHeader )
478+ {
479+ sb . Append ( PSStyle . Instance . Formatting . TableHeader ) ;
480+ }
481+
465482 sb . Append ( rowField ) ;
466483
467- if ( rowField is not null && rowField . Contains ( ValueStringDecorated . ESC ) && ! rowField . AsSpan ( ) . TrimEnd ( ) . EndsWith ( PSStyle . Instance . Reset ) )
484+ if ( isHeader || ( rowField is not null && rowField . Contains ( ValueStringDecorated . ESC ) && ! rowField . AsSpan ( ) . TrimEnd ( ) . EndsWith ( PSStyle . Instance . Reset ) ) )
468485 {
469486 // Reset the console output if the content of this column contains ESC
470487 sb . Append ( PSStyle . Instance . Reset ) ;
0 commit comments