MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5 6namespace Microsoft.PowerShell.Commands.Internal.Format7{8 /// <summary>9 /// Class providing an algorithm for automatic resizing10 /// of table columns.11 /// </summary>12 internal sealed class ColumnWidthManager13 {14 /// <summary>15 /// Class providing an algorithm for automatic resizing.16 /// </summary>17 /// <param name="tableWidth">Overall width of the table in characters.</param>18 /// <param name="minimumColumnWidth">Minimum usable column width.</param>19 /// <param name="separatorWidth">Number of separator characters.</param>20 internal ColumnWidthManager(int tableWidth, int minimumColumnWidth, int separatorWidth)21 {22 _tableWidth = tableWidth;23 _minimumColumnWidth = minimumColumnWidth;24 _separatorWidth = separatorWidth;25 }26 27 /// <summary>28 /// Calculate the widths by applying some heuristics to get them to fit on the29 /// allotted table width. It first assigns widths to the columns that do not have a specified30 /// width, then it checks if the total width exceeds the screen widths. If so, it proceeds31 /// with column elimination, starting from the right most column.32 /// </summary>33 /// <param name="columnWidths">Array of column widths to appropriately size.</param>34 internal void CalculateColumnWidths(Span<int> columnWidths)35 {36 if (AssignColumnWidths(columnWidths))37 {38 // we do not have any trimming to do, we are done39 return;40 }41 42 // total width exceeds screen width, go on with trimming43 TrimToFit(columnWidths);44 }45 46 /// <summary>47 /// Do not remove columns, just assign widths to columns that have a zero width48 /// (meaning unassigned)49 /// </summary>50 /// <param name="columnWidths">Columns to process.</param>51 /// <returns>True if there was a fit, false if there is need for trimming.</returns>52 private bool AssignColumnWidths(Span<int> columnWidths)53 {54 // run a quick check to see if all the columns have a specified width,55 // if so, we are done56 bool allSpecified = true;57 int maxInitialWidthSum = 0;58 59 for (int k = 0; k < columnWidths.Length; k++)60 {61 if (columnWidths[k] <= 0)62 {63 allSpecified = false;64 break;65 }66 67 maxInitialWidthSum += columnWidths[k];68 }69 70 if (allSpecified)71 {72 // compute the total table width (columns and separators)73 maxInitialWidthSum += _separatorWidth * (columnWidths.Length - 1);74 if (maxInitialWidthSum <= _tableWidth)75 {76 // we fit with all the columns specified77 return true;78 }79 // we do not fit, we will have to trim80 return false;81 }82 83 // we have columns with no width assigned84 // remember the columns we are trying to size85 // assign them the minimum column size86 bool[] fixedColumn = new bool[columnWidths.Length];87 for (int k = 0; k < columnWidths.Length; k++)88 {89 fixedColumn[k] = columnWidths[k] > 0;90 if (columnWidths[k] == 0)91 columnWidths[k] = _minimumColumnWidth;92 }93 94 // see if we fit95 int currentTableWidth = CurrentTableWidth(columnWidths);96 int availableWidth = _tableWidth - currentTableWidth;97 98 if (availableWidth < 0)99 {100 // if the total width is too much, we will have to remove some columns101 return false;102 }103 else if (availableWidth == 0)104 {105 // we just fit106 return true;107 }108 109 // we still have room and we want to add more width110 111 while (availableWidth > 0)112 {113 for (int k = 0; k < columnWidths.Length; k++)114 {115 if (fixedColumn[k])116 continue;117 118 columnWidths[k]++;119 availableWidth--;120 if (availableWidth == 0)121 break;122 }123 }124 125 return true; // we fit126 }127 128 /// <summary>129 /// Trim columns if the total column width is too much for the screen.130 /// </summary>131 /// <param name="columnWidths">Column widths to trim.</param>132 private void TrimToFit(Span<int> columnWidths)133 {134 while (true)135 {136 int currentTableWidth = CurrentTableWidth(columnWidths);137 int widthInExcess = currentTableWidth - _tableWidth;138 if (widthInExcess <= 0)139 {140 return; // we are done, because we fit141 }142 143 // we need to remove or shrink the last visible column144 int lastVisibleColumn = GetLastVisibleColumn(columnWidths);145 146 if (lastVisibleColumn < 0)147 return; // nothing left to hide, because all the columns are hidden148 149 // try to trim the last column to fit150 int newLastVisibleColumnWidth = columnWidths[lastVisibleColumn] - widthInExcess;151 152 if (newLastVisibleColumnWidth < _minimumColumnWidth)153 {154 // cannot fit it in, just hide155 columnWidths[lastVisibleColumn] = -1;156 continue;157 }158 else159 {160 // shrink the column to fit161 columnWidths[lastVisibleColumn] = newLastVisibleColumnWidth;162 }163 }164 }165 166 /// <summary>167 /// Computes the total table width from the column width array.168 /// </summary>169 /// <param name="columnWidths">Column widths array.</param>170 /// <returns></returns>171 private int CurrentTableWidth(Span<int> columnWidths)172 {173 int sum = 0;174 int visibleColumns = 0;175 176 for (int k = 0; k < columnWidths.Length; k++)177 {178 if (columnWidths[k] > 0)179 {180 sum += columnWidths[k];181 visibleColumns++;182 }183 }184 185 return sum + _separatorWidth * (visibleColumns - 1);186 }187 188 /// <summary>189 /// Get the last visible column (i.e. with a width >= 0)190 /// </summary>191 /// <param name="columnWidths">Column widths array.</param>192 /// <returns>Index of the last visible column, -1 if none.</returns>193 private static int GetLastVisibleColumn(Span<int> columnWidths)194 {195 for (int k = 0; k < columnWidths.Length; k++)196 {197 if (columnWidths[k] < 0)198 return k - 1;199 }200 201 return columnWidths.Length - 1;202 }203 204 private readonly int _tableWidth;205 private readonly int _minimumColumnWidth;206 private readonly int _separatorWidth;207 }208}209 