mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-10-31 17:15:28 +08:00 
			
		
		
		
	 Patrik Svensson
					Patrik Svensson
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						parent
						
							57731c0d55
						
					
				
				
					commit
					e169df6303
				
			
							
								
								
									
										2
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -89,7 +89,7 @@ jobs: | ||||
|         shell: bash | ||||
|         run: | | ||||
|           dotnet tool restore | ||||
|           dotnet example --all | ||||
|           dotnet example --all --skip live --skip livetable --skip prompt | ||||
|  | ||||
|       - name: Build | ||||
|         shell: bash | ||||
|   | ||||
| @@ -15,7 +15,7 @@ | ||||
|       ] | ||||
|     }, | ||||
|     "dotnet-example": { | ||||
|       "version": "1.3.1", | ||||
|       "version": "1.5.0", | ||||
|       "commands": [ | ||||
|         "dotnet-example" | ||||
|       ] | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
|     <TargetFramework>net5.0</TargetFramework> | ||||
|     <ExampleTitle>Live</ExampleTitle> | ||||
|     <ExampleDescription>Demonstrates how to do live updates.</ExampleDescription> | ||||
|     <ExampleGroup>Misc</ExampleGroup> | ||||
|     <ExampleGroup>Live</ExampleGroup> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|   | ||||
							
								
								
									
										15
									
								
								examples/Console/LiveTable/LiveTable.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								examples/Console/LiveTable/LiveTable.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <OutputType>Exe</OutputType> | ||||
|     <TargetFramework>net5.0</TargetFramework> | ||||
|     <ExampleTitle>LiveTable</ExampleTitle> | ||||
|     <ExampleDescription>Demonstrates how to do live updates in a table.</ExampleDescription> | ||||
|     <ExampleGroup>Live</ExampleGroup> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\..\Shared\Shared.csproj" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
| </Project> | ||||
							
								
								
									
										86
									
								
								examples/Console/LiveTable/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								examples/Console/LiveTable/Program.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| using System; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Spectre.Console.Examples | ||||
| { | ||||
|     public static class Program | ||||
|     { | ||||
|         private const int NumberOfRows = 10; | ||||
|  | ||||
|         private static readonly Random _random = new(); | ||||
|         private static readonly string[] _exchanges = new string[] | ||||
|         { | ||||
|             "SGD", "SEK", "PLN", | ||||
|             "MYR", "EUR", "USD", | ||||
|             "AUD", "JPY", "CNH", | ||||
|             "HKD", "CAD", "INR", | ||||
|             "DKK", "GBP", "RUB", | ||||
|             "NZD", "MXN", "IDR", | ||||
|             "TWD", "THB", "VND", | ||||
|         }; | ||||
|  | ||||
|         public static async Task Main(string[] args) | ||||
|         { | ||||
|             var table = new Table().Expand().BorderColor(Color.Grey); | ||||
|             table.AddColumn("[yellow]Source currency[/]"); | ||||
|             table.AddColumn("[yellow]Destination currency[/]"); | ||||
|             table.AddColumn("[yellow]Exchange rate[/]"); | ||||
|  | ||||
|             AnsiConsole.MarkupLine("Press [yellow]CTRL+C[/] to exit"); | ||||
|  | ||||
|             await AnsiConsole.Live(table) | ||||
|                 .AutoClear(false) | ||||
|                 .Overflow(VerticalOverflow.Ellipsis) | ||||
|                 .Cropping(VerticalOverflowCropping.Bottom) | ||||
|                 .StartAsync(async ctx => | ||||
|                 { | ||||
|                     // Add some initial rows | ||||
|                     foreach (var _ in Enumerable.Range(0, NumberOfRows)) | ||||
|                     { | ||||
|                         AddExchangeRateRow(table); | ||||
|                     } | ||||
|  | ||||
|                     // Continously update the table | ||||
|                     while (true) | ||||
|                     { | ||||
|                         // More rows than we want? | ||||
|                         if (table.Rows.Count > NumberOfRows) | ||||
|                         { | ||||
|                             // Remove the first one | ||||
|                             table.Rows.RemoveAt(0); | ||||
|                         } | ||||
|  | ||||
|                         // Add a new row | ||||
|                         AddExchangeRateRow(table); | ||||
|  | ||||
|                         // Refresh and wait for a while | ||||
|                         ctx.Refresh(); | ||||
|                         await Task.Delay(400); | ||||
|                     } | ||||
|                 }); | ||||
|         } | ||||
|  | ||||
|         private static void AddExchangeRateRow(Table table) | ||||
|         { | ||||
|             var (source, destination, rate) = GetExchangeRate(); | ||||
|             table.AddRow( | ||||
|                 source, destination, | ||||
|                 _random.NextDouble() > 0.35D ? $"[green]{rate}[/]" : $"[red]{rate}[/]"); | ||||
|         } | ||||
|  | ||||
|         private static (string Source, string Destination, double Rate) GetExchangeRate() | ||||
|         { | ||||
|             var source = _exchanges[_random.Next(0, _exchanges.Length)]; | ||||
|             var dest = _exchanges[_random.Next(0, _exchanges.Length)]; | ||||
|             var rate = 200 / ((_random.NextDouble() * 320) + 1); | ||||
|  | ||||
|             while (source == dest) | ||||
|             { | ||||
|                 dest = _exchanges[_random.Next(0, _exchanges.Length)]; | ||||
|             } | ||||
|  | ||||
|             return (source, dest, rate); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -63,6 +63,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tables", "Console\Tables\Ta | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trees", "Console\Trees\Trees.csproj", "{2BD88288-E05D-4978-B045-17937078E63C}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiveTable", "Console\LiveTable\LiveTable.csproj", "{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Any CPU = Debug|Any CPU | ||||
| @@ -409,6 +411,18 @@ Global | ||||
| 		{2BD88288-E05D-4978-B045-17937078E63C}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{2BD88288-E05D-4978-B045-17937078E63C}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{2BD88288-E05D-4978-B045-17937078E63C}.Release|x86.Build.0 = Release|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x64.Build.0 = Debug|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x86.ActiveCfg = Debug|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x86.Build.0 = Debug|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x64.ActiveCfg = Release|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x86.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Spectre.Console.Rendering; | ||||
|  | ||||
| @@ -35,6 +36,28 @@ namespace Spectre.Console | ||||
|             return table; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds a row to the table. | ||||
|         /// </summary> | ||||
|         /// <param name="table">The table to add the row to.</param> | ||||
|         /// <param name="columns">The row columns to add.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static Table AddRow(this Table table, IEnumerable<IRenderable> columns) | ||||
|         { | ||||
|             if (table is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(table)); | ||||
|             } | ||||
|  | ||||
|             if (columns is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(columns)); | ||||
|             } | ||||
|  | ||||
|             table.Rows.Add(new TableRow(columns)); | ||||
|             return table; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds a row to the table. | ||||
|         /// </summary> | ||||
| @@ -48,7 +71,7 @@ namespace Spectre.Console | ||||
|                 throw new ArgumentNullException(nameof(table)); | ||||
|             } | ||||
|  | ||||
|             return table.AddRow(columns); | ||||
|             return table.AddRow((IEnumerable<IRenderable>)columns); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
| @@ -143,6 +166,80 @@ namespace Spectre.Console | ||||
|             return table; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Inserts a row in the table at the specified index. | ||||
|         /// </summary> | ||||
|         /// <param name="table">The table to add the row to.</param> | ||||
|         /// <param name="index">The index to insert the row at.</param> | ||||
|         /// <param name="columns">The row columns to add.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static Table InsertRow(this Table table, int index, IEnumerable<IRenderable> columns) | ||||
|         { | ||||
|             if (table is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(table)); | ||||
|             } | ||||
|  | ||||
|             if (columns is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(columns)); | ||||
|             } | ||||
|  | ||||
|             table.Rows.Insert(index, new TableRow(columns)); | ||||
|             return table; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Inserts a row in the table at the specified index. | ||||
|         /// </summary> | ||||
|         /// <param name="table">The table to add the row to.</param> | ||||
|         /// <param name="index">The index to insert the row at.</param> | ||||
|         /// <param name="columns">The row columns to add.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static Table InsertRow(this Table table, int index, params IRenderable[] columns) | ||||
|         { | ||||
|             if (table is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(table)); | ||||
|             } | ||||
|  | ||||
|             return InsertRow(table, index, (IEnumerable<IRenderable>)columns); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Inserts a row in the table at the specified index. | ||||
|         /// </summary> | ||||
|         /// <param name="table">The table to add the row to.</param> | ||||
|         /// <param name="index">The index to insert the row at.</param> | ||||
|         /// <param name="columns">The row columns to add.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static Table InsertRow(this Table table, int index, params string[] columns) | ||||
|         { | ||||
|             if (table is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(table)); | ||||
|             } | ||||
|  | ||||
|             return InsertRow(table, index, columns.Select(column => new Markup(column))); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Removes a row from the table with the specified index. | ||||
|         /// </summary> | ||||
|         /// <param name="table">The table to add the row to.</param> | ||||
|         /// <param name="index">The index to remove the row at.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static Table RemoveRow(this Table table, int index) | ||||
|         { | ||||
|             if (table is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(table)); | ||||
|             } | ||||
|  | ||||
|             table.Rows.RemoveAt(index); | ||||
|             return table; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Sets the table width. | ||||
|         /// </summary> | ||||
|   | ||||
| @@ -11,7 +11,6 @@ namespace Spectre.Console | ||||
|     public sealed class Table : Renderable, IHasTableBorder, IExpandable, IAlignable | ||||
|     { | ||||
|         private readonly List<TableColumn> _columns; | ||||
|         private readonly List<TableRow> _rows; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the table columns. | ||||
| @@ -21,7 +20,7 @@ namespace Spectre.Console | ||||
|         /// <summary> | ||||
|         /// Gets the table rows. | ||||
|         /// </summary> | ||||
|         public IReadOnlyList<TableRow> Rows => _rows; | ||||
|         public TableRowCollection Rows { get; } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public TableBorder Border { get; set; } = TableBorder.Square; | ||||
| @@ -81,7 +80,7 @@ namespace Spectre.Console | ||||
|         public Table() | ||||
|         { | ||||
|             _columns = new List<TableColumn>(); | ||||
|             _rows = new List<TableRow>(); | ||||
|             Rows = new TableRowCollection(this); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
| @@ -96,7 +95,7 @@ namespace Spectre.Console | ||||
|                 throw new ArgumentNullException(nameof(column)); | ||||
|             } | ||||
|  | ||||
|             if (_rows.Count > 0) | ||||
|             if (Rows.Count > 0) | ||||
|             { | ||||
|                 throw new InvalidOperationException("Cannot add new columns to table with existing rows."); | ||||
|             } | ||||
| @@ -105,36 +104,6 @@ namespace Spectre.Console | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds a row to the table. | ||||
|         /// </summary> | ||||
|         /// <param name="columns">The row columns to add.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public Table AddRow(IEnumerable<IRenderable> columns) | ||||
|         { | ||||
|             if (columns is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(columns)); | ||||
|             } | ||||
|  | ||||
|             var rowColumnCount = columns.GetCount(); | ||||
|             if (rowColumnCount > _columns.Count) | ||||
|             { | ||||
|                 throw new InvalidOperationException("The number of row columns are greater than the number of table columns."); | ||||
|             } | ||||
|  | ||||
|             _rows.Add(new TableRow(columns)); | ||||
|  | ||||
|             // Need to add missing columns? | ||||
|             if (rowColumnCount < _columns.Count) | ||||
|             { | ||||
|                 var diff = _columns.Count - rowColumnCount; | ||||
|                 Enumerable.Range(0, diff).ForEach(_ => _rows.Last().Add(Text.Empty)); | ||||
|             } | ||||
|  | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override Measurement Measure(RenderContext context, int maxWidth) | ||||
|         { | ||||
| @@ -190,7 +159,7 @@ namespace Spectre.Console | ||||
|             } | ||||
|  | ||||
|             // Add rows | ||||
|             rows.AddRange(_rows); | ||||
|             rows.AddRange(Rows); | ||||
|  | ||||
|             // Show footers? | ||||
|             if (ShowFooters && _columns.Any(c => c.Footer != null)) | ||||
|   | ||||
| @@ -12,6 +12,11 @@ namespace Spectre.Console | ||||
|     { | ||||
|         private readonly List<IRenderable> _items; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the number of columns in the row. | ||||
|         /// </summary> | ||||
|         public int Count => _items.Count; | ||||
|  | ||||
|         internal bool IsHeader { get; } | ||||
|         internal bool IsFooter { get; } | ||||
|  | ||||
|   | ||||
							
								
								
									
										160
									
								
								src/Spectre.Console/Widgets/Table/TableRowCollection.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								src/Spectre.Console/Widgets/Table/TableRowCollection.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | ||||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Spectre.Console.Rendering; | ||||
|  | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents a collection holding table rows. | ||||
|     /// </summary> | ||||
|     public sealed class TableRowCollection : IReadOnlyList<TableRow> | ||||
|     { | ||||
|         private readonly Table _table; | ||||
|         private readonly IList<TableRow> _list; | ||||
|         private readonly object _lock; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         TableRow IReadOnlyList<TableRow>.this[int index] | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 lock (_lock) | ||||
|                 { | ||||
|                     return _list[index]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the number of rows in the collection. | ||||
|         /// </summary> | ||||
|         public int Count | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 lock (_lock) | ||||
|                 { | ||||
|                     return _list.Count; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         internal TableRowCollection(Table table) | ||||
|         { | ||||
|             _table = table ?? throw new ArgumentNullException(nameof(table)); | ||||
|             _list = new List<TableRow>(); | ||||
|             _lock = new object(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds a new row. | ||||
|         /// </summary> | ||||
|         /// <param name="columns">The columns that are part of the row to add.</param> | ||||
|         /// <returns>The index of the added item.</returns> | ||||
|         public int Add(IEnumerable<IRenderable> columns) | ||||
|         { | ||||
|             if (columns is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(columns)); | ||||
|             } | ||||
|  | ||||
|             lock (_lock) | ||||
|             { | ||||
|                 var row = CreateRow(columns); | ||||
|                 _list.Add(row); | ||||
|                 return _list.IndexOf(row); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Inserts a new row at the specified index. | ||||
|         /// </summary> | ||||
|         /// <param name="index">The index to insert the row at.</param> | ||||
|         /// <param name="columns">The columns that are part of the row to insert.</param> | ||||
|         /// <returns>The index of the inserted item.</returns> | ||||
|         public int Insert(int index, IEnumerable<IRenderable> columns) | ||||
|         { | ||||
|             if (columns is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(columns)); | ||||
|             } | ||||
|  | ||||
|             lock (_lock) | ||||
|             { | ||||
|                 var row = CreateRow(columns); | ||||
|                 _list.Insert(index, row); | ||||
|                 return _list.IndexOf(row); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Removes a row at the specified index. | ||||
|         /// </summary> | ||||
|         /// <param name="index">The index to remove a row at.</param> | ||||
|         public void RemoveAt(int index) | ||||
|         { | ||||
|             lock (_lock) | ||||
|             { | ||||
|                 if (index < 0) | ||||
|                 { | ||||
|                     throw new IndexOutOfRangeException("Table row index cannot be negative."); | ||||
|                 } | ||||
|                 else if (index >= _list.Count) | ||||
|                 { | ||||
|                     throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table."); | ||||
|                 } | ||||
|  | ||||
|                 _list.RemoveAt(index); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Clears all rows. | ||||
|         /// </summary> | ||||
|         public void Clear() | ||||
|         { | ||||
|             lock (_lock) | ||||
|             { | ||||
|                 _list.Clear(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public IEnumerator<TableRow> GetEnumerator() | ||||
|         { | ||||
|             lock (_lock) | ||||
|             { | ||||
|                 var items = new TableRow[_list.Count]; | ||||
|                 _list.CopyTo(items, 0); | ||||
|                 return new TableRowEnumerator(items); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         IEnumerator IEnumerable.GetEnumerator() | ||||
|         { | ||||
|             return GetEnumerator(); | ||||
|         } | ||||
|  | ||||
|         private TableRow CreateRow(IEnumerable<IRenderable> columns) | ||||
|         { | ||||
|             var row = new TableRow(columns); | ||||
|  | ||||
|             if (row.Count > _table.Columns.Count) | ||||
|             { | ||||
|                 throw new InvalidOperationException("The number of row columns are greater than the number of table columns."); | ||||
|             } | ||||
|  | ||||
|             // Need to add missing columns | ||||
|             if (row.Count < _table.Columns.Count) | ||||
|             { | ||||
|                 var diff = _table.Columns.Count - row.Count; | ||||
|                 Enumerable.Range(0, diff).ForEach(_ => row.Add(Text.Empty)); | ||||
|             } | ||||
|  | ||||
|             return row; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										36
									
								
								src/Spectre.Console/Widgets/Table/TableRowEnumerator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/Spectre.Console/Widgets/Table/TableRowEnumerator.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     internal sealed class TableRowEnumerator : IEnumerator<TableRow> | ||||
|     { | ||||
|         private readonly TableRow[] _items; | ||||
|         private int _index; | ||||
|  | ||||
|         public TableRow Current => _items[_index]; | ||||
|         object? IEnumerator.Current => _items[_index]; | ||||
|  | ||||
|         public TableRowEnumerator(TableRow[] items) | ||||
|         { | ||||
|             _items = items ?? throw new ArgumentNullException(nameof(items)); | ||||
|             _index = -1; | ||||
|         } | ||||
|  | ||||
|         public void Dispose() | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         public bool MoveNext() | ||||
|         { | ||||
|             _index++; | ||||
|             return _index < _items.Length; | ||||
|         } | ||||
|  | ||||
|         public void Reset() | ||||
|         { | ||||
|             _index = -1; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| ┌───────────┐ | ||||
| │ Column #1 │ | ||||
| ├───────────┤ | ||||
| │ 1         │ | ||||
| │ 2         │ | ||||
| │ 3         │ | ||||
| └───────────┘ | ||||
| @@ -0,0 +1,7 @@ | ||||
| ┌───────────┬───────────┐ | ||||
| │ Column #1 │ Column #2 │ | ||||
| ├───────────┼───────────┤ | ||||
| │ 1         │ 1-2       │ | ||||
| │ 2         │ 2-2       │ | ||||
| │ 3         │ 3-2       │ | ||||
| └───────────┴───────────┘ | ||||
| @@ -0,0 +1,7 @@ | ||||
| ┌───────────┬───────────┐ | ||||
| │ Column #1 │ Column #2 │ | ||||
| ├───────────┼───────────┤ | ||||
| │ 1         │ 1-2       │ | ||||
| │ 2         │ 2-2       │ | ||||
| │ 3         │ 3-2       │ | ||||
| └───────────┴───────────┘ | ||||
| @@ -0,0 +1,7 @@ | ||||
| ┌───────────┬───────────┐ | ||||
| │ Column #1 │ Column #2 │ | ||||
| ├───────────┼───────────┤ | ||||
| │ 1         │ 1-2       │ | ||||
| │ 3         │ 3-2       │ | ||||
| │ 2         │ 2-2       │ | ||||
| └───────────┴───────────┘ | ||||
| @@ -0,0 +1,7 @@ | ||||
| ┌───────────┬───────────┐ | ||||
| │ Column #1 │ Column #2 │ | ||||
| ├───────────┼───────────┤ | ||||
| │ 1         │ 1-2       │ | ||||
| │ 3         │ 3-2       │ | ||||
| │ 2         │ 2-2       │ | ||||
| └───────────┴───────────┘ | ||||
| @@ -0,0 +1,6 @@ | ||||
| ┌───────────┬───────────┐ | ||||
| │ Column #1 │ Column #2 │ | ||||
| ├───────────┼───────────┤ | ||||
| │ 1         │ 1-2       │ | ||||
| │ 3         │ 3-2       │ | ||||
| └───────────┴───────────┘ | ||||
| @@ -0,0 +1,7 @@ | ||||
| ┌───────────┐ | ||||
| │ Column #1 │ | ||||
| ├───────────┤ | ||||
| │ 1         │ | ||||
| │ 3         │ | ||||
| │ 2         │ | ||||
| └───────────┘ | ||||
| @@ -0,0 +1,6 @@ | ||||
| ┌───────────┐ | ||||
| │ Column #1 │ | ||||
| ├───────────┤ | ||||
| │ 1         │ | ||||
| │ 3         │ | ||||
| └───────────┘ | ||||
| @@ -37,6 +37,7 @@ | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Folder Include="Expectations\Widgets\Table\Rows\Extensions\" /> | ||||
|     <Folder Include="Expectations\Widgets\Tree\" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,3 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Shouldly; | ||||
| using Spectre.Console.Testing; | ||||
| using Spectre.Console.Tests.Data; | ||||
|   | ||||
| @@ -0,0 +1,131 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using Spectre.Console.Testing; | ||||
| using Spectre.Verify.Extensions; | ||||
| using VerifyXunit; | ||||
| using Xunit; | ||||
|  | ||||
| namespace Spectre.Console.Tests.Unit | ||||
| { | ||||
|     [UsesVerify] | ||||
|     [ExpectationPath("Widgets/Table/Rows/Extensions")] | ||||
|     public sealed class TableRowCollectionExtensionsTests | ||||
|     { | ||||
|         [UsesVerify] | ||||
|         public sealed class TheAddRowMethod | ||||
|         { | ||||
|             [Fact] | ||||
|             [Expectation("Add", "Renderables")] | ||||
|             public Task Should_Add_Renderables() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole(); | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.AddColumn("Column #2"); | ||||
|                 table.AddRow(new[] { new Text("1"), new Text("1-2") }); | ||||
|                 table.AddRow(new[] { new Text("2"), new Text("2-2") }); | ||||
|                 table.AddRow(new[] { new Text("3"), new Text("3-2") }); | ||||
|  | ||||
|                 // When | ||||
|                 console.Write(table); | ||||
|  | ||||
|                 // Then | ||||
|                 return Verifier.Verify(console.Output); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             [Expectation("Add", "Strings")] | ||||
|             public Task Should_Add_Strings() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole(); | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.AddColumn("Column #2"); | ||||
|                 table.AddRow("1", "1-2"); | ||||
|                 table.AddRow("2", "2-2"); | ||||
|                 table.AddRow("3", "3-2"); | ||||
|  | ||||
|                 // When | ||||
|                 console.Write(table); | ||||
|  | ||||
|                 // Then | ||||
|                 return Verifier.Verify(console.Output); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [UsesVerify] | ||||
|         public sealed class TheInsertRowMethod | ||||
|         { | ||||
|             [Fact] | ||||
|             [Expectation("Insert", "Renderables")] | ||||
|             public Task Should_Insert_Renderables() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole(); | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.AddColumn("Column #2"); | ||||
|                 table.AddRow(new[] { new Text("1"), new Text("1-2") }); | ||||
|                 table.AddRow(new[] { new Text("2"), new Text("2-2") }); | ||||
|  | ||||
|                 // When | ||||
|                 table.InsertRow(1, new[] { new Text("3"), new Text("3-2") }); | ||||
|  | ||||
|                 // Then | ||||
|                 console.Write(table); | ||||
|                 return Verifier.Verify(console.Output); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             [Expectation("Insert", "Strings")] | ||||
|             public Task Should_Insert_Strings() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole(); | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.AddColumn("Column #2"); | ||||
|                 table.AddRow("1", "1-2"); | ||||
|                 table.AddRow("2", "2-2"); | ||||
|  | ||||
|                 // When | ||||
|                 table.InsertRow(1, "3", "3-2"); | ||||
|  | ||||
|  | ||||
|                 // Then | ||||
|                 console.Write(table); | ||||
|                 return Verifier.Verify(console.Output); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [UsesVerify] | ||||
|         public sealed class TheRemoveRowMethod | ||||
|         { | ||||
|             [Fact] | ||||
|             [Expectation("Remove")] | ||||
|             public Task Should_Remove_Row() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole(); | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.AddColumn("Column #2"); | ||||
|                 table.AddRow(new[] { new Text("1"), new Text("1-2") }); | ||||
|                 table.AddRow(new[] { new Text("2"), new Text("2-2") }); | ||||
|                 table.AddRow(new[] { new Text("3"), new Text("3-2") }); | ||||
|  | ||||
|                 // When | ||||
|                 table.RemoveRow(1); | ||||
|  | ||||
|                 // Then | ||||
|                 console.Write(table); | ||||
|                 return Verifier.Verify(console.Output); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,226 @@ | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| using Shouldly; | ||||
| using Spectre.Console.Testing; | ||||
| using Spectre.Verify.Extensions; | ||||
| using VerifyXunit; | ||||
| using Xunit; | ||||
|  | ||||
| namespace Spectre.Console.Tests.Unit | ||||
| { | ||||
|     [ExpectationPath("Widgets/Table/Rows")] | ||||
|     public sealed class TableRowCollectionTests | ||||
|     { | ||||
|         [UsesVerify] | ||||
|         public sealed class TheAddMethod | ||||
|         { | ||||
|             [Fact] | ||||
|             public void Should_Throw_If_Columns_Are_Null() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
|  | ||||
|                 // When | ||||
|                 var result = Record.Exception(() => table.Rows.Add(null)); | ||||
|  | ||||
|                 // Then | ||||
|                 result.ShouldBeOfType<ArgumentNullException>() | ||||
|                     .ParamName.ShouldBe("columns"); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             public void Should_Add_Row_To_Collection() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|  | ||||
|                 // When | ||||
|                 table.Rows.Add(new[] { Text.Empty }); | ||||
|  | ||||
|                 // Then | ||||
|                 table.Rows.Count.ShouldBe(1); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             public void Should_Return_Index_Of_Added_Row() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.Rows.Add(new[] { Text.Empty }); | ||||
|  | ||||
|                 // When | ||||
|                 var result = table.Rows.Add(new[] { Text.Empty }); | ||||
|  | ||||
|                 // Then | ||||
|                 result.ShouldBe(1); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             [Expectation("Add")] | ||||
|             public Task Should_Add_Item_At_Correct_Place() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole(); | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.Rows.Add(new[] { new Text("1") }); | ||||
|                 table.Rows.Add(new[] { new Text("2") }); | ||||
|                 table.Rows.Add(new[] { new Text("3") }); | ||||
|  | ||||
|                 // When | ||||
|                 console.Write(table); | ||||
|  | ||||
|                 // Then | ||||
|                 return Verifier.Verify(console.Output); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [UsesVerify] | ||||
|         public sealed class TheInsertMethod | ||||
|         { | ||||
|             [Fact] | ||||
|             public void Should_Throw_If_Columns_Are_Null() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
|  | ||||
|                 // When | ||||
|                 var result = Record.Exception(() => table.Rows.Insert(0, null)); | ||||
|  | ||||
|                 // Then | ||||
|                 result.ShouldBeOfType<ArgumentNullException>() | ||||
|                     .ParamName.ShouldBe("columns"); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             public void Should_Insert_Row() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.Rows.Add(new[] { Text.Empty }); | ||||
|  | ||||
|                 // When | ||||
|                 table.Rows.Insert(0, new[] { Text.Empty }); | ||||
|  | ||||
|                 // Then | ||||
|                 table.Rows.Count.ShouldBe(2); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             public void Should_Return_Index_Of_Inserted_Row() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.Rows.Add(new[] { new Text("1") }); | ||||
|                 table.Rows.Add(new[] { new Text("2") }); | ||||
|  | ||||
|                 // When | ||||
|                 var result = table.Rows.Insert(1, new[] { new Text("3") }); | ||||
|  | ||||
|                 // Then | ||||
|                 result.ShouldBe(1); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             [Expectation("Insert")] | ||||
|             public Task Should_Insert_Item_At_Correct_Place() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole(); | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.Rows.Add(new[] { new Text("1") }); | ||||
|                 table.Rows.Add(new[] { new Text("2") }); | ||||
|                 table.Rows.Insert(1, new[] { new Text("3") }); | ||||
|  | ||||
|                 // When | ||||
|                 console.Write(table); | ||||
|  | ||||
|                 // Then | ||||
|                 return Verifier.Verify(console.Output); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [UsesVerify] | ||||
|         public sealed class TheRemoveMethod | ||||
|         { | ||||
|             [Fact] | ||||
|             public void Should_Throw_If_Index_Is_Negative() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|  | ||||
|                 // When | ||||
|                 var result = Record.Exception(() => table.Rows.RemoveAt(-1)); | ||||
|  | ||||
|                 // Then | ||||
|                 result.ShouldBeOfType<IndexOutOfRangeException>() | ||||
|                     .Message.ShouldBe("Table row index cannot be negative."); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Rows() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.Rows.Add(new[] { new Text("1") }); | ||||
|                 table.Rows.Add(new[] { new Text("2") }); | ||||
|                 table.Rows.Add(new[] { new Text("3") }); | ||||
|  | ||||
|                 // When | ||||
|                 var result = Record.Exception(() => table.Rows.RemoveAt(3)); | ||||
|  | ||||
|                 // Then | ||||
|                 result.ShouldBeOfType<IndexOutOfRangeException>() | ||||
|                     .Message.ShouldBe("Table row index cannot exceed the number of rows in the table."); | ||||
|             } | ||||
|  | ||||
|             [Fact] | ||||
|             [Expectation("Remove")] | ||||
|             public Task Should_Remove_Row() | ||||
|             { | ||||
|                 // Given | ||||
|                 var console = new TestConsole(); | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.Rows.Add(new[] { new Text("1") }); | ||||
|                 table.Rows.Add(new[] { new Text("2") }); | ||||
|                 table.Rows.Add(new[] { new Text("3") }); | ||||
|                 table.Rows.RemoveAt(1); | ||||
|  | ||||
|                 // When | ||||
|                 console.Write(table); | ||||
|  | ||||
|                 // Then | ||||
|                 return Verifier.Verify(console.Output); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public sealed class TheClearMethod | ||||
|         { | ||||
|             [Fact] | ||||
|             public void Should_Remove_All_Rows() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
|                 table.AddColumn("Column #1"); | ||||
|                 table.Rows.Add(new[] { new Text("1") }); | ||||
|                 table.Rows.Add(new[] { new Text("2") }); | ||||
|                 table.Rows.Add(new[] { new Text("3") }); | ||||
|                 table.Rows.Clear(); | ||||
|  | ||||
|                 // When | ||||
|                 var result = table.Rows.Count; | ||||
|  | ||||
|                 // Then | ||||
|                 result.ShouldBe(0); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -78,20 +78,6 @@ namespace Spectre.Console.Tests.Unit | ||||
|                     .ParamName.ShouldBe("columns"); | ||||
|             } | ||||
| 
 | ||||
|             [Fact] | ||||
|             public void Should_Throw_If_Renderable_Rows_Are_Null() | ||||
|             { | ||||
|                 // Given | ||||
|                 var table = new Table(); | ||||
| 
 | ||||
|                 // When | ||||
|                 var result = Record.Exception(() => table.AddRow(null)); | ||||
| 
 | ||||
|                 // Then | ||||
|                 result.ShouldBeOfType<ArgumentNullException>() | ||||
|                     .ParamName.ShouldBe("columns"); | ||||
|             } | ||||
| 
 | ||||
|             [Fact] | ||||
|             public void Should_Add_Empty_Items_If_User_Provides_Less_Row_Items_Than_Columns() | ||||
|             { | ||||
| @@ -1,8 +1,4 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace Spectre.Console.Tests | ||||
| { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user