Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

[REPL] Help improvements for repl #478

Merged
merged 2 commits into from
Sep 7, 2018
Merged
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
15 changes: 13 additions & 2 deletions src/Microsoft.HttpRepl/Commands/BaseHttpCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public abstract class BaseHttpCommand : CommandWithStructuredInputBase<HttpState

protected abstract bool RequiresBody { get; }

protected override CommandInputSpecification InputSpec
public override CommandInputSpecification InputSpec
{
get
{
Expand Down Expand Up @@ -469,7 +469,18 @@ private static async Task<bool> FormatJsonAsync(HttpState programState, IWritabl

protected override string GetHelpDetails(IShellState shellState, HttpState programState, DefaultCommandInput<ICoreParseResult> commandInput, ICoreParseResult parseResult)
{
return $"Issues a {Verb.ToUpperInvariant()} request";
var helpText = new StringBuilder();
helpText.Append("Usage: ".Bold());
helpText.AppendLine($"{Verb.ToUpperInvariant()} [Options]");
helpText.AppendLine();
helpText.AppendLine($"Issues a {Verb.ToUpperInvariant()} request.");

if (RequiresBody)
{
helpText.AppendLine("Your default editor will be opened with a sample body if no options are provided.");
}

return helpText.ToString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to set these to a particular color from preferences?

}

public override string GetHelpSummary(IShellState shellState, HttpState programState)
Expand Down
15 changes: 9 additions & 6 deletions src/Microsoft.HttpRepl/Commands/ChangeDirectoryCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.HttpRepl.Suggestions;
using Microsoft.Repl;
using Microsoft.Repl.Commanding;
using Microsoft.Repl.ConsoleHandling;
using Microsoft.Repl.Parsing;

namespace Microsoft.HttpRepl.Commands
Expand Down Expand Up @@ -60,18 +62,19 @@ protected override Task ExecuteAsync(IShellState shellState, HttpState programSt
return Task.CompletedTask;
}

protected override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("cd")
public override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("cd")
.MaximumArgCount(1)
.Finish();

protected override string GetHelpDetails(IShellState shellState, HttpState programState, DefaultCommandInput<ICoreParseResult> commandInput, ICoreParseResult parseResult)
{
if (commandInput.Arguments.Count == 1 && !string.IsNullOrEmpty(commandInput.Arguments[0]?.Text))
{
return "Prints the current directory if no argument is specified, otherwise changes to the specified directory";
}
var help = new StringBuilder();
help.Append("Usage:".Bold());
help.AppendLine("cd [directory]");
help.AppendLine();
help.AppendLine("Prints the current directory if no argument is specified, otherwise changes to the specified directory");

return "Changes to the directory " + commandInput.Arguments[0].Text;
return help.ToString();
}

public override string GetHelpSummary(IShellState shellState, HttpState programState)
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.HttpRepl/Commands/ConfigCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected override async Task ExecuteAsync(IShellState shellState, HttpState pro
}
}

protected override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("config").Finish();
public override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("config").Finish();

protected override string GetHelpDetails(IShellState shellState, HttpState programState, DefaultCommandInput<ICoreParseResult> commandInput, ICoreParseResult parseResult)
{
Expand Down
10 changes: 8 additions & 2 deletions src/Microsoft.HttpRepl/Commands/EchoCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Repl;
Expand Down Expand Up @@ -37,11 +38,16 @@ protected override Task ExecuteAsync(IShellState shellState, HttpState programSt
return Task.CompletedTask;
}

protected override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("echo").ExactArgCount(1).Finish();
public override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("echo").ExactArgCount(1).Finish();

protected override string GetHelpDetails(IShellState shellState, HttpState programState, DefaultCommandInput<ICoreParseResult> commandInput, ICoreParseResult parseResult)
{
return "Turns request echoing on or off";
var helpText = new StringBuilder();
helpText.Append("Usage: ".Bold());
helpText.AppendLine($"echo [on|off]");
helpText.AppendLine();
helpText.AppendLine($"Turns request echoing on or off. When request echoing is on we will display a text representation of requests made by the CLI.");
return helpText.ToString();
}

public override string GetHelpSummary(IShellState shellState, HttpState programState)
Expand Down
11 changes: 9 additions & 2 deletions src/Microsoft.HttpRepl/Commands/ExitCommand.cs
Original file line number Diff line number Diff line change
@@ -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.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Repl;
using Microsoft.Repl.Commanding;
using Microsoft.Repl.ConsoleHandling;
using Microsoft.Repl.Parsing;

namespace Microsoft.HttpRepl.Commands
Expand All @@ -17,11 +19,16 @@ protected override Task ExecuteAsync(IShellState shellState, object programState
return Task.CompletedTask;
}

protected override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("exit").ExactArgCount(0).Finish();
public override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("exit").ExactArgCount(0).Finish();

protected override string GetHelpDetails(IShellState shellState, object programState, DefaultCommandInput<ICoreParseResult> commandInput, ICoreParseResult parseResult)
{
return "Exits the shell";
var helpText = new StringBuilder();
helpText.Append("Usage: ".Bold());
helpText.AppendLine($"exit");
helpText.AppendLine();
helpText.AppendLine($"Exits the shell");
return helpText.ToString();
}

public override string GetHelpSummary(IShellState shellState, object programState)
Expand Down
130 changes: 98 additions & 32 deletions src/Microsoft.HttpRepl/Commands/HelpCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.HttpRepl.Suggestions;
using Microsoft.Repl;
using Microsoft.Repl.Commanding;
using Microsoft.Repl.ConsoleHandling;
using Microsoft.Repl.Parsing;

namespace Microsoft.HttpRepl.Commands
Expand Down Expand Up @@ -46,7 +47,30 @@ public async Task ExecuteAsync(IShellState shellState, HttpState programState, I
if (!string.IsNullOrEmpty(help))
{
anyHelp = true;
shellState.ConsoleManager.WriteLine();
shellState.ConsoleManager.WriteLine(help);

var structuredCommand = command as CommandWithStructuredInputBase<HttpState, ICoreParseResult>;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an attempt to provide a consistent options help section for everything that needs it. Though I don't know that it's the best approach. Looking for ideas from you @mlorbetske.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a reasonable way of going about this. I'm not a huge fan of this only working for things that derive from CommandWithStructuredInputBase<HttpState, ICoreParseResult>, but I don't see us adding additional commands that don't derive from that - making InputSpec public doesn't feel the best either, but I don't think it hurts anything.

We could add a method to get the options help to ICommand<,> and implement it in CommandWithStructuredInputBase<,> with the logic below - this would allow InputSpec to become protected again and lift the base type restriction, but it probably doesn't matter too much.

if (structuredCommand != null && structuredCommand.InputSpec.Options.Any())
{
shellState.ConsoleManager.WriteLine();
shellState.ConsoleManager.WriteLine("Options:".Bold());
foreach (var option in structuredCommand.InputSpec.Options)
{
var optionText = string.Empty;
foreach (var form in option.Forms)
{
if (!string.IsNullOrEmpty(optionText))
{
optionText += "|";
}
optionText += form;
}
shellState.ConsoleManager.WriteLine($" {optionText}");
}
}

break;
}
}
}
Expand All @@ -67,39 +91,42 @@ public async Task ExecuteAsync(IShellState shellState, HttpState programState, I
}
}

IDirectoryStructure structure = programState.Structure.TraverseTo(parseResult.Sections[1]);
if (structure.DirectoryNames.Any())
//Structure is null because, for example, SwaggerEndpoint exists but is not reachable.
if (programState.Structure != null)
{
shellState.ConsoleManager.WriteLine("Child directories:");

foreach (string name in structure.DirectoryNames)
IDirectoryStructure structure = programState.Structure.TraverseTo(parseResult.Sections[1]);
if (structure.DirectoryNames.Any())
{
shellState.ConsoleManager.WriteLine(" " + name + "/");
}

anyHelp = true;
}
shellState.ConsoleManager.WriteLine("Child directories:");

if (structure.RequestInfo != null)
{
if (structure.RequestInfo.Methods.Count > 0)
{
if (anyHelp)
foreach (string name in structure.DirectoryNames)
{
shellState.ConsoleManager.WriteLine();
shellState.ConsoleManager.WriteLine(" " + name + "/");
}

anyHelp = true;
shellState.ConsoleManager.WriteLine("Available methods:");
}

foreach (string method in structure.RequestInfo.Methods)
if (structure.RequestInfo != null)
{
if (structure.RequestInfo.Methods.Count > 0)
{
shellState.ConsoleManager.WriteLine(" " + method.ToUpperInvariant());
IReadOnlyList<string> accepts = structure.RequestInfo.ContentTypesByMethod[method];
string acceptsString = string.Join(", ", accepts.Where(x => !string.IsNullOrEmpty(x)));
if (!string.IsNullOrEmpty(acceptsString))
if (anyHelp)
{
shellState.ConsoleManager.WriteLine();
}

anyHelp = true;
shellState.ConsoleManager.WriteLine("Available methods:");

foreach (string method in structure.RequestInfo.Methods)
{
shellState.ConsoleManager.WriteLine(" Accepts: " + acceptsString);
shellState.ConsoleManager.WriteLine(" " + method.ToUpperInvariant());
IReadOnlyList<string> accepts = structure.RequestInfo.ContentTypesByMethod[method];
string acceptsString = string.Join(", ", accepts.Where(x => !string.IsNullOrEmpty(x)));
if (!string.IsNullOrEmpty(acceptsString))
{
shellState.ConsoleManager.WriteLine(" Accepts: " + acceptsString);
}
}
}
}
Expand Down Expand Up @@ -176,15 +203,54 @@ public IEnumerable<string> Suggest(IShellState shellState, HttpState programStat

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);
shellState.ConsoleManager.WriteLine();
shellState.ConsoleManager.WriteLine("HTTP Commands:".Bold().Cyan());
shellState.ConsoleManager.WriteLine("Use these commands to execute requests against your application.");
shellState.ConsoleManager.WriteLine();

if (!string.IsNullOrEmpty(help))
{
shellState.ConsoleManager.WriteLine(help);
}
}
const int navCommandColumn = -15;

shellState.ConsoleManager.WriteLine($"{"GET",navCommandColumn}{"Issues a GET request."}");
shellState.ConsoleManager.WriteLine($"{"POST",navCommandColumn}{"Issues a POST request."}");
shellState.ConsoleManager.WriteLine($"{"PUT",navCommandColumn}{"Issues a PUT request."}");
shellState.ConsoleManager.WriteLine($"{"DELETE",navCommandColumn}{"Issues a DELETE request."}");
shellState.ConsoleManager.WriteLine($"{"PATCH",navCommandColumn}{"Issues a PATCH request."}");
shellState.ConsoleManager.WriteLine($"{"HEAD",navCommandColumn}{"Issues a HEAD request."}");
shellState.ConsoleManager.WriteLine($"{"OPTIONS",navCommandColumn}{"Issues an OPTIONS request."}");
shellState.ConsoleManager.WriteLine();
shellState.ConsoleManager.WriteLine($"{"set header",navCommandColumn}{"Sets or clears a header for all requests. e.g. `set header content-type:application/json`"}");
shellState.ConsoleManager.WriteLine();

shellState.ConsoleManager.WriteLine();
shellState.ConsoleManager.WriteLine("Navigation Commands:".Bold().Cyan());
shellState.ConsoleManager.WriteLine("The REPL allows you to navigate your URL space and focus on specific APIS that you are working on.");
shellState.ConsoleManager.WriteLine();

shellState.ConsoleManager.WriteLine($"{"set base",navCommandColumn}{"Set the base URI. e.g. `set base http://locahost:5000`"}");
shellState.ConsoleManager.WriteLine($"{"set swagger",navCommandColumn}{"Set the URI, relative to your base if set, of the Swagger document for this API. e.g. `set swagger /swagger/v1/swagger.json`"}");
shellState.ConsoleManager.WriteLine($"{"ls",navCommandColumn}{"Show all endpoints for the current path."}");
shellState.ConsoleManager.WriteLine($"{"cd",navCommandColumn}{"Append the given directory to the currently selected path, or move up a path when using `cd ..`."}");

shellState.ConsoleManager.WriteLine();
shellState.ConsoleManager.WriteLine("Shell Commands:".Bold().Cyan());
shellState.ConsoleManager.WriteLine("Use these commands to interact with the REPL shell.");
shellState.ConsoleManager.WriteLine();

shellState.ConsoleManager.WriteLine($"{"clear",navCommandColumn}{"Removes all text from the shell."}");
shellState.ConsoleManager.WriteLine($"{"echo [on/off]",navCommandColumn}{"Turns request echoing on or off, show the request that was mode when using request commands."}");
shellState.ConsoleManager.WriteLine($"{"exit",navCommandColumn}{"Exit the shell."}");

shellState.ConsoleManager.WriteLine();
shellState.ConsoleManager.WriteLine("REPL Customization Commands:".Bold().Cyan());
shellState.ConsoleManager.WriteLine("Use these commands to customize the REPL behavior..");
shellState.ConsoleManager.WriteLine();

shellState.ConsoleManager.WriteLine($"{"pref [get/set]",navCommandColumn}{"Allows viewing or changing preferences, e.g. 'pref set editor.command.default 'C:\\Program Files\\Microsoft VS Code\\Code.exe'`"}");
shellState.ConsoleManager.WriteLine($"{"run",navCommandColumn}{"Runs the script at the given path. A script is a set of commands that can be typed with one command per line."}");
shellState.ConsoleManager.WriteLine($"{"ui",navCommandColumn}{"Displays the swagger UI page, if available, in the default browser."}");
shellState.ConsoleManager.WriteLine();
shellState.ConsoleManager.WriteLine("Use help <COMMAND> to learn more details about individual commands. e.g. `help get`".Bold().Cyan());
shellState.ConsoleManager.WriteLine();
}
}
}
13 changes: 10 additions & 3 deletions src/Microsoft.HttpRepl/Commands/ListCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.HttpRepl.Preferences;
using Microsoft.Repl;
using Microsoft.Repl.Commanding;
using Microsoft.Repl.ConsoleHandling;
using Microsoft.Repl.Parsing;

namespace Microsoft.HttpRepl.Commands
Expand Down Expand Up @@ -118,19 +120,24 @@ private static void Recurse(TreeNode parentNode, IDirectoryStructure parent, int



protected override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("ls").AlternateName("dir")
public override CommandInputSpecification InputSpec { get; } = CommandInputSpecification.Create("ls").AlternateName("dir")
.MaximumArgCount(1)
.WithOption(new CommandOptionSpecification(RecursiveOption, maximumOccurrences: 1, acceptsValue: true, forms: new[] {"-r", "--recursive"}))
.Finish();

protected override string GetHelpDetails(IShellState shellState, HttpState programState, DefaultCommandInput<ICoreParseResult> commandInput, ICoreParseResult parseResult)
{
return "Lists the contents of a directory";
var helpText = new StringBuilder();
helpText.Append("Usage: ".Bold());
helpText.AppendLine($"ls [Options]");
helpText.AppendLine();
helpText.AppendLine($"Displays the known routes at the current location. Requires a Swagger document to be set.");
return helpText.ToString();
}

public override string GetHelpSummary(IShellState shellState, HttpState programState)
{
return "ls - Performs a directory listing";
return "ls - List known routes for the current location";
}

protected override IEnumerable<string> GetArgumentSuggestionsForText(IShellState shellState, HttpState programState, ICoreParseResult parseResult, DefaultCommandInput<ICoreParseResult> commandInput, string normalCompletionString)
Expand Down
Loading