Skip to content

Commit b035d90

Browse files
authored
feat: add text justification style (#2410)
* feat: add text justification style * fix: apply review changes * fix: remove extra space and set default justification to right for numeric columns * fix: use uniform justification for header columns * fix: use HeaderSeparator
1 parent 2a8bab5 commit b035d90

File tree

35 files changed

+408
-318
lines changed

35 files changed

+408
-318
lines changed

src/BenchmarkDotNet/Exporters/MarkdownExporter.cs

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ public enum MarkdownHighlightStrategy
7979
[PublicAPI] protected string CodeBlockStart = "```";
8080
[PublicAPI] protected string CodeBlockEnd = "```";
8181
[PublicAPI] protected MarkdownHighlightStrategy StartOfGroupHighlightStrategy = MarkdownHighlightStrategy.None;
82-
[PublicAPI] protected string TableHeaderSeparator = " |";
83-
[PublicAPI] protected string TableColumnSeparator = " |";
82+
[PublicAPI] protected string TableHeaderSeparator = " | ";
83+
[PublicAPI] protected string TableColumnSeparator = " | ";
8484
[PublicAPI] protected bool UseHeaderSeparatingRow = true;
8585
[PublicAPI] protected bool ColumnsStartWithSeparator;
8686
[PublicAPI] protected string BoldMarkupFormat = "**{0}**";
@@ -156,21 +156,17 @@ private void PrintTable(SummaryTable table, ILogger logger)
156156
logger.WriteLine();
157157
}
158158

159-
if (ColumnsStartWithSeparator)
160-
{
161-
logger.WriteStatistic(TableHeaderSeparator.TrimStart());
162-
}
159+
logger.WriteStatistic(ColumnsStartWithSeparator ? TableHeaderSeparator.TrimStart() : " ");
163160

164161
table.PrintLine(table.FullHeader, logger, string.Empty, TableHeaderSeparator);
165162
if (UseHeaderSeparatingRow)
166163
{
167-
if (ColumnsStartWithSeparator)
168-
{
169-
logger.WriteStatistic(TableHeaderSeparator.TrimStart());
170-
}
164+
logger.WriteStatistic(ColumnsStartWithSeparator ? TableHeaderSeparator.TrimStart().TrimEnd() + "-" : "-");
171165

172166
logger.WriteLineStatistic(string.Join("",
173-
table.Columns.Where(c => c.NeedToShow).Select(column => new string('-', column.Width) + GetJustificationIndicator(column.Justify) + "|")));
167+
table.Columns.Where(c => c.NeedToShow).Select(column =>
168+
new string('-', column.Width - 1) + GetHeaderSeparatorIndicator(column.OriginalColumn.IsNumeric) +
169+
GetHeaderSeparatorColumnDivider(column.Index, table.ColumnCount))));
174170
}
175171

176172
int rowCounter = 0;
@@ -181,8 +177,8 @@ private void PrintTable(SummaryTable table, ILogger logger)
181177
if (rowCounter > 0 && table.FullContentStartOfLogicalGroup[rowCounter] && table.SeparateLogicalGroups)
182178
{
183179
// Print logical separator
184-
if (ColumnsStartWithSeparator)
185-
logger.WriteStatistic(TableColumnSeparator.TrimStart());
180+
logger.WriteStatistic(ColumnsStartWithSeparator ? TableColumnSeparator.TrimStart() : " ");
181+
186182
table.PrintLine(separatorLine, logger, string.Empty, TableColumnSeparator, highlightRow, false, StartOfGroupHighlightStrategy,
187183
BoldMarkupFormat, false);
188184
}
@@ -193,26 +189,24 @@ private void PrintTable(SummaryTable table, ILogger logger)
193189
highlightRow = !highlightRow;
194190
}
195191

196-
if (ColumnsStartWithSeparator)
197-
logger.WriteStatistic(TableColumnSeparator.TrimStart());
192+
193+
logger.WriteStatistic(ColumnsStartWithSeparator ? TableColumnSeparator.TrimStart() : " ");
198194

199195
table.PrintLine(line, logger, string.Empty, TableColumnSeparator, highlightRow, table.FullContentStartOfHighlightGroup[rowCounter],
200196
StartOfGroupHighlightStrategy, BoldMarkupFormat, EscapeHtml);
201197
rowCounter++;
202198
}
203199
}
204200

205-
private static string GetJustificationIndicator(SummaryTable.SummaryTableColumn.TextJustification textJustification)
201+
private static string GetHeaderSeparatorIndicator(bool isNumeric)
206202
{
207-
switch (textJustification)
208-
{
209-
case SummaryTable.SummaryTableColumn.TextJustification.Left:
210-
return " ";
211-
case SummaryTable.SummaryTableColumn.TextJustification.Right:
212-
return ":";
213-
default:
214-
return " ";
215-
}
203+
return isNumeric ? ":" : " ";
204+
}
205+
206+
private static string GetHeaderSeparatorColumnDivider(int columnIndex, int columnCount)
207+
{
208+
var isLastColumn = columnIndex != columnCount - 1;
209+
return isLastColumn ? "|-" : "|";
216210
}
217211
}
218212
}

src/BenchmarkDotNet/Helpers/HashCode.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,23 @@ public static int Combine<T1, T2, T3, T4, T5, T6, T7, T8>(T1 value1, T2 value2,
109109
return hashCode;
110110
}
111111

112+
public static int Combine<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7,
113+
T8 value8, T9 value9, T10 value10)
114+
{
115+
int hashCode = 0;
116+
hashCode = Hash(hashCode, value1);
117+
hashCode = Hash(hashCode, value2);
118+
hashCode = Hash(hashCode, value3);
119+
hashCode = Hash(hashCode, value4);
120+
hashCode = Hash(hashCode, value5);
121+
hashCode = Hash(hashCode, value6);
122+
hashCode = Hash(hashCode, value7);
123+
hashCode = Hash(hashCode, value8);
124+
hashCode = Hash(hashCode, value9);
125+
hashCode = Hash(hashCode, value10);
126+
return hashCode;
127+
}
128+
112129
[MethodImpl(MethodImplOptions.AggressiveInlining)]
113130
private static int Hash<T>(int hashCode, T value)
114131
{
@@ -128,7 +145,8 @@ private static int Hash<T>(int hashCode, T value, IEqualityComparer<T> comparer)
128145
}
129146

130147
#pragma warning disable CS0809 // Obsolete member 'HashCode.GetHashCode()' overrides non-obsolete member 'object.GetHashCode()'
131-
[Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)]
148+
[Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.",
149+
error: true)]
132150
[EditorBrowsable(EditorBrowsableState.Never)]
133151
public override int GetHashCode() => throw new NotSupportedException();
134152

src/BenchmarkDotNet/Reports/Summary.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ public Summary(
4747
TimeSpan totalTime,
4848
CultureInfo cultureInfo,
4949
ImmutableArray<ValidationError> validationErrors,
50-
ImmutableArray<IColumnHidingRule> columnHidingRules)
50+
ImmutableArray<IColumnHidingRule> columnHidingRules,
51+
SummaryStyle summaryStyle = null)
5152
{
5253
Title = title;
5354
ResultsDirectoryPath = resultsDirectoryPath;
@@ -64,7 +65,7 @@ public Summary(
6465
BenchmarksCases = Orderer.GetSummaryOrder(reports.Select(report => report.BenchmarkCase).ToImmutableArray(), this).ToImmutableArray(); // we sort it first
6566
Reports = BenchmarksCases.Select(b => ReportMap[b]).ToImmutableArray(); // we use sorted collection to re-create reports list
6667
BaseliningStrategy = BaseliningStrategy.Create(BenchmarksCases);
67-
Style = GetConfiguredSummaryStyleOrDefaultOne(BenchmarksCases).WithCultureInfo(cultureInfo);
68+
Style = (summaryStyle ?? GetConfiguredSummaryStyleOrDefaultOne(BenchmarksCases)).WithCultureInfo(cultureInfo);
6869
Table = GetTable(Style);
6970
AllRuntimes = BuildAllRuntimes(HostEnvironmentInfo, Reports);
7071
}

src/BenchmarkDotNet/Reports/SummaryStyle.cs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
using BenchmarkDotNet.Columns;
44
using BenchmarkDotNet.Helpers;
55
using Perfolizer.Horology;
6+
using static BenchmarkDotNet.Reports.SummaryTable.SummaryTableColumn;
67

78
// ReSharper disable MemberCanBePrivate.Global
89

910
namespace BenchmarkDotNet.Reports
1011
{
1112
public class SummaryStyle : IEquatable<SummaryStyle>
1213
{
13-
public static readonly SummaryStyle Default = new SummaryStyle(DefaultCultureInfo.Instance, printUnitsInHeader: false, printUnitsInContent: true, printZeroValuesInContent: false, sizeUnit: null, timeUnit: null);
14+
public static readonly SummaryStyle Default = new SummaryStyle(DefaultCultureInfo.Instance, printUnitsInHeader: false, printUnitsInContent: true,
15+
printZeroValuesInContent: false, sizeUnit: null, timeUnit: null);
16+
1417
internal const int DefaultMaxParameterColumnWidth = 15 + 5; // 5 is for postfix " [15]"
1518

1619
public bool PrintUnitsInHeader { get; }
@@ -24,8 +27,12 @@ public class SummaryStyle : IEquatable<SummaryStyle>
2427

2528
public RatioStyle RatioStyle { get; }
2629

30+
public TextJustification TextColumnJustification { get; }
31+
public TextJustification NumericColumnJustification { get; }
32+
2733
public SummaryStyle(CultureInfo? cultureInfo, bool printUnitsInHeader, SizeUnit sizeUnit, TimeUnit timeUnit, bool printUnitsInContent = true,
28-
bool printZeroValuesInContent = false, int maxParameterColumnWidth = DefaultMaxParameterColumnWidth, RatioStyle ratioStyle = RatioStyle.Value)
34+
bool printZeroValuesInContent = false, int maxParameterColumnWidth = DefaultMaxParameterColumnWidth, RatioStyle ratioStyle = RatioStyle.Value,
35+
TextJustification textColumnJustification = TextJustification.Left, TextJustification numericColumnJustification = TextJustification.Right)
2936
{
3037
if (maxParameterColumnWidth < DefaultMaxParameterColumnWidth)
3138
throw new ArgumentOutOfRangeException(nameof(maxParameterColumnWidth), $"{DefaultMaxParameterColumnWidth} is the minimum.");
@@ -35,29 +42,37 @@ public SummaryStyle(CultureInfo? cultureInfo, bool printUnitsInHeader, SizeUnit
3542
PrintUnitsInContent = printUnitsInContent;
3643
SizeUnit = sizeUnit;
3744
TimeUnit = timeUnit;
38-
PrintZeroValuesInContent = printZeroValuesInContent;
3945
MaxParameterColumnWidth = maxParameterColumnWidth;
4046
RatioStyle = ratioStyle;
4147
CodeSizeUnit = SizeUnit.B;
48+
PrintZeroValuesInContent = printZeroValuesInContent;
49+
TextColumnJustification = textColumnJustification;
50+
NumericColumnJustification = numericColumnJustification;
4251
}
4352

4453
public SummaryStyle WithTimeUnit(TimeUnit timeUnit)
45-
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, timeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, RatioStyle);
54+
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, timeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth,
55+
RatioStyle, TextColumnJustification, NumericColumnJustification);
4656

4757
public SummaryStyle WithSizeUnit(SizeUnit sizeUnit)
48-
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, sizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, RatioStyle);
58+
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, sizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth,
59+
RatioStyle, TextColumnJustification, NumericColumnJustification);
4960

5061
public SummaryStyle WithZeroMetricValuesInContent()
51-
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, printZeroValuesInContent: true, MaxParameterColumnWidth, RatioStyle);
62+
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, printZeroValuesInContent: true,
63+
MaxParameterColumnWidth, RatioStyle, TextColumnJustification, NumericColumnJustification);
5264

5365
public SummaryStyle WithMaxParameterColumnWidth(int maxParameterColumnWidth)
54-
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, maxParameterColumnWidth, RatioStyle);
66+
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, maxParameterColumnWidth,
67+
RatioStyle, TextColumnJustification, NumericColumnJustification);
5568

5669
public SummaryStyle WithCultureInfo(CultureInfo cultureInfo)
57-
=> new SummaryStyle(cultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, RatioStyle);
70+
=> new SummaryStyle(cultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth,
71+
RatioStyle, TextColumnJustification, NumericColumnJustification);
5872

5973
public SummaryStyle WithRatioStyle(RatioStyle ratioStyle)
60-
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth, ratioStyle);
74+
=> new SummaryStyle(CultureInfo, PrintUnitsInHeader, SizeUnit, TimeUnit, PrintUnitsInContent, PrintZeroValuesInContent, MaxParameterColumnWidth,
75+
ratioStyle, TextColumnJustification, NumericColumnJustification);
6176

6277
public bool Equals(SummaryStyle other)
6378
{
@@ -73,7 +88,9 @@ public bool Equals(SummaryStyle other)
7388
&& Equals(CodeSizeUnit, other.CodeSizeUnit)
7489
&& Equals(TimeUnit, other.TimeUnit)
7590
&& MaxParameterColumnWidth == other.MaxParameterColumnWidth
76-
&& RatioStyle == other.RatioStyle;
91+
&& RatioStyle == other.RatioStyle
92+
&& TextColumnJustification == other.TextColumnJustification
93+
&& NumericColumnJustification == other.NumericColumnJustification;
7794
}
7895

7996
public override bool Equals(object obj) => obj is SummaryStyle summary && Equals(summary);
@@ -87,7 +104,9 @@ public override int GetHashCode() =>
87104
CodeSizeUnit,
88105
TimeUnit,
89106
MaxParameterColumnWidth,
90-
RatioStyle);
107+
RatioStyle,
108+
TextColumnJustification,
109+
NumericColumnJustification);
91110

92111
public static bool operator ==(SummaryStyle left, SummaryStyle right) => Equals(left, right);
93112

src/BenchmarkDotNet/Reports/SummaryTable.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ internal SummaryTable(Summary summary, SummaryStyle style = null)
5555
.Select(r => r.GcStats.GetBytesAllocatedPerOperation(r.BenchmarkCase).Value)
5656
.ToArray()));
5757
}
58+
EffectiveSummaryStyle = style;
5859

5960
var columns = summary.GetColumns();
6061
ColumnCount = columns.Length;
@@ -96,8 +97,6 @@ internal SummaryTable(Summary summary, SummaryStyle style = null)
9697
bool hide = summary.ColumnHidingRules.Any(rule => rule.NeedToHide(column));
9798
Columns[i] = new SummaryTableColumn(this, i, column, hide);
9899
}
99-
100-
EffectiveSummaryStyle = style;
101100
}
102101

103102
public class SummaryTableColumn
@@ -123,7 +122,7 @@ public SummaryTableColumn(SummaryTable table, int index, IColumn column, bool hi
123122
IsDefault = table.IsDefault[index];
124123
OriginalColumn = column;
125124

126-
Justify = column.IsNumeric ? TextJustification.Right : TextJustification.Left;
125+
Justify = column.IsNumeric ? table.EffectiveSummaryStyle.NumericColumnJustification : table.EffectiveSummaryStyle.TextColumnJustification;
127126

128127
bool needToShow = column.AlwaysShow || Content.Distinct().Count() > 1;
129128
NeedToShow = !hide && needToShow;

src/BenchmarkDotNet/Reports/SummaryTableExtensions.cs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ public static void PrintLine(this SummaryTable table, string[] line, ILogger log
4848
}
4949

5050
public static void PrintLine(this SummaryTable table, string[] line, ILogger logger, string leftDel, string rightDel,
51-
bool highlightRow, bool startOfGroup, MarkdownExporter.MarkdownHighlightStrategy startOfGroupHighlightStrategy, string boldMarkupFormat, bool escapeHtml)
51+
bool highlightRow, bool startOfGroup, MarkdownExporter.MarkdownHighlightStrategy startOfGroupHighlightStrategy, string boldMarkupFormat,
52+
bool escapeHtml)
5253
{
5354
for (int columnIndex = 0; columnIndex < table.ColumnCount; columnIndex++)
5455
{
@@ -82,23 +83,47 @@ public static void PrintLine(this SummaryTable table, string[] line, ILogger log
8283
private static string BuildStandardText(SummaryTable table, string[] line, string leftDel, string rightDel, int columnIndex)
8384
{
8485
var buffer = GetClearBuffer();
86+
var isBuildingHeader = table.FullHeader[columnIndex] == line[columnIndex];
87+
var columnJustification = isBuildingHeader ? SummaryTable.SummaryTableColumn.TextJustification.Left : table.Columns[columnIndex].Justify;
8588

8689
buffer.Append(leftDel);
87-
PadLeft(table, line, leftDel, rightDel, columnIndex, buffer);
90+
if (columnJustification == SummaryTable.SummaryTableColumn.TextJustification.Right)
91+
{
92+
AddPadding(table, line, leftDel, rightDel, columnIndex, buffer);
93+
}
94+
8895
buffer.Append(line[columnIndex]);
89-
buffer.Append(rightDel);
96+
97+
if (columnJustification == SummaryTable.SummaryTableColumn.TextJustification.Left)
98+
{
99+
AddPadding(table, line, leftDel, rightDel, columnIndex, buffer);
100+
}
101+
var isLastColumn = columnIndex == table.ColumnCount - 1;
102+
buffer.Append(isLastColumn ? rightDel.TrimEnd() : rightDel);
90103

91104
return buffer.ToString();
92105
}
93106

94107
private static string BuildBoldText(SummaryTable table, string[] line, string leftDel, string rightDel, int columnIndex, string boldMarkupFormat)
95108
{
96109
var buffer = GetClearBuffer();
110+
var isBuildingHeader = table.FullHeader[columnIndex] == line[columnIndex];
111+
var columnJustification = isBuildingHeader ? SummaryTable.SummaryTableColumn.TextJustification.Left : table.Columns[columnIndex].Justify;
97112

98113
buffer.Append(leftDel);
99-
PadLeft(table, line, leftDel, rightDel, columnIndex, buffer);
114+
if (columnJustification == SummaryTable.SummaryTableColumn.TextJustification.Right)
115+
{
116+
AddPadding(table, line, leftDel, rightDel, columnIndex, buffer);
117+
}
118+
100119
buffer.AppendFormat(boldMarkupFormat, line[columnIndex]);
101-
buffer.Append(rightDel);
120+
121+
if (columnJustification == SummaryTable.SummaryTableColumn.TextJustification.Left)
122+
{
123+
AddPadding(table, line, leftDel, rightDel, columnIndex, buffer);
124+
}
125+
var isLastColumn = columnIndex == table.ColumnCount - 1;
126+
buffer.Append(isLastColumn ? rightDel.TrimEnd() : rightDel);
102127

103128
return buffer.ToString();
104129
}
@@ -116,7 +141,7 @@ private static StringBuilder GetClearBuffer()
116141
}
117142

118143
[MethodImpl(MethodImplOptions.AggressiveInlining)]
119-
private static void PadLeft(SummaryTable table, string[] line, string leftDel, string rightDel, int columnIndex, StringBuilder buffer)
144+
private static void AddPadding(SummaryTable table, string[] line, string leftDel, string rightDel, int columnIndex, StringBuilder buffer)
120145
{
121146
const char space = ' ';
122147
const int extraWidth = 2; // " |".Length is not included in the column's Width

tests/BenchmarkDotNet.Tests/Attributes/VerifiedFiles/ParamsAllValuesVerifyTests.BenchmarkShouldProduceSummary_WithAllValuesOfBool.verified.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ Frequency: 2531248 Hz, Resolution: 395.0620 ns, Timer: TSC
77
DefaultJob : extra output line
88

99

10-
Method | ParamProperty | Mean | Error | StdDev |
10+
Method | ParamProperty | Mean | Error | StdDev |
1111
---------- |-------------- |---------:|--------:|--------:|
12-
Benchmark | False | 102.0 ns | 6.09 ns | 1.58 ns | ^
13-
Benchmark | True | 202.0 ns | 6.09 ns | 1.58 ns | ^
12+
Benchmark | False | 102.0 ns | 6.09 ns | 1.58 ns | ^
13+
Benchmark | True | 202.0 ns | 6.09 ns | 1.58 ns | ^
1414

1515
Errors: 0

0 commit comments

Comments
 (0)