From 2a8d2d556399511fa013b0fa51a361f0c8764a95 Mon Sep 17 00:00:00 2001 From: Ersin Dagli Date: Sat, 27 Oct 2018 21:11:55 +1100 Subject: [PATCH 01/36] Two new styles added --- ConTabs/Styles.cs | 27 ++++++++++++++++++++++++++ ConTabsDemo-DotNetFramework/Program.cs | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/ConTabs/Styles.cs b/ConTabs/Styles.cs index b939308..1dba9f4 100644 --- a/ConTabs/Styles.cs +++ b/ConTabs/Styles.cs @@ -78,6 +78,33 @@ public Style(char wall, char floor, Corners corners) TeeNoRight = '╣' }); + public static Style Hash => new Style('#', '#', new Corners + { + CornerTopLeft = '#', + CornerTopRight = '#', + CornerBottomLeft = '#', + CornerBottomRight = '#', + Intersection = '#', + TeeNoUp = '#', + TeeNoLeft = '#', + TeeNoDown = '#', + TeeNoRight = '#' + }); + + + public static Style Plus => new Style('+', '+', new Corners + { + CornerTopLeft = '+', + CornerTopRight = '+', + CornerBottomLeft = '+', + CornerBottomRight = '+', + Intersection = '+', + TeeNoUp = '+', + TeeNoLeft = '+', + TeeNoDown = '+', + TeeNoRight = '+' + }); + /// /// Built-in style /// diff --git a/ConTabsDemo-DotNetFramework/Program.cs b/ConTabsDemo-DotNetFramework/Program.cs index 77c9f66..4c2dd8d 100644 --- a/ConTabsDemo-DotNetFramework/Program.cs +++ b/ConTabsDemo-DotNetFramework/Program.cs @@ -25,7 +25,7 @@ static void Main(string[] args) */ // Set the style (and enable Unicode for the console - table.TableStyle = Style.UnicodePipes; + table.TableStyle = Style.Hash; Console.OutputEncoding = Encoding.Unicode; // Hide the diameter column From 62cd5111e0f3ee03dee222e4883c73785e00ed32 Mon Sep 17 00:00:00 2001 From: Marek Zitnansky Date: Sun, 4 Nov 2018 20:56:03 +0100 Subject: [PATCH 02/36] Changing date format tests to use all cultures without hard-coding --- ConTabs.Tests/FormatTests.cs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/ConTabs.Tests/FormatTests.cs b/ConTabs.Tests/FormatTests.cs index 2dda40e..b9bcf85 100644 --- a/ConTabs.Tests/FormatTests.cs +++ b/ConTabs.Tests/FormatTests.cs @@ -9,22 +9,23 @@ namespace ConTabs.Tests { class FormatTests { - [TestCase("da" , "yyyy/MM/dd", "2018-01-31")] - [TestCase("en-GB", "dd/MM/yy" , "31/01/18" )] - [TestCase("en-US", "MM/dd/yy" , "01/31/18" )] - [TestCase("sk" , "d/M/yyyy" , "31.1.2018" )] - public void DateTimeFieldCanBeFormatted(string culture, string format, string expected) + [Test] + public void DateTimeFieldCanBeFormatted() { - // Arrange - Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culture); - var tableObj = Table.Create(); - - // Act - tableObj.Columns[3].FormatString = format; - var val = tableObj.Columns[3].StringValForCol(new DateTime(2018, 01, 31)); - - // Assert - val.ShouldBe(expected); + foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures)) + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name); + var tableObj = Table.Create(); + + // Act + tableObj.Columns[3].FormatString = CultureInfo.CurrentCulture.DateTimeFormat.FullDateTimePattern; + var val = tableObj.Columns[3].StringValForCol(new DateTime(2018, 01, 31)); + Console.WriteLine($"Culture {ci.DisplayName} ({ci.Name}): {val}"); + + // Assert + val.ShouldBe(new DateTime(2018, 01, 31).ToString(ci.DateTimeFormat.FullDateTimePattern), "Wrong date formatting for culture " + ci.Name); + } } [TestCase("en-GB", "£1.91")] From 79c956c1b079145cfff67f39d818fb27e1324a6f Mon Sep 17 00:00:00 2001 From: tdwright Date: Tue, 6 Nov 2018 11:14:07 +1100 Subject: [PATCH 03/36] Supressing problematic test for now --- ConTabs.Tests/FormatTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConTabs.Tests/FormatTests.cs b/ConTabs.Tests/FormatTests.cs index 2dda40e..2a61013 100644 --- a/ConTabs.Tests/FormatTests.cs +++ b/ConTabs.Tests/FormatTests.cs @@ -12,7 +12,7 @@ class FormatTests [TestCase("da" , "yyyy/MM/dd", "2018-01-31")] [TestCase("en-GB", "dd/MM/yy" , "31/01/18" )] [TestCase("en-US", "MM/dd/yy" , "01/31/18" )] - [TestCase("sk" , "d/M/yyyy" , "31.1.2018" )] + //[TestCase("sk" , "d/M/yyyy" , "31.1.2018" )] public void DateTimeFieldCanBeFormatted(string culture, string format, string expected) { // Arrange From ae7cfa2b8f82a21d62032c71d2b6bb064e09a479 Mon Sep 17 00:00:00 2001 From: Marek Zitnansky Date: Thu, 8 Nov 2018 17:48:39 +0100 Subject: [PATCH 04/36] Date in tests is formatted using culture and regardless of culture --- ConTabs.Tests/FormatTests.cs | 57 ++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/ConTabs.Tests/FormatTests.cs b/ConTabs.Tests/FormatTests.cs index b9bcf85..01a4420 100644 --- a/ConTabs.Tests/FormatTests.cs +++ b/ConTabs.Tests/FormatTests.cs @@ -9,23 +9,48 @@ namespace ConTabs.Tests { class FormatTests { - [Test] - public void DateTimeFieldCanBeFormatted() + [TestCase("da-DK")] + [TestCase("en-GB")] + [TestCase("en-US")] + [TestCase("sk-SK")] + [TestCase("zh-CN")] + public void DateTimeFieldCanBeFormattedRegardlessOfCulture(string cultureName) + { + // Arrange + var refDate = new DateTime(2018, 01, 31); + var culture = CultureInfo.CreateSpecificCulture(cultureName); + Thread.CurrentThread.CurrentCulture = culture; + + // Act + var tableObj = Table.Create(); + tableObj.Columns[3].FormatString = "d-M-yyyy"; + var val = tableObj.Columns[3].StringValForCol(refDate); + + // Assert + val.ShouldBe("31-1-2018"); + } + + [TestCase("da-DK")] + [TestCase("en-GB")] + [TestCase("en-US")] + [TestCase("sk-SK")] + [TestCase("zh-CN")] + public void DateTimeFieldCanBeFormattedUsingCulture(string cultureName) { - foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures)) - { - // Arrange - Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name); - var tableObj = Table.Create(); - - // Act - tableObj.Columns[3].FormatString = CultureInfo.CurrentCulture.DateTimeFormat.FullDateTimePattern; - var val = tableObj.Columns[3].StringValForCol(new DateTime(2018, 01, 31)); - Console.WriteLine($"Culture {ci.DisplayName} ({ci.Name}): {val}"); - - // Assert - val.ShouldBe(new DateTime(2018, 01, 31).ToString(ci.DateTimeFormat.FullDateTimePattern), "Wrong date formatting for culture " + ci.Name); - } + // Arrange + var refDate = new DateTime(2018, 01, 31); + var formatString = "dddd"; + var culture = CultureInfo.CreateSpecificCulture(cultureName); + Thread.CurrentThread.CurrentCulture = culture; + var dateString = refDate.ToString(formatString, culture); + + // Act + var tableObj = Table.Create(); + tableObj.Columns[3].FormatString = formatString; + var val = tableObj.Columns[3].StringValForCol(refDate); + + // Assert + val.ShouldBe(dateString); } [TestCase("en-GB", "£1.91")] From a9d7a7457b1e9cd2c6b52eae7ffb6a08c9bd08a8 Mon Sep 17 00:00:00 2001 From: Marek Zitnansky Date: Sun, 11 Nov 2018 19:25:50 +0100 Subject: [PATCH 05/36] First attempt to make stretch styles for tables --- ConTabs/Alignment.cs | 7 +++- ConTabs/Column.cs | 15 ------- ConTabs/OutputBuilder.cs | 22 +++++++--- ConTabs/Padding.cs | 8 ++++ ConTabs/Table.cs | 15 +++++++ ConTabs/TableStretchStyles.cs | 78 +++++++++++++++++++++++++++++++++++ 6 files changed, 123 insertions(+), 22 deletions(-) create mode 100644 ConTabs/TableStretchStyles.cs diff --git a/ConTabs/Alignment.cs b/ConTabs/Alignment.cs index f856c22..ffead75 100644 --- a/ConTabs/Alignment.cs +++ b/ConTabs/Alignment.cs @@ -46,7 +46,7 @@ private static string AlignCenter(string input, int colMaxWidth) private static string GetPaddingSpaces(int amount) { - return new string(' ', amount); + return new string(' ', amount > 0 ? amount : 0); } /// @@ -61,6 +61,11 @@ public string ProcessString(string input, int colMaxWidth) { GetPaddingSpaces(colMaxWidth); } + else if (input.Length > colMaxWidth) + { + //todo: maybe default long string behavior? + input = input.Substring(0, colMaxWidth); + } return Method(input, colMaxWidth); } diff --git a/ConTabs/Column.cs b/ConTabs/Column.cs index af61cf5..d12ca6c 100644 --- a/ConTabs/Column.cs +++ b/ConTabs/Column.cs @@ -54,21 +54,6 @@ public class Column /// A List of the values stored /// public List Values { get; set; } - public int MaxWidth - { - get - { - if (Values == null || Values.Count() == 0) return ColumnName.Length; - - if (LongStringBehaviour.Width > 0) return LongStringBehaviour.Width; - - return Values - .Select(v => StringValForCol(v)) - .Union(new List { ColumnName }) - .Select(v => v.Length) - .Max(); - } - } public Column(Type type, string name) { diff --git a/ConTabs/OutputBuilder.cs b/ConTabs/OutputBuilder.cs index 73c8988..20e665b 100644 --- a/ConTabs/OutputBuilder.cs +++ b/ConTabs/OutputBuilder.cs @@ -15,6 +15,7 @@ private sealed class OutputBuilder where T2 : class private readonly StringBuilder sb; private readonly Table table; private readonly Style style; + private readonly int leftOffset; internal static string BuildOutput(Table t, Style s) { @@ -27,7 +28,15 @@ private OutputBuilder(Table t, Style s) table = t; style = s; sb = new StringBuilder(); - + int colWidths = table._colsShown.Sum(c => t.TableStretchStyles.Method(t._colsShown, c.ColumnName, t.CanvasWidth, t.Padding.GetHorizontalPadding())); + int realWidth = colWidths + (table._colsShown.Count * (table.Padding.Left + table.Padding.Right)) + table._colsShown.Count + 1; + leftOffset = + table.CanvasWidth < realWidth ? 0 : + table.TableAlignment.Equals(Alignment.Right) ? table.CanvasWidth - realWidth : + table.TableAlignment.Equals(Alignment.Center) ? (table.CanvasWidth - realWidth) / 2 : + 0; + + sb.Append(new string(' ', leftOffset)); HLine(TopMidBot.Top); InsertVerticalPadding(table.Padding.Top, style.Wall); NewLine(); Headers(); @@ -60,7 +69,7 @@ private void InsertVerticalPadding(byte padding, char columnSeparator) sb.Append(style.Wall); for (int i = 0; i < table._colsShown.Count; i++) { - sb.Append(new string(' ', table._colsShown[i].MaxWidth + (table.Padding.Left + table.Padding.Right))); + sb.Append(new string(' ', table.TableStretchStyles.Method(table._colsShown, table._colsShown[i].ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding()) + table.Padding.GetHorizontalPadding())); if (i < table._colsShown.Count - 1) { @@ -74,6 +83,7 @@ private void InsertVerticalPadding(byte padding, char columnSeparator) private void NewLine() { sb.Append(Environment.NewLine); + sb.Append(new string(' ', leftOffset)); } private void HLine(TopMidBot v) @@ -82,7 +92,7 @@ private void HLine(TopMidBot v) for (int i = 0; i < table._colsShown.Count; i++) { - sb.Append(new string(style.Floor, table._colsShown[i].MaxWidth + (table.Padding.Left + table.Padding.Right))); + sb.Append(new string(style.Floor, table.TableStretchStyles.Method(table._colsShown, table._colsShown[i].ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding()) + table.Padding.GetHorizontalPadding())); if (i < table._colsShown.Count - 1) { @@ -95,7 +105,7 @@ private void HLine(TopMidBot v) private void NoDataLine() { var noDataText = "no data"; - int colWidths = table._colsShown.Sum(c => c.MaxWidth); + int colWidths = table._colsShown.Sum(c => table.TableStretchStyles.Method(table._colsShown, c.ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding())); int innerWidth = colWidths + (table._colsShown.Count * (table.Padding.Left + table.Padding.Right)) + table._colsShown.Count - 1; int leftPad = (innerWidth - noDataText.Length) / 2; int rightPad = innerWidth - (leftPad + noDataText.Length); @@ -107,13 +117,13 @@ private void Headers() sb.Append(style.Wall); foreach (var col in table._colsShown) { - sb.Append(GetPaddingString(table.Padding.Left) + table.HeaderAlignment.ProcessString(col.ColumnName, col.MaxWidth) + GetPaddingString(table.Padding.Right) + style.Wall); + sb.Append(GetPaddingString(table.Padding.Left) + table.HeaderAlignment.ProcessString(col.ColumnName, table.TableStretchStyles.Method(table._colsShown, col.ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding())) + GetPaddingString(table.Padding.Right) + style.Wall); } } private void DataRow(int i) { - var cols = table._colsShown.Select(c => new CellParts(c.StringValForCol(c.Values[i]), c.MaxWidth, c.Alignment)).ToList(); + var cols = table._colsShown.Select(c => new CellParts(c.StringValForCol(c.Values[i]), table.TableStretchStyles.Method(table._colsShown, c.ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding()), c.Alignment)).ToList(); var maxLines = cols.Max(c => c.LineCount); diff --git a/ConTabs/Padding.cs b/ConTabs/Padding.cs index 8136a89..e6629e0 100644 --- a/ConTabs/Padding.cs +++ b/ConTabs/Padding.cs @@ -65,5 +65,13 @@ public Padding(byte top = 0, byte right = 1, byte bottom = 0, byte left = 1) Bottom = bottom; Left = left; } + + /// + /// Returns total horizontal padding + /// + public int GetHorizontalPadding() + { + return Left + Right; + } } } \ No newline at end of file diff --git a/ConTabs/Table.cs b/ConTabs/Table.cs index 07c95ed..dba57a0 100644 --- a/ConTabs/Table.cs +++ b/ConTabs/Table.cs @@ -79,6 +79,18 @@ public IEnumerable Data } } + /// + /// Width of canvas where table will be outputted + /// + public int CanvasWidth { get; set; } + + /// + /// Alignment of the table in the console + /// + public Alignment TableAlignment { get; set; } + + public TableStretchStyles TableStretchStyles { get; set; } + /// /// Creates an empty table /// @@ -110,6 +122,9 @@ private Table() TableStyle = Style.Default; HeaderAlignment = Alignment.Default; ColumnAlignment = Alignment.Default; + TableAlignment = Alignment.Default; + CanvasWidth = Console.WindowWidth - 1; // it would word wrap + TableStretchStyles = TableStretchStyles.Default; var props = GetDeclaredAndInheritedProperties(typeof(T).GetTypeInfo()); var cols = props diff --git a/ConTabs/TableStretchStyles.cs b/ConTabs/TableStretchStyles.cs new file mode 100644 index 0000000..f519c7c --- /dev/null +++ b/ConTabs/TableStretchStyles.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ConTabs +{ + /// + /// Defines how should table be stretched / squeezed on a canvas + /// + public class TableStretchStyles + { + public Func, string, int, int, int> Method { get; set; } + + /// + /// Does not stretch / squeeze the table + /// + public static TableStretchStyles Default => DoNothing; + + /// + /// Does not stretch / squeeze the table + /// + public static TableStretchStyles DoNothing => new TableStretchStyles { Method = GetStaticColumnWidth }; + + /// + /// Sets all columns to the same width (approximately) + /// + public static TableStretchStyles EvenColumnWidth => new TableStretchStyles { Method = GetUniformColumnWidth }; + + /* + /// + /// Stretches / squeezes all columns by approximately the same width + /// + public static TableStretchStyles StretchAllColumnsEvenly => new TableStretchStyles { Method = GetEvenlyStretchedColumnWidth }; + + /// + /// Stretches / squeezes all columns by approximately the same width + /// + public static TableStretchStyles StretchLongStrings => new TableStretchStyles { Method = GetLongStringStretchedFirstColumnWidth }; + */ + public static int GetStaticColumnWidth(List columns, string columnName, int canvasWidth, int horizontalPadding) + { + Column column = columns.Find(c => c.ColumnName == columnName); + + if (column.Values == null || column.Values.Count == 0) return columnName.Length; + + if (column.LongStringBehaviour.Width > 0) return column.LongStringBehaviour.Width; + + return column.Values + .Select(v => column.StringValForCol(v)) + .Union(new List { column.ColumnName }) + .Select(v => v.Length) + .Max(); + } + + public static int GetUniformColumnWidth(List columns, string columnName, int canvasWidth, int horizontalPadding) + { + // calculate the minimal width + int uniformWidth = canvasWidth / columns.Count - horizontalPadding - 1; + + for (int i = 0; i < columns.Count; i++) + { + if (columns[i].ColumnName == columnName) + { + // if we still have space left, add width to columns starting from first one + if (i < canvasWidth % columns.Count - 1) + { + uniformWidth++; + } + //todo: I should not change the value of Width + columns[i].LongStringBehaviour.Width = uniformWidth; + break; + } + } + return uniformWidth; + } + + } +} From f379b87e2c33e584410f272bbdaf937323fa4574 Mon Sep 17 00:00:00 2001 From: Marek Zitnansky Date: Sat, 17 Nov 2018 20:57:25 +0100 Subject: [PATCH 06/36] Small refactorings, 3 styles working --- ConTabs.Tests/ColumnTests.cs | 8 +-- ConTabs/LongStringBehaviour.cs | 7 +- ConTabs/OutputBuilder.cs | 29 ++++++--- ConTabs/TableStretchStyles.cs | 64 ++++++++++--------- .../ConTabsDemo-DotNetCore.csproj | 2 +- ConTabsDemo-DotNetCore/Program.cs | 3 + ConTabsDemo-DotNetFramework/Program.cs | 3 +- 7 files changed, 70 insertions(+), 46 deletions(-) diff --git a/ConTabs.Tests/ColumnTests.cs b/ConTabs.Tests/ColumnTests.cs index c8f8091..45edb53 100644 --- a/ConTabs.Tests/ColumnTests.cs +++ b/ConTabs.Tests/ColumnTests.cs @@ -27,7 +27,7 @@ public void MaxWidthWithNullValuesReturnsColumnNameLength() var column = new Column(typeof(string), "StringColumn"); //Act - var result = column.MaxWidth; + var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column); //Assert result.ShouldBe(12); @@ -41,7 +41,7 @@ public void MaxWidthWithZeroValuesReturnsColumnNameLength() column.Values = new List(); //Act - var result = column.MaxWidth; + var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column); //Assert result.ShouldBe(12); @@ -57,7 +57,7 @@ public void MaxWidthWithLongStringBehaviourReturnsLongStringBehaviourWidth() column.LongStringBehaviour.Width = 15; //Act - var result = column.MaxWidth; + var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column); //Assert result.ShouldBe(15); @@ -71,7 +71,7 @@ public void MaxWidthWithValuesReturnsLengthOfLongestValue() column.Values = new List() { "one", "two", "three", "four" }; //Act - var result = column.MaxWidth; + var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column); //Assert result.ShouldBe(5); diff --git a/ConTabs/LongStringBehaviour.cs b/ConTabs/LongStringBehaviour.cs index f453058..2af81d4 100644 --- a/ConTabs/LongStringBehaviour.cs +++ b/ConTabs/LongStringBehaviour.cs @@ -17,6 +17,11 @@ public class LongStringBehaviour /// public int Width { get; set; } + /// + /// A property to store the correct width of column to be displayed + /// + public int DisplayWidth { get; set; } + /// /// The ellipsis to use when the behvaiour is set to TruncateWithEliipsis /// @@ -72,7 +77,7 @@ private static string WrapString(string input, string ellipsis, int width) /// A new formatted string public string ProcessString(string input) { - return Method(input, EllipsisString, Width); + return Method(input, EllipsisString, DisplayWidth); } // The following word wrapping methods inspired by an SO answer by "ICR" diff --git a/ConTabs/OutputBuilder.cs b/ConTabs/OutputBuilder.cs index 20e665b..1b79f5c 100644 --- a/ConTabs/OutputBuilder.cs +++ b/ConTabs/OutputBuilder.cs @@ -28,12 +28,23 @@ private OutputBuilder(Table t, Style s) table = t; style = s; sb = new StringBuilder(); - int colWidths = table._colsShown.Sum(c => t.TableStretchStyles.Method(t._colsShown, c.ColumnName, t.CanvasWidth, t.Padding.GetHorizontalPadding())); + + int colWidths = 0; + foreach (Column column in table._colsShown) + { + int colWidth = table.TableStretchStyles.CalculateOptimalWidth(column); + column.LongStringBehaviour.DisplayWidth = colWidth; + colWidths += colWidth; + } + // int colWidths = table._colsShown.Sum(c => t.TableStretchStyles.CalculateOptimalWidth(c)); int realWidth = colWidths + (table._colsShown.Count * (table.Padding.Left + table.Padding.Right)) + table._colsShown.Count + 1; + + int adaptedWidth = t.TableStretchStyles.CalculateAdditionalWidth(table._colsShown, realWidth, table.CanvasWidth); + leftOffset = - table.CanvasWidth < realWidth ? 0 : - table.TableAlignment.Equals(Alignment.Right) ? table.CanvasWidth - realWidth : - table.TableAlignment.Equals(Alignment.Center) ? (table.CanvasWidth - realWidth) / 2 : + table.CanvasWidth < adaptedWidth ? 0 : + table.TableAlignment.Equals(Alignment.Right) ? table.CanvasWidth - adaptedWidth : + table.TableAlignment.Equals(Alignment.Center) ? (table.CanvasWidth - adaptedWidth) / 2 : 0; sb.Append(new string(' ', leftOffset)); @@ -69,7 +80,7 @@ private void InsertVerticalPadding(byte padding, char columnSeparator) sb.Append(style.Wall); for (int i = 0; i < table._colsShown.Count; i++) { - sb.Append(new string(' ', table.TableStretchStyles.Method(table._colsShown, table._colsShown[i].ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding()) + table.Padding.GetHorizontalPadding())); + sb.Append(new string(' ', table._colsShown[i].LongStringBehaviour.DisplayWidth + table.Padding.GetHorizontalPadding())); if (i < table._colsShown.Count - 1) { @@ -92,7 +103,7 @@ private void HLine(TopMidBot v) for (int i = 0; i < table._colsShown.Count; i++) { - sb.Append(new string(style.Floor, table.TableStretchStyles.Method(table._colsShown, table._colsShown[i].ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding()) + table.Padding.GetHorizontalPadding())); + sb.Append(new string(style.Floor, table._colsShown[i].LongStringBehaviour.DisplayWidth + table.Padding.GetHorizontalPadding())); if (i < table._colsShown.Count - 1) { @@ -105,7 +116,7 @@ private void HLine(TopMidBot v) private void NoDataLine() { var noDataText = "no data"; - int colWidths = table._colsShown.Sum(c => table.TableStretchStyles.Method(table._colsShown, c.ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding())); + int colWidths = table._colsShown.Sum(c => c.LongStringBehaviour.DisplayWidth); int innerWidth = colWidths + (table._colsShown.Count * (table.Padding.Left + table.Padding.Right)) + table._colsShown.Count - 1; int leftPad = (innerWidth - noDataText.Length) / 2; int rightPad = innerWidth - (leftPad + noDataText.Length); @@ -117,13 +128,13 @@ private void Headers() sb.Append(style.Wall); foreach (var col in table._colsShown) { - sb.Append(GetPaddingString(table.Padding.Left) + table.HeaderAlignment.ProcessString(col.ColumnName, table.TableStretchStyles.Method(table._colsShown, col.ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding())) + GetPaddingString(table.Padding.Right) + style.Wall); + sb.Append(GetPaddingString(table.Padding.Left) + table.HeaderAlignment.ProcessString(col.ColumnName, col.LongStringBehaviour.DisplayWidth) + GetPaddingString(table.Padding.Right) + style.Wall); } } private void DataRow(int i) { - var cols = table._colsShown.Select(c => new CellParts(c.StringValForCol(c.Values[i]), table.TableStretchStyles.Method(table._colsShown, c.ColumnName, table.CanvasWidth, table.Padding.GetHorizontalPadding()), c.Alignment)).ToList(); + var cols = table._colsShown.Select(c => new CellParts(c.StringValForCol(c.Values[i]), c.LongStringBehaviour.DisplayWidth, c.Alignment)).ToList(); var maxLines = cols.Max(c => c.LineCount); diff --git a/ConTabs/TableStretchStyles.cs b/ConTabs/TableStretchStyles.cs index f519c7c..725aa72 100644 --- a/ConTabs/TableStretchStyles.cs +++ b/ConTabs/TableStretchStyles.cs @@ -9,7 +9,8 @@ namespace ConTabs /// public class TableStretchStyles { - public Func, string, int, int, int> Method { get; set; } + public Func CalculateOptimalWidth { get; set; } + public Func, int, int, int> CalculateAdditionalWidth { get; set; } /// /// Does not stretch / squeeze the table @@ -19,29 +20,50 @@ public class TableStretchStyles /// /// Does not stretch / squeeze the table /// - public static TableStretchStyles DoNothing => new TableStretchStyles { Method = GetStaticColumnWidth }; + public static TableStretchStyles DoNothing => new TableStretchStyles { CalculateOptimalWidth = GetOptimalColumnWidth, CalculateAdditionalWidth = UseDefaultDisplayWidths }; + + private static int UseDefaultDisplayWidths(List columns, int totalWidth, int canvasWidth) + { + for (int i = 0; i < columns.Count; i++) + { + columns[i].LongStringBehaviour.DisplayWidth = GetOptimalColumnWidth(columns[i]); + } + return totalWidth; + } + + private static int AdaptDisplayWidths(List columns, int totalWidth, int canvasWidth) + { + int difference = canvasWidth - totalWidth; + for (int i = 0; i < columns.Count; i++) + { + columns[i].LongStringBehaviour.DisplayWidth += difference / columns.Count; + if (i < difference % columns.Count) + { + columns[i].LongStringBehaviour.DisplayWidth++; + } + } + return canvasWidth; + } /// /// Sets all columns to the same width (approximately) /// - public static TableStretchStyles EvenColumnWidth => new TableStretchStyles { Method = GetUniformColumnWidth }; + public static TableStretchStyles EvenColumnWidth => new TableStretchStyles { CalculateOptimalWidth = GetUniformColumnWidth, CalculateAdditionalWidth = AdaptDisplayWidths }; - /* /// /// Stretches / squeezes all columns by approximately the same width /// - public static TableStretchStyles StretchAllColumnsEvenly => new TableStretchStyles { Method = GetEvenlyStretchedColumnWidth }; + public static TableStretchStyles StretchOrSqueezeAllColumnsEvenly => new TableStretchStyles { CalculateOptimalWidth = GetOptimalColumnWidth, CalculateAdditionalWidth = AdaptDisplayWidths }; + /* /// - /// Stretches / squeezes all columns by approximately the same width + /// Stretches / squeezes long strings by approximately the same width /// public static TableStretchStyles StretchLongStrings => new TableStretchStyles { Method = GetLongStringStretchedFirstColumnWidth }; */ - public static int GetStaticColumnWidth(List columns, string columnName, int canvasWidth, int horizontalPadding) + private static int GetOptimalColumnWidth(Column column) { - Column column = columns.Find(c => c.ColumnName == columnName); - - if (column.Values == null || column.Values.Count == 0) return columnName.Length; + if (column.Values == null || column.Values.Count == 0) return column.ColumnName.Length; if (column.LongStringBehaviour.Width > 0) return column.LongStringBehaviour.Width; @@ -52,27 +74,9 @@ public static int GetStaticColumnWidth(List columns, string columnName, .Max(); } - public static int GetUniformColumnWidth(List columns, string columnName, int canvasWidth, int horizontalPadding) + private static int GetUniformColumnWidth(Column column) { - // calculate the minimal width - int uniformWidth = canvasWidth / columns.Count - horizontalPadding - 1; - - for (int i = 0; i < columns.Count; i++) - { - if (columns[i].ColumnName == columnName) - { - // if we still have space left, add width to columns starting from first one - if (i < canvasWidth % columns.Count - 1) - { - uniformWidth++; - } - //todo: I should not change the value of Width - columns[i].LongStringBehaviour.Width = uniformWidth; - break; - } - } - return uniformWidth; + return byte.MinValue; } - } } diff --git a/ConTabsDemo-DotNetCore/ConTabsDemo-DotNetCore.csproj b/ConTabsDemo-DotNetCore/ConTabsDemo-DotNetCore.csproj index 4f2912d..257e410 100644 --- a/ConTabsDemo-DotNetCore/ConTabsDemo-DotNetCore.csproj +++ b/ConTabsDemo-DotNetCore/ConTabsDemo-DotNetCore.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp1.1 + netcoreapp2.1 diff --git a/ConTabsDemo-DotNetCore/Program.cs b/ConTabsDemo-DotNetCore/Program.cs index 85b8e1f..0a7b3c5 100644 --- a/ConTabsDemo-DotNetCore/Program.cs +++ b/ConTabsDemo-DotNetCore/Program.cs @@ -53,6 +53,9 @@ static void Main(string[] args) // Add some padding table.Padding = new Padding(1, 1); + table.TableAlignment = Alignment.Right; + // table.CanvasWidth = 40; + table.TableStretchStyles = TableStretchStyles.EvenColumnWidth; // Finally, spit out the finished table Console.WriteLine(table); diff --git a/ConTabsDemo-DotNetFramework/Program.cs b/ConTabsDemo-DotNetFramework/Program.cs index f286182..4416a6e 100644 --- a/ConTabsDemo-DotNetFramework/Program.cs +++ b/ConTabsDemo-DotNetFramework/Program.cs @@ -53,7 +53,8 @@ static void Main(string[] args) // Add some padding table.Padding = new Padding(1, 1); - + table.TableAlignment = Alignment.Center; + // Finally, spit out the finished table Console.WriteLine(table); From 9bedf657a6ef26105ed0c130d25a328902cd99d4 Mon Sep 17 00:00:00 2001 From: tdwright Date: Wed, 21 Nov 2018 21:13:58 +1100 Subject: [PATCH 07/36] Removed deprecated members of the Style class --- .gitignore | 1 + ConTabs/Styles.cs | 43 ------------------------------------------- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index ae36470..0a1ff7e 100644 --- a/.gitignore +++ b/.gitignore @@ -294,3 +294,4 @@ TestResult.xml # NDepend *.ndproj /NDependOut +.vscode/ diff --git a/ConTabs/Styles.cs b/ConTabs/Styles.cs index b939308..c381eed 100644 --- a/ConTabs/Styles.cs +++ b/ConTabs/Styles.cs @@ -129,48 +129,5 @@ public Style(char wall, char floor, Corners corners) TeeNoDown = ':', TeeNoRight = ':' }); - - // Deprecated setters/getters - [Obsolete("Use Corners.CornerTopLeft")] - public char CornerTopLeft { get { return Corners[0, 0]; } set { Corners.CornerTopLeft = value; } } - [Obsolete("Use Corners.CornerTopRight")] - public char CornerTopRight { get { return Corners[2, 0]; } set { Corners.CornerTopRight = value; } } - [Obsolete("Use Corners.CornerBottomLeft")] - public char CornerBottomLeft { get { return Corners[0, 2]; } set { Corners.CornerBottomLeft = value; } } - [Obsolete("Use Corners.CornerBottomRight")] - public char CornerBottomRight { get { return Corners[2, 2]; } set { Corners.CornerBottomRight = value; } } - [Obsolete("Use Corners.Intersection")] - public char Intersection { get { return Corners[1, 1]; } set { Corners.Intersection = value; } } - [Obsolete("Use Corners.TeeNoUp")] - public char TeeNoUp { get { return Corners[1, 0]; } set { Corners.TeeNoUp = value; } } - [Obsolete("Use Corners.TeeNoRight")] - public char TeeNoRight { get { return Corners[2, 1]; } set { Corners.TeeNoRight = value; } } - [Obsolete("Use Corners.TeeNoDown")] - public char TeeNoDown { get { return Corners[1, 2]; } set { Corners.TeeNoDown = value; } } - [Obsolete("Use Corners.TeeNoLeft")] - public char TeeNoLeft { get { return Corners[0, 1]; } set { Corners.TeeNoLeft = value; } } - - // Old constructor with too many params - [Obsolete] - - /// - /// Creates a new style using the given characters - /// - public Style(char wall, char floor, char tl, char tr, char bl, char br, char i, char tnu, char tnr, char tnd, char tnl) - { - Wall = wall; - Floor = floor; - - Corners.CornerTopLeft = tl; - Corners.CornerTopRight = tr; - Corners.CornerBottomLeft = bl; - Corners.CornerBottomRight = br; - Corners.Intersection = i; - Corners.TeeNoUp = tnu; - Corners.TeeNoRight = tnr; - Corners.TeeNoDown = tnd; - Corners.TeeNoLeft = tnl; - } - } } \ No newline at end of file From 290a408a31150795b4a275af28f48910d0168f7f Mon Sep 17 00:00:00 2001 From: tdwright Date: Wed, 21 Nov 2018 22:50:46 +1100 Subject: [PATCH 08/36] First pass at an attribute system (no tests yet!) --- ConTabs/Attributes.cs | 51 +++++++++++++++++++++++++++++++++++++++++++ ConTabs/Column.cs | 29 ++++++++++++++++++++++++ ConTabs/Columns.cs | 20 +++++++++++++++++ ConTabs/Table.cs | 2 +- 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 ConTabs/Attributes.cs diff --git a/ConTabs/Attributes.cs b/ConTabs/Attributes.cs new file mode 100644 index 0000000..4c3aeb8 --- /dev/null +++ b/ConTabs/Attributes.cs @@ -0,0 +1,51 @@ +using System; +using ConTabs; + +namespace ConTabs.Attributes +{ + public interface IConTabsColumnAttribute + { + void ActOnColumn(Column column); + } + + [AttributeUsage(AttributeTargets.Property)] + public class ConTabsColumnHide : Attribute, IConTabsColumnAttribute + { + public void ActOnColumn(Column column) + { + column.Hide = true; + } + } + + [AttributeUsage(AttributeTargets.Property)] + public class ConTabsColumnName : Attribute, IConTabsColumnAttribute + { + public string ColumnName { get; } + + public ConTabsColumnName(string name) + { + ColumnName = name; + } + + public void ActOnColumn(Column column) + { + column.ColumnName = ColumnName; + } + } + + [AttributeUsage(AttributeTargets.Property)] + public class ConTabsColumnPosition : Attribute, IConTabsColumnAttribute + { + public int ColumnPosition { get; } + + public ConTabsColumnPosition(int position) + { + ColumnPosition = position; + } + + public void ActOnColumn(Column column) + { + column.InitialPosition = ColumnPosition; + } + } +} diff --git a/ConTabs/Column.cs b/ConTabs/Column.cs index af61cf5..2f2f27c 100644 --- a/ConTabs/Column.cs +++ b/ConTabs/Column.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Linq; using System.Reflection; +using ConTabs.Attributes; namespace ConTabs { @@ -37,6 +38,7 @@ public class Column /// A control to show/hide the column /// public bool Hide { get; set; } + /// /// The method the column uses to display long strings @@ -49,6 +51,7 @@ public class Column public Alignment Alignment { get; set; } private readonly MethodInfo toStringMethod; + internal int? InitialPosition; /// /// A List of the values stored @@ -70,6 +73,32 @@ public int MaxWidth } } + /// + /// Constructor used by the main table creation process, using reflection + /// + /// Information reflected from the public property of a Table + public Column(PropertyInfo propertyInfo) + { + LongStringBehaviour = LongStringBehaviour.Default; + Alignment = Alignment.Default; + SourceType = propertyInfo.PropertyType; + PropertyName = propertyInfo.Name; + ColumnName = propertyInfo.Name; + toStringMethod = GetToStringMethod(); + + // check for each of the attributes and act accordingly + var attributes = propertyInfo.GetCustomAttributes(); + foreach (var attribute in attributes) + { + if (attribute is IConTabsColumnAttribute castedAttribute) castedAttribute.ActOnColumn(this); + } + } + + /// + /// Constructor used when adding additional columns to a table + /// + /// The Type of the data in the column + /// A name for the column (shown in the header row) public Column(Type type, string name) { LongStringBehaviour = LongStringBehaviour.Default; diff --git a/ConTabs/Columns.cs b/ConTabs/Columns.cs index 906da4b..cdd38dd 100644 --- a/ConTabs/Columns.cs +++ b/ConTabs/Columns.cs @@ -1,6 +1,7 @@ using ConTabs.Exceptions; using System; using System.Collections.Generic; +using System.Linq; namespace ConTabs { @@ -16,6 +17,11 @@ public class Columns : List public Columns(List columns) { AddRange(columns); + + if(columns.Any(c=>c.InitialPosition.HasValue)) + { + TryToSetInitialPositions(); + } } /// @@ -77,6 +83,20 @@ private void MoveColumn(Column col, int newPos) this.Insert(newPos, col); } + private void TryToSetInitialPositions() + { + var movesToTry = this + .Where(c => c.InitialPosition.HasValue) + .Select(c => new { name = c.PropertyName, pos = c.InitialPosition.Value }) + .OrderBy(a=>a.pos); + + foreach (var moveToTry in movesToTry) + { + var pos = (moveToTry.pos >= this.Count) ? this.Count - 1 : moveToTry.pos; + MoveColumn(moveToTry.name, pos); + } + } + private Column FindColByName(string name) { Column backup = null; diff --git a/ConTabs/Table.cs b/ConTabs/Table.cs index 07c95ed..20f5864 100644 --- a/ConTabs/Table.cs +++ b/ConTabs/Table.cs @@ -114,7 +114,7 @@ private Table() var props = GetDeclaredAndInheritedProperties(typeof(T).GetTypeInfo()); var cols = props .Where(p => p.GetMethod.IsPublic) - .Select(p => new Column(p.PropertyType, p.Name)) + .Select(p => new Column(p)) .ToList(); Columns = new Columns(cols); From cae37218610c108036eedef463c601924822936d Mon Sep 17 00:00:00 2001 From: tdwright Date: Wed, 21 Nov 2018 22:51:06 +1100 Subject: [PATCH 09/36] Demo program to show the attribute system in action --- ConTabs.sln | 13 ++++++- .../ConTabsDemo-Attributes.csproj | 13 +++++++ ConTabsDemo-Attributes/Data.cs | 39 +++++++++++++++++++ ConTabsDemo-Attributes/Program.cs | 22 +++++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 ConTabsDemo-Attributes/ConTabsDemo-Attributes.csproj create mode 100644 ConTabsDemo-Attributes/Data.cs create mode 100644 ConTabsDemo-Attributes/Program.cs diff --git a/ConTabs.sln b/ConTabs.sln index 15f0197..7847bad 100644 --- a/ConTabs.sln +++ b/ConTabs.sln @@ -5,13 +5,15 @@ VisualStudioVersion = 15.0.26403.7 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConTabs", "ConTabs\ConTabs.csproj", "{D9A96908-33E7-4D02-B713-A894D862B5AA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConTabsDemo-DotNetCore", "ConTabsDemo-DotNetCore\ConTabsDemo-DotNetCore.csproj", "{5C495FC2-0E9A-4767-8D92-EB44D612EA12}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConTabsDemo-DotNetCore", "ConTabsDemo-DotNetCore\ConTabsDemo-DotNetCore.csproj", "{5C495FC2-0E9A-4767-8D92-EB44D612EA12}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConTabsDemo-DotNetFramework", "ConTabsDemo-DotNetFramework\ConTabsDemo-DotNetFramework.csproj", "{16584C0D-E2C3-4653-9DC6-E480660FA112}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConTabs.Tests", "ConTabs.Tests\ConTabs.Tests.csproj", "{B2347DC8-CBDE-42E3-BCA0-484DB2B3ED34}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConTabsTestData", "ConTabsTestData\ConTabsTestData.csproj", "{70B98506-64D7-4822-B461-0A6BC66A2656}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConTabsTestData", "ConTabsTestData\ConTabsTestData.csproj", "{70B98506-64D7-4822-B461-0A6BC66A2656}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConTabsDemo-Attributes", "ConTabsDemo-Attributes\ConTabsDemo-Attributes.csproj", "{A68D182D-96B2-44B4-8495-99236461FD04}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -39,10 +41,17 @@ Global {70B98506-64D7-4822-B461-0A6BC66A2656}.Debug|Any CPU.Build.0 = Debug|Any CPU {70B98506-64D7-4822-B461-0A6BC66A2656}.Release|Any CPU.ActiveCfg = Release|Any CPU {70B98506-64D7-4822-B461-0A6BC66A2656}.Release|Any CPU.Build.0 = Release|Any CPU + {A68D182D-96B2-44B4-8495-99236461FD04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A68D182D-96B2-44B4-8495-99236461FD04}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A68D182D-96B2-44B4-8495-99236461FD04}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A68D182D-96B2-44B4-8495-99236461FD04}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0E0686CF-72D2-4CF3-B98F-4C9E9D987A84} + EndGlobalSection GlobalSection(NDepend) = preSolution Project = ".\ConTabs.ndproj" EndGlobalSection diff --git a/ConTabsDemo-Attributes/ConTabsDemo-Attributes.csproj b/ConTabsDemo-Attributes/ConTabsDemo-Attributes.csproj new file mode 100644 index 0000000..5345df6 --- /dev/null +++ b/ConTabsDemo-Attributes/ConTabsDemo-Attributes.csproj @@ -0,0 +1,13 @@ + + + + Exe + netcoreapp2.1 + ConTabsDemo_Attributes + + + + + + + diff --git a/ConTabsDemo-Attributes/Data.cs b/ConTabsDemo-Attributes/Data.cs new file mode 100644 index 0000000..febcc38 --- /dev/null +++ b/ConTabsDemo-Attributes/Data.cs @@ -0,0 +1,39 @@ +using ConTabs.Attributes; +using System.Collections.Generic; + +namespace ConTabsDemo_Attributes +{ + public static class DemoDataProvider + { + public static List ListOfDemoData() + { + return new List + { + new Planet{Name="Mercury", DistanceFromSun=57909227, OrbitalPeriod=88F, Diameter=4879, Fact="Mercury is the smallest planet."}, + new Planet{Name="Venus", DistanceFromSun=108209475, OrbitalPeriod=225F, Diameter=12104, Fact="Venus is the hottest planet."}, + new Planet{Name="Earth", DistanceFromSun=149598262, OrbitalPeriod=365.24F, Diameter=12742, Fact="Earth is where we live"}, + new Planet{Name="Mars", DistanceFromSun=227943824, OrbitalPeriod=693.5F, Diameter=6779, Fact="Mars is also often described as the “Red Planet” due to its reddish appearance."}, + new Planet{Name="Jupiter", DistanceFromSun=778340821, OrbitalPeriod=4343.5F, Diameter=139822, Fact="Jupiter is the largest planet."}, + new Planet{Name="Saturn", DistanceFromSun=1426666422, OrbitalPeriod=10767.5F, Diameter=116464, Fact="Saturn is best known for its fabulous ring system that was first observed in 1610 by the astronomer Galileo Galilei."}, + new Planet{Name="Uranus", DistanceFromSun=2870658186, OrbitalPeriod=30660F, Diameter=50724, Fact="Uranus became the first planet discovered with the use of a telescope."}, + new Planet{Name="Neptune", DistanceFromSun=4498396441, OrbitalPeriod=60152F, Diameter=49244, Fact="On average Neptune is the coldest planet"} + }; + } + } + + public class Planet + { + public string Name { get; set; } + + [ConTabsColumnPosition(3)] + public long DistanceFromSun { get; set; } + + public int Diameter { get; set; } + + [ConTabsColumnName("Year length")] + public float OrbitalPeriod { get; set; } + + [ConTabsColumnHide()] + public string Fact { get; set; } + } +} diff --git a/ConTabsDemo-Attributes/Program.cs b/ConTabsDemo-Attributes/Program.cs new file mode 100644 index 0000000..0df9d62 --- /dev/null +++ b/ConTabsDemo-Attributes/Program.cs @@ -0,0 +1,22 @@ +using ConTabs; +using System; + +namespace ConTabsDemo_Attributes +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("CONTABS ATTRIBUTE DEMO"); + + // Get some data (can be an IEnumerable of anything) + var Data = DemoDataProvider.ListOfDemoData(); + + // Write it to the console (in one line!) + Console.WriteLine(Table.Create(Data)); + + Console.WriteLine("Press return to exit..."); + Console.ReadLine(); + } + } +} From b0f4a878bbf2419ffd52b50733548e1f6da30438 Mon Sep 17 00:00:00 2001 From: wargamer Date: Thu, 22 Nov 2018 22:06:32 +0100 Subject: [PATCH 10/36] Added the Empty style where the separator is a single whitespace --- ConTabs.Tests/ConformanceTests.cs | 21 +++++++++++++++++++++ ConTabs/Styles.cs | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/ConTabs.Tests/ConformanceTests.cs b/ConTabs.Tests/ConformanceTests.cs index deca0dc..164fa67 100644 --- a/ConTabs.Tests/ConformanceTests.cs +++ b/ConTabs.Tests/ConformanceTests.cs @@ -207,6 +207,27 @@ public void TableStyledAsHeavyShouldLookLikeThis() tableString.ShouldBe(expected); } + [Test] + public void TableStyledAsEmptyShouldLookLikeThis() + { + // Arrange + var listOfTestClasses = DataProvider.ListOfMinimalData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.TableStyle = Style.Empty; + + // Act + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += " " + Environment.NewLine; + expected += " IntA IntB " + Environment.NewLine; + expected += " " + Environment.NewLine; + expected += " 1 3 " + Environment.NewLine; + expected += " "; + tableString.ShouldBe(expected); + } + [Test] public void TableWithCustomStyleShouldLookLikeThis() { diff --git a/ConTabs/Styles.cs b/ConTabs/Styles.cs index c381eed..020b940 100644 --- a/ConTabs/Styles.cs +++ b/ConTabs/Styles.cs @@ -60,6 +60,11 @@ public Style(char wall, char floor, Corners corners) /// public static Style Heavy => new Style('#', '=', '#'); + /// + /// Built-in style + /// + public static Style Empty => new Style(' ', ' ', ' '); + /// /// Built-in style /// From c8fab77f482a41af49c4c9eadb8ec11bbfa92523 Mon Sep 17 00:00:00 2001 From: wargamer Date: Thu, 22 Nov 2018 22:32:06 +0100 Subject: [PATCH 11/36] Renamed Empty style to Whitespace --- ConTabs.Tests/ConformanceTests.cs | 2 +- ConTabs/Styles.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ConTabs.Tests/ConformanceTests.cs b/ConTabs.Tests/ConformanceTests.cs index 164fa67..7d8887a 100644 --- a/ConTabs.Tests/ConformanceTests.cs +++ b/ConTabs.Tests/ConformanceTests.cs @@ -213,7 +213,7 @@ public void TableStyledAsEmptyShouldLookLikeThis() // Arrange var listOfTestClasses = DataProvider.ListOfMinimalData(1); var tableObj = Table.Create(listOfTestClasses); - tableObj.TableStyle = Style.Empty; + tableObj.TableStyle = Style.Whitespace; // Act var tableString = tableObj.ToString(); diff --git a/ConTabs/Styles.cs b/ConTabs/Styles.cs index 020b940..4117df7 100644 --- a/ConTabs/Styles.cs +++ b/ConTabs/Styles.cs @@ -63,7 +63,7 @@ public Style(char wall, char floor, Corners corners) /// /// Built-in style /// - public static Style Empty => new Style(' ', ' ', ' '); + public static Style Whitespace => new Style(' ', ' ', ' '); /// /// Built-in style From 495506d16c8b72ac07e242467396af0ca9988fb0 Mon Sep 17 00:00:00 2001 From: wargamer Date: Thu, 22 Nov 2018 22:38:03 +0100 Subject: [PATCH 12/36] Also renamed unit test --- ConTabs.Tests/ConformanceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConTabs.Tests/ConformanceTests.cs b/ConTabs.Tests/ConformanceTests.cs index 7d8887a..fe4fd49 100644 --- a/ConTabs.Tests/ConformanceTests.cs +++ b/ConTabs.Tests/ConformanceTests.cs @@ -208,7 +208,7 @@ public void TableStyledAsHeavyShouldLookLikeThis() } [Test] - public void TableStyledAsEmptyShouldLookLikeThis() + public void TableStyledAsWhitespaceShouldLookLikeThis() { // Arrange var listOfTestClasses = DataProvider.ListOfMinimalData(1); From a63bd9953bd0a549608695387d5a6be3fd70c702 Mon Sep 17 00:00:00 2001 From: tdwright Date: Sat, 24 Nov 2018 09:48:39 +1100 Subject: [PATCH 13/36] Added unit tests for the 3 new column attributes --- ConTabs.Tests/ColumnAttributeTests.cs | 70 +++++++++++++++++++++++++++ ConTabs.Tests/ConTabs.Tests.csproj | 12 +++++ ConTabs.Tests/app.config | 11 +++++ ConTabs.Tests/packages.config | 3 ++ 4 files changed, 96 insertions(+) create mode 100644 ConTabs.Tests/ColumnAttributeTests.cs create mode 100644 ConTabs.Tests/app.config diff --git a/ConTabs.Tests/ColumnAttributeTests.cs b/ConTabs.Tests/ColumnAttributeTests.cs new file mode 100644 index 0000000..1e77fac --- /dev/null +++ b/ConTabs.Tests/ColumnAttributeTests.cs @@ -0,0 +1,70 @@ +using AutoFixture.NUnit3; +using ConTabs.Attributes; +using NUnit.Framework; +using Shouldly; +using System.Collections.Generic; +using System.Linq; + +namespace ConTabs.Tests +{ + [TestFixture] + public class ColumnAttributeTests + { + [Test, AutoData] + public void ConTabsColumnHideAttributeShouldResultInColumnBeingHidden(List data) + { + // act + var table = Table.Create(data); + + // assert + table.Columns.Count(c => c.Hide).ShouldBe(1); + table.Columns.Count(c => !c.Hide).ShouldBe(1); + } + + [Test, AutoData] + public void ConTabsColumnPositionAttributeShouldResultInColumnBeingMoved(List data) + { + // act + var table = Table.Create(data); + + // assert + table.Columns[0].ColumnName.ShouldBe("ColumnB"); + table.Columns[1].ColumnName.ShouldBe("ColumnA"); + } + + [Test, AutoData] + public void ConTabsColumnNameAttributeShouldResultInColumnBeingRenamed(List data) + { + // act + var table = Table.Create(data); + + // assert + table.Columns[1].ColumnName.ShouldBe("ColumnX"); + } + } + + + public class ClassWithOneHiddenColumn + { + public int ColumnA { get; set; } + + [ConTabsColumnHide] + public int ColumnB { get; set; } + } + + public class ClassWithReorderedColumns + { + public int ColumnA { get; set; } + + [ConTabsColumnPosition(0)] + public int ColumnB { get; set; } + } + + public class ClassWithRenamedColumns + { + public int ColumnA { get; set; } + + [ConTabsColumnName("ColumnX")] + public int ColumnB { get; set; } + } +} diff --git a/ConTabs.Tests/ConTabs.Tests.csproj b/ConTabs.Tests/ConTabs.Tests.csproj index 3b640db..bf29c9d 100644 --- a/ConTabs.Tests/ConTabs.Tests.csproj +++ b/ConTabs.Tests/ConTabs.Tests.csproj @@ -39,6 +39,15 @@ 4 + + ..\packages\AutoFixture.4.5.0\lib\net452\AutoFixture.dll + + + ..\packages\AutoFixture.NUnit3.4.5.0\lib\net452\AutoFixture.NUnit3.dll + + + ..\packages\Fare.2.1.1\lib\net35\Fare.dll + ..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll @@ -46,9 +55,11 @@ ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll + + @@ -62,6 +73,7 @@ + diff --git a/ConTabs.Tests/app.config b/ConTabs.Tests/app.config new file mode 100644 index 0000000..bf5b48a --- /dev/null +++ b/ConTabs.Tests/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ConTabs.Tests/packages.config b/ConTabs.Tests/packages.config index 5176a44..9d512ac 100644 --- a/ConTabs.Tests/packages.config +++ b/ConTabs.Tests/packages.config @@ -1,6 +1,9 @@  + + + From bfdd5130d20b4bdbaec72411fc38c9d36edf30ca Mon Sep 17 00:00:00 2001 From: tdwright Date: Sat, 24 Nov 2018 10:28:46 +1100 Subject: [PATCH 14/36] Updated all NuGet packages --- ConTabs.Tests/ConTabs.Tests.csproj | 15 +++++++++------ ConTabs.Tests/app.config | 2 +- ConTabs.Tests/packages.config | 20 ++++++++++---------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/ConTabs.Tests/ConTabs.Tests.csproj b/ConTabs.Tests/ConTabs.Tests.csproj index bf29c9d..bbd59e0 100644 --- a/ConTabs.Tests/ConTabs.Tests.csproj +++ b/ConTabs.Tests/ConTabs.Tests.csproj @@ -1,6 +1,7 @@  - + + Debug AnyCPU @@ -48,11 +49,12 @@ ..\packages\Fare.2.1.1\lib\net35\Fare.dll - - ..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll + + + ..\packages\NUnit.3.11.0\lib\net45\nunit.framework.dll - - ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll + + ..\packages\Shouldly.3.0.2\lib\net451\Shouldly.dll @@ -92,6 +94,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + + \ No newline at end of file diff --git a/ConTabs.Tests/app.config b/ConTabs.Tests/app.config index bf5b48a..f9c6ff3 100644 --- a/ConTabs.Tests/app.config +++ b/ConTabs.Tests/app.config @@ -4,7 +4,7 @@ - + diff --git a/ConTabs.Tests/packages.config b/ConTabs.Tests/packages.config index 9d512ac..f5459c2 100644 --- a/ConTabs.Tests/packages.config +++ b/ConTabs.Tests/packages.config @@ -4,15 +4,15 @@ - - - - - - - - - + + + + + + + + + - + \ No newline at end of file From 9a47c06f208d01512a94eb91db75a73bb00df065 Mon Sep 17 00:00:00 2001 From: tdwright Date: Sat, 24 Nov 2018 10:41:55 +1100 Subject: [PATCH 15/36] Does NUnit + Autofixture require 32 bit? https://github.com/nunit/nunit-console/issues/241 --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index d03bbb6..4e69241 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,6 +7,9 @@ before_build: environment: COVERALLS_REPO_TOKEN: secure: teIOuW1LrOWSvBgkHaDQu1ctWqA+yJyufe6YDmDrl9MMxqvCqH1C7fKo8hisScLT + +platform: + - x86 build: project: ConTabs.sln From da113e66d8f1b90629731b593f4bdba570a19607 Mon Sep 17 00:00:00 2001 From: tdwright Date: Sat, 24 Nov 2018 20:59:14 +1100 Subject: [PATCH 16/36] Try specifying which assembly contains our tests --- appveyor.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 4e69241..0f5551c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,13 +7,15 @@ before_build: environment: COVERALLS_REPO_TOKEN: secure: teIOuW1LrOWSvBgkHaDQu1ctWqA+yJyufe6YDmDrl9MMxqvCqH1C7fKo8hisScLT - -platform: - - x86 build: project: ConTabs.sln verbosity: minimal + +test: + assemblies: + only: + - ConTabs.Tests.dll after_test: # Tell OpenCover to generate coverage.xml From 2ac16a706079e89cf5947da53d19961aedd0ed25 Mon Sep 17 00:00:00 2001 From: tdwright Date: Sat, 24 Nov 2018 21:10:19 +1100 Subject: [PATCH 17/36] Updated version of NUnit console runner in appveyor config --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 0f5551c..0701244 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ test: after_test: # Tell OpenCover to generate coverage.xml - - cmd: packages\OpenCover.4.6.519\tools\OpenCover.Console.exe -register:user -filter:"+[ConTabs]*" -target:"packages\NUnit.ConsoleRunner.3.7.0\tools\nunit3-console.exe" -targetargs:"/domain:single ConTabs.Tests/bin/debug/ConTabs.Tests.dll" -output:coverage.xml -excludebyattribute:"System.ObsoleteAttribute" + - cmd: packages\OpenCover.4.6.519\tools\OpenCover.Console.exe -register:user -filter:"+[ConTabs]*" -target:"packages\NUnit.ConsoleRunner.3.9.0\tools\nunit3-console.exe" -targetargs:"/domain:single ConTabs.Tests/bin/debug/ConTabs.Tests.dll" -output:coverage.xml -excludebyattribute:"System.ObsoleteAttribute" # Send coverage.xml to Coveralls - cmd: packages\coveralls.io.1.4.2\tools\coveralls.net.exe --opencover coverage.xml -r %COVERALLS_REPO_TOKEN% From 5dbf8dc5b1b10a4b42d02e3f6952ff95d97645ac Mon Sep 17 00:00:00 2001 From: tdwright Date: Sat, 24 Nov 2018 21:37:18 +1100 Subject: [PATCH 18/36] Added an attribute to set column format strings --- ConTabs.Tests/ColumnAttributeTests.cs | 18 ++++++++++++++++++ ConTabs/Attributes.cs | 16 ++++++++++++++++ ConTabsDemo-Attributes/Data.cs | 1 + 3 files changed, 35 insertions(+) diff --git a/ConTabs.Tests/ColumnAttributeTests.cs b/ConTabs.Tests/ColumnAttributeTests.cs index 1e77fac..12ee8e7 100644 --- a/ConTabs.Tests/ColumnAttributeTests.cs +++ b/ConTabs.Tests/ColumnAttributeTests.cs @@ -41,6 +41,16 @@ public void ConTabsColumnNameAttributeShouldResultInColumnBeingRenamed(List data) + { + // act + var table = Table.Create(data); + + // assert + table.Columns[1].StringValForCol(79.11d).ShouldBe("079.110"); + } } @@ -67,4 +77,12 @@ public class ClassWithRenamedColumns [ConTabsColumnName("ColumnX")] public int ColumnB { get; set; } } + + public class ClassWithFormattedColumns + { + public int ColumnA { get; set; } + + [ConTabsColumnFormatString("000.000")] + public double ColumnB { get; set; } + } } diff --git a/ConTabs/Attributes.cs b/ConTabs/Attributes.cs index 4c3aeb8..eab9624 100644 --- a/ConTabs/Attributes.cs +++ b/ConTabs/Attributes.cs @@ -48,4 +48,20 @@ public void ActOnColumn(Column column) column.InitialPosition = ColumnPosition; } } + + [AttributeUsage(AttributeTargets.Property)] + public class ConTabsColumnFormatString : Attribute, IConTabsColumnAttribute + { + public string FormatString { get; } + + public ConTabsColumnFormatString(string formatString) + { + FormatString = formatString; + } + + public void ActOnColumn(Column column) + { + column.FormatString = FormatString; + } + } } diff --git a/ConTabsDemo-Attributes/Data.cs b/ConTabsDemo-Attributes/Data.cs index febcc38..abed08f 100644 --- a/ConTabsDemo-Attributes/Data.cs +++ b/ConTabsDemo-Attributes/Data.cs @@ -26,6 +26,7 @@ public class Planet public string Name { get; set; } [ConTabsColumnPosition(3)] + [ConTabsColumnFormatString("###,###,###0 km")] public long DistanceFromSun { get; set; } public int Diameter { get; set; } From 59a517c7241ee67da2354f4d0e8080f4d84db740 Mon Sep 17 00:00:00 2001 From: Marek Zitnansky Date: Sun, 2 Dec 2018 09:52:03 +0100 Subject: [PATCH 19/36] All long string behaviors implemented --- ConTabs/LongStringBehaviour.cs | 12 +++- ConTabs/OutputBuilder.cs | 63 +++++++++++++----- ConTabs/TableStretchStyles.cs | 105 ++++++++++++++++++++++++------ ConTabsDemo-DotNetCore/Program.cs | 10 ++- 4 files changed, 149 insertions(+), 41 deletions(-) diff --git a/ConTabs/LongStringBehaviour.cs b/ConTabs/LongStringBehaviour.cs index 2af81d4..06c1a21 100644 --- a/ConTabs/LongStringBehaviour.cs +++ b/ConTabs/LongStringBehaviour.cs @@ -12,10 +12,18 @@ public class LongStringBehaviour { public Func Method { get; set; } + private int _width; /// /// The width of the string /// - public int Width { get; set; } + public int Width + { + get => _width; set + { + _width = value; + DisplayWidth = value; + } + } /// /// A property to store the correct width of column to be displayed @@ -35,7 +43,7 @@ public class LongStringBehaviour /// /// Does not interpret the string /// - public static LongStringBehaviour DoNothing => new LongStringBehaviour { Method = PassThrough }; + public static LongStringBehaviour DoNothing => new LongStringBehaviour { Method = PassThrough, Width = 0 }; /// /// Shortens the string to LongstringBehaviour.Width diff --git a/ConTabs/OutputBuilder.cs b/ConTabs/OutputBuilder.cs index 1b79f5c..c422b1e 100644 --- a/ConTabs/OutputBuilder.cs +++ b/ConTabs/OutputBuilder.cs @@ -29,22 +29,12 @@ private OutputBuilder(Table t, Style s) style = s; sb = new StringBuilder(); - int colWidths = 0; - foreach (Column column in table._colsShown) - { - int colWidth = table.TableStretchStyles.CalculateOptimalWidth(column); - column.LongStringBehaviour.DisplayWidth = colWidth; - colWidths += colWidth; - } - // int colWidths = table._colsShown.Sum(c => t.TableStretchStyles.CalculateOptimalWidth(c)); - int realWidth = colWidths + (table._colsShown.Count * (table.Padding.Left + table.Padding.Right)) + table._colsShown.Count + 1; - - int adaptedWidth = t.TableStretchStyles.CalculateAdditionalWidth(table._colsShown, realWidth, table.CanvasWidth); + List hiddenCols = new List(); + int tableWidth = GetOptimalTableWidth(hiddenCols); leftOffset = - table.CanvasWidth < adaptedWidth ? 0 : - table.TableAlignment.Equals(Alignment.Right) ? table.CanvasWidth - adaptedWidth : - table.TableAlignment.Equals(Alignment.Center) ? (table.CanvasWidth - adaptedWidth) / 2 : + table.TableAlignment.Equals(Alignment.Right) ? table.CanvasWidth - tableWidth : + table.TableAlignment.Equals(Alignment.Center) ? (table.CanvasWidth - tableWidth) / 2 : 0; sb.Append(new string(' ', leftOffset)); @@ -70,6 +60,11 @@ private OutputBuilder(Table t, Style s) InsertVerticalPadding(table.Padding.Bottom, style.Wall); NewLine(); } HLine(TopMidBot.Bot); + + foreach (var col in hiddenCols) + { + col.Hide = false; + } } private void InsertVerticalPadding(byte padding, char columnSeparator) @@ -117,7 +112,7 @@ private void NoDataLine() { var noDataText = "no data"; int colWidths = table._colsShown.Sum(c => c.LongStringBehaviour.DisplayWidth); - int innerWidth = colWidths + (table._colsShown.Count * (table.Padding.Left + table.Padding.Right)) + table._colsShown.Count - 1; + int innerWidth = colWidths + (table._colsShown.Count * table.Padding.GetHorizontalPadding()) + table._colsShown.Count - 1; int leftPad = (innerWidth - noDataText.Length) / 2; int rightPad = innerWidth - (leftPad + noDataText.Length); sb.Append(style.Wall + new String(' ', leftPad) + noDataText + new string(' ', rightPad) + style.Wall); @@ -161,6 +156,44 @@ private void DataLine(List parts, int line) } } + private int GetWidthOfPaddingAndBorders() + { + return table._colsShown.Count * table.Padding.GetHorizontalPadding() + table._colsShown.Count + 1; + } + + private int GetTableWidthAfterApplyingStretchStyles() + { + int colWidths = 0; + foreach (Column column in table._colsShown) + { + int colWidth = table.TableStretchStyles.CalculateOptimalWidth(column); + column.LongStringBehaviour.DisplayWidth = colWidth; + colWidths += colWidth; + } + int realWidth = colWidths + GetWidthOfPaddingAndBorders(); + int adaptedWidth = table.TableStretchStyles.CalculateAdditionalWidth(table._colsShown, realWidth, table.CanvasWidth); + return adaptedWidth + GetWidthOfPaddingAndBorders(); + } + + private int GetOptimalTableWidth(List hiddenCols) + { + int tableWidth = GetTableWidthAfterApplyingStretchStyles(); + + while (table.CanvasWidth < tableWidth && table._colsShown.Count > 0) + { + var columnToHide = table._colsShown[table._colsShown.Count - 1]; + hiddenCols.Add(columnToHide); + tableWidth -= columnToHide.LongStringBehaviour.DisplayWidth + table.Padding.GetHorizontalPadding() + 1; + columnToHide.Hide = true; + } + + if (hiddenCols.Count > 0) //if we hid some of the columns, we should adapt the display widths again + { + tableWidth = GetTableWidthAfterApplyingStretchStyles(); + } + return tableWidth; + } + private enum TopMidBot { Top, diff --git a/ConTabs/TableStretchStyles.cs b/ConTabs/TableStretchStyles.cs index 725aa72..a2a2389 100644 --- a/ConTabs/TableStretchStyles.cs +++ b/ConTabs/TableStretchStyles.cs @@ -9,6 +9,8 @@ namespace ConTabs /// public class TableStretchStyles { + private static readonly int MIN_WIDTH = 2; //if set to 1, word wrap in long strings would fail + public Func CalculateOptimalWidth { get; set; } public Func, int, int, int> CalculateAdditionalWidth { get; set; } @@ -22,45 +24,106 @@ public class TableStretchStyles /// public static TableStretchStyles DoNothing => new TableStretchStyles { CalculateOptimalWidth = GetOptimalColumnWidth, CalculateAdditionalWidth = UseDefaultDisplayWidths }; + /// + /// Sets all columns to the same width (approximately) + /// + public static TableStretchStyles EvenColumnWidth => new TableStretchStyles { CalculateOptimalWidth = GetUniformColumnWidth, CalculateAdditionalWidth = StretchOrSqueezeDisplayWidths }; + + /// + /// Stretches / squeezes all columns by approximately the same width + /// + public static TableStretchStyles StretchOrSqueezeAllColumnsEvenly => new TableStretchStyles { CalculateOptimalWidth = GetOptimalColumnWidth, CalculateAdditionalWidth = StretchOrSqueezeDisplayWidths }; + + /// + /// Stretches / squeezes all columns by approximately the same width + /// + public static TableStretchStyles SqueezeAllColumnsEvenly => new TableStretchStyles { CalculateOptimalWidth = GetOptimalColumnWidth, CalculateAdditionalWidth = SqueezeDisplayWidths }; + + /// + /// Stretches / squeezes long strings by approximately the same width + /// + public static TableStretchStyles StretchOrSqueezeLongStrings => new TableStretchStyles { CalculateOptimalWidth = GetOptimalColumnWidth, CalculateAdditionalWidth = StretchOrSqueezeLongStringDisplayWidths }; + + /// + /// Stretches / squeezes long strings by approximately the same width + /// + public static TableStretchStyles SqueezeLongStrings => new TableStretchStyles { CalculateOptimalWidth = GetOptimalColumnWidth, CalculateAdditionalWidth = SqueezeLongStringDisplayWidths }; + private static int UseDefaultDisplayWidths(List columns, int totalWidth, int canvasWidth) { for (int i = 0; i < columns.Count; i++) { columns[i].LongStringBehaviour.DisplayWidth = GetOptimalColumnWidth(columns[i]); } - return totalWidth; + return columns + .Select(v => v.LongStringBehaviour.DisplayWidth) + .Sum(); } - private static int AdaptDisplayWidths(List columns, int totalWidth, int canvasWidth) + private static int StretchOrSqueezeDisplayWidths(List columns, int totalWidth, int canvasWidth) { int difference = canvasWidth - totalWidth; - for (int i = 0; i < columns.Count; i++) + if (difference > 0) { - columns[i].LongStringBehaviour.DisplayWidth += difference / columns.Count; - if (i < difference % columns.Count) + for (int i = 0; i < columns.Count; i++) { - columns[i].LongStringBehaviour.DisplayWidth++; + columns[i].LongStringBehaviour.DisplayWidth += difference / columns.Count; + if (i < difference % columns.Count) + { + columns[i].LongStringBehaviour.DisplayWidth++; + } } } - return canvasWidth; + else if (difference < 0) + { + List squeezableColumns = columns.Where(c => c.LongStringBehaviour.DisplayWidth > MIN_WIDTH).ToList(); + var columnIndex = 0; + while (difference < 0 && squeezableColumns.Count > 0) + { + squeezableColumns[columnIndex].LongStringBehaviour.DisplayWidth--; + difference++; + if (squeezableColumns[columnIndex].LongStringBehaviour.DisplayWidth == MIN_WIDTH) + { + squeezableColumns.RemoveAt(columnIndex); + } + else + { + columnIndex++; + } + if (columnIndex >= squeezableColumns.Count) columnIndex = 0; + } + } + return columns + .Select(v => v.LongStringBehaviour.DisplayWidth) + .Sum(); } - /// - /// Sets all columns to the same width (approximately) - /// - public static TableStretchStyles EvenColumnWidth => new TableStretchStyles { CalculateOptimalWidth = GetUniformColumnWidth, CalculateAdditionalWidth = AdaptDisplayWidths }; + private static int StretchOrSqueezeLongStringDisplayWidths(List columns, int totalWidth, int canvasWidth) + { + var longStringColumns = columns.Where(c => c.LongStringBehaviour.Width > 0).ToList(); + StretchOrSqueezeDisplayWidths(longStringColumns, totalWidth, canvasWidth); + return columns + .Select(v => v.LongStringBehaviour.DisplayWidth) + .Sum(); + } - /// - /// Stretches / squeezes all columns by approximately the same width - /// - public static TableStretchStyles StretchOrSqueezeAllColumnsEvenly => new TableStretchStyles { CalculateOptimalWidth = GetOptimalColumnWidth, CalculateAdditionalWidth = AdaptDisplayWidths }; + private static int SqueezeLongStringDisplayWidths(List columns, int totalWidth, int canvasWidth) + { + var longStringColumns = columns.Where(c => c.LongStringBehaviour.Width > 0).ToList(); + SqueezeDisplayWidths(longStringColumns, totalWidth, canvasWidth); + return columns + .Select(v => v.LongStringBehaviour.DisplayWidth) + .Sum(); + } + + private static int SqueezeDisplayWidths(List columns, int totalWidth, int canvasWidth) + { + int difference = canvasWidth - totalWidth; + return difference >= 0 + ? UseDefaultDisplayWidths(columns, totalWidth, canvasWidth) + : StretchOrSqueezeDisplayWidths(columns, totalWidth, canvasWidth); + } - /* - /// - /// Stretches / squeezes long strings by approximately the same width - /// - public static TableStretchStyles StretchLongStrings => new TableStretchStyles { Method = GetLongStringStretchedFirstColumnWidth }; - */ private static int GetOptimalColumnWidth(Column column) { if (column.Values == null || column.Values.Count == 0) return column.ColumnName.Length; diff --git a/ConTabsDemo-DotNetCore/Program.cs b/ConTabsDemo-DotNetCore/Program.cs index 0a7b3c5..abe946f 100644 --- a/ConTabsDemo-DotNetCore/Program.cs +++ b/ConTabsDemo-DotNetCore/Program.cs @@ -53,9 +53,13 @@ static void Main(string[] args) // Add some padding table.Padding = new Padding(1, 1); - table.TableAlignment = Alignment.Right; - // table.CanvasWidth = 40; - table.TableStretchStyles = TableStretchStyles.EvenColumnWidth; + + // Center the outputted table + table.TableAlignment = Alignment.Center; + + // Just for testing purposes + table.CanvasWidth = 40; + table.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; // Finally, spit out the finished table Console.WriteLine(table); From b66db99f352624601a298e1777f17ea7588c1bfc Mon Sep 17 00:00:00 2001 From: tdwright Date: Sat, 15 Dec 2018 19:00:05 +1100 Subject: [PATCH 20/36] Simplified style declarations, added XML comments, and added tests --- ConTabs.Tests/ConformanceTests.cs | 42 +++++++++++++++++++++++++++++++ ConTabs/Styles.cs | 32 ++++++----------------- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/ConTabs.Tests/ConformanceTests.cs b/ConTabs.Tests/ConformanceTests.cs index fe4fd49..ebde596 100644 --- a/ConTabs.Tests/ConformanceTests.cs +++ b/ConTabs.Tests/ConformanceTests.cs @@ -207,6 +207,48 @@ public void TableStyledAsHeavyShouldLookLikeThis() tableString.ShouldBe(expected); } + [Test] + public void TableStyledAsHashesShouldLookLikeThis() + { + // Arrange + var listOfTestClasses = DataProvider.ListOfMinimalData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.TableStyle = Style.Hash; + + // Act + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "###############" + Environment.NewLine; + expected += "# IntA # IntB #" + Environment.NewLine; + expected += "###############" + Environment.NewLine; + expected += "# 1 # 3 #" + Environment.NewLine; + expected += "###############"; + tableString.ShouldBe(expected); + } + + [Test] + public void TableStyledAsPlussesShouldLookLikeThis() + { + // Arrange + var listOfTestClasses = DataProvider.ListOfMinimalData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.TableStyle = Style.Plus; + + // Act + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "+++++++++++++++" + Environment.NewLine; + expected += "+ IntA + IntB +" + Environment.NewLine; + expected += "+++++++++++++++" + Environment.NewLine; + expected += "+ 1 + 3 +" + Environment.NewLine; + expected += "+++++++++++++++"; + tableString.ShouldBe(expected); + } + [Test] public void TableStyledAsWhitespaceShouldLookLikeThis() { diff --git a/ConTabs/Styles.cs b/ConTabs/Styles.cs index 4dd1183..0078ca8 100644 --- a/ConTabs/Styles.cs +++ b/ConTabs/Styles.cs @@ -83,32 +83,16 @@ public Style(char wall, char floor, Corners corners) TeeNoRight = '╣' }); - public static Style Hash => new Style('#', '#', new Corners - { - CornerTopLeft = '#', - CornerTopRight = '#', - CornerBottomLeft = '#', - CornerBottomRight = '#', - Intersection = '#', - TeeNoUp = '#', - TeeNoLeft = '#', - TeeNoDown = '#', - TeeNoRight = '#' - }); + /// + /// Built-in style + /// + public static Style Hash => new Style('#', '#', '#'); - public static Style Plus => new Style('+', '+', new Corners - { - CornerTopLeft = '+', - CornerTopRight = '+', - CornerBottomLeft = '+', - CornerBottomRight = '+', - Intersection = '+', - TeeNoUp = '+', - TeeNoLeft = '+', - TeeNoDown = '+', - TeeNoRight = '+' - }); + /// + /// Built-in style + /// + public static Style Plus => new Style('+', '+', '+'); /// /// Built-in style From bc419446e004c91c08cf2cae2ca35ca79cf04e66 Mon Sep 17 00:00:00 2001 From: tdwright Date: Sat, 15 Dec 2018 20:48:42 +1100 Subject: [PATCH 21/36] Implemented a range of suggestions made by ReSharper (thanks JetBrains!) --- ConTabs.Tests/ColumnAttributeTests.cs | 2 +- ConTabs.Tests/UsageTests.cs | 21 +++++++++ ConTabs.sln.DotSettings | 3 ++ ConTabs/Alignment.cs | 4 +- ConTabs/{ => Attributes}/Attributes.cs | 1 - ConTabs/Column.cs | 14 ++---- ConTabs/Columns.cs | 21 +++++---- ConTabs/Corners.cs | 28 +++++------ ConTabs/Exceptions/ColumnNotFoundException.cs | 2 +- ConTabs/LongStringBehaviour.cs | 23 +++++----- ConTabs/OutputBuilder.cs | 32 ++++++------- ConTabs/Padding.cs | 6 +-- ConTabs/Styles.cs | 4 +- ConTabs/Table.cs | 46 +++++++------------ ConTabsDemo-Attributes/Data.cs | 4 +- 15 files changed, 105 insertions(+), 106 deletions(-) create mode 100644 ConTabs.sln.DotSettings rename ConTabs/{ => Attributes}/Attributes.cs (99%) diff --git a/ConTabs.Tests/ColumnAttributeTests.cs b/ConTabs.Tests/ColumnAttributeTests.cs index 12ee8e7..ef63d4e 100644 --- a/ConTabs.Tests/ColumnAttributeTests.cs +++ b/ConTabs.Tests/ColumnAttributeTests.cs @@ -1,9 +1,9 @@ using AutoFixture.NUnit3; -using ConTabs.Attributes; using NUnit.Framework; using Shouldly; using System.Collections.Generic; using System.Linq; +using ConTabs.Attributes; namespace ConTabs.Tests { diff --git a/ConTabs.Tests/UsageTests.cs b/ConTabs.Tests/UsageTests.cs index 3dc56ae..19de0eb 100644 --- a/ConTabs.Tests/UsageTests.cs +++ b/ConTabs.Tests/UsageTests.cs @@ -23,6 +23,27 @@ public void OnceFormatSet_FormatCanBeUpdated() result[0].ShouldBe('#'); } + [Test] + public void CustomStyle_OnceSet_AffectsAllElements() + { + // Arrange + var tableObj = Table.Create(); + + // Act + tableObj.TableStyle = new Style('W','F','C'); + + // Assert + tableObj.TableStyle.Corners.CornerBottomLeft.ShouldBe('C'); + tableObj.TableStyle.Corners.CornerBottomRight.ShouldBe('C'); + tableObj.TableStyle.Corners.CornerTopLeft.ShouldBe('C'); + tableObj.TableStyle.Corners.CornerTopRight.ShouldBe('C'); + tableObj.TableStyle.Corners.Intersection.ShouldBe('C'); + tableObj.TableStyle.Corners.TeeNoDown.ShouldBe('C'); + tableObj.TableStyle.Corners.TeeNoLeft.ShouldBe('C'); + tableObj.TableStyle.Corners.TeeNoRight.ShouldBe('C'); + tableObj.TableStyle.Corners.TeeNoUp.ShouldBe('C'); + } + [Test] public void OnceFormatSet_CornerCanBeUpdated() { diff --git a/ConTabs.sln.DotSettings b/ConTabs.sln.DotSettings new file mode 100644 index 0000000..11f2c26 --- /dev/null +++ b/ConTabs.sln.DotSettings @@ -0,0 +1,3 @@ + + <data><IncludeFilters /><ExcludeFilters /></data> + <data /> \ No newline at end of file diff --git a/ConTabs/Alignment.cs b/ConTabs/Alignment.cs index f856c22..411ac29 100644 --- a/ConTabs/Alignment.cs +++ b/ConTabs/Alignment.cs @@ -70,9 +70,7 @@ public string ProcessString(string input, int colMaxWidth) /// public override bool Equals(object obj) { - var comp = obj as Alignment; - - return comp != null && Method.Equals(comp.Method); + return obj is Alignment comp && Method.Equals(comp.Method); } } } \ No newline at end of file diff --git a/ConTabs/Attributes.cs b/ConTabs/Attributes/Attributes.cs similarity index 99% rename from ConTabs/Attributes.cs rename to ConTabs/Attributes/Attributes.cs index eab9624..a24fb29 100644 --- a/ConTabs/Attributes.cs +++ b/ConTabs/Attributes/Attributes.cs @@ -1,5 +1,4 @@ using System; -using ConTabs; namespace ConTabs.Attributes { diff --git a/ConTabs/Column.cs b/ConTabs/Column.cs index 2f2f27c..ce26ccf 100644 --- a/ConTabs/Column.cs +++ b/ConTabs/Column.cs @@ -8,10 +8,6 @@ namespace ConTabs { [DebuggerDisplay("Column for '{PropertyName}'")] - - /// - /// Acts as a column within a table - /// public class Column { /// @@ -50,7 +46,7 @@ public class Column /// public Alignment Alignment { get; set; } - private readonly MethodInfo toStringMethod; + private readonly MethodInfo _toStringMethod; internal int? InitialPosition; /// @@ -84,7 +80,7 @@ public Column(PropertyInfo propertyInfo) SourceType = propertyInfo.PropertyType; PropertyName = propertyInfo.Name; ColumnName = propertyInfo.Name; - toStringMethod = GetToStringMethod(); + _toStringMethod = GetToStringMethod(); // check for each of the attributes and act accordingly var attributes = propertyInfo.GetCustomAttributes(); @@ -106,7 +102,7 @@ public Column(Type type, string name) SourceType = type; PropertyName = name; ColumnName = name; - toStringMethod = GetToStringMethod(); + _toStringMethod = GetToStringMethod(); } public string StringValForCol(Object o) @@ -118,13 +114,13 @@ public string StringValForCol(Object o) } else { - if (toStringMethod == null) + if (_toStringMethod == null) { return (casted ?? string.Empty).ToString(); } else { - return (string)toStringMethod.Invoke(o, new object[] { FormatString }); + return (string)_toStringMethod.Invoke(o, new object[] { FormatString }); } } } diff --git a/ConTabs/Columns.cs b/ConTabs/Columns.cs index cdd38dd..7deb425 100644 --- a/ConTabs/Columns.cs +++ b/ConTabs/Columns.cs @@ -79,8 +79,8 @@ public void MoveColumn(string name, int newPosition) private void MoveColumn(Column col, int newPos) { - this.Remove(col); - this.Insert(newPos, col); + Remove(col); + Insert(newPos, col); } private void TryToSetInitialPositions() @@ -92,7 +92,7 @@ private void TryToSetInitialPositions() foreach (var moveToTry in movesToTry) { - var pos = (moveToTry.pos >= this.Count) ? this.Count - 1 : moveToTry.pos; + var pos = (moveToTry.pos >= Count) ? Count - 1 : moveToTry.pos; MoveColumn(moveToTry.name, pos); } } @@ -124,10 +124,10 @@ public void AddGeneratedColumn(Func expression var results = new List(); - for (int i = 0; i < column.Values.Count; i++) + for (var i = 0; i < column.Values.Count; i++) results.Add(expression((TInput)column.Values[i])); - this.Add(new Column(typeof(TOutput), columnName) { Values = results }); + Add(new Column(typeof(TOutput), columnName) { Values = results }); } /// @@ -147,7 +147,8 @@ public void AddGeneratedColumn(Func ex /// /// Adds a new column to the table of computed values. /// - /// Parameter Type + /// Parameter Type + /// Parameter Type /// Output Type /// The expression used to compute values /// The name of the new column @@ -162,10 +163,10 @@ public void AddGeneratedColumn(Func(); - for (int i = 0; i < column1.Values.Count; i++) + for (var i = 0; i < column1.Values.Count; i++) results.Add(expression((TInput1)column1.Values[i], (TInput2)column2.Values[i])); - this.Add(new Column(typeof(TOutput), columnName) { Values = results }); + Add(new Column(typeof(TOutput), columnName) { Values = results }); } /// @@ -179,7 +180,7 @@ public void AddGeneratedColumnFromRange(Func, TOutput> exp { var results = new List(); - for (int i = 0; i < columns[0].Values.Count; i++) + for (var i = 0; i < columns[0].Values.Count; i++) { var operands = new List(); @@ -189,7 +190,7 @@ public void AddGeneratedColumnFromRange(Func, TOutput> exp results.Add(expression(operands)); } - this.Add(new Column(typeof(TOutput), columnName) { Values = results }); + Add(new Column(typeof(TOutput), columnName) { Values = results }); } } } \ No newline at end of file diff --git a/ConTabs/Corners.cs b/ConTabs/Corners.cs index a0a4769..6416a3f 100644 --- a/ConTabs/Corners.cs +++ b/ConTabs/Corners.cs @@ -2,7 +2,7 @@ { public class Corners { - private readonly char[,] cornerChars = new char[3, 3]; + private readonly char[,] _cornerChars = new char[3, 3]; /* * ╔═══╤═════╤═════╤═════╗ @@ -20,53 +20,53 @@ public class Corners /// /// The character representing the top-left corner /// - public char CornerTopLeft { get { return cornerChars[0, 0]; } set { cornerChars[0, 0] = value; } } + public char CornerTopLeft { get { return _cornerChars[0, 0]; } set { _cornerChars[0, 0] = value; } } /// /// The character representing the top-right corner /// - public char CornerTopRight { get { return cornerChars[2, 0]; } set { cornerChars[2, 0] = value; } } + public char CornerTopRight { get { return _cornerChars[2, 0]; } set { _cornerChars[2, 0] = value; } } /// /// The character representing the bottom-left corner /// - public char CornerBottomLeft { get { return cornerChars[0, 2]; } set { cornerChars[0, 2] = value; } } + public char CornerBottomLeft { get { return _cornerChars[0, 2]; } set { _cornerChars[0, 2] = value; } } /// /// The character representing the bottom-right corner /// - public char CornerBottomRight { get { return cornerChars[2, 2]; } set { cornerChars[2, 2] = value; } } + public char CornerBottomRight { get { return _cornerChars[2, 2]; } set { _cornerChars[2, 2] = value; } } /// /// The character representing intersections /// - public char Intersection { get { return cornerChars[1, 1]; } set { cornerChars[1, 1] = value; } } + public char Intersection { get { return _cornerChars[1, 1]; } set { _cornerChars[1, 1] = value; } } /// /// The character representing the intersections on the top /// - public char TeeNoUp { get { return cornerChars[1, 0]; } set { cornerChars[1, 0] = value; } } + public char TeeNoUp { get { return _cornerChars[1, 0]; } set { _cornerChars[1, 0] = value; } } /// /// The character representing the intersections on the right /// - public char TeeNoRight { get { return cornerChars[2, 1]; } set { cornerChars[2, 1] = value; } } + public char TeeNoRight { get { return _cornerChars[2, 1]; } set { _cornerChars[2, 1] = value; } } /// /// The character representing the intersections on the bottom /// - public char TeeNoDown { get { return cornerChars[1, 2]; } set { cornerChars[1, 2] = value; } } + public char TeeNoDown { get { return _cornerChars[1, 2]; } set { _cornerChars[1, 2] = value; } } /// /// The character representing the intersections on the left /// - public char TeeNoLeft { get { return cornerChars[0, 1]; } set { cornerChars[0, 1] = value; } } + public char TeeNoLeft { get { return _cornerChars[0, 1]; } set { _cornerChars[0, 1] = value; } } public char this[int i, int j] { get { - return cornerChars[i, j]; + return _cornerChars[i, j]; } } @@ -76,11 +76,11 @@ public class Corners /// The character to set all corners to public void SetAllCorners(char corner) { - for (int i = 0; i < cornerChars.GetLength(0); i++) + for (var i = 0; i < _cornerChars.GetLength(0); i++) { - for (int j = 0; j < cornerChars.GetLength(1); j++) + for (var j = 0; j < _cornerChars.GetLength(1); j++) { - cornerChars[i, j] = corner; + _cornerChars[i, j] = corner; } } } diff --git a/ConTabs/Exceptions/ColumnNotFoundException.cs b/ConTabs/Exceptions/ColumnNotFoundException.cs index d73b051..8bcd340 100644 --- a/ConTabs/Exceptions/ColumnNotFoundException.cs +++ b/ConTabs/Exceptions/ColumnNotFoundException.cs @@ -4,7 +4,7 @@ namespace ConTabs.Exceptions { public class ColumnNotFoundException : Exception { - private readonly bool _named = false; + private readonly bool _named; private readonly string _colName; private readonly int _index; diff --git a/ConTabs/LongStringBehaviour.cs b/ConTabs/LongStringBehaviour.cs index f453058..1524d1b 100644 --- a/ConTabs/LongStringBehaviour.cs +++ b/ConTabs/LongStringBehaviour.cs @@ -61,8 +61,7 @@ private static string TruncateString(string input, string ellipsis, int width) private static string WrapString(string input, string ellipsis, int width) { - if (input.Length <= width) return input; - return LongStringBehaviour.WordWrap(input, width); + return input.Length <= width ? input : WordWrap(input, width); } /// @@ -82,13 +81,13 @@ public string ProcessString(string input) private static string WordWrap(string str, int width) { - string[] words = Explode(str, SplitChars); + var words = Explode(str, SplitChars); - int curLineLength = 0; - StringBuilder strBuilder = new StringBuilder(); - for (int i = 0; i < words.Length; i += 1) + var curLineLength = 0; + var strBuilder = new StringBuilder(); + for (var i = 0; i < words.Length; i += 1) { - string word = words[i]; + var word = words[i]; // If adding the new word to the current line would be too long, // then put it on a new line (and split it up if it's too long). if (curLineLength + word.Length > width) @@ -124,11 +123,11 @@ private static string WordWrap(string str, int width) private static string[] Explode(string str, char[] splitChars) { - List parts = new List(); - int startIndex = 0; + var parts = new List(); + var startIndex = 0; while (true) { - int index = str.IndexOfAny(splitChars, startIndex); + var index = str.IndexOfAny(splitChars, startIndex); if (index == -1) { @@ -136,8 +135,8 @@ private static string[] Explode(string str, char[] splitChars) return parts.ToArray(); } - string word = str.Substring(startIndex, index - startIndex); - char nextChar = str.Substring(index, 1)[0]; + var word = str.Substring(startIndex, index - startIndex); + var nextChar = str.Substring(index, 1)[0]; // Dashes and the likes should stick to the word occuring before it. Whitespace doesn't have to. if (char.IsWhiteSpace(nextChar)) { diff --git a/ConTabs/OutputBuilder.cs b/ConTabs/OutputBuilder.cs index 73c8988..01552a8 100644 --- a/ConTabs/OutputBuilder.cs +++ b/ConTabs/OutputBuilder.cs @@ -42,7 +42,7 @@ private OutputBuilder(Table t, Style s) } else { - for (int i = 0; i < table.Data.Count(); i++) + for (var i = 0; i < table.Data.Count(); i++) { InsertVerticalPadding(table.Padding.Top, style.Wall); NewLine(); DataRow(i); @@ -54,15 +54,15 @@ private OutputBuilder(Table t, Style s) private void InsertVerticalPadding(byte padding, char columnSeparator) { - for (int paddingLevel = 0; paddingLevel < padding; paddingLevel++) + for (var paddingLevel = 0; paddingLevel < padding; paddingLevel++) { NewLine(); sb.Append(style.Wall); - for (int i = 0; i < table._colsShown.Count; i++) + for (var i = 0; i < table.ColsShown.Count; i++) { - sb.Append(new string(' ', table._colsShown[i].MaxWidth + (table.Padding.Left + table.Padding.Right))); + sb.Append(new string(' ', table.ColsShown[i].MaxWidth + (table.Padding.Left + table.Padding.Right))); - if (i < table._colsShown.Count - 1) + if (i < table.ColsShown.Count - 1) { sb.Append(columnSeparator); } @@ -80,11 +80,11 @@ private void HLine(TopMidBot v) { sb.Append(GetCorner(v, LeftCentreRight.Left)); - for (int i = 0; i < table._colsShown.Count; i++) + for (var i = 0; i < table.ColsShown.Count; i++) { - sb.Append(new string(style.Floor, table._colsShown[i].MaxWidth + (table.Padding.Left + table.Padding.Right))); + sb.Append(new string(style.Floor, table.ColsShown[i].MaxWidth + (table.Padding.Left + table.Padding.Right))); - if (i < table._colsShown.Count - 1) + if (i < table.ColsShown.Count - 1) { sb.Append(GetCorner(v, LeftCentreRight.Centre)); } @@ -95,17 +95,17 @@ private void HLine(TopMidBot v) private void NoDataLine() { var noDataText = "no data"; - int colWidths = table._colsShown.Sum(c => c.MaxWidth); - int innerWidth = colWidths + (table._colsShown.Count * (table.Padding.Left + table.Padding.Right)) + table._colsShown.Count - 1; - int leftPad = (innerWidth - noDataText.Length) / 2; - int rightPad = innerWidth - (leftPad + noDataText.Length); + var colWidths = table.ColsShown.Sum(c => c.MaxWidth); + var innerWidth = colWidths + (table.ColsShown.Count * (table.Padding.Left + table.Padding.Right)) + table.ColsShown.Count - 1; + var leftPad = (innerWidth - noDataText.Length) / 2; + var rightPad = innerWidth - (leftPad + noDataText.Length); sb.Append(style.Wall + new String(' ', leftPad) + noDataText + new string(' ', rightPad) + style.Wall); } private void Headers() { sb.Append(style.Wall); - foreach (var col in table._colsShown) + foreach (var col in table.ColsShown) { sb.Append(GetPaddingString(table.Padding.Left) + table.HeaderAlignment.ProcessString(col.ColumnName, col.MaxWidth) + GetPaddingString(table.Padding.Right) + style.Wall); } @@ -113,11 +113,11 @@ private void Headers() private void DataRow(int i) { - var cols = table._colsShown.Select(c => new CellParts(c.StringValForCol(c.Values[i]), c.MaxWidth, c.Alignment)).ToList(); + var cols = table.ColsShown.Select(c => new CellParts(c.StringValForCol(c.Values[i]), c.MaxWidth, c.Alignment)).ToList(); var maxLines = cols.Max(c => c.LineCount); - for (int j = 0; j < maxLines; j++) + for (var j = 0; j < maxLines; j++) { DataLine(cols, j); if (j != maxLines - 1) @@ -132,7 +132,7 @@ private void DataLine(List parts, int line) sb.Append(style.Wall); foreach (var part in parts) { - string val = part.GetLine(line); + var val = part.GetLine(line); sb.Append(GetPaddingString(table.Padding.Left) + part.Alignment.ProcessString(val, part.ColMaxWidth) + GetPaddingString(table.Padding.Right) diff --git a/ConTabs/Padding.cs b/ConTabs/Padding.cs index 8136a89..99858ee 100644 --- a/ConTabs/Padding.cs +++ b/ConTabs/Padding.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ConTabs +namespace ConTabs { public class Padding { diff --git a/ConTabs/Styles.cs b/ConTabs/Styles.cs index 0078ca8..3f8cc86 100644 --- a/ConTabs/Styles.cs +++ b/ConTabs/Styles.cs @@ -1,6 +1,4 @@ -using System; - -namespace ConTabs +namespace ConTabs { /// /// The properties used to define a table's visual styling diff --git a/ConTabs/Table.cs b/ConTabs/Table.cs index 20f5864..680c591 100644 --- a/ConTabs/Table.cs +++ b/ConTabs/Table.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Reflection; using ConTabs.Exceptions; @@ -8,10 +7,6 @@ namespace ConTabs { [DebuggerDisplay("Table with {Columns.Count} available columns")] - - /// - /// A static class used to create new tables. - /// public sealed partial class Table where T : class { /// @@ -22,35 +17,29 @@ public sealed partial class Table where T : class /// /// The collection of columns within the table /// - public Columns Columns { get; set; } + public Columns Columns { get; } /// /// The horizontal alignment of the column titles /// public Alignment HeaderAlignment { get; set; } - private Alignment _columnAlignment { get; set; } + private Alignment _columnAlignment; /// /// The horizontal alignment of the cell values /// public Alignment ColumnAlignment { - get - { - return _columnAlignment; - } + get => _columnAlignment; set { _columnAlignment = value; - if (Columns != null) - { - Columns.ForEach(c => c.Alignment = _columnAlignment); - } + Columns?.ForEach(c => c.Alignment = _columnAlignment); } } - internal List _colsShown => Columns.Where(c => !c.Hide).ToList(); + private List ColsShown => Columns.Where(c => !c.Hide).ToList(); /// /// The table's display properties @@ -58,21 +47,19 @@ public Alignment ColumnAlignment public Style TableStyle { get; set; } private IEnumerable _data; - public IEnumerable Data + + private IEnumerable Data { - get - { - return _data; - } + get => _data; set { _data = value; foreach (var col in Columns) { - col.Values = _data.Select(d => + col.Values = _data.ToList().Select(d => { - Type t = typeof(T); - PropertyInfo p = t.GetRuntimeProperty(col.PropertyName); + var t = typeof(T); + var p = t.GetRuntimeProperty(col.PropertyName); return p.GetValue(d); }).ToList(); } @@ -91,13 +78,13 @@ public static Table Create() /// /// Creates a new table /// - /// The collection of objects to place into the table + /// The collection of objects to place into the table /// A new table - public static Table Create(IEnumerable Source) + public static Table Create(IEnumerable source) { return new Table() { - Data = Source + Data = source }; } @@ -125,10 +112,11 @@ private Table() /// /// Formats the table for output /// + /// /// The table as a string public override string ToString() { - if (_colsShown.Count == 0) + if (ColsShown != null && ColsShown.Count == 0) throw new EmptyTableException(this.GetType()); return OutputBuilder.BuildOutput(this, TableStyle); diff --git a/ConTabsDemo-Attributes/Data.cs b/ConTabsDemo-Attributes/Data.cs index abed08f..7c55d64 100644 --- a/ConTabsDemo-Attributes/Data.cs +++ b/ConTabsDemo-Attributes/Data.cs @@ -1,5 +1,5 @@ -using ConTabs.Attributes; -using System.Collections.Generic; +using System.Collections.Generic; +using ConTabs.Attributes; namespace ConTabsDemo_Attributes { From 5ae7d71e3d788b961f50ff4bf7d5c4bffd4e383c Mon Sep 17 00:00:00 2001 From: Oisin McLaughlin Date: Mon, 17 Dec 2018 15:43:29 +0000 Subject: [PATCH 22/36] UnicodeDoubleWalled and UnicodeDoubleFloored Added. Added UnicodeDoubleWalled and UnicodeDoubleFloored with accompanying unit tests. --- ConTabs.Tests/ConformanceTests.cs | 94 +++++++++++++++++++++++++++++++ ConTabs/Styles.cs | 36 ++++++++++++ 2 files changed, 130 insertions(+) diff --git a/ConTabs.Tests/ConformanceTests.cs b/ConTabs.Tests/ConformanceTests.cs index ebde596..a0f0445 100644 --- a/ConTabs.Tests/ConformanceTests.cs +++ b/ConTabs.Tests/ConformanceTests.cs @@ -380,6 +380,100 @@ public void TableStyledAsDotsShouldLookLikeThis() tableString.ShouldBe(expected); } + [Test] + public void TableStyledAsUnicodeDoubleWalledShouldLookLikeThis() + { + // Arrange + var listOfTestClasses = DataProvider.ListOfMinimalData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.TableStyle = Style.UnicodeDoubleWalled; + + // Act + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "╓──────╥──────╖" + Environment.NewLine; + expected += "║ IntA ║ IntB ║" + Environment.NewLine; + expected += "╟──────╫──────╢" + Environment.NewLine; + expected += "║ 1 ║ 3 ║" + Environment.NewLine; + expected += "╙──────╨──────╜"; + tableString.ShouldBe(expected); + } + + [Test] + public void TableStyledAsUnicodeDoubleWalledWithExplicitPaddingShouldLookLikeThis() + { + // Arrange + var listOfTestClasses = DataProvider.ListOfMinimalData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.TableStyle = Style.UnicodeDoubleWalled; + tableObj.Padding = new Padding(1, 2); + + // Act + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "╓────────╥────────╖" + Environment.NewLine; + expected += "║ ║ ║" + Environment.NewLine; + expected += "║ IntA ║ IntB ║" + Environment.NewLine; + expected += "║ ║ ║" + Environment.NewLine; + expected += "╟────────╫────────╢" + Environment.NewLine; + expected += "║ ║ ║" + Environment.NewLine; + expected += "║ 1 ║ 3 ║" + Environment.NewLine; + expected += "║ ║ ║" + Environment.NewLine; + expected += "╙────────╨────────╜"; + tableString.ShouldBe(expected); + } + + [Test] + public void TableStyledAsUnicodeDoubleFlooredShouldLookLikeThis() + { + // Arrange + var listOfTestClasses = DataProvider.ListOfMinimalData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.TableStyle = Style.UnicodeDoubleFloored; + + // Act + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "╒══════╤══════╕" + Environment.NewLine; + expected += "│ IntA │ IntB │" + Environment.NewLine; + expected += "╞══════╪══════╡" + Environment.NewLine; + expected += "│ 1 │ 3 │" + Environment.NewLine; + expected += "╘══════╧══════╛"; + tableString.ShouldBe(expected); + } + + [Test] + public void TableStyledAsUnicodeDoubleFlooredWithExplicitPaddingShouldLookLikeThis() + { + // Arrange + var listOfTestClasses = DataProvider.ListOfMinimalData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.TableStyle = Style.UnicodeDoubleFloored; + tableObj.Padding = new Padding(1, 2); + + // Act + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "╒════════╤════════╕" + Environment.NewLine; + expected += "│ │ │" + Environment.NewLine; + expected += "│ IntA │ IntB │" + Environment.NewLine; + expected += "│ │ │" + Environment.NewLine; + expected += "╞════════╪════════╡" + Environment.NewLine; + expected += "│ │ │" + Environment.NewLine; + expected += "│ 1 │ 3 │" + Environment.NewLine; + expected += "│ │ │" + Environment.NewLine; + expected += "╘════════╧════════╛"; + tableString.ShouldBe(expected); + } + [Test] public void BasicTableWithWrappedStringShouldLookLikeThis() { diff --git a/ConTabs/Styles.cs b/ConTabs/Styles.cs index 3f8cc86..e92d17c 100644 --- a/ConTabs/Styles.cs +++ b/ConTabs/Styles.cs @@ -143,5 +143,41 @@ public Style(char wall, char floor, Corners corners) TeeNoDown = ':', TeeNoRight = ':' }); + + /// + /// Built-in style + /// + /// *May require Console.OutputEncoding = Encoding.Unicode; + /// + public static Style UnicodeDoubleWalled => new Style('║', '─', new Corners + { + CornerTopLeft = '╓', + CornerTopRight = '╖', + CornerBottomLeft = '╙', + CornerBottomRight = '╜', + Intersection = '╫', + TeeNoUp = '╥', + TeeNoLeft = '╟', + TeeNoDown = '╨', + TeeNoRight = '╢' + }); + + /// + /// Built-in style + /// + /// *May require Console.OutputEncoding = Encoding.Unicode; + /// + public static Style UnicodeDoubleFloored => new Style('│', '═', new Corners + { + CornerTopLeft = '╒', + CornerTopRight = '╕', + CornerBottomLeft = '╘', + CornerBottomRight = '╛', + Intersection = '╪', + TeeNoUp = '╤', + TeeNoLeft = '╞', + TeeNoDown = '╧', + TeeNoRight = '╡' + }); } } \ No newline at end of file From 6fc1f88343c2467c88a8b9a3cd3836580ba1606b Mon Sep 17 00:00:00 2001 From: TomWright Date: Wed, 19 Dec 2018 10:39:14 +1100 Subject: [PATCH 23/36] Playing with retargeting to .Net Standard 2.0 --- ConTabs.Tests/ConTabs.Tests.csproj | 2 +- ConTabs/ConTabs.csproj | 2 +- ConTabsDemo-DotNetCore/ConTabsDemo-DotNetCore.csproj | 2 +- ConTabsDemo-DotNetFramework/ConTabsDemo-DotNetFramework.csproj | 2 +- ConTabsTestData/ConTabsTestData.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ConTabs.Tests/ConTabs.Tests.csproj b/ConTabs.Tests/ConTabs.Tests.csproj index bbd59e0..cca12bf 100644 --- a/ConTabs.Tests/ConTabs.Tests.csproj +++ b/ConTabs.Tests/ConTabs.Tests.csproj @@ -10,7 +10,7 @@ Properties ConTabs.Tests ConTabs.Tests - v4.6 + v4.6.1 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15.0 diff --git a/ConTabs/ConTabs.csproj b/ConTabs/ConTabs.csproj index a20a0d6..4c02aec 100644 --- a/ConTabs/ConTabs.csproj +++ b/ConTabs/ConTabs.csproj @@ -1,6 +1,6 @@  - netstandard1.3;netstandard2.0;net45 + netstandard2.0 ConTabs.tdwright 1.3.0 tdwright diff --git a/ConTabsDemo-DotNetCore/ConTabsDemo-DotNetCore.csproj b/ConTabsDemo-DotNetCore/ConTabsDemo-DotNetCore.csproj index 4f2912d..4302948 100644 --- a/ConTabsDemo-DotNetCore/ConTabsDemo-DotNetCore.csproj +++ b/ConTabsDemo-DotNetCore/ConTabsDemo-DotNetCore.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp1.1 + netcoreapp2.0 diff --git a/ConTabsDemo-DotNetFramework/ConTabsDemo-DotNetFramework.csproj b/ConTabsDemo-DotNetFramework/ConTabsDemo-DotNetFramework.csproj index c856c9d..f6be1f5 100644 --- a/ConTabsDemo-DotNetFramework/ConTabsDemo-DotNetFramework.csproj +++ b/ConTabsDemo-DotNetFramework/ConTabsDemo-DotNetFramework.csproj @@ -8,7 +8,7 @@ Exe ConTabsDemo_DotNetFramework ConTabsDemo-DotNetFramework - v4.6 + v4.6.1 512 true diff --git a/ConTabsTestData/ConTabsTestData.csproj b/ConTabsTestData/ConTabsTestData.csproj index 1536775..eb7e066 100644 --- a/ConTabsTestData/ConTabsTestData.csproj +++ b/ConTabsTestData/ConTabsTestData.csproj @@ -1,7 +1,7 @@  - netstandard1.0 + netstandard2.0 \ No newline at end of file From 9ddd81719735eb44213a8d18130f2c2338599be7 Mon Sep 17 00:00:00 2001 From: tdwright Date: Wed, 19 Dec 2018 19:22:39 +1100 Subject: [PATCH 24/36] Updated package info for preview release --- ConTabs/ConTabs.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ConTabs/ConTabs.csproj b/ConTabs/ConTabs.csproj index a20a0d6..f809c68 100644 --- a/ConTabs/ConTabs.csproj +++ b/ConTabs/ConTabs.csproj @@ -2,14 +2,14 @@ netstandard1.3;netstandard2.0;net45 ConTabs.tdwright - 1.3.0 tdwright + 2.0.0-preview1 + 2.0.0-preview1 Simple yet flexible ascii tables for your console output. false - This release sees the introduction of computed columns. These can be easily added to your table with values derived from values in other columns. + First preview of version 2.0.0 of ConTabs Copyright 2018 (c) tdwright. All rights reserved. See license. ascii tables console - 1.3.0 https://github.com/tdwright/contabs https://github.com/tdwright/contabs/blob/master/LICENSE https://raw.githubusercontent.com/tdwright/contabs/master/RepoAssets/contabs%20logo%20small.png From df5a99cb10ed710e0c1dcbf03e91e8d24275efd9 Mon Sep 17 00:00:00 2001 From: Marek Zitnansky Date: Wed, 26 Dec 2018 17:36:10 +0100 Subject: [PATCH 25/36] Added tests, aligned demo programs, fixed format abbreviation from previous MR --- ConTabs.Tests/ConformanceTests.cs | 256 +++++++++++++++++++++++++ ConTabs.Tests/FormatTests.cs | 2 +- ConTabs/TableStretchStyles.cs | 1 + ConTabsDemo-DotNetFramework/Program.cs | 10 +- 4 files changed, 266 insertions(+), 3 deletions(-) diff --git a/ConTabs.Tests/ConformanceTests.cs b/ConTabs.Tests/ConformanceTests.cs index deca0dc..ed81b02 100644 --- a/ConTabs.Tests/ConformanceTests.cs +++ b/ConTabs.Tests/ConformanceTests.cs @@ -665,5 +665,261 @@ public void BasicTableWithCenterHeaderAlignmentShouldLookLikeThis() expected += "+---------------------------+-----------+----------------+"; tableString.ShouldBe(expected); } + + [Test] + public void BasicTableShouldByDefaultHideOverlappingColumn() + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + + // Act + tableObj.CanvasWidth = 50; + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "+--------------+-----------+----------------+" + Environment.NewLine; + expected += "| StringColumn | IntColumn | CurrencyColumn |" + Environment.NewLine; + expected += "+--------------+-----------+----------------+" + Environment.NewLine; + expected += "| AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += "+--------------+-----------+----------------+"; + tableString.ShouldBe(expected); + } + + [Test] + public void BasicTableShouldByDefaultHideOverlappingColumnButNotLeaveItAsHidden() + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + + // Act + tableObj.CanvasWidth = 50; + var tableString = tableObj.ToString(); + + // Assert + tableObj.Columns[3].Hide.ShouldBe(false); + } + + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + public void BasicTableWithoutStretchShouldNotBeStretched(int stylesWithoutStretch) + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.Columns[3].Hide = true; // hide date field + + // Act + tableObj.CanvasWidth = 60; + switch (stylesWithoutStretch) + { + case 0: tableObj.TableStretchStyles = TableStretchStyles.DoNothing; break; + case 1: tableObj.TableStretchStyles = TableStretchStyles.SqueezeAllColumnsEvenly; break; + case 2: + { + tableObj.TableStretchStyles = TableStretchStyles.SqueezeLongStrings; + tableObj.Columns[0].LongStringBehaviour = LongStringBehaviour.Truncate; + tableObj.Columns[0].LongStringBehaviour.Width = 0; // autodetect width + break; + } + default: break; + } + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "+--------------+-----------+----------------+" + Environment.NewLine; + expected += "| StringColumn | IntColumn | CurrencyColumn |" + Environment.NewLine; + expected += "+--------------+-----------+----------------+" + Environment.NewLine; + expected += "| AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += "+--------------+-----------+----------------+"; + tableString.ShouldBe(expected); + } + + [Test] + public void BasicTableWithEvenColumnWidthShouldLookLikeThis() + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.Columns[3].Hide = true; // hide date field + + // Act + tableObj.CanvasWidth = 40; + tableObj.TableStretchStyles = TableStretchStyles.EvenColumnWidth; + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "+------------+------------+------------+" + Environment.NewLine; + expected += "| StringColu | IntColumn | CurrencyCo |" + Environment.NewLine; + expected += "+------------+------------+------------+" + Environment.NewLine; + expected += "| AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += "+------------+------------+------------+"; + tableString.ShouldBe(expected); + } + + [Test] + public void BasicTableWithEvenStretchShouldLookLikeThis() + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.Columns[3].Hide = true; // hide date field + + // Act + tableObj.CanvasWidth = 60; + tableObj.TableStretchStyles = TableStretchStyles.StretchOrSqueezeAllColumnsEvenly; + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "+-------------------+----------------+---------------------+" + Environment.NewLine; + expected += "| StringColumn | IntColumn | CurrencyColumn |" + Environment.NewLine; + expected += "+-------------------+----------------+---------------------+" + Environment.NewLine; + expected += "| AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += "+-------------------+----------------+---------------------+"; + tableString.ShouldBe(expected); + } + + [TestCase(0)] + [TestCase(1)] + public void BasicTableWithEvenSqueezeShouldLookLikeThis(int evenSqueezeStretchStyles) + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.Columns[3].Hide = true; // hide date field + + // Act + tableObj.CanvasWidth = 40; + switch (evenSqueezeStretchStyles) + { + case 0: tableObj.TableStretchStyles = TableStretchStyles.SqueezeAllColumnsEvenly; break; + case 1: tableObj.TableStretchStyles = TableStretchStyles.StretchOrSqueezeAllColumnsEvenly; break; + default: break; + } + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "+------------+---------+---------------+" + Environment.NewLine; + expected += "| StringColu | IntColu | CurrencyColum |" + Environment.NewLine; + expected += "+------------+---------+---------------+" + Environment.NewLine; + expected += "| AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += "+------------+---------+---------------+"; + tableString.ShouldBe(expected); + } + + [Test] + public void BasicTableWithLongStringStretchShouldLookLikeThis() + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.Columns[3].Hide = true; // hide date field + + // Act + tableObj.CanvasWidth = 60; + tableObj.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; + tableObj.Columns[0].LongStringBehaviour = LongStringBehaviour.Truncate; // to test long string stretches + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "+-----------------------------+-----------+----------------+" + Environment.NewLine; + expected += "| StringColumn | IntColumn | CurrencyColumn |" + Environment.NewLine; + expected += "+-----------------------------+-----------+----------------+" + Environment.NewLine; + expected += "| AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += "+-----------------------------+-----------+----------------+"; + tableString.ShouldBe(expected); + } + + [TestCase(0)] + [TestCase(1)] + public void BasicTableWithLongStringSqueezeShouldLookLikeThis(int evenSqueezeStretchStyles) + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.Columns[3].Hide = true; // hide date field + + // Act + tableObj.CanvasWidth = 40; + switch (evenSqueezeStretchStyles) + { + case 0: tableObj.TableStretchStyles = TableStretchStyles.SqueezeLongStrings; break; + case 1: tableObj.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; break; + default: break; + } + tableObj.Columns[0].LongStringBehaviour = LongStringBehaviour.Truncate; // to test long string stretches + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "+---------+-----------+----------------+" + Environment.NewLine; + expected += "| StringC | IntColumn | CurrencyColumn |" + Environment.NewLine; + expected += "+---------+-----------+----------------+" + Environment.NewLine; + expected += "| AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += "+---------+-----------+----------------+"; + tableString.ShouldBe(expected); + } + + [Test] + public void BasicTableWithCentralAlignmentShouldLookLikeThis() + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + + // Act + tableObj.CanvasWidth = 50; + tableObj.TableAlignment = Alignment.Center; + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += " +--------------+-----------+----------------+" + Environment.NewLine; + expected += " | StringColumn | IntColumn | CurrencyColumn |" + Environment.NewLine; + expected += " +--------------+-----------+----------------+" + Environment.NewLine; + expected += " | AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += " +--------------+-----------+----------------+"; + tableString.ShouldBe(expected); + } + + [Test] + public void BasicTableWithRightAlignmentShouldLookLikeThis() + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + + // Act + tableObj.CanvasWidth = 50; + tableObj.TableAlignment = Alignment.Right; + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += " +--------------+-----------+----------------+" + Environment.NewLine; + expected += " | StringColumn | IntColumn | CurrencyColumn |" + Environment.NewLine; + expected += " +--------------+-----------+----------------+" + Environment.NewLine; + expected += " | AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += " +--------------+-----------+----------------+"; + tableString.ShouldBe(expected); + } } } diff --git a/ConTabs.Tests/FormatTests.cs b/ConTabs.Tests/FormatTests.cs index 01a4420..4397975 100644 --- a/ConTabs.Tests/FormatTests.cs +++ b/ConTabs.Tests/FormatTests.cs @@ -54,7 +54,7 @@ public void DateTimeFieldCanBeFormattedUsingCulture(string cultureName) } [TestCase("en-GB", "£1.91")] - [TestCase("sk" , "£1,91")] + [TestCase("sk-SK", "£1,91")] public void CurrencyFieldCanBeFormatted(string culture, string expected) { // Arrange diff --git a/ConTabs/TableStretchStyles.cs b/ConTabs/TableStretchStyles.cs index a2a2389..46a30ba 100644 --- a/ConTabs/TableStretchStyles.cs +++ b/ConTabs/TableStretchStyles.cs @@ -119,6 +119,7 @@ private static int SqueezeLongStringDisplayWidths(List columns, int tota private static int SqueezeDisplayWidths(List columns, int totalWidth, int canvasWidth) { int difference = canvasWidth - totalWidth; + Console.WriteLine(difference); return difference >= 0 ? UseDefaultDisplayWidths(columns, totalWidth, canvasWidth) : StretchOrSqueezeDisplayWidths(columns, totalWidth, canvasWidth); diff --git a/ConTabsDemo-DotNetFramework/Program.cs b/ConTabsDemo-DotNetFramework/Program.cs index 4416a6e..133033b 100644 --- a/ConTabsDemo-DotNetFramework/Program.cs +++ b/ConTabsDemo-DotNetFramework/Program.cs @@ -16,7 +16,7 @@ static void Main(string[] args) // Create a table object var table = Table.Create(Data); - + /* * * Everything that follows is optional. @@ -53,8 +53,14 @@ static void Main(string[] args) // Add some padding table.Padding = new Padding(1, 1); + + // Center the outputted table table.TableAlignment = Alignment.Center; + // Just for testing purposes + table.CanvasWidth = 40; + table.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; + // Finally, spit out the finished table Console.WriteLine(table); @@ -62,4 +68,4 @@ static void Main(string[] args) Console.ReadLine(); } } -} +} \ No newline at end of file From 5d54d4e55cdd8984e0122ee33b9a4d1c67201931 Mon Sep 17 00:00:00 2001 From: Marek Zitnansky Date: Wed, 26 Dec 2018 17:44:26 +0100 Subject: [PATCH 26/36] Setting display width to internal, not to be changed by users --- ConTabs/LongStringBehaviour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConTabs/LongStringBehaviour.cs b/ConTabs/LongStringBehaviour.cs index 06c1a21..18f70bc 100644 --- a/ConTabs/LongStringBehaviour.cs +++ b/ConTabs/LongStringBehaviour.cs @@ -28,7 +28,7 @@ public int Width /// /// A property to store the correct width of column to be displayed /// - public int DisplayWidth { get; set; } + internal int DisplayWidth { get; set; } /// /// The ellipsis to use when the behvaiour is set to TruncateWithEliipsis From ef5c8497f7c147da801e35c28e5affdc3e8cc943 Mon Sep 17 00:00:00 2001 From: tdwright Date: Tue, 15 Jan 2019 20:16:39 +1100 Subject: [PATCH 27/36] Package metadata for the .net standard 2.0 preview --- ConTabs/ConTabs.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ConTabs/ConTabs.csproj b/ConTabs/ConTabs.csproj index 88dc859..7053dd1 100644 --- a/ConTabs/ConTabs.csproj +++ b/ConTabs/ConTabs.csproj @@ -3,11 +3,11 @@ netstandard2.0 ConTabs.tdwright tdwright - 2.0.0-preview1 + 2.0.0-netstandard2-preview1 2.0.0-preview1 Simple yet flexible ascii tables for your console output. false - First preview of version 2.0.0 of ConTabs + Special preview of version 2.0.0 of ConTabs targeting .Net Standard 2.0 Copyright 2018 (c) tdwright. All rights reserved. See license. ascii tables console https://github.com/tdwright/contabs From 98590d30e4f46d80b93a02ad6f25620d5da333a5 Mon Sep 17 00:00:00 2001 From: Marek Zitnansky Date: Sat, 9 Feb 2019 17:04:36 +0100 Subject: [PATCH 28/36] Adding support for CanvasWidth = 0 and setting it to default, adding unit tests to confirm it works --- ConTabs.Tests/ColumnTests.cs | 8 +-- ConTabs.Tests/ConformanceTests.cs | 44 +++++++++++++++-- ConTabs/Column.cs | 6 +++ ConTabs/OutputBuilder.cs | 68 ++++++++++++++------------ ConTabs/Table.cs | 6 +-- ConTabs/TableStretchStyles.cs | 14 +++--- ConTabsDemo-DotNetCore/Program.cs | 4 +- ConTabsDemo-DotNetFramework/Program.cs | 4 +- 8 files changed, 103 insertions(+), 51 deletions(-) diff --git a/ConTabs.Tests/ColumnTests.cs b/ConTabs.Tests/ColumnTests.cs index 45edb53..757b9bc 100644 --- a/ConTabs.Tests/ColumnTests.cs +++ b/ConTabs.Tests/ColumnTests.cs @@ -27,7 +27,7 @@ public void MaxWidthWithNullValuesReturnsColumnNameLength() var column = new Column(typeof(string), "StringColumn"); //Act - var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column); + var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column, 0); //Assert result.ShouldBe(12); @@ -41,7 +41,7 @@ public void MaxWidthWithZeroValuesReturnsColumnNameLength() column.Values = new List(); //Act - var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column); + var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column, 0); //Assert result.ShouldBe(12); @@ -57,7 +57,7 @@ public void MaxWidthWithLongStringBehaviourReturnsLongStringBehaviourWidth() column.LongStringBehaviour.Width = 15; //Act - var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column); + var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column, 0); //Assert result.ShouldBe(15); @@ -71,7 +71,7 @@ public void MaxWidthWithValuesReturnsLengthOfLongestValue() column.Values = new List() { "one", "two", "three", "four" }; //Act - var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column); + var result = TableStretchStyles.DoNothing.CalculateOptimalWidth(column, 0); //Assert result.ShouldBe(5); diff --git a/ConTabs.Tests/ConformanceTests.cs b/ConTabs.Tests/ConformanceTests.cs index ed81b02..aba318c 100644 --- a/ConTabs.Tests/ConformanceTests.cs +++ b/ConTabs.Tests/ConformanceTests.cs @@ -667,7 +667,7 @@ public void BasicTableWithCenterHeaderAlignmentShouldLookLikeThis() } [Test] - public void BasicTableShouldByDefaultHideOverlappingColumn() + public void BasicTableShouldByDefaultSuppressOverlappingColumn() { // Arrange Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); @@ -689,7 +689,7 @@ public void BasicTableShouldByDefaultHideOverlappingColumn() } [Test] - public void BasicTableShouldByDefaultHideOverlappingColumnButNotLeaveItAsHidden() + public void BasicTableShouldByDefaultSuppressOverlappingColumnButNotLeaveItAsSuppressed() { // Arrange Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); @@ -701,7 +701,7 @@ public void BasicTableShouldByDefaultHideOverlappingColumnButNotLeaveItAsHidden( var tableString = tableObj.ToString(); // Assert - tableObj.Columns[3].Hide.ShouldBe(false); + tableObj.Columns[3].Suppressed.ShouldBe(false); } [TestCase(0)] @@ -742,6 +742,44 @@ public void BasicTableWithoutStretchShouldNotBeStretched(int stylesWithoutStretc tableString.ShouldBe(expected); } + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + [TestCase(4)] + [TestCase(5)] + public void BasicTableWithoutCanvasWidthShouldIgnoreTableStretchStyles(int tableStretchStyles) + { + // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); + var listOfTestClasses = DataProvider.ListOfTestData(1); + var tableObj = Table.Create(listOfTestClasses); + tableObj.Columns[3].Hide = true; // hide date field + + // Act + tableObj.CanvasWidth = 0; + switch (tableStretchStyles) + { + case 0: tableObj.TableStretchStyles = TableStretchStyles.DoNothing; break; + case 1: tableObj.TableStretchStyles = TableStretchStyles.EvenColumnWidth; break; + case 2: tableObj.TableStretchStyles = TableStretchStyles.StretchOrSqueezeAllColumnsEvenly; break; + case 3: tableObj.TableStretchStyles = TableStretchStyles.SqueezeAllColumnsEvenly; break; + case 4: tableObj.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; break; + case 5: tableObj.TableStretchStyles = TableStretchStyles.SqueezeLongStrings; break; + default: break; + } + var tableString = tableObj.ToString(); + + // Assert + string expected = ""; + expected += "+--------------+-----------+----------------+" + Environment.NewLine; + expected += "| StringColumn | IntColumn | CurrencyColumn |" + Environment.NewLine; + expected += "+--------------+-----------+----------------+" + Environment.NewLine; + expected += "| AAAA | 999 | 19.95 |" + Environment.NewLine; + expected += "+--------------+-----------+----------------+"; + tableString.ShouldBe(expected); + } + [Test] public void BasicTableWithEvenColumnWidthShouldLookLikeThis() { diff --git a/ConTabs/Column.cs b/ConTabs/Column.cs index d12ca6c..9c6d9d8 100644 --- a/ConTabs/Column.cs +++ b/ConTabs/Column.cs @@ -38,6 +38,11 @@ public class Column /// public bool Hide { get; set; } + /// + /// A control to suppress the columns if they would not fit on a canvas + /// + public bool Suppressed { get; internal set; } + /// /// The method the column uses to display long strings /// @@ -63,6 +68,7 @@ public Column(Type type, string name) PropertyName = name; ColumnName = name; toStringMethod = GetToStringMethod(); + Suppressed = false; } public string StringValForCol(Object o) diff --git a/ConTabs/OutputBuilder.cs b/ConTabs/OutputBuilder.cs index c422b1e..539013c 100644 --- a/ConTabs/OutputBuilder.cs +++ b/ConTabs/OutputBuilder.cs @@ -29,15 +29,19 @@ private OutputBuilder(Table t, Style s) style = s; sb = new StringBuilder(); - List hiddenCols = new List(); - int tableWidth = GetOptimalTableWidth(hiddenCols); + int tableWidth = GetOptimalTableWidth(); - leftOffset = - table.TableAlignment.Equals(Alignment.Right) ? table.CanvasWidth - tableWidth : - table.TableAlignment.Equals(Alignment.Center) ? (table.CanvasWidth - tableWidth) / 2 : - 0; + if (table.CanvasWidth > 0) + { + leftOffset = + table.TableAlignment.Equals(Alignment.Right) ? table.CanvasWidth - tableWidth : + table.TableAlignment.Equals(Alignment.Center) ? (table.CanvasWidth - tableWidth) / 2 : + 0; + + sb.Append(new string(' ', leftOffset)); + } + else leftOffset = 0; - sb.Append(new string(' ', leftOffset)); HLine(TopMidBot.Top); InsertVerticalPadding(table.Padding.Top, style.Wall); NewLine(); Headers(); @@ -61,9 +65,11 @@ private OutputBuilder(Table t, Style s) } HLine(TopMidBot.Bot); - foreach (var col in hiddenCols) + //release suppressed + var suppressedCols = table.Columns.Where(c => c.Suppressed); + foreach (var col in suppressedCols) { - col.Hide = false; + col.Suppressed = false; } } @@ -73,11 +79,11 @@ private void InsertVerticalPadding(byte padding, char columnSeparator) { NewLine(); sb.Append(style.Wall); - for (int i = 0; i < table._colsShown.Count; i++) + for (int i = 0; i < table.ColsShown.Count; i++) { - sb.Append(new string(' ', table._colsShown[i].LongStringBehaviour.DisplayWidth + table.Padding.GetHorizontalPadding())); + sb.Append(new string(' ', table.ColsShown[i].LongStringBehaviour.DisplayWidth + table.Padding.GetHorizontalPadding())); - if (i < table._colsShown.Count - 1) + if (i < table.ColsShown.Count - 1) { sb.Append(columnSeparator); } @@ -96,11 +102,11 @@ private void HLine(TopMidBot v) { sb.Append(GetCorner(v, LeftCentreRight.Left)); - for (int i = 0; i < table._colsShown.Count; i++) + for (int i = 0; i < table.ColsShown.Count; i++) { - sb.Append(new string(style.Floor, table._colsShown[i].LongStringBehaviour.DisplayWidth + table.Padding.GetHorizontalPadding())); + sb.Append(new string(style.Floor, table.ColsShown[i].LongStringBehaviour.DisplayWidth + table.Padding.GetHorizontalPadding())); - if (i < table._colsShown.Count - 1) + if (i < table.ColsShown.Count - 1) { sb.Append(GetCorner(v, LeftCentreRight.Centre)); } @@ -111,8 +117,8 @@ private void HLine(TopMidBot v) private void NoDataLine() { var noDataText = "no data"; - int colWidths = table._colsShown.Sum(c => c.LongStringBehaviour.DisplayWidth); - int innerWidth = colWidths + (table._colsShown.Count * table.Padding.GetHorizontalPadding()) + table._colsShown.Count - 1; + int colWidths = table.ColsShown.Sum(c => c.LongStringBehaviour.DisplayWidth); + int innerWidth = colWidths + (table.ColsShown.Count * table.Padding.GetHorizontalPadding()) + table.ColsShown.Count - 1; int leftPad = (innerWidth - noDataText.Length) / 2; int rightPad = innerWidth - (leftPad + noDataText.Length); sb.Append(style.Wall + new String(' ', leftPad) + noDataText + new string(' ', rightPad) + style.Wall); @@ -121,7 +127,7 @@ private void NoDataLine() private void Headers() { sb.Append(style.Wall); - foreach (var col in table._colsShown) + foreach (var col in table.ColsShown) { sb.Append(GetPaddingString(table.Padding.Left) + table.HeaderAlignment.ProcessString(col.ColumnName, col.LongStringBehaviour.DisplayWidth) + GetPaddingString(table.Padding.Right) + style.Wall); } @@ -129,7 +135,7 @@ private void Headers() private void DataRow(int i) { - var cols = table._colsShown.Select(c => new CellParts(c.StringValForCol(c.Values[i]), c.LongStringBehaviour.DisplayWidth, c.Alignment)).ToList(); + var cols = table.ColsShown.Select(c => new CellParts(c.StringValForCol(c.Values[i]), c.LongStringBehaviour.DisplayWidth, c.Alignment)).ToList(); var maxLines = cols.Max(c => c.LineCount); @@ -158,36 +164,38 @@ private void DataLine(List parts, int line) private int GetWidthOfPaddingAndBorders() { - return table._colsShown.Count * table.Padding.GetHorizontalPadding() + table._colsShown.Count + 1; + return table.ColsShown.Count * table.Padding.GetHorizontalPadding() + table.ColsShown.Count + 1; } private int GetTableWidthAfterApplyingStretchStyles() { int colWidths = 0; - foreach (Column column in table._colsShown) + foreach (Column column in table.ColsShown) { - int colWidth = table.TableStretchStyles.CalculateOptimalWidth(column); + int colWidth = table.TableStretchStyles.CalculateOptimalWidth(column, table.CanvasWidth); column.LongStringBehaviour.DisplayWidth = colWidth; colWidths += colWidth; } int realWidth = colWidths + GetWidthOfPaddingAndBorders(); - int adaptedWidth = table.TableStretchStyles.CalculateAdditionalWidth(table._colsShown, realWidth, table.CanvasWidth); + int adaptedWidth = table.TableStretchStyles.CalculateAdditionalWidth(table.ColsShown, realWidth, table.CanvasWidth); return adaptedWidth + GetWidthOfPaddingAndBorders(); } - private int GetOptimalTableWidth(List hiddenCols) + private int GetOptimalTableWidth() { int tableWidth = GetTableWidthAfterApplyingStretchStyles(); - while (table.CanvasWidth < tableWidth && table._colsShown.Count > 0) + bool recalculationNeeded = false; + + while (table.CanvasWidth > 0 && table.CanvasWidth < tableWidth && table.ColsShown.Count > 0) { - var columnToHide = table._colsShown[table._colsShown.Count - 1]; - hiddenCols.Add(columnToHide); - tableWidth -= columnToHide.LongStringBehaviour.DisplayWidth + table.Padding.GetHorizontalPadding() + 1; - columnToHide.Hide = true; + var columnToSuppress = table.ColsShown[table.ColsShown.Count - 1]; + tableWidth -= columnToSuppress.LongStringBehaviour.DisplayWidth + table.Padding.GetHorizontalPadding() + 1; + columnToSuppress.Suppressed = true; + recalculationNeeded = true; } - if (hiddenCols.Count > 0) //if we hid some of the columns, we should adapt the display widths again + if (recalculationNeeded) //if we hid some of the columns, we should adapt the display widths again { tableWidth = GetTableWidthAfterApplyingStretchStyles(); } diff --git a/ConTabs/Table.cs b/ConTabs/Table.cs index dba57a0..2ba1bd3 100644 --- a/ConTabs/Table.cs +++ b/ConTabs/Table.cs @@ -50,7 +50,7 @@ public Alignment ColumnAlignment } } - internal List _colsShown => Columns.Where(c => !c.Hide).ToList(); + internal List ColsShown => Columns.Where(c => !(c.Hide || c.Suppressed)).ToList(); /// /// The table's display properties @@ -123,7 +123,7 @@ private Table() HeaderAlignment = Alignment.Default; ColumnAlignment = Alignment.Default; TableAlignment = Alignment.Default; - CanvasWidth = Console.WindowWidth - 1; // it would word wrap + CanvasWidth = 0; TableStretchStyles = TableStretchStyles.Default; var props = GetDeclaredAndInheritedProperties(typeof(T).GetTypeInfo()); @@ -143,7 +143,7 @@ private Table() /// The table as a string public override string ToString() { - if (_colsShown.Count == 0) + if (ColsShown.Count == 0) throw new EmptyTableException(this.GetType()); return OutputBuilder.BuildOutput(this, TableStyle); diff --git a/ConTabs/TableStretchStyles.cs b/ConTabs/TableStretchStyles.cs index 46a30ba..81be3cb 100644 --- a/ConTabs/TableStretchStyles.cs +++ b/ConTabs/TableStretchStyles.cs @@ -11,7 +11,7 @@ public class TableStretchStyles { private static readonly int MIN_WIDTH = 2; //if set to 1, word wrap in long strings would fail - public Func CalculateOptimalWidth { get; set; } + public Func CalculateOptimalWidth { get; set; } public Func, int, int, int> CalculateAdditionalWidth { get; set; } /// @@ -53,7 +53,7 @@ private static int UseDefaultDisplayWidths(List columns, int totalWidth, { for (int i = 0; i < columns.Count; i++) { - columns[i].LongStringBehaviour.DisplayWidth = GetOptimalColumnWidth(columns[i]); + columns[i].LongStringBehaviour.DisplayWidth = GetOptimalColumnWidth(columns[i], canvasWidth); } return columns .Select(v => v.LongStringBehaviour.DisplayWidth) @@ -62,7 +62,7 @@ private static int UseDefaultDisplayWidths(List columns, int totalWidth, private static int StretchOrSqueezeDisplayWidths(List columns, int totalWidth, int canvasWidth) { - int difference = canvasWidth - totalWidth; + int difference = canvasWidth > 0 ? canvasWidth - totalWidth : 0; if (difference > 0) { for (int i = 0; i < columns.Count; i++) @@ -118,14 +118,14 @@ private static int SqueezeLongStringDisplayWidths(List columns, int tota private static int SqueezeDisplayWidths(List columns, int totalWidth, int canvasWidth) { - int difference = canvasWidth - totalWidth; + int difference = canvasWidth > 0 ? canvasWidth - totalWidth : 0; Console.WriteLine(difference); return difference >= 0 ? UseDefaultDisplayWidths(columns, totalWidth, canvasWidth) : StretchOrSqueezeDisplayWidths(columns, totalWidth, canvasWidth); } - private static int GetOptimalColumnWidth(Column column) + private static int GetOptimalColumnWidth(Column column, int canvasWidth) { if (column.Values == null || column.Values.Count == 0) return column.ColumnName.Length; @@ -138,9 +138,9 @@ private static int GetOptimalColumnWidth(Column column) .Max(); } - private static int GetUniformColumnWidth(Column column) + private static int GetUniformColumnWidth(Column column, int canvasWidth) { - return byte.MinValue; + return canvasWidth > 0 ? byte.MinValue : GetOptimalColumnWidth(column, canvasWidth); } } } diff --git a/ConTabsDemo-DotNetCore/Program.cs b/ConTabsDemo-DotNetCore/Program.cs index abe946f..4ce024c 100644 --- a/ConTabsDemo-DotNetCore/Program.cs +++ b/ConTabsDemo-DotNetCore/Program.cs @@ -57,8 +57,8 @@ static void Main(string[] args) // Center the outputted table table.TableAlignment = Alignment.Center; - // Just for testing purposes - table.CanvasWidth = 40; + // Stretch long string columns to fit console + table.CanvasWidth = Console.WindowWidth - 1; table.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; // Finally, spit out the finished table diff --git a/ConTabsDemo-DotNetFramework/Program.cs b/ConTabsDemo-DotNetFramework/Program.cs index 133033b..026b504 100644 --- a/ConTabsDemo-DotNetFramework/Program.cs +++ b/ConTabsDemo-DotNetFramework/Program.cs @@ -57,8 +57,8 @@ static void Main(string[] args) // Center the outputted table table.TableAlignment = Alignment.Center; - // Just for testing purposes - table.CanvasWidth = 40; + // Stretch long string columns to fit console + table.CanvasWidth = Console.WindowWidth - 1; table.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; // Finally, spit out the finished table From 410c750013f45bc4187922dbf6dabbb4742de5d0 Mon Sep 17 00:00:00 2001 From: Marek Zitnansky Date: Sat, 9 Feb 2019 17:07:28 +0100 Subject: [PATCH 29/36] Adding forgotten summary --- ConTabs/Table.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ConTabs/Table.cs b/ConTabs/Table.cs index 2ba1bd3..64bc151 100644 --- a/ConTabs/Table.cs +++ b/ConTabs/Table.cs @@ -85,10 +85,13 @@ public IEnumerable Data public int CanvasWidth { get; set; } /// - /// Alignment of the table in the console + /// Alignment of the table in the console. Ignored if CanvasWidth is zero or negative. /// public Alignment TableAlignment { get; set; } + /// + /// Stretching or fitting tables in the console. Uses DoNothing if CanvasWidth is zero or negative. + /// public TableStretchStyles TableStretchStyles { get; set; } /// From 7ddf787ce548a51158accb526ba06828e4874de6 Mon Sep 17 00:00:00 2001 From: tdwright Date: Tue, 19 Feb 2019 21:34:05 +1100 Subject: [PATCH 30/36] Tiny stylistic change to suit the temperament of this particular autocrat --- ConTabsDemo-DotNetCore/Program.cs | 6 +++--- ConTabsDemo-DotNetFramework/Program.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ConTabsDemo-DotNetCore/Program.cs b/ConTabsDemo-DotNetCore/Program.cs index 4ce024c..c13c16f 100644 --- a/ConTabsDemo-DotNetCore/Program.cs +++ b/ConTabsDemo-DotNetCore/Program.cs @@ -54,13 +54,13 @@ static void Main(string[] args) // Add some padding table.Padding = new Padding(1, 1); - // Center the outputted table - table.TableAlignment = Alignment.Center; - // Stretch long string columns to fit console table.CanvasWidth = Console.WindowWidth - 1; table.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; + // Center the outputted table + table.TableAlignment = Alignment.Center; + // Finally, spit out the finished table Console.WriteLine(table); diff --git a/ConTabsDemo-DotNetFramework/Program.cs b/ConTabsDemo-DotNetFramework/Program.cs index 4ac3318..1258b18 100644 --- a/ConTabsDemo-DotNetFramework/Program.cs +++ b/ConTabsDemo-DotNetFramework/Program.cs @@ -54,13 +54,13 @@ static void Main(string[] args) // Add some padding table.Padding = new Padding(1, 1); - // Center the outputted table - table.TableAlignment = Alignment.Center; - // Stretch long string columns to fit console table.CanvasWidth = Console.WindowWidth - 1; table.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; + // Center the outputted table + table.TableAlignment = Alignment.Center; + // Finally, spit out the finished table Console.WriteLine(table); From 9ca6478d0d384321c4df3eb0de67b26a5f1e86e2 Mon Sep 17 00:00:00 2001 From: tdwright Date: Tue, 19 Feb 2019 21:35:32 +1100 Subject: [PATCH 31/36] Updated package metadata for second preview of v2.0.0 --- ConTabs/ConTabs.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ConTabs/ConTabs.csproj b/ConTabs/ConTabs.csproj index f809c68..bb9f2e8 100644 --- a/ConTabs/ConTabs.csproj +++ b/ConTabs/ConTabs.csproj @@ -3,11 +3,11 @@ netstandard1.3;netstandard2.0;net45 ConTabs.tdwright tdwright - 2.0.0-preview1 - 2.0.0-preview1 + 2.0.0-preview2 + 2.0.0-preview2 Simple yet flexible ascii tables for your console output. false - First preview of version 2.0.0 of ConTabs + Second preview of version 2.0.0 of ConTabs, featuring table-level width and alignment controls Copyright 2018 (c) tdwright. All rights reserved. See license. ascii tables console https://github.com/tdwright/contabs From 015c726a5a4748c0314ec3a075c352ceeec1813c Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Sun, 17 Apr 2022 19:32:30 +0100 Subject: [PATCH 32/36] Added demo app for targetting .NET 5 --- ConTabs.sln | 12 +++- .../ConTabsDemo-DotNet5.csproj | 14 ++++ ConTabsDemo-DotNet5/Program.cs | 71 +++++++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 ConTabsDemo-DotNet5/ConTabsDemo-DotNet5.csproj create mode 100644 ConTabsDemo-DotNet5/Program.cs diff --git a/ConTabs.sln b/ConTabs.sln index 7847bad..11f65f4 100644 --- a/ConTabs.sln +++ b/ConTabs.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32407.337 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConTabs", "ConTabs\ConTabs.csproj", "{D9A96908-33E7-4D02-B713-A894D862B5AA}" EndProject @@ -13,7 +13,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConTabs.Tests", "ConTabs.Te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConTabsTestData", "ConTabsTestData\ConTabsTestData.csproj", "{70B98506-64D7-4822-B461-0A6BC66A2656}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConTabsDemo-Attributes", "ConTabsDemo-Attributes\ConTabsDemo-Attributes.csproj", "{A68D182D-96B2-44B4-8495-99236461FD04}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConTabsDemo-Attributes", "ConTabsDemo-Attributes\ConTabsDemo-Attributes.csproj", "{A68D182D-96B2-44B4-8495-99236461FD04}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConTabsDemo-DotNet5", "ConTabsDemo-DotNet5\ConTabsDemo-DotNet5.csproj", "{DF87919E-55C0-4F6E-8FAA-6DC7A95863E2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -45,6 +47,10 @@ Global {A68D182D-96B2-44B4-8495-99236461FD04}.Debug|Any CPU.Build.0 = Debug|Any CPU {A68D182D-96B2-44B4-8495-99236461FD04}.Release|Any CPU.ActiveCfg = Release|Any CPU {A68D182D-96B2-44B4-8495-99236461FD04}.Release|Any CPU.Build.0 = Release|Any CPU + {DF87919E-55C0-4F6E-8FAA-6DC7A95863E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF87919E-55C0-4F6E-8FAA-6DC7A95863E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF87919E-55C0-4F6E-8FAA-6DC7A95863E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF87919E-55C0-4F6E-8FAA-6DC7A95863E2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ConTabsDemo-DotNet5/ConTabsDemo-DotNet5.csproj b/ConTabsDemo-DotNet5/ConTabsDemo-DotNet5.csproj new file mode 100644 index 0000000..1e563f7 --- /dev/null +++ b/ConTabsDemo-DotNet5/ConTabsDemo-DotNet5.csproj @@ -0,0 +1,14 @@ + + + + Exe + net5.0 + ConTabsDemo_DotNet5 + + + + + + + + diff --git a/ConTabsDemo-DotNet5/Program.cs b/ConTabsDemo-DotNet5/Program.cs new file mode 100644 index 0000000..0f6cac1 --- /dev/null +++ b/ConTabsDemo-DotNet5/Program.cs @@ -0,0 +1,71 @@ +using System; +using ConTabs; +using ConTabs.TestData; +using System.Text; + +namespace ConTabsDemo_DotNetFramework +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("CONTABS .NET 5 DEMO"); + + // Get some data (can be an IEnumerable of anything) + var Data = DemoDataProvider.ListOfDemoData(); + + // Create a table object + var table = Table.Create(Data); + + /* + * + * Everything that follows is optional. + * You could just skip to a Console.Writeline here. + * + */ + + // Set the style (and enable Unicode for the console) + table.TableStyle = Style.UnicodeArcs; + Console.OutputEncoding = Encoding.Unicode; + + // Hide the diameter column + table.Columns["Diameter"].Hide = true; + + // Add a computed column for the radius + table.Columns.AddGeneratedColumn(d => d / 2, "Radius", table.Columns["Diameter"]); + + // Move the orbital period column next to the name + table.Columns.MoveColumn("OrbitalPeriod", 1); + + // Rename the orbital period column + table.Columns["OrbitalPeriod"].ColumnName = "Year length"; + + // Add a format string to orbital period + table.Columns["Year length"].FormatString = "# days"; + + // Right-align the distance from sun column (and format it nicely) + table.Columns["DistanceFromSun"].Alignment = Alignment.Right; + table.Columns["DistanceFromSun"].FormatString = "###,###,###0 km"; + + // Handle the length of the fact + table.Columns["Fact"].LongStringBehaviour = LongStringBehaviour.Wrap; + table.Columns["Fact"].LongStringBehaviour.Width = 25; + + // Add some padding + table.Padding = new Padding(1, 1); + + // Stretch long string columns to fit console + table.CanvasWidth = Console.WindowWidth - 1; + table.TableStretchStyles = TableStretchStyles.StretchOrSqueezeLongStrings; + + // Center the outputted table + table.TableAlignment = Alignment.Center; + + // Finally, spit out the finished table + Console.WriteLine(table); + + Console.WriteLine("Press return to exit..."); + Console.ReadLine(); + } + } +} \ No newline at end of file From c4b2f58f6a101fe7818cca61821b6d8833c3227b Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Sun, 17 Apr 2022 19:36:08 +0100 Subject: [PATCH 33/36] Updated Appveyor image to support .NET 5 builds --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 0701244..9bf8baf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ version: 1.0.{build} -image: Visual Studio 2017 +image: Visual Studio 2019 Preview before_build: - cmd: nuget restore From 0ba4b04bf9b4c3279718368e93c0b6a825857a4c Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Mon, 18 Apr 2022 18:31:15 +0100 Subject: [PATCH 34/36] Basic implementation of a flexible bar chart method --- ConTabs.Tests/BarChartTests.cs | 179 +++++++++++++++++++++++++++++ ConTabs.Tests/ConTabs.Tests.csproj | 1 + ConTabs/Columns.cs | 31 +++++ 3 files changed, 211 insertions(+) create mode 100644 ConTabs.Tests/BarChartTests.cs diff --git a/ConTabs.Tests/BarChartTests.cs b/ConTabs.Tests/BarChartTests.cs new file mode 100644 index 0000000..2ee3b9e --- /dev/null +++ b/ConTabs.Tests/BarChartTests.cs @@ -0,0 +1,179 @@ +using ConTabs.TestData; +using NUnit.Framework; +using Shouldly; +using System; + +namespace ConTabs.Tests +{ + [TestFixture] + public class BarChartTests + { + [Test] + public void GenerateBarChart_ResultsInNewColumnAdded() + { + // Arrange + var data = DataProvider.ListOfMinimalData(); + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"]); + + // Assert + table.Columns.Count.ShouldBe(3); + } + + [Test] + public void GenerateBarChart_ResultsInStringColumnAdded() + { + // Arrange + var data = DataProvider.ListOfMinimalData(); + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"]); + + // Assert + table.Columns[2].SourceType.ShouldBe(typeof(string)); + } + + [Test] + public void GenerateBarChart_WithDefaultParams_BarsAreExpectedSize() + { + // Arrange + var data = DataProvider.ListOfMinimalData(3); // IntB = 3, 9, 27 + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"]); // Max = 27, default width = 25, unit scale = 25/27 = 0.92592... + + // Assert + table.Columns[2].Values[0].ShouldBe("###"); // 0.92592 * 3 = 2.77 = 3 + table.Columns[2].Values[1].ShouldBe("########"); // 0.92592 * 9 = 8.33 = 8 + table.Columns[2].Values[2].ShouldBe("#########################"); // 0.92592 * 27 = 25.00 = 25 + } + + [Test] + public void GenerateBarChart_MaxWidthCanBeSpecified() + { + // Arrange + var data = DataProvider.ListOfMinimalData(1); // single row, IntB = 3 + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"], maxLength: 3); + + // Assert + table.Columns[2].Values[0].ShouldBe("###"); + } + + [Test] + public void GenerateBarChart_ForSingleValues_ResultsInFullWidthBar() + { + // Arrange + var data = DataProvider.ListOfMinimalData(1); // single row, IntB = 3 + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"], maxLength: 10); + + // Assert + table.Columns[2].Values[0].ShouldBe("##########"); // count = 10 = maxLength + } + + [Test] + public void GenerateBarChart_UnitCharCanBeChanged() + { + // Arrange + var data = DataProvider.ListOfMinimalData(1); // single row, IntB = 3 + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"], maxLength:3, unitChar: '>'); + + // Assert + table.Columns[2].Values[0].ShouldBe(">>>"); + } + + [Test] + public void GenerateBarChart_ScaleCanBeSpecified() + { + // Arrange + var data = DataProvider.ListOfMinimalData(1); // single row, IntB = 3 + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"], unitSize: 3); + + // Assert + table.Columns[2].Values[0].ShouldBe("#"); // one unit of size 3, for a value of 3 + } + + [Test] + public void GenerateBarChart_WhenBothScaleAndMaxWidthAreSpecified_ScaleTakesPrecedence() + { + // Arrange + var data = DataProvider.ListOfMinimalData(1); // single row, IntB = 3 + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"], unitSize: 3, maxLength: 9); + + // Assert + table.Columns[2].Values[0].ShouldBe("#"); // one unit of size 3, for a value of 3, rather than 100% of maxLength + } + + [Test] + public void GenerateBarChart_WhenBothScaleAndMaxWidthAreSpecified_NeverExceedsMax() + { + // Arrange + var data = DataProvider.ListOfMinimalData(3); // single row, IntB = 3 + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"], unitSize: 3, maxLength: 5); + + // Assert + table.Columns[2].Values[0].ShouldBe("#"); + table.Columns[2].Values[1].ShouldBe("###"); + table.Columns[2].Values[2].ShouldBe("#####"); // capped at 5 instead of value 27 / scale 3 = 9 units + } + + [Test] + public void GenerateBarChart_NegativeValuesGetHandledGracefully() + { + // Arrange + var data = DataProvider.ListOfMinimalData(1); // single row, IntB = 3 + data[0].IntB = -1; + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"]); + + // Assert + table.Columns[2].Values[0].ShouldBe(""); + } + + [Test] + public void GenerateBarChart_ConformanceTest() + { + // Arrange + var data = DataProvider.ListOfMinimalData(3); // IntB = 3, 9, 27 + var table = Table.Create(data); + + // Act + table.Columns.AddBarChart("Chart", table.Columns["IntB"], maxLength: 10, unitChar: '='); + + // Assert + string expected = ""; + expected += "+------+------+------------+" + Environment.NewLine; + expected += "| IntA | IntB | Chart |" + Environment.NewLine; + expected += "+------+------+------------+" + Environment.NewLine; + expected += "| 1 | 3 | = |" + Environment.NewLine; + expected += "| 2 | 9 | === |" + Environment.NewLine; + expected += "| 3 | 27 | ========== |" + Environment.NewLine; + expected += "+------+------+------------+"; + + table.ToString().ShouldBe(expected); + } + } +} \ No newline at end of file diff --git a/ConTabs.Tests/ConTabs.Tests.csproj b/ConTabs.Tests/ConTabs.Tests.csproj index cca12bf..2428e9f 100644 --- a/ConTabs.Tests/ConTabs.Tests.csproj +++ b/ConTabs.Tests/ConTabs.Tests.csproj @@ -61,6 +61,7 @@ + diff --git a/ConTabs/Columns.cs b/ConTabs/Columns.cs index 7deb425..f52320e 100644 --- a/ConTabs/Columns.cs +++ b/ConTabs/Columns.cs @@ -192,5 +192,36 @@ public void AddGeneratedColumnFromRange(Func, TOutput> exp Add(new Column(typeof(TOutput), columnName) { Values = results }); } + + public void AddBarChart (string columnName, Column sourceColumn, char unitChar = '#', double? unitSize = null, int maxLength = 25) where TInput : unmanaged, IComparable + { + if (unitSize is null) + { + var max = sourceColumn.Values.Select(v => Convert.ToDouble(v)).Max(); + unitSize = max / maxLength; + } + + AddGeneratedColumn( + d => + { + var d_casted = Convert.ToDouble(d); + var size = (int)Math.Round(d_casted / unitSize.Value); + + if (size < 0 || d_casted < 0) + { + size = 0; + } + + if (size > maxLength) + { + size = maxLength; + } + + return new string(unitChar, size); + }, + columnName, + sourceColumn + ); + } } } \ No newline at end of file From 374bf45e762eacd1ef9e3f8f0375c32d53fea511 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Mon, 18 Apr 2022 18:38:44 +0100 Subject: [PATCH 35/36] Added XML documentation for the new method --- ConTabs/Columns.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ConTabs/Columns.cs b/ConTabs/Columns.cs index f52320e..0ca9e64 100644 --- a/ConTabs/Columns.cs +++ b/ConTabs/Columns.cs @@ -193,6 +193,15 @@ public void AddGeneratedColumnFromRange(Func, TOutput> exp Add(new Column(typeof(TOutput), columnName) { Values = results }); } + /// + /// Adds a special bar chart column based on another numeric column. + /// + /// Type of source column (should be numeric) + /// The name of the new column + /// The column from which to derive the bar chart + /// The character used to build the bar (Optional; default = '#') + /// The value of each unit (Optional; defaults to being dynamic based on the max value) + /// The maximum width of a bar (Optional; defaults to 25) public void AddBarChart (string columnName, Column sourceColumn, char unitChar = '#', double? unitSize = null, int maxLength = 25) where TInput : unmanaged, IComparable { if (unitSize is null) From 855709d633ee445febf53f24c8b00dcabfd55e95 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Mon, 18 Apr 2022 18:43:08 +0100 Subject: [PATCH 36/36] Improved generic type constraints, with link to source --- ConTabs/Columns.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ConTabs/Columns.cs b/ConTabs/Columns.cs index 0ca9e64..82e97c8 100644 --- a/ConTabs/Columns.cs +++ b/ConTabs/Columns.cs @@ -202,7 +202,8 @@ public void AddGeneratedColumnFromRange(Func, TOutput> exp /// The character used to build the bar (Optional; default = '#') /// The value of each unit (Optional; defaults to being dynamic based on the max value) /// The maximum width of a bar (Optional; defaults to 25) - public void AddBarChart (string columnName, Column sourceColumn, char unitChar = '#', double? unitSize = null, int maxLength = 25) where TInput : unmanaged, IComparable + public void AddBarChart (string columnName, Column sourceColumn, char unitChar = '#', double? unitSize = null, int maxLength = 25) + where TInput : unmanaged, IComparable, IEquatable // from https://stackoverflow.com/a/60022011/50151 (better support coming in future versions of .NET) { if (unitSize is null) {