diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props
index a44977b00..8c24929f4 100644
--- a/build/DependencyVersions.props
+++ b/build/DependencyVersions.props
@@ -12,6 +12,7 @@
$(MicrosoftBuildPackageVersion)
10.1.4-rtm-180513-0
2.8.1-beta6-62911-06
+ $(MicrosoftCodeAnalysisCSharpPackageVersion)
$(MicrosoftCodeAnalysisCSharpPackageVersion)
$(MicrosoftCodeAnalysisCSharpPackageVersion)
$(MicrosoftCodeAnalysisCSharpPackageVersion)
diff --git a/build/test/TestPackageProjects.targets b/build/test/TestPackageProjects.targets
index 260eda2c8..4e4e8b1d2 100644
--- a/build/test/TestPackageProjects.targets
+++ b/build/test/TestPackageProjects.targets
@@ -29,6 +29,7 @@
$(CliVersionPrefix)
$(VersionSuffix)
False
+ /p:NoWarn=NU5104
diff --git a/src/dotnet/Parser.cs b/src/dotnet/Parser.cs
index 644ddd5c0..0697058d4 100644
--- a/src/dotnet/Parser.cs
+++ b/src/dotnet/Parser.cs
@@ -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)));
}
}
diff --git a/src/dotnet/commands/dotnet-complete/CompleteCommand.cs b/src/dotnet/commands/dotnet-complete/CompleteCommand.cs
index a6061d37d..e3571f13b 100644
--- a/src/dotnet/commands/dotnet-complete/CompleteCommand.cs
+++ b/src/dotnet/commands/dotnet-complete/CompleteCommand.cs
@@ -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)
diff --git a/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommandParser.cs b/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommandParser.cs
index 1c83a86b3..303abb1fe 100644
--- a/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommandParser.cs
+++ b/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommandParser.cs
@@ -10,7 +10,8 @@ namespace Microsoft.DotNet.Cli
{
public static Command InternalReportinstallsuccess() =>
Create.Command(
- "internal-reportinstallsuccess", "internal only",
+ "internal-reportinstallsuccess",
+ "",
Accept.ExactlyOneArgument());
}
}
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-new/NewCommandParser.cs b/src/dotnet/commands/dotnet-new/NewCommandParser.cs
deleted file mode 100644
index 88d81208a..000000000
--- a/src/dotnet/commands/dotnet-new/NewCommandParser.cs
+++ /dev/null
@@ -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"));
- }
-}
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-new/NewCommandShim.cs b/src/dotnet/commands/dotnet-new/NewCommandShim.cs
index 8c0c2acd4..b01f3c143 100644
--- a/src/dotnet/commands/dotnet-new/NewCommandShim.cs
+++ b/src/dotnet/commands/dotnet-new/NewCommandShim.cs
@@ -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)
{
diff --git a/src/dotnet/commands/dotnet-nuget/NuGetCommandParser.cs b/src/dotnet/commands/dotnet-nuget/NuGetCommandParser.cs
index fd80e5589..8832950c2 100644
--- a/src/dotnet/commands/dotnet-nuget/NuGetCommandParser.cs
+++ b/src/dotnet/commands/dotnet-nuget/NuGetCommandParser.cs
@@ -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)));
}
-}
\ No newline at end of file
+}
diff --git a/test/dotnet.Tests/CommandTests/CompleteCommandTests.cs b/test/dotnet.Tests/CommandTests/CompleteCommandTests.cs
new file mode 100644
index 000000000..c9323ea45
--- /dev/null
+++ b/test/dotnet.Tests/CommandTests/CompleteCommandTests.cs
@@ -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));
+ }
+ }
+}
diff --git a/test/dotnet.Tests/TelemetryCommandTest.cs b/test/dotnet.Tests/TelemetryCommandTest.cs
index 3668dee1a..3ab0b0f18 100644
--- a/test/dotnet.Tests/TelemetryCommandTest.cs
+++ b/test/dotnet.Tests/TelemetryCommandTest.cs
@@ -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