mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-08-02 18:17:30 +08:00
Merge pull request #1747 from phil-scott-78/spinner-extension
This commit is contained in:
@ -0,0 +1,92 @@
|
||||
namespace Spectre.Console.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for running tasks with a spinner animation.
|
||||
/// </summary>
|
||||
public static class SpinnerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs a task with a spinner animation.
|
||||
/// </summary>
|
||||
/// <param name="task">The task to run.</param>
|
||||
/// <param name="spinner">The spinner to use.</param>
|
||||
/// <param name="style">The style to apply to the spinner.</param>
|
||||
/// <param name="ansiConsole">The console to write to.</param>
|
||||
/// <returns>The result of the task.</returns>
|
||||
public static async Task Spinner(this Task task, Spinner? spinner = null, Style? style = null, IAnsiConsole? ansiConsole = null)
|
||||
{
|
||||
await SpinnerInternal<object>(task, spinner ?? Console.Spinner.Known.Default, style, ansiConsole);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a task with a spinner animation.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the task result.</typeparam>
|
||||
/// <param name="task">The task to run.</param>
|
||||
/// <param name="spinner">The spinner to use.</param>
|
||||
/// <param name="style">The style to apply to the spinner.</param>
|
||||
/// <param name="ansiConsole">The console to write to.</param>
|
||||
/// <returns>The result of the task.</returns>
|
||||
public static async Task<T> Spinner<T>(this Task<T> task, Spinner? spinner = null, Style? style = null, IAnsiConsole? ansiConsole = null)
|
||||
{
|
||||
return (await SpinnerInternal<T>(task, spinner ?? Console.Spinner.Known.Default, style, ansiConsole))!;
|
||||
}
|
||||
|
||||
private static async Task<T?> SpinnerInternal<T>(Task task, Spinner spinner, Style? style = null, IAnsiConsole? ansiConsole = null)
|
||||
{
|
||||
ansiConsole ??= AnsiConsole.Console;
|
||||
|
||||
style ??= Style.Plain;
|
||||
var currentFrame = 0;
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// Start spinner animation in background
|
||||
var spinnerTask = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
while (!cancellationTokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
ansiConsole.Cursor.Show(false);
|
||||
|
||||
var spinnerFrame = spinner.Frames[currentFrame];
|
||||
|
||||
// Write the spinner frame
|
||||
ansiConsole.Write(new Text(spinnerFrame, style));
|
||||
ansiConsole.Write(new ControlCode(AnsiSequences.CUB(spinnerFrame.Length)));
|
||||
|
||||
currentFrame = (currentFrame + 1) % spinner.Frames.Count;
|
||||
await Task.Delay(spinner.Interval, cancellationTokenSource.Token);
|
||||
}
|
||||
}, cancellationTokenSource.Token);
|
||||
|
||||
try
|
||||
{
|
||||
// Wait for the actual task to complete
|
||||
if (task is Task<T> taskWithResult)
|
||||
{
|
||||
var result = await taskWithResult;
|
||||
await cancellationTokenSource.CancelAsync();
|
||||
await spinnerTask.ContinueWith(_ => { }, TaskContinuationOptions.OnlyOnCanceled);
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
await task;
|
||||
await cancellationTokenSource.CancelAsync();
|
||||
await spinnerTask.ContinueWith(_ => { }, TaskContinuationOptions.OnlyOnCanceled);
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
var spinnerFrame = spinner.Frames[currentFrame];
|
||||
|
||||
ansiConsole.Write(new string(' ', spinnerFrame.Length));
|
||||
ansiConsole.Write(new ControlCode(AnsiSequences.CUB(spinnerFrame.Length)));
|
||||
ansiConsole.Cursor.Show();
|
||||
await cancellationTokenSource.CancelAsync();
|
||||
}
|
||||
}
|
||||
}
|
13
src/Spectre.Console/Internal/Polyfill/CancellationToken.cs
Normal file
13
src/Spectre.Console/Internal/Polyfill/CancellationToken.cs
Normal file
@ -0,0 +1,13 @@
|
||||
#if NETSTANDARD2_0
|
||||
namespace Spectre.Console
|
||||
{
|
||||
internal static class CancellationTokenHelpers
|
||||
{
|
||||
public static Task CancelAsync(this CancellationTokenSource cts)
|
||||
{
|
||||
cts.Cancel();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user