Positioned Progress Tasks - Before or After Other Tasks (#1250)

This commit is contained in:
Tom Longhurst 2024-03-07 08:38:59 +00:00 committed by GitHub
parent 5acd83a3ef
commit d921ac6f02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 212 additions and 14 deletions

View File

@ -14,7 +14,16 @@ public sealed class ProgressContext
/// <summary>
/// Gets a value indicating whether or not all started tasks have completed.
/// </summary>
public bool IsFinished => _tasks.Where(x => x.IsStarted).All(task => task.IsFinished);
public bool IsFinished
{
get
{
lock (_taskLock)
{
return _tasks.Where(x => x.IsStarted).All(task => task.IsFinished);
}
}
}
internal ProgressContext(IAnsiConsole console, ProgressRenderer renderer)
{
@ -33,11 +42,68 @@ public sealed class ProgressContext
/// <returns>The newly created task.</returns>
public ProgressTask AddTask(string description, bool autoStart = true, double maxValue = 100)
{
return AddTask(description, new ProgressTaskSettings
lock (_taskLock)
{
AutoStart = autoStart,
MaxValue = maxValue,
});
var settings = new ProgressTaskSettings { AutoStart = autoStart, MaxValue = maxValue, };
return AddTaskAtInternal(description, settings, _tasks.Count);
}
}
/// <summary>
/// Adds a task.
/// </summary>
/// <param name="description">The task description.</param>
/// <param name="index">The index at which the task should be inserted.</param>
/// <param name="autoStart">Whether or not the task should start immediately.</param>
/// <param name="maxValue">The task's max value.</param>
/// <returns>The newly created task.</returns>
public ProgressTask AddTaskAt(string description, int index, bool autoStart = true, double maxValue = 100)
{
lock (_taskLock)
{
var settings = new ProgressTaskSettings { AutoStart = autoStart, MaxValue = maxValue, };
return AddTaskAtInternal(description, settings, index);
}
}
/// <summary>
/// Adds a task before the reference task.
/// </summary>
/// <param name="description">The task description.</param>
/// <param name="referenceProgressTask">The reference task to add before.</param>
/// <param name="autoStart">Whether or not the task should start immediately.</param>
/// <param name="maxValue">The task's max value.</param>
/// <returns>The newly created task.</returns>
public ProgressTask AddTaskBefore(string description, ProgressTask referenceProgressTask, bool autoStart = true, double maxValue = 100)
{
lock (_taskLock)
{
var settings = new ProgressTaskSettings { AutoStart = autoStart, MaxValue = maxValue, };
var indexOfReference = _tasks.IndexOf(referenceProgressTask);
return AddTaskAtInternal(description, settings, indexOfReference);
}
}
/// <summary>
/// Adds a task after the reference task.
/// </summary>
/// <param name="description">The task description.</param>
/// <param name="referenceProgressTask">The reference task to add after.</param>
/// <param name="autoStart">Whether or not the task should start immediately.</param>
/// <param name="maxValue">The task's max value.</param>
/// <returns>The newly created task.</returns>
public ProgressTask AddTaskAfter(string description, ProgressTask referenceProgressTask, bool autoStart = true, double maxValue = 100)
{
lock (_taskLock)
{
var settings = new ProgressTaskSettings { AutoStart = autoStart, MaxValue = maxValue, };
var indexOfReference = _tasks.IndexOf(referenceProgressTask);
return AddTaskAtInternal(description, settings, indexOfReference + 1);
}
}
/// <summary>
@ -48,18 +114,58 @@ public sealed class ProgressContext
/// <returns>The newly created task.</returns>
public ProgressTask AddTask(string description, ProgressTaskSettings settings)
{
if (settings is null)
{
throw new ArgumentNullException(nameof(settings));
}
lock (_taskLock)
{
var task = new ProgressTask(_taskId++, description, settings.MaxValue, settings.AutoStart);
return AddTaskAtInternal(description, settings, _tasks.Count);
}
}
_tasks.Add(task);
/// <summary>
/// Adds a task at the specified index.
/// </summary>
/// <param name="description">The task description.</param>
/// <param name="settings">The task settings.</param>
/// <param name="index">The index at which the task should be inserted.</param>
/// <returns>The newly created task.</returns>
public ProgressTask AddTaskAt(string description, ProgressTaskSettings settings, int index)
{
lock (_taskLock)
{
return AddTaskAtInternal(description, settings, index);
}
}
return task;
/// <summary>
/// Adds a task before the reference task.
/// </summary>
/// <param name="description">The task description.</param>
/// <param name="settings">The task settings.</param>
/// <param name="referenceProgressTask">The reference task to add before.</param>
/// <returns>The newly created task.</returns>
public ProgressTask AddTaskBefore(string description, ProgressTaskSettings settings, ProgressTask referenceProgressTask)
{
lock (_taskLock)
{
var indexOfReference = _tasks.IndexOf(referenceProgressTask);
return AddTaskAtInternal(description, settings, indexOfReference);
}
}
/// <summary>
/// Adds a task after the reference task.
/// </summary>
/// <param name="description">The task description.</param>
/// <param name="settings">The task settings.</param>
/// <param name="referenceProgressTask">The reference task to add after.</param>
/// <returns>The newly created task.</returns>
public ProgressTask AddTaskAfter(string description, ProgressTaskSettings settings, ProgressTask referenceProgressTask)
{
lock (_taskLock)
{
var indexOfReference = _tasks.IndexOf(referenceProgressTask);
return AddTaskAtInternal(description, settings, indexOfReference + 1);
}
}
@ -72,6 +178,20 @@ public sealed class ProgressContext
_console.Write(new ControlCode(string.Empty));
}
private ProgressTask AddTaskAtInternal(string description, ProgressTaskSettings settings, int position)
{
if (settings is null)
{
throw new ArgumentNullException(nameof(settings));
}
var task = new ProgressTask(_taskId++, description, settings.MaxValue, settings.AutoStart);
_tasks.Insert(position, task);
return task;
}
internal IReadOnlyList<ProgressTask> GetTasks()
{
lock (_taskLock)
@ -79,4 +199,4 @@ public sealed class ProgressContext
return new List<ProgressTask>(_tasks);
}
}
}
}

View File

@ -263,4 +263,82 @@ public sealed class ProgressTests
// Then
task.RemainingTime.ShouldBe(TimeSpan.MaxValue);
}
[Fact]
public void Should_Render_Tasks_Added_Before_And_After_Correctly()
{
// Given
var console = new TestConsole()
.Width(10)
.Interactive()
.EmitAnsiSequences();
var progress = new Progress(console)
.Columns(new TaskDescriptionColumn())
.AutoRefresh(false)
.AutoClear(true);
// When
progress.Start(ctx =>
{
var foo1 = ctx.AddTask("foo1");
var foo2 = ctx.AddTask("foo2");
var foo3 = ctx.AddTask("foo3");
var afterFoo1 = ctx.AddTaskAfter("afterFoo1", foo1);
var beforeFoo3 = ctx.AddTaskBefore("beforeFoo3", foo3);
});
// Then
console.Output.SplitLines().Select(x => x.Trim()).ToArray()
.ShouldBeEquivalentTo(new[]
{
"[?25l",
"foo1",
"afterFoo1",
"foo2",
"beforeFoo3",
"foo3",
"[?25h",
});
}
[Fact]
public void Should_Render_Tasks_At_Specified_Indexes_Correctly()
{
// Given
var console = new TestConsole()
.Width(10)
.Interactive()
.EmitAnsiSequences();
var progress = new Progress(console)
.Columns(new TaskDescriptionColumn())
.AutoRefresh(false)
.AutoClear(true);
// When
progress.Start(ctx =>
{
var foo1 = ctx.AddTask("foo1");
var foo2 = ctx.AddTask("foo2");
var foo3 = ctx.AddTask("foo3");
var afterFoo1 = ctx.AddTaskAt("afterFoo1", 1);
var beforeFoo3 = ctx.AddTaskAt("beforeFoo3", 3);
});
// Then
console.Output.SplitLines().Select(x => x.Trim()).ToArray()
.ShouldBeEquivalentTo(new[]
{
"[?25l",
"foo1",
"afterFoo1",
"foo2",
"beforeFoo3",
"foo3",
"[?25h",
});
}
}