Skip to content

Instantly share code, notes, and snippets.

@cdiggins
Created March 26, 2020 16:41
Show Gist options
  • Save cdiggins/102164183f6a3ad166c225a924abc47a to your computer and use it in GitHub Desktop.
Save cdiggins/102164183f6a3ad166c225a924abc47a to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using Vim.Logger;
namespace Vim.DotNetUtilities
{
/// <summary>
/// This interface is different from the IProgress&lt;T&gt; class in the system library
/// https://docs.microsoft.com/en-us/dotnet/api/system.iprogress-1?view=netframework-4.8
/// It was designed to simplify the use case of percentage reporting where the number of steps
/// is known in advance, and each step simply advances the counter by "1", and the percentage
/// can be reported easily. The goal is for the progress reporting to just have to
/// call an "Update()" extension method multiple times with no values and have it "just" work.
/// More sophisticated scenarios are possible, if the current status can be completed precisely.
/// This implements IDisposable so that cancelation and completion can properly clean up resources,
/// and communicates that the progress is over.
/// </summary>
public interface IProgress : IDisposable
{
float Total { get; }
float Current { get; }
void Update(float current, string message);
}
public static class ProgressExtensions
{
public static void NextStep(this IProgress progress, string message = "", float stepSize = 1f)
{
progress.Update(progress.Current + stepSize, message);
}
public static void Complete(this IProgress progress, string message = "")
{
progress.Update(progress.Total, message);
progress.Dispose();
}
public static void Cancel(this IProgress progress, string message = "")
{
progress.Update(progress.Current, message);
progress.Dispose();
}
public static IProgress SubProgress(this IProgress progress, float total, string message, float parentStepSize = 1)
{
if (progress == null) return null;
var r = new SubProgress(progress, total, parentStepSize);
r.NextStep(message, 0f);
return r;
}
}
public sealed class IndeterminateProgress : IProgress
{
public float Total => 1f;
public float Current => 1f;
public Action<string> OnUpdate { get; }
public Action OnComplete { get; }
public IndeterminateProgress(Action<string> update, Action completed = null)
=> (OnUpdate, OnComplete) = (update, completed);
public void Dispose()
=> OnComplete?.Invoke();
public void Update(float current, string message)
=> OnUpdate?.Invoke(message);
}
public class StdProgress : IProgress
{
public float Total { get; set; }
public float Current { get; set; }
public Action<StdProgress> Action { get; set; }
public string LastMessage { get; set; }
public long LastTime { get; set; }
public Stopwatch Stopwatch { get; } = new Stopwatch();
public float LastProgressPercentage { get; set; }
public float ProgressPercentage => Current / Total;
public StdProgress(float total = 1f, Action<StdProgress> action = null)
{
Stopwatch.Start();
Total = total;
Action = action;
}
public virtual void Update(float current, string message)
{
LastTime = Stopwatch.ElapsedMilliseconds;
LastMessage = message;
Current = current;
// Only update every 1% of the time. More often is too much
if (ProgressPercentage - LastProgressPercentage >= 0.01f)
{
LastProgressPercentage = ProgressPercentage;
Action?.Invoke(this);
}
}
public void Dispose()
{ }
}
public class StdLoggingProgress : StdProgress
{
public ILogger Logger;
public StdLoggingProgress(float total, ILogger logger = null)
: base(total)
{
Logger = logger ?? new StdLogger();
Action = (progress) => Logger.Log(progress.LastMessage);
}
}
public sealed class SubProgress : IProgress
{
public float Total { get; set; }
public float Current { get; set; }
public IProgress Parent { get; }
public float ParentCurrent { get; set; }
public float ParentStepSize { get; set; }
public SubProgress(IProgress parent, float total, float parentStepSize = 1)
{
Parent = parent;
Total = total;
ParentCurrent = parent.Current;
ParentStepSize = parentStepSize;
}
public void Update(float current, string message)
{
Current = current;
Parent.Update(ParentCurrent + (Current / Total) * ParentStepSize, message);
}
public void Dispose()
{
Parent.Dispose();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment