Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/dotnet-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
#working-directory: ./ConsoleProgressBar

pack_and_publish:
if: github.event_name == 'push'
needs: build
runs-on: ubuntu-latest

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<IsPackable>true</IsPackable>
<PackageId>ErikForwerk.ConsoleTools.ProgressBar</PackageId>
<Version>1.0.2</Version>
<Version>1.0.3</Version>
<Authors>Erik Forwerk</Authors>
<Company>-</Company>
<Description>This package provides functions to easily and conveniently display a progress bar in a C# console window when iterating over an enumeration. </Description>
Expand Down
33 changes: 12 additions & 21 deletions ConsoleProgressBar/ConsoleProgressBar/ConsoleProgressHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,12 @@
//-----------------------------------------------------------------------------------------------------------------------------------------
namespace ConsoleProgressBar;

// TODO: Why is this necessary? Why has it to be done *after* the namespace declaration?
using Console = System.Console;

//-----------------------------------------------------------------------------------------------------------------------------------------

public sealed class ConsoleProgressHandler<T> : ProgressProxy<T>
{
//-----------------------------------------------------------------------------------------------------------------
#region Fields

internal const int DEBUG_CONSOLE_WIDTH = 80;

private int _topPos;
private int _lastProgress = -1;

Expand All @@ -29,15 +23,14 @@ public sealed class ConsoleProgressHandler<T> : ProgressProxy<T>
//-------------------------------------------------------------------------------------------------------------
#region Construction

public ConsoleProgressHandler(IEnumerable<T> collection, string? action = null, string? item = null)

public ConsoleProgressHandler(IEnumerable<T> collection, string? action, string? item)
: base(collection, action, item)
{
if (Console.OutputEncoding != Encoding.UTF8)
Console.OutputEncoding = Encoding.UTF8;
}

public ConsoleProgressHandler(T[] collection)
public ConsoleProgressHandler(IEnumerable<T> collection)
: base(collection)
{
if (Console.OutputEncoding != Encoding.UTF8)
Expand Down Expand Up @@ -67,20 +60,17 @@ public ConsoleProgressColors Colors
#region Methods

//--- only for satisfying the IOS-principle (IOSP) ---
private static void NewLine()
private void NewLine()
=> Console.WriteLine();

private void ClearConsolLine(int topPos)
{
int width = DebugFlag ? DEBUG_CONSOLE_WIDTH : Console.WindowWidth;
if (!DebugFlag)
Console.SetCursorPosition(0, topPos);
Console.Write(new string(' ', width));
Console.SetCursorPosition(0, topPos);
Console.Write(new string(' ', Console.WindowWidth));
}

private void PrintProgress(int stepNum, double progress)
{
int consoleWidth = DebugFlag ? DEBUG_CONSOLE_WIDTH : Console.WindowWidth;

//---------------------------------------------------------------------
StringBuilder sbCaption = new ();
Expand Down Expand Up @@ -119,7 +109,7 @@ private void PrintProgress(int stepNum, double progress)
//---------------------------------------------------------------------
string caption = sbCaption.ToString();
string ending = sbEnding.ToString();
int barSpace = consoleWidth -caption.Length -ending.Length;
int barSpace = Console.WindowWidth -caption.Length -ending.Length;

if (MaxBarLength > 0)
barSpace = int.Min(MaxBarLength, barSpace);
Expand All @@ -131,13 +121,13 @@ private void PrintProgress(int stepNum, double progress)
byte fractionIndex = (byte)((progressWidthF - progressWidth) * Style.ProgressCharFractions.Length);
bool hasFraction = Style.ShowFractions && progressWidth < barSpace && Style.ProgressCharFractions.Length > 0;


_lastProgress = progressWidth;

//---------------------------------------------------------------------
if (!DebugFlag)
Console.SetCursorPosition(0, _topPos);
Console.Write($"{caption}");
Console.CursorVisible = false;
Console.SetCursorPosition(0, _topPos);

Console.Write(caption);

ConsoleColor oldColorFG = Console.ForegroundColor;
ConsoleColor oldColorBG = Console.BackgroundColor;
Expand Down Expand Up @@ -168,6 +158,7 @@ private void PrintProgress(int stepNum, double progress)
}

Console.Write(ending);
Console.CursorVisible = true;
}

#endregion Methods
Expand All @@ -183,7 +174,7 @@ protected override void InitProgress()
if (StartAtNewLine)
NewLine();

_topPos = DebugFlag ? 0 : Console.CursorTop;
_topPos = Console.CursorTop;

//--- clear whole console line ---
ClearConsolLine(_topPos);
Expand Down
55 changes: 55 additions & 0 deletions ConsoleProgressBar/ConsoleProgressBar/ConsoleReal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

using System.Text;

//-----------------------------------------------------------------------------------------------------------------------------------------
namespace ConsoleProgressBar;

//--- implement everything using the console itself ---
public sealed class ConsoleReal : IConsole
{
public Encoding OutputEncoding
{
get => Console.OutputEncoding;
set => Console.OutputEncoding = value;
}

public ConsoleColor ForegroundColor
{
get => Console.ForegroundColor;
set => Console.ForegroundColor = value;
}

public ConsoleColor BackgroundColor
{
get => Console.BackgroundColor;
set => Console.BackgroundColor = value;
}

public int WindowWidth
=> Console.WindowWidth;

public int CursorLeft
{
get => Console.CursorLeft;
set => Console.CursorLeft = value;
}

public int CursorTop
{
get => Console.CursorTop;
set => Console.CursorTop = value;
}

public bool CursorVisible
{
//get => Console.CursorVisible;
set => Console.CursorVisible = value;
}

public void SetCursorPosition(int left, int top) => Console.SetCursorPosition(left, top);
public void Write(char value) => Console.Write(value);
public void Write(string value) => Console.Write(value);
public void WriteLine() => Console.WriteLine();
public void WriteLine(string value) => Console.WriteLine(value);
}

55 changes: 55 additions & 0 deletions ConsoleProgressBar/ConsoleProgressBar/ConsoleTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

using System.Text;

//-----------------------------------------------------------------------------------------------------------------------------------------
namespace ConsoleProgressBar;

//-----------------------------------------------------------------------------------------------------------------------------------------
public sealed class ConsoleTest : IConsole
{
internal const int DEBUG_CONSOLE_WIDTH = 80;

public Encoding OutputEncoding
{
get => Console.OutputEncoding;
set => Console.OutputEncoding = value;
}

public void Write(char value)
=> Console.Write(value);

public void Write(string value)
=> Console.Write(value);

public void WriteLine()
=> Console.WriteLine();

public void WriteLine(string value)
=> Console.WriteLine(value);

//--- not supported while testing console ---

/// <summary>
/// Will not create an exception when called with the real console during tests,
/// but will not change the color either.
/// </summary>
public ConsoleColor BackgroundColor
{ get; set; }

/// <summary>
/// Will not create an exception when called with the real console during tests,
/// but will not change the color either.
/// </summary>
public ConsoleColor ForegroundColor
{ get; set; }

public int WindowWidth => DEBUG_CONSOLE_WIDTH;
public int CursorLeft { get; set; } = 0;
public int CursorTop { get; set; } = 0;

public bool CursorVisible { set { } }

public void SetCursorPosition(int left, int top)
{ }
}

40 changes: 40 additions & 0 deletions ConsoleProgressBar/ConsoleProgressBar/IConsole.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

// ignore spelling: bg

using System.Text;

//-----------------------------------------------------------------------------------------------------------------------------------------
namespace ConsoleProgressBar;

//-----------------------------------------------------------------------------------------------------------------------------------------
public interface IConsole
{
Encoding OutputEncoding
{ get; set;}

int WindowWidth
{ get; /*set;*/ }

int CursorLeft
{ get; set; }

int CursorTop
{ get; set; }

bool CursorVisible
{ /*get;*/ set; }

ConsoleColor ForegroundColor
{ get; set; }

ConsoleColor BackgroundColor
{ get; set; }

void Write(char value);
void Write(string value);

void WriteLine();
void WriteLine(string value);

void SetCursorPosition(int left, int top);
}
51 changes: 17 additions & 34 deletions ConsoleProgressBar/ConsoleProgressBar/ProgressProxy.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;

//-----------------------------------------------------------------------------------------------------------------------------------------
namespace ConsoleProgressBar;
Expand All @@ -17,6 +18,9 @@ public abstract class ProgressProxy<T>(IEnumerable<T> collection, string? action
//-------------------------------------------------------------------------------------------------------------
#region Properties

internal IConsole Console
{ get; set; } = new ConsoleReal();

internal int? TotalSteps
{ get; set; }

Expand All @@ -33,43 +37,29 @@ protected string? ItemDesc
public int? CancelAfter
{ get; internal set; }

internal bool DebugFlag
{ get; set; }

#endregion Properties

//-------------------------------------------------------------------------------------------------------------
#region Methods

[MemberNotNull(nameof(TotalSteps))]
internal void UpdateTotalStepsIfUnset()
{
if (TotalSteps is null)
{
TotalSteps = _collection.TryGetNonEnumeratedCount(out int count) ? count : _collection.Count();
TotalSteps = Math.Max(1, TotalSteps.Value);
}
}
=> TotalSteps ??= Math.Max(0, _collection.Count());
Comment thread
3rikF marked this conversation as resolved.

[MemberNotNull(nameof(TotalSteps))]
protected virtual void InitProgress()
=> UpdateTotalStepsIfUnset();

protected abstract void UpdateProgress(int stepNum, double progress, T? item);

protected abstract void FinishProgress();

/// <summary>Calculates the progress of a process as a fraction of completion.</summary>
/// <remarks>Cannot be called if <see cref="TotalSteps"/> is not set or null.</remarks>
/// <param name="stepNum">The current step number, which must be a non-negative integer.</param>
/// <returns>A double value representing the progress, clamped between 0.0 and 1.0.</returns>
private double GetProgress(int stepNum)
{
#pragma warning disable IDE0046 // In bedingten Ausdruck konvertieren
if (TotalSteps is null)
return 0D;

else if (TotalSteps == 0)
return 1D;

else
return double.Clamp(stepNum / (double)TotalSteps, 0.0, 1.0);
#pragma warning restore IDE0046 // In bedingten Ausdruck konvertieren
}
=> double.Clamp(stepNum / (double)TotalSteps!, 0.0, 1.0);
Comment thread
3rikF marked this conversation as resolved.

/// <summary>
/// If <see cref="CancelAfter"/> is set, the enumeration will be limited to this number of items.
Expand All @@ -78,19 +68,13 @@ private double GetProgress(int stepNum)
/// <returns>New enumerable with the actual number of items to iterate over</returns>
private IEnumerable<T> GetActualEnumeration()
{
//--- [AsQueryable] is important when the enumerable is a EntityFramework query ---
//--- otherwise the query will be executed completely even if the foreach loop is canceled ---
if (CancelAfter.HasValue)
{
//--- this is important when the enumerable is a EntityFramework query ---
//--- otherwise the query will be executed completely even if the foreach loop is canceled ---
IEnumerable<T> tmp = _collection
.AsQueryable()
.Take(CancelAfter.Value);

//--- fall-back if something goes wrong with the query-able ---
return tmp ?? _collection.Take(CancelAfter.Value);
}
return _collection.AsQueryable().Take(CancelAfter.Value);

return _collection;
else
return _collection;
}

#endregion Methods
Expand All @@ -103,7 +87,6 @@ public IEnumerator<T> GetEnumerator()
InitProgress();
int stepNum = 0;


foreach (T item in GetActualEnumeration())
{
//--- pass-through the item ---
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//-----------------------------------------------------------------------------------------------------------------------------------------

//-----------------------------------------------------------------------------------------------------------------------------------------
namespace ConsoleProgressBar;

//-----------------------------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -33,9 +34,9 @@ public static ProgressProxy<T> CancelAfter<T>(this ProgressProxy<T> progress, in
return progress;
}

internal static ProgressProxy<T> WithDebugMode<T>(this ProgressProxy<T> progress)
internal static ProgressProxy<T> WithTestMode<T>(this ProgressProxy<T> progress)
{
progress.DebugFlag = true;
progress.Console = new ConsoleTest();
return progress;
}
}
Loading