Address most code review comments
Pagination hasn't been dealt with yet mac keybindings may be able to be simplified, pending some additional verification Fix 3rd party signing cert Bind a few common bash/zsh mappings Check for output redirection Make package version variable name consistent Add --help option for the command line Remove regions Remove old pointer resolution code Make command not found message more friendly Fix issue where base address was required for requests to absolute URIs Add options to suppress response formatting and streaming Turn off packing for the REPL project
This commit is contained in:
parent
7487da155f
commit
af5d8a8244
|
|
@ -1,4 +1,4 @@
|
|||
<Project>
|
||||
<Project>
|
||||
<Import
|
||||
Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))\AspNetCoreSettings.props"
|
||||
Condition=" '$(CI)' != 'true' AND '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))' != '' " />
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
<SignAssembly>true</SignAssembly>
|
||||
<PackageSigningCertName>MicrosoftNuGet</PackageSigningCertName>
|
||||
<AssemblySigningCertName>Microsoft</AssemblySigningCertName>
|
||||
<AssemblySigning3rdPartyCertName>Microsoft3rdPartyAppComponentDual</AssemblySigning3rdPartyCertName>
|
||||
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
|
||||
<SystemDataSqlClientPackageVersion>4.5.1</SystemDataSqlClientPackageVersion>
|
||||
<SystemSecurityCryptographyCngPackageVersion>4.5.0</SystemSecurityCryptographyCngPackageVersion>
|
||||
<CommandLine_NewtonsoftJsonPackageVersion>11.0.2</CommandLine_NewtonsoftJsonPackageVersion>
|
||||
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
|
||||
<VisualStudio_NewtonsoftJsonPackageVersion>9.0.1</VisualStudio_NewtonsoftJsonPackageVersion>
|
||||
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
||||
<XunitRunnerVisualStudioPackageVersion>2.4.0</XunitRunnerVisualStudioPackageVersion>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ namespace Microsoft.HttpRepl.Commands
|
|||
private const string ResponseFileOption = nameof(ResponseFileOption);
|
||||
private const string BodyFileOption = nameof(BodyFileOption);
|
||||
private const string NoBodyOption = nameof(NoBodyOption);
|
||||
private const string NoFormattingOption = nameof(NoFormattingOption);
|
||||
private const string NoStreamingOption = nameof(NoStreamingOption);
|
||||
private const string BodyContentOption = nameof(BodyContentOption);
|
||||
private static readonly char[] HeaderSeparatorChars = new[] { '=', ':' };
|
||||
|
||||
|
|
@ -54,7 +56,9 @@ namespace Microsoft.HttpRepl.Commands
|
|||
.WithOption(new CommandOptionSpecification(HeaderOption, requiresValue: true, forms: new[] {"--header", "-h"}))
|
||||
.WithOption(new CommandOptionSpecification(ResponseFileOption, requiresValue: true, maximumOccurrences: 1, forms: new[] { "--response", }))
|
||||
.WithOption(new CommandOptionSpecification(ResponseHeadersFileOption, requiresValue: true, maximumOccurrences: 1, forms: new[] { "--response:headers", }))
|
||||
.WithOption(new CommandOptionSpecification(ResponseBodyFileOption, requiresValue: true, maximumOccurrences: 1, forms: new[] { "--response:body", }));
|
||||
.WithOption(new CommandOptionSpecification(ResponseBodyFileOption, requiresValue: true, maximumOccurrences: 1, forms: new[] { "--response:body", }))
|
||||
.WithOption(new CommandOptionSpecification(NoFormattingOption, maximumOccurrences: 1, forms: new[] { "--no-formatting", "-F" }))
|
||||
.WithOption(new CommandOptionSpecification(NoStreamingOption, maximumOccurrences: 1, forms: new[] { "--no-streaming", "-S" }));
|
||||
|
||||
if (RequiresBody)
|
||||
{
|
||||
|
|
@ -70,9 +74,9 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
protected override async Task ExecuteAsync(IShellState shellState, HttpState programState, DefaultCommandInput<ICoreParseResult> commandInput, ICoreParseResult parseResult, CancellationToken cancellationToken)
|
||||
{
|
||||
if (programState.BaseAddress == null)
|
||||
if (programState.BaseAddress == null && (commandInput.Arguments.Count == 0 || !Uri.TryCreate(commandInput.Arguments[0].Text, UriKind.Absolute, out Uri _)))
|
||||
{
|
||||
shellState.ConsoleManager.Error.WriteLine("'set base {url}' must be called before issuing requests".Bold().Red());
|
||||
shellState.ConsoleManager.Error.WriteLine("'set base {url}' must be called before issuing requests to a relative path".Bold().Red());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -203,10 +207,10 @@ namespace Microsoft.HttpRepl.Commands
|
|||
string bodyTarget = commandInput.Options[ResponseBodyFileOption].FirstOrDefault()?.Text ?? commandInput.Options[ResponseFileOption].FirstOrDefault()?.Text;
|
||||
|
||||
HttpResponseMessage response = await programState.Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
await HandleResponseAsync(programState, shellState.ConsoleManager, response, programState.EchoRequest, headersTarget, bodyTarget, cancellationToken).ConfigureAwait(false);
|
||||
await HandleResponseAsync(programState, commandInput, shellState.ConsoleManager, response, programState.EchoRequest, headersTarget, bodyTarget, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task HandleResponseAsync(HttpState programState, IConsoleManager consoleManager, HttpResponseMessage response, bool echoRequest, string headersTargetFile, string bodyTargetFile, CancellationToken cancellationToken)
|
||||
private static async Task HandleResponseAsync(HttpState programState, DefaultCommandInput<ICoreParseResult> commandInput, IConsoleManager consoleManager, HttpResponseMessage response, bool echoRequest, string headersTargetFile, string bodyTargetFile, CancellationToken cancellationToken)
|
||||
{
|
||||
RequestConfig requestConfig = new RequestConfig(programState);
|
||||
ResponseConfig responseConfig = new ResponseConfig(programState);
|
||||
|
|
@ -244,7 +248,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
{
|
||||
using (StreamWriter writer = new StreamWriter(new MemoryStream()))
|
||||
{
|
||||
await FormatBodyAsync(programState, consoleManager, response.RequestMessage.Content, writer, cancellationToken).ConfigureAwait(false);
|
||||
await FormatBodyAsync(commandInput, programState, consoleManager, response.RequestMessage.Content, writer, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -311,7 +315,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
if (response.Content != null)
|
||||
{
|
||||
await FormatBodyAsync(programState, consoleManager, response.Content, bodyFileWriter, cancellationToken).ConfigureAwait(false);
|
||||
await FormatBodyAsync(commandInput, programState, consoleManager, response.Content, bodyFileWriter, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
bodyFileWriter.Flush();
|
||||
|
|
@ -321,7 +325,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
consoleManager.WriteLine();
|
||||
}
|
||||
|
||||
private static async Task FormatBodyAsync(HttpState programState, IConsoleManager consoleManager, HttpContent content, StreamWriter bodyFileWriter, CancellationToken cancellationToken)
|
||||
private static async Task FormatBodyAsync(DefaultCommandInput<ICoreParseResult> commandInput, HttpState programState, IConsoleManager consoleManager, HttpContent content, StreamWriter bodyFileWriter, CancellationToken cancellationToken)
|
||||
{
|
||||
string contentType = null;
|
||||
if (content.Headers.TryGetValues("Content-Type", out IEnumerable<string> contentTypeValues))
|
||||
|
|
@ -331,33 +335,36 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
contentType = contentType?.ToUpperInvariant() ?? "text/plain";
|
||||
|
||||
if (contentType.EndsWith("/JSON", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("-JSON", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("+JSON", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("/JAVASCRIPT", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("-JAVASCRIPT", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("+JAVASCRIPT", StringComparison.OrdinalIgnoreCase))
|
||||
if (commandInput.Options[NoFormattingOption].Count == 0)
|
||||
{
|
||||
if (await FormatJsonAsync(programState, consoleManager, content, bodyFileWriter))
|
||||
if (contentType.EndsWith("/JSON", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("-JSON", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("+JSON", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("/JAVASCRIPT", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("-JAVASCRIPT", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("+JAVASCRIPT", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
if (await FormatJsonAsync(programState, consoleManager, content, bodyFileWriter))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (contentType.EndsWith("/HTML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("-HTML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("+HTML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("/XML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("-XML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("+XML", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (await FormatXmlAsync(consoleManager, content, bodyFileWriter))
|
||||
else if (contentType.EndsWith("/HTML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("-HTML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("+HTML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("/XML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("-XML", StringComparison.OrdinalIgnoreCase)
|
||||
|| contentType.EndsWith("+XML", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
if (await FormatXmlAsync(consoleManager, content, bodyFileWriter))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If we don't have content length, assume streaming
|
||||
if (!content.Headers.TryGetValues("Content-Length", out IEnumerable<string> _))
|
||||
//Unless the user has explicitly specified to not stream the response, if we don't have content length, assume streaming
|
||||
if (commandInput.Options[NoStreamingOption].Count == 0 && !content.Headers.TryGetValues("Content-Length", out IEnumerable<string> _))
|
||||
{
|
||||
Memory<char> buffer = new Memory<char>(new char[2048]);
|
||||
Stream s = await content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
|
|
|
|||
|
|
@ -30,15 +30,7 @@ namespace Microsoft.HttpRepl.Commands
|
|||
{
|
||||
if (parseResult.Sections.Count == 1)
|
||||
{
|
||||
foreach (ICommand<HttpState, ICoreParseResult> command in dispatcher.Commands)
|
||||
{
|
||||
string help = command.GetHelpSummary(shellState, programState);
|
||||
|
||||
if (!string.IsNullOrEmpty(help))
|
||||
{
|
||||
shellState.ConsoleManager.WriteLine(help);
|
||||
}
|
||||
}
|
||||
CoreGetHelp(shellState, dispatcher, programState);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -171,5 +163,18 @@ namespace Microsoft.HttpRepl.Commands
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void CoreGetHelp(IShellState shellState, ICommandDispatcher<HttpState, ICoreParseResult> dispatcher, HttpState programState)
|
||||
{
|
||||
foreach (ICommand<HttpState, ICoreParseResult> command in dispatcher.Commands)
|
||||
{
|
||||
string help = command.GetHelpSummary(shellState, programState);
|
||||
|
||||
if (!string.IsNullOrEmpty(help))
|
||||
{
|
||||
shellState.ConsoleManager.WriteLine(help);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
|
||||
<ItemGroup>
|
||||
<SignedPackageFile Include="tools/$(TargetFramework)/any/Microsoft.Repl.dll" Certificate="$(AssemblySigningCertName)" />
|
||||
<SignedPackageFile Include="tools/$(TargetFramework)/any/Newtonsoft.Json.dll" Certificate="$(Microsoft3rdPartyAppComponentDual)" />
|
||||
<SignedPackageFile Include="tools/$(TargetFramework)/any/Newtonsoft.Json.Bson.dll" Certificate="$(Microsoft3rdPartyAppComponentDual)" />
|
||||
<SignedPackageFile Include="tools/$(TargetFramework)/any/Newtonsoft.Json.dll" Certificate="$(AssemblySigning3rdPartyCertName)" />
|
||||
<SignedPackageFile Include="tools/$(TargetFramework)/any/Newtonsoft.Json.Bson.dll" Certificate="$(AssemblySigning3rdPartyCertName)" />
|
||||
<SignedPackageFile Include="tools/$(TargetFramework)/any/System.Net.Http.Formatting.dll" Certificate="$(AssemblySigningCertName)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -145,77 +145,5 @@ namespace Microsoft.HttpRepl.OpenApi
|
|||
|
||||
return toResolve;
|
||||
}
|
||||
|
||||
//public static async Task<JToken> ResolvePointerAsync(this JToken root, HttpClient client, string pointer)
|
||||
//{
|
||||
// if (!pointer.StartsWith("#/", StringComparison.Ordinal))
|
||||
// {
|
||||
// HttpResponseMessage response = await client.GetAsync(pointer).ConfigureAwait(false);
|
||||
|
||||
// if (!response.IsSuccessStatusCode)
|
||||
// {
|
||||
// //TODO: Failed to resolve pointer message
|
||||
// return new JValue((object)null);
|
||||
// }
|
||||
|
||||
// string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
|
||||
// try
|
||||
// {
|
||||
// root = JToken.Parse(responseString);
|
||||
// int hashIndex = pointer.IndexOf("#");
|
||||
|
||||
// if (hashIndex < 0)
|
||||
// {
|
||||
// return root;
|
||||
// }
|
||||
|
||||
// pointer = pointer.Substring(hashIndex);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// //TODO: Failed to deserialize pointer message
|
||||
// return new JValue((object)null);
|
||||
// }
|
||||
// }
|
||||
|
||||
// string[] pointerParts = pointer.Split('/');
|
||||
|
||||
// for (int i = 1; !(root is null) && i < pointerParts.Length; ++i)
|
||||
// {
|
||||
// if (root is JArray arr)
|
||||
// {
|
||||
// if (!int.TryParse(pointerParts[i], System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out int result) || result < 0 || result >= arr.Count)
|
||||
// {
|
||||
// //TODO: Failed to resolve pointer part message (non-integer index to array or index out of range)
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// root = arr[result];
|
||||
// }
|
||||
// else if (root is JObject obj)
|
||||
// {
|
||||
// root = obj[pointerParts[i]];
|
||||
|
||||
// if (root is null)
|
||||
// {
|
||||
// //TODO: Failed to resolve pointer part message (no such path in object)
|
||||
// }
|
||||
|
||||
// JToken nestedRef = root["$ref"];
|
||||
// if (nestedRef is JValue value && value.Type == JTokenType.String)
|
||||
// {
|
||||
// root = await ResolvePointerAsync(root, )
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// //TODO: Failed to resolve pointer part message (pathing into literal)
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return root;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ namespace Microsoft.HttpRepl.Preferences
|
|||
}
|
||||
}
|
||||
|
||||
#region JSON
|
||||
public static string JsonArrayBraceColor { get; } = "colors.json.arrayBrace";
|
||||
|
||||
public static string JsonObjectBraceColor { get; } = "colors.json.objectBrace";
|
||||
|
|
@ -66,7 +65,6 @@ namespace Microsoft.HttpRepl.Preferences
|
|||
public static string JsonSyntaxColor { get; } = "colors.json.syntax";
|
||||
|
||||
public static string JsonBraceColor { get; } = "colors.json.brace";
|
||||
#endregion JSON
|
||||
|
||||
public static string RequestColor { get; } = "colors.request";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Repl;
|
||||
using Microsoft.Repl.Commanding;
|
||||
using Microsoft.Repl.ConsoleHandling;
|
||||
using Microsoft.Repl.Parsing;
|
||||
using Microsoft.HttpRepl.Commands;
|
||||
|
||||
|
|
@ -14,6 +16,12 @@ namespace Microsoft.HttpRepl
|
|||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
if(Console.IsOutputRedirected)
|
||||
{
|
||||
Reporter.Error.WriteLine("Cannot start the REPL when output is being redirected".Bold().Red());
|
||||
return;
|
||||
}
|
||||
|
||||
var state = new HttpState();
|
||||
var dispatcher = DefaultCommandDispatcher.Create(state.GetPrompt, state);
|
||||
|
||||
|
|
@ -44,6 +52,22 @@ namespace Microsoft.HttpRepl
|
|||
shell.ShellState.ConsoleManager.AddBreakHandler(() => source.Cancel());
|
||||
if (args.Length > 0)
|
||||
{
|
||||
if (string.Equals(args[0], "--help", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
shell.ShellState.ConsoleManager.WriteLine("Usage: dotnet httprepl [<BASE_ADDRESS>] [options]");
|
||||
shell.ShellState.ConsoleManager.WriteLine();
|
||||
shell.ShellState.ConsoleManager.WriteLine("Arguments:");
|
||||
shell.ShellState.ConsoleManager.WriteLine(" <BASE_ADDRESS> - The initial base address for the REPL.");
|
||||
shell.ShellState.ConsoleManager.WriteLine();
|
||||
shell.ShellState.ConsoleManager.WriteLine("Options:");
|
||||
shell.ShellState.ConsoleManager.WriteLine(" --help - Show help information.");
|
||||
|
||||
shell.ShellState.ConsoleManager.WriteLine();
|
||||
shell.ShellState.ConsoleManager.WriteLine("REPL Commands:");
|
||||
new HelpCommand().CoreGetHelp(shell.ShellState, (ICommandDispatcher<HttpState, ICoreParseResult>)shell.ShellState.CommandDispatcher, state);
|
||||
return;
|
||||
}
|
||||
|
||||
shell.ShellState.CommandDispatcher.OnReady(shell.ShellState);
|
||||
shell.ShellState.InputManager.SetInput(shell.ShellState, $"set base \"{args[0]}\"");
|
||||
await shell.ShellState.CommandDispatcher.ExecuteCommandAsync(shell.ShellState, CancellationToken.None).ConfigureAwait(false);
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ namespace Microsoft.Repl.Commanding
|
|||
}
|
||||
|
||||
shellState.ConsoleManager.Error.WriteLine("No matching command found".Red().Bold());
|
||||
shellState.ConsoleManager.Error.WriteLine("Execute 'help' to se available commands".Red().Bold());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ namespace Microsoft.Repl.Input
|
|||
|
||||
IInputManager RegisterKeyHandler(ConsoleKey key, AsyncKeyPressHandler handler);
|
||||
|
||||
IInputManager RegisterKeyHandler(ConsoleKey key, ConsoleModifiers modifiers, AsyncKeyPressHandler handler);
|
||||
|
||||
void ResetInput();
|
||||
|
||||
Task StartAsync(IShellState state, CancellationToken cancellationToken);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.Repl.Input
|
|||
{
|
||||
public class InputManager : IInputManager
|
||||
{
|
||||
private readonly Dictionary<ConsoleKey, AsyncKeyPressHandler> _handlers = new Dictionary<ConsoleKey, AsyncKeyPressHandler>();
|
||||
private readonly Dictionary<ConsoleKey, Dictionary<ConsoleModifiers, AsyncKeyPressHandler>> _handlers = new Dictionary<ConsoleKey, Dictionary<ConsoleModifiers, AsyncKeyPressHandler>>();
|
||||
private readonly List<char> _inputBuffer = new List<char>();
|
||||
|
||||
public bool IsOverwriteMode { get; set; }
|
||||
|
|
@ -29,13 +29,37 @@ namespace Microsoft.Repl.Input
|
|||
|
||||
public IInputManager RegisterKeyHandler(ConsoleKey key, AsyncKeyPressHandler handler)
|
||||
{
|
||||
if (!_handlers.TryGetValue(key, out Dictionary<ConsoleModifiers, AsyncKeyPressHandler> handlers))
|
||||
{
|
||||
_handlers[key] = handlers = new Dictionary<ConsoleModifiers, AsyncKeyPressHandler>();
|
||||
}
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
_handlers.Remove(key);
|
||||
handlers.Remove(default(ConsoleModifiers));
|
||||
}
|
||||
else
|
||||
{
|
||||
_handlers[key] = handler;
|
||||
handlers[default(ConsoleModifiers)] = handler;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IInputManager RegisterKeyHandler(ConsoleKey key, ConsoleModifiers modifiers, AsyncKeyPressHandler handler)
|
||||
{
|
||||
if (!_handlers.TryGetValue(key, out Dictionary<ConsoleModifiers, AsyncKeyPressHandler> handlers))
|
||||
{
|
||||
_handlers[key] = handlers = new Dictionary<ConsoleModifiers, AsyncKeyPressHandler>();
|
||||
}
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
handlers.Remove(modifiers);
|
||||
}
|
||||
else
|
||||
{
|
||||
handlers[modifiers] = handler;
|
||||
}
|
||||
|
||||
return this;
|
||||
|
|
@ -169,7 +193,7 @@ namespace Microsoft.Repl.Input
|
|||
{
|
||||
ConsoleKeyInfo keyPress = state.ConsoleManager.ReadKey(cancellationToken);
|
||||
|
||||
if (_handlers.TryGetValue(keyPress.Key, out AsyncKeyPressHandler handler))
|
||||
if (_handlers.TryGetValue(keyPress.Key, out Dictionary<ConsoleModifiers, AsyncKeyPressHandler> handlerLookup) && handlerLookup.TryGetValue(keyPress.Modifiers, out AsyncKeyPressHandler handler))
|
||||
{
|
||||
using (CancellationTokenSource source = new CancellationTokenSource())
|
||||
using (state.ConsoleManager.AddBreakHandler(() => source.Cancel()))
|
||||
|
|
@ -189,6 +213,7 @@ namespace Microsoft.Repl.Input
|
|||
FlushInput(state, ref presses);
|
||||
}
|
||||
|
||||
//TODO: Verify on a mac whether these are still needed
|
||||
if (keyPress.Key == ConsoleKey.A)
|
||||
{
|
||||
state.ConsoleManager.MoveCaret(-state.ConsoleManager.CaretPosition);
|
||||
|
|
@ -198,6 +223,7 @@ namespace Microsoft.Repl.Input
|
|||
state.ConsoleManager.MoveCaret(_inputBuffer.Count - state.ConsoleManager.CaretPosition);
|
||||
}
|
||||
}
|
||||
//TODO: Register these like regular commands
|
||||
else if (!string.IsNullOrEmpty(_ttyState) && keyPress.Modifiers == ConsoleModifiers.Alt)
|
||||
{
|
||||
if (presses != null)
|
||||
|
|
|
|||
|
|
@ -14,9 +14,13 @@ namespace Microsoft.Repl.Input
|
|||
{
|
||||
//Navigation in line
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.LeftArrow, LeftArrow);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.LeftArrow, ConsoleModifiers.Control, LeftArrow);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.RightArrow, RightArrow);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.RightArrow, ConsoleModifiers.Control, RightArrow);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.Home, Home);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.A, ConsoleModifiers.Control, Home);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.End, End);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.E, ConsoleModifiers.Control, End);
|
||||
|
||||
//Command history
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.UpArrow, UpArrow);
|
||||
|
|
@ -24,9 +28,11 @@ namespace Microsoft.Repl.Input
|
|||
|
||||
//Completion
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.Tab, Tab);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.Tab, ConsoleModifiers.Shift, Tab);
|
||||
|
||||
//Input manipulation
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.Escape, Escape);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.U, ConsoleModifiers.Control, Escape);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.Delete, Delete);
|
||||
inputManager.RegisterKeyHandler(ConsoleKey.Backspace, Backspace);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<Description>A framework for creating REPLs in .NET Core.</Description>
|
||||
<PackageTags>dotnet;repl</PackageTags>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
|
||||
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(CommandLine_NewtonsoftJsonPackageVersion)" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in New Issue