Merge pull request #9294 from peterhuene/fix-completion

Improve command completion.
This commit is contained in:
Peter Huene 2018-05-17 11:02:57 -07:00 committed by GitHub
commit b5b47dd40d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 271 additions and 116 deletions

View file

@ -3,14 +3,23 @@
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Tools.Help;
using Microsoft.DotNet.Tools.New;
using static System.Environment;
using static Microsoft.DotNet.Cli.CommandLine.LocalizableStrings;
using LocalizableStrings = Microsoft.DotNet.Tools.Run.LocalizableStrings;
using NewCommandParser = Microsoft.TemplateEngine.Cli.CommandParsing.CommandParserSupport;
namespace Microsoft.DotNet.Cli
{
public static class Parser
{
// This is used for descriptions of commands and options that are only defined for `dotnet complete` (i.e. command line completion).
// For example, a NuGet assembly handles parsing the `nuget` command and options.
// To get completion for such a command, we have to define a parser that is used for the completion.
// Command and option help text cannot be empty, otherwise the parser will hide them from the completion list.
// The value of `-` has no special meaning; it simply prevents these commands and options from being hidden.
internal const string CompletionOnlyDescription = "-";
static Parser()
{
ConfigureCommandLineLocalizedStrings();
@ -35,7 +44,7 @@ namespace Microsoft.DotNet.Cli
options: Create.Command("dotnet",
".NET Command Line Tools",
Accept.NoArguments(),
NewCommandParser.New(),
NewCommandParser.CreateNewCommandWithoutTemplateInfo(NewCommandShim.CommandName),
RestoreCommandParser.Restore(),
BuildCommandParser.Build(),
PublishCommandParser.Publish(),
@ -51,15 +60,17 @@ namespace Microsoft.DotNet.Cli
NuGetCommandParser.NuGet(),
StoreCommandParser.Store(),
HelpCommandParser.Help(),
Create.Command("msbuild", ""),
Create.Command("vstest", ""),
Create.Command("msbuild", CompletionOnlyDescription),
Create.Command("vstest", CompletionOnlyDescription),
CompleteCommandParser.Complete(),
InternalReportinstallsuccessCommandParser.InternalReportinstallsuccess(),
ToolCommandParser.Tool(),
BuildServerCommandParser.CreateCommand(),
CommonOptions.HelpOption(),
Create.Option("--info", ""),
Create.Option("-d", ""),
Create.Option("--debug", "")));
Create.Option("--info", CompletionOnlyDescription),
Create.Option("-d|--diagnostics", CompletionOnlyDescription),
Create.Option("--version", CompletionOnlyDescription),
Create.Option("--list-sdks", CompletionOnlyDescription),
Create.Option("--list-runtimes", CompletionOnlyDescription)));
}
}

View file

@ -12,6 +12,16 @@ namespace Microsoft.DotNet.Cli
{
public static int Run(string[] args)
{
return RunWithReporter(args, Reporter.Output);
}
public static int RunWithReporter(string [] args, IReporter reporter)
{
if (reporter == null)
{
throw new ArgumentNullException(nameof(reporter));
}
try
{
DebugHelper.HandleDebugSwitch(ref args);
@ -28,7 +38,7 @@ namespace Microsoft.DotNet.Cli
foreach (var suggestion in suggestions)
{
Console.WriteLine(suggestion);
reporter.WriteLine(suggestion);
}
}
catch (Exception)

View file

@ -10,7 +10,8 @@ namespace Microsoft.DotNet.Cli
{
public static Command InternalReportinstallsuccess() =>
Create.Command(
"internal-reportinstallsuccess", "internal only",
"internal-reportinstallsuccess",
"",
Accept.ExactlyOneArgument());
}
}

View file

@ -1,39 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.Cli.CommandLine;
namespace Microsoft.DotNet.Cli
{
internal static class NewCommandParser
{
public static Command New() =>
Create.Command("new",
"Initialize .NET projects.",
Accept
.ExactlyOneArgument()
.WithSuggestionsFrom(
"console",
"classlib",
"mstest",
"xunit",
"web",
"mvc",
"webapi",
"sln"),
Create.Option("-l|--list",
"List templates containing the specified name."),
Create.Option("-lang|--language",
"Specifies the language of the template to create",
Accept.WithSuggestionsFrom("C#", "F#")
.With(defaultValue: () => "C#")),
Create.Option("-n|--name",
"The name for the output being created. If no name is specified, the name of the current directory is used."),
Create.Option("-o|--output",
"Location to place the generated output."),
Create.Option("-h|--help",
"Displays help for this command."),
Create.Option("-all|--show-all",
"Shows all templates"));
}
}

View file

@ -24,8 +24,8 @@ namespace Microsoft.DotNet.Tools.New
{
internal class NewCommandShim
{
public const string CommandName = "new";
private const string HostIdentifier = "dotnetcli";
private const string CommandName = "new";
public static int Run(string[] args)
{

View file

@ -5,74 +5,52 @@ using Microsoft.DotNet.Cli.CommandLine;
namespace Microsoft.DotNet.Cli
{
// This parser is used for completion and telemetry.
// See https://github.com/NuGet/NuGet.Client for the actual implementation.
internal static class NuGetCommandParser
{
public static Command NuGet() =>
Create.Command("nuget",
"NuGet Command Line 4.0.0.0",
CommonOptions.HelpOption(),
Create.Option("--version",
"Show version information"),
Create.Option("-v|--verbosity",
"The verbosity of logging to use. Allowed values: Debug, Verbose, Information, Minimal, Warning, Error.",
Accept.ExactlyOneArgument()
.With(name: "verbosity")),
Create.Command("delete",
"Deletes a package from the server.",
Accept.ExactlyOneArgument()
.With(name: "root",
description: "The Package Id and version."),
CommonOptions.HelpOption(),
Create.Option("--force-english-output",
"Forces the application to run using an invariant, English-based culture."),
Create.Option("-s|--source",
"Specifies the server URL",
Accept.ExactlyOneArgument()
.With(name: "source")),
Create.Option("--non-interactive",
"Do not prompt for user input or confirmations."),
Create.Option("-k|--api-key",
"The API key for the server.",
Accept.ExactlyOneArgument()
.With(name: "apiKey"))),
Create.Command("locals",
"Clears or lists local NuGet resources such as http requests cache, packages cache or machine-wide global packages folder.",
Accept.AnyOneOf(@"all",
@"http-cache",
@"global-packages",
@"temp")
.With(description: "Cache Location(s) Specifies the cache location(s) to list or clear."),
CommonOptions.HelpOption(),
Create.Option("--force-english-output",
"Forces the application to run using an invariant, English-based culture."),
Create.Option("-c|--clear", "Clear the selected local resources or cache location(s)."),
Create.Option("-l|--list", "List the selected local resources or cache location(s).")),
Create.Command("push",
"Pushes a package to the server and publishes it.",
CommonOptions.HelpOption(),
Create.Option("--force-english-output",
"Forces the application to run using an invariant, English-based culture."),
Create.Option("-s|--source",
"Specifies the server URL",
Accept.ExactlyOneArgument()
.With(name: "source")),
Create.Option("-ss|--symbol-source",
"Specifies the symbol server URL. If not specified, nuget.smbsrc.net is used when pushing to nuget.org.",
Accept.ExactlyOneArgument()
.With(name: "source")),
Create.Option("-t|--timeout",
"Specifies the timeout for pushing to a server in seconds. Defaults to 300 seconds (5 minutes).",
Accept.ExactlyOneArgument()
.With(name: "timeout")),
Create.Option("-k|--api-key", "The API key for the server.",
Accept.ExactlyOneArgument()
.With(name: "apiKey")),
Create.Option("-sk|--symbol-api-key", "The API key for the symbol server.",
Accept.ExactlyOneArgument()
.With(name: "apiKey")),
Create.Option("-d|--disable-buffering",
"Disable buffering when pushing to an HTTP(S) server to decrease memory usage."),
Create.Option("-n|--no-symbols",
"If a symbols package exists, it will not be pushed to a symbols server.")));
Create.Command(
"nuget",
Parser.CompletionOnlyDescription,
Create.Option("-h|--help", Parser.CompletionOnlyDescription),
Create.Option("--version", Parser.CompletionOnlyDescription),
Create.Option("-v|--verbosity", Parser.CompletionOnlyDescription, Accept.ExactlyOneArgument()),
Create.Command(
"delete",
Parser.CompletionOnlyDescription,
Accept.OneOrMoreArguments(),
Create.Option("-h|--help", Parser.CompletionOnlyDescription),
Create.Option("--force-english-output", Parser.CompletionOnlyDescription),
Create.Option("-s|--source", Parser.CompletionOnlyDescription, Accept.ExactlyOneArgument()),
Create.Option("--non-interactive", Parser.CompletionOnlyDescription),
Create.Option("-k|--api-key", Parser.CompletionOnlyDescription, Accept.ExactlyOneArgument()),
Create.Option("--no-service-endpoint", Parser.CompletionOnlyDescription)),
Create.Command(
"locals",
Parser.CompletionOnlyDescription,
Accept.AnyOneOf(
"all",
"http-cache",
"global-packages",
"temp"),
Create.Option("-h|--help", Parser.CompletionOnlyDescription),
Create.Option("--force-english-output", Parser.CompletionOnlyDescription),
Create.Option("-c|--clear", Parser.CompletionOnlyDescription),
Create.Option("-l|--list", Parser.CompletionOnlyDescription)),
Create.Command(
"push",
Parser.CompletionOnlyDescription,
Accept.OneOrMoreArguments(),
Create.Option("-h|--help", Parser.CompletionOnlyDescription),
Create.Option("--force-english-output", Parser.CompletionOnlyDescription),
Create.Option("-s|--source", Parser.CompletionOnlyDescription, Accept.ExactlyOneArgument()),
Create.Option("-ss|--symbol-source", Parser.CompletionOnlyDescription, Accept.ExactlyOneArgument()),
Create.Option("-t|--timeout", Parser.CompletionOnlyDescription, Accept.ExactlyOneArgument()),
Create.Option("-k|--api-key", Parser.CompletionOnlyDescription, Accept.ExactlyOneArgument()),
Create.Option("-sk|--symbol-api-key", Parser.CompletionOnlyDescription, Accept.ExactlyOneArgument()),
Create.Option("-d|--disable-buffering", Parser.CompletionOnlyDescription),
Create.Option("-n|--no-symbols", Parser.CompletionOnlyDescription),
Create.Option("--no-service-endpoint", Parser.CompletionOnlyDescription)));
}
}
}

View file

@ -0,0 +1,194 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Linq;
using FluentAssertions;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
namespace Microsoft.DotNet.Tests.Commands
{
public class CompleteCommandTests : TestBase
{
[Fact]
public void GivenOnlyDotnetItSuggestsTopLevelCommandsAndOptions()
{
var expected = new string[] {
"--diagnostics",
"--help",
"--info",
"--list-runtimes",
"--list-sdks",
"--version",
"-d",
"-h",
"add",
"build",
"build-server",
"clean",
"help",
"list",
"migrate",
"msbuild",
"new",
"nuget",
"pack",
"publish",
"remove",
"restore",
"run",
"sln",
"store",
"test",
"tool",
"vstest"
};
var reporter = new BufferedReporter();
CompleteCommand.RunWithReporter(new[] { "dotnet " }, reporter).Should().Be(0);
reporter.Lines.Should().Equal(expected.OrderBy(c => c));
}
[Fact]
public void GivenASlashItSuggestsTopLevelOptions()
{
var expected = new string[] {
"--diagnostics",
"--help",
"--info",
"--list-runtimes",
"--list-sdks",
"--version",
"-d",
"-h",
"build-server" // This should be removed when completion is based on "starts with" rather than "contains".
// See https://github.com/dotnet/cli/issues/8958.
};
var reporter = new BufferedReporter();
CompleteCommand.RunWithReporter(new[] { "dotnet -" }, reporter).Should().Be(0);
reporter.Lines.Should().Equal(expected.OrderBy(c => c));
}
[Fact]
public void GivenNewCommandItDisplaysCompletions()
{
var expected = new string[] {
"--force",
"--help",
"--install",
"--language",
"--list",
"--name",
"--nuget-source",
"--output",
"--type",
"--uninstall",
"-h",
"-i",
"-l",
"-lang",
"-n",
"-o",
"-u"
};
var reporter = new BufferedReporter();
CompleteCommand.RunWithReporter(new[] { "dotnet new " }, reporter).Should().Be(0);
reporter.Lines.Should().Equal(expected.OrderBy(c => c));
}
[Fact]
public void GivenNuGetCommandItDisplaysCompletions()
{
var expected = new string[] {
"--help",
"--verbosity",
"--version",
"-h",
"-v",
"delete",
"locals",
"push",
};
var reporter = new BufferedReporter();
CompleteCommand.RunWithReporter(new[] { "dotnet nuget " }, reporter).Should().Be(0);
reporter.Lines.Should().Equal(expected.OrderBy(c => c));
}
[Fact]
public void GivenNuGetDeleteCommandItDisplaysCompletions()
{
var expected = new string[] {
"--api-key",
"--force-english-output",
"--help",
"--no-service-endpoint",
"--non-interactive",
"--source",
"-h",
"-k",
"-s"
};
var reporter = new BufferedReporter();
CompleteCommand.RunWithReporter(new[] { "dotnet nuget delete " }, reporter).Should().Be(0);
reporter.Lines.Should().Equal(expected.OrderBy(c => c));
}
[Fact]
public void GivenNuGetLocalsCommandItDisplaysCompletions()
{
var expected = new string[] {
"--clear",
"--force-english-output",
"--help",
"--list",
"-c",
"-h",
"-l",
"all",
"global-packages",
"http-cache",
"temp"
};
var reporter = new BufferedReporter();
CompleteCommand.RunWithReporter(new[] { "dotnet nuget locals " }, reporter).Should().Be(0);
reporter.Lines.Should().Equal(expected.OrderBy(c => c));
}
[Fact]
public void GivenNuGetPushCommandItDisplaysCompletions()
{
var expected = new string[] {
"--api-key",
"--disable-buffering",
"--force-english-output",
"--help",
"--no-service-endpoint",
"--no-symbols",
"--source",
"--symbol-api-key",
"--symbol-source",
"--timeout",
"-d",
"-h",
"-k",
"-n",
"-s",
"-sk",
"-ss",
"-t"
};
var reporter = new BufferedReporter();
CompleteCommand.RunWithReporter(new[] { "dotnet nuget push " }, reporter).Should().Be(0);
reporter.Lines.Should().Equal(expected.OrderBy(c => c));
}
}
}

View file

@ -163,7 +163,7 @@ namespace Microsoft.DotNet.Tests
{
const string argumentToSend = "push";
string[] args = { "nuget", argumentToSend };
string[] args = { "nuget", argumentToSend, "path" };
Cli.Program.ProcessArgs(args);
_fakeTelemetry