Add autocomplete for text prompt

Closes #166
This commit is contained in:
Patrik Svensson
2020-12-21 03:21:02 +01:00
committed by Patrik Svensson
parent e280b82679
commit 1cf30f62fc
41 changed files with 218 additions and 104 deletions

View File

@@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Spectre.Console
{
@@ -7,7 +10,7 @@ namespace Spectre.Console
/// </summary>
public static partial class AnsiConsoleExtensions
{
internal static string ReadLine(this IAnsiConsole console, Style? style, bool secret)
internal static string ReadLine(this IAnsiConsole console, Style? style, bool secret, IEnumerable<string>? items = null)
{
if (console is null)
{
@@ -15,35 +18,83 @@ namespace Spectre.Console
}
style ??= Style.Plain;
var text = string.Empty;
var autocomplete = new List<string>(items ?? Enumerable.Empty<string>());
var result = string.Empty;
while (true)
{
var key = console.Input.ReadKey(true);
if (key.Key == ConsoleKey.Enter)
{
return result;
return text;
}
if (key.Key == ConsoleKey.Tab && autocomplete.Count > 0)
{
var replace = AutoComplete(autocomplete, text);
if (!string.IsNullOrEmpty(replace))
{
// Render the suggestion
console.Write("\b \b".Repeat(text.Length), style);
console.Write(replace);
text = replace;
continue;
}
}
if (key.Key == ConsoleKey.Backspace)
{
if (result.Length > 0)
if (text.Length > 0)
{
result = result.Substring(0, result.Length - 1);
text = text.Substring(0, text.Length - 1);
console.Write("\b \b");
}
continue;
}
result += key.KeyChar.ToString();
if (!char.IsControl(key.KeyChar))
{
text += key.KeyChar.ToString();
console.Write(secret ? "*" : key.KeyChar.ToString(), style);
}
}
}
private static string AutoComplete(List<string> autocomplete, string text)
{
var found = autocomplete.Find(i => i == text);
var replace = string.Empty;
if (found == null)
{
// Get the closest match
var next = autocomplete.Find(i => i.StartsWith(text, true, CultureInfo.InvariantCulture));
if (next != null)
{
replace = next;
}
else if (string.IsNullOrEmpty(text))
{
// Use the first item
replace = autocomplete[0];
}
}
else
{
// Get the next match
var index = autocomplete.IndexOf(found) + 1;
if (index >= autocomplete.Count)
{
index = 0;
}
replace = autocomplete[index];
}
return replace;
}
}
}