Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CancellationToken on Read/ReadPassword #60

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
55 changes: 54 additions & 1 deletion src/ReadLine.Demo/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;

using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
public class Program
Expand All @@ -20,6 +22,57 @@ public static void Main(string[] args)

input = ReadLine.ReadPassword("Enter Password> ");
Console.WriteLine(input);

using (CancellationTokenSource cts = new CancellationTokenSource(5000))
{

try {
input = ReadLine.Read($"prompt [5 sec]> ",cancellationToken: cts.Token);
Console.WriteLine($"Result: {input}");
}
catch (OperationCanceledException)
{
Console.WriteLine(" [Operation was aborted]");
}
}

int interrupted=0;
Console.Write("Reading several lines using ReadKey [Terminate with CTRL+Q]: ");
StringBuilder sb = new StringBuilder();
while (true)
using (CancellationTokenSource cts=new CancellationTokenSource(10)) //10 ms to stress the loop
{
try
{
var rki=ReadLine.ReadKey(cancellationToken: cts.Token);
if (rki.KeyChar=='\u0011') // CTRL-Q in unicode
break;
sb.Append(rki.KeyChar);
if (rki.KeyChar=='\r')
sb.Append('\n');
}
catch (OperationCanceledException)
{
interrupted++;
}
}
Console.WriteLine("ReadKey was interrupted {0} times during read",interrupted);
Console.WriteLine("Entered value:");
Console.WriteLine("---------------------");
Console.WriteLine(sb.ToString());
Console.WriteLine("---------------------");

using (CancellationTokenSource cts = new CancellationTokenSource(5000))
{
try {
input=ReadLine.ReadAsync("Reading line in a Task (5 secs before cancellation)>",cts.Token).GetAwaiter().GetResult();
Console.WriteLine(input);
}
catch (OperationCanceledException)
{
Console.WriteLine(" [Interupted by timer]");
}
}
}
}

Expand Down
70 changes: 61 additions & 9 deletions src/ReadLine/ReadLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,42 @@
using Internal.ReadLine.Abstractions;

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace System
{
public static class ReadLine
{
private static List<string> _history;

private static Thread _reader;
private static ManualResetEventSlim _getInput, _gotInput;
private static ConsoleKeyInfo _input;
private static object _lock = new object();

static ReadLine()
{
_history = new List<string>();
_getInput = new ManualResetEventSlim();
_gotInput = new ManualResetEventSlim();
_reader = new Thread(ReaderLoop) {
IsBackground = true,
Name = "ReadLine background reader Loop"
};
_reader.Start();
}

public static void AddHistory(params string[] text) => _history.AddRange(text);
public static List<string> GetHistory() => _history;
public static void ClearHistory() => _history = new List<string>();
public static bool HistoryEnabled { get; set; }
public static IAutoCompleteHandler AutoCompletionHandler { private get; set; }

public static string Read(string prompt = "", string @default = "")
public static string Read(string prompt = "", string @default = "", CancellationToken cancellationToken=default)
{
Console.Write(prompt);
KeyHandler keyHandler = new KeyHandler(new Console2(), _history, AutoCompletionHandler);
string text = GetText(keyHandler);
string text = GetText(keyHandler,cancellationToken);

if (String.IsNullOrWhiteSpace(text) && !String.IsNullOrWhiteSpace(@default))
{
Expand All @@ -39,24 +52,63 @@ public static string Read(string prompt = "", string @default = "")
return text;
}

public static string ReadPassword(string prompt = "")
public static string ReadPassword(string prompt = "",CancellationToken cancellationToken=default)
{
Console.Write(prompt);
KeyHandler keyHandler = new KeyHandler(new Console2() { PasswordMode = true }, null, null);
return GetText(keyHandler);
return GetText(keyHandler,cancellationToken);
}

private static string GetText(KeyHandler keyHandler)
private static string GetText(KeyHandler keyHandler,CancellationToken cancellationToken)
{
ConsoleKeyInfo keyInfo = Console.ReadKey(true);
ConsoleKeyInfo keyInfo = ReadKeyInternal(cancellationToken);
while (keyInfo.Key != ConsoleKey.Enter)
{
keyHandler.Handle(keyInfo);
keyInfo = Console.ReadKey(true);
keyInfo = ReadKeyInternal(cancellationToken);
}

Console.WriteLine();
return keyHandler.Text;
}

public static ConsoleKeyInfo ReadKey(bool intercept = false, CancellationToken cancellationToken=default)
{
ConsoleKeyInfo consoleKeyInfo=ReadKeyInternal(cancellationToken);
if (!intercept)
{
if (consoleKeyInfo.Key==ConsoleKey.Enter)
Console.WriteLine();
else
Console.Write(consoleKeyInfo.KeyChar);
}
return consoleKeyInfo;
}

public static Task<string> ReadAsync(string prompt,CancellationToken cancellationToken)
{
return Task.Run(()=>ReadLine.Read(prompt,cancellationToken: cancellationToken));
}

private static ConsoleKeyInfo ReadKeyInternal(CancellationToken cancellation)
{
if (!_gotInput.IsSet)
{
_getInput.Set();
_gotInput.Wait(cancellation);
}
_gotInput.Reset();
return _input;
}

private static void ReaderLoop()
{
while (true) {
_getInput.Wait();
_input = Console.ReadKey(true);
_getInput.Reset();
_gotInput.Set();
}
}
}
}