diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx
index 75c76f009030..f967ccdc2e1a 100644
--- a/src/Cli/dotnet/Commands/CliCommandStrings.resx
+++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx
@@ -1501,6 +1501,37 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
Convert a file-based program to a project-based program.
+
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+
+
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
PROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
index d4ae5207be00..7f8f6a0ee747 100644
--- a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
+++ b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.CommandLine;
+using System.Diagnostics.CodeAnalysis;
using Microsoft.Build.Evaluation;
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Utils;
@@ -17,17 +18,14 @@ internal sealed class ProjectConvertCommand(ParseResult parseResult) : CommandBa
public override int Execute()
{
+ // Check the entry point file path.
string file = Path.GetFullPath(_file);
if (!VirtualProjectBuildingCommand.IsValidEntryPointPath(file))
{
throw new GracefulException(CliCommandStrings.InvalidFilePath, file);
}
- string targetDirectory = _outputDirectory ?? Path.ChangeExtension(file, null);
- if (Directory.Exists(targetDirectory))
- {
- throw new GracefulException(CliCommandStrings.DirectoryAlreadyExists, targetDirectory);
- }
+ string targetDirectory = DetermineOutputDirectory(file);
// Find directives (this can fail, so do this before creating the target directory).
var sourceFile = VirtualProjectBuildingCommand.LoadSourceFile(file);
@@ -36,28 +34,37 @@ public override int Execute()
// Find other items to copy over, e.g., default Content items like JSON files in Web apps.
var includeItems = FindIncludedItems().ToList();
- Directory.CreateDirectory(targetDirectory);
+ bool dryRun = _parseResult.GetValue(ProjectConvertCommandParser.DryRunOption);
+
+ CreateDirectory(targetDirectory);
var targetFile = Path.Join(targetDirectory, Path.GetFileName(file));
- // If there were any directives, remove them from the file.
- if (directives.Length != 0)
+ // Process the entry point file.
+ if (dryRun)
{
- VirtualProjectBuildingCommand.RemoveDirectivesFromFile(directives, sourceFile.Text, targetFile);
- File.Delete(file);
+ Reporter.Output.WriteLine(CliCommandStrings.ProjectConvertWouldCopyFile, file, targetFile);
+ Reporter.Output.WriteLine(CliCommandStrings.ProjectConvertWouldConvertFile, targetFile);
}
else
{
- File.Move(file, targetFile);
+ VirtualProjectBuildingCommand.RemoveDirectivesFromFile(directives, sourceFile.Text, targetFile);
}
// Create project file.
string projectFile = Path.Join(targetDirectory, Path.GetFileNameWithoutExtension(file) + ".csproj");
- using var stream = File.Open(projectFile, FileMode.Create, FileAccess.Write);
- using var writer = new StreamWriter(stream, Encoding.UTF8);
- VirtualProjectBuildingCommand.WriteProjectFile(writer, directives, isVirtualProject: false);
+ if (dryRun)
+ {
+ Reporter.Output.WriteLine(CliCommandStrings.ProjectConvertWouldCreateFile, projectFile);
+ }
+ else
+ {
+ using var stream = File.Open(projectFile, FileMode.Create, FileAccess.Write);
+ using var writer = new StreamWriter(stream, Encoding.UTF8);
+ VirtualProjectBuildingCommand.WriteProjectFile(writer, directives, isVirtualProject: false);
+ }
- // Copy over included items.
+ // Copy or move over included items.
foreach (var item in includeItems)
{
string targetItemFullPath = Path.Combine(targetDirectory, item.RelativePath);
@@ -69,12 +76,39 @@ public override int Execute()
}
string targetItemDirectory = Path.GetDirectoryName(targetItemFullPath)!;
- Directory.CreateDirectory(targetItemDirectory);
- File.Copy(item.FullPath, targetItemFullPath);
+ CreateDirectory(targetItemDirectory);
+ CopyFile(item.FullPath, targetItemFullPath);
}
return 0;
+ void CreateDirectory(string path)
+ {
+ if (dryRun)
+ {
+ if (!Directory.Exists(path))
+ {
+ Reporter.Output.WriteLine(CliCommandStrings.ProjectConvertWouldCreateDirectory, path);
+ }
+ }
+ else
+ {
+ Directory.CreateDirectory(path);
+ }
+ }
+
+ void CopyFile(string source, string target)
+ {
+ if (dryRun)
+ {
+ Reporter.Output.WriteLine(CliCommandStrings.ProjectConvertWouldCopyFile, source, target);
+ }
+ else
+ {
+ File.Copy(source, target);
+ }
+ }
+
IEnumerable<(string FullPath, string RelativePath)> FindIncludedItems()
{
string entryPointFileDirectory = PathUtility.EnsureTrailingSlash(Path.GetDirectoryName(file)!);
@@ -118,4 +152,42 @@ public override int Execute()
}
}
}
+
+ private string DetermineOutputDirectory(string file)
+ {
+ string defaultValue = Path.ChangeExtension(file, null);
+ string defaultValueRelative = Path.GetRelativePath(relativeTo: Environment.CurrentDirectory, defaultValue);
+ string targetDirectory = _outputDirectory
+ ?? TryAskForOutputDirectory(defaultValueRelative)
+ ?? defaultValue;
+ if (Directory.Exists(targetDirectory))
+ {
+ throw new GracefulException(CliCommandStrings.DirectoryAlreadyExists, targetDirectory);
+ }
+
+ return targetDirectory;
+ }
+
+ private string? TryAskForOutputDirectory(string defaultValueRelative)
+ {
+ return InteractiveConsole.Ask(
+ string.Format(CliCommandStrings.ProjectConvertAskForOutputDirectory, defaultValueRelative),
+ _parseResult,
+ (path, out result, [NotNullWhen(returnValue: false)] out error) =>
+ {
+ if (Directory.Exists(path))
+ {
+ result = null;
+ error = string.Format(CliCommandStrings.DirectoryAlreadyExists, Path.GetFullPath(path));
+ return false;
+ }
+
+ result = path is null ? null : Path.GetFullPath(path);
+ error = null;
+ return true;
+ },
+ out var result)
+ ? result
+ : null;
+ }
}
diff --git a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommandParser.cs b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommandParser.cs
index a933c8068652..c3104e995cd3 100644
--- a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommandParser.cs
+++ b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommandParser.cs
@@ -20,6 +20,12 @@ internal sealed class ProjectConvertCommandParser
Arity = ArgumentArity.Zero,
};
+ public static readonly Option DryRunOption = new("--dry-run")
+ {
+ Description = CliCommandStrings.ProjectConvertDryRun,
+ Arity = ArgumentArity.Zero,
+ };
+
public static Command GetCommand()
{
Command command = new("convert", CliCommandStrings.ProjectConvertAppFullName)
@@ -27,6 +33,8 @@ public static Command GetCommand()
FileArgument,
SharedOptions.OutputOption,
ForceOption,
+ CommonOptions.InteractiveOption(),
+ DryRunOption,
};
command.SetAction((parseResult) => new ProjectConvertCommand(parseResult).Execute());
diff --git a/src/Cli/dotnet/Commands/Tool/Execute/ToolExecuteCommand.cs b/src/Cli/dotnet/Commands/Tool/Execute/ToolExecuteCommand.cs
index cb74f08d05ff..bdfdacb42d58 100644
--- a/src/Cli/dotnet/Commands/Tool/Execute/ToolExecuteCommand.cs
+++ b/src/Cli/dotnet/Commands/Tool/Execute/ToolExecuteCommand.cs
@@ -30,7 +30,6 @@ internal class ToolExecuteCommand(ParseResult result, ToolManifestFinder? toolMa
private readonly string[] _addSource = result.GetValue(ToolExecuteCommandParser.AddSourceOption) ?? [];
private readonly bool _interactive = result.GetValue(ToolExecuteCommandParser.InteractiveOption);
private readonly VerbosityOptions _verbosity = result.GetValue(ToolExecuteCommandParser.VerbosityOption);
- private readonly bool _yes = result.GetValue(ToolExecuteCommandParser.YesOption);
private readonly IToolPackageDownloader _toolPackageDownloader = ToolPackageFactory.CreateToolPackageStoresAndDownloader().downloader;
private readonly RestoreActionConfig _restoreActionConfig = new RestoreActionConfig(DisableParallel: result.GetValue(ToolCommandRestorePassThroughOptions.DisableParallelOption),
@@ -128,47 +127,7 @@ public override int Execute()
private bool UserAgreedToRunFromSource(PackageId packageId, NuGetVersion version, PackageSource source)
{
- if (_yes)
- {
- return true;
- }
-
- if (!_interactive)
- {
- return false;
- }
-
string promptMessage = string.Format(CliCommandStrings.ToolDownloadConfirmationPrompt, packageId, version.ToString(), source.Source);
-
- static string AddPromptOptions(string message)
- {
- return $"{message} [{CliCommandStrings.ConfirmationPromptYesValue}/{CliCommandStrings.ConfirmationPromptNoValue}] ({CliCommandStrings.ConfirmationPromptYesValue}): ";
- }
-
- Console.Write(AddPromptOptions(promptMessage));
-
- static bool KeyMatches(ConsoleKeyInfo pressedKey, string valueKey)
- {
- // Apparently you can't do invariant case insensitive comparison on a char directly, so we have to convert it to a string.
- // The resource string should be a single character, but we take the first character just to be sure.
- return pressedKey.KeyChar.ToString().ToLowerInvariant().Equals(
- valueKey.ToLowerInvariant().Substring(0, 1));
- }
-
- while (true)
- {
- var key = Console.ReadKey();
- Console.WriteLine();
- if (key.Key == ConsoleKey.Enter || KeyMatches(key, CliCommandStrings.ConfirmationPromptYesValue))
- {
- return true;
- }
- if (key.Key == ConsoleKey.Escape || KeyMatches(key, CliCommandStrings.ConfirmationPromptNoValue))
- {
- return false;
- }
-
- Console.Write(AddPromptOptions(string.Format(CliCommandStrings.ConfirmationPromptInvalidChoiceMessage, CliCommandStrings.ConfirmationPromptYesValue, CliCommandStrings.ConfirmationPromptNoValue)));
- }
+ return InteractiveConsole.Confirm(promptMessage, _parseResult, acceptEscapeForFalse: true) == true;
}
}
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf
index fcab6e61ce88..651d84d4c430 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf
@@ -2290,6 +2290,46 @@ Nástroj {1} (verze {2}) se úspěšně nainstaloval. Do souboru manifestu {3} s
Umožňuje převést program na bázi souboru na program na bázi projektu.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf
index f70cb9600a52..1f6d15287918 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf
@@ -2290,6 +2290,46 @@ Das Tool "{1}" (Version {2}) wurde erfolgreich installiert. Der Eintrag wird der
Konvertieren Sie ein dateibasiertes Programm in ein projektbasiertes Programm.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf
index e349ccd9aae5..5091be25b583 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf
@@ -2290,6 +2290,46 @@ La herramienta "{1}" (versión "{2}") se instaló correctamente. Se ha agregado
Convertir un programa basado en archivos en un programa basado en proyecto.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf
index 9243b52795f5..457afcb8a738 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf
@@ -2290,6 +2290,46 @@ L'outil '{1}' (version '{2}') a été correctement installé. L'entrée est ajou
Convertissez un programme basé sur des fichiers en programme basé sur un projet.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf
index a383e84c2284..9af4a5bae904 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf
@@ -2290,6 +2290,46 @@ Lo strumento '{1}' versione '{2}' è stato installato. La voce è stata aggiunta
Converti un programma basato su file in un programma basato su progetto.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf
index 12b1a2d0d6e0..22a6f15f811c 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf
@@ -2290,6 +2290,46 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
ファイルベースのプログラムをプロジェクトベースのプログラムに変換します。
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf
index 6f1e0be285b6..e32cb9dfbeba 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf
@@ -2290,6 +2290,46 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
파일 기반 프로그램을 프로젝트 기반 프로그램으로 변환합니다.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf
index 950656ce22f2..c0bbbc1caa67 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf
@@ -2290,6 +2290,46 @@ Narzędzie „{1}” (wersja „{2}”) zostało pomyślnie zainstalowane. Wpis
Konwertuj program oparty na plikach na program oparty na projekcie.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf
index d8bb4763fa06..979a8397c694 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf
@@ -2290,6 +2290,46 @@ A ferramenta '{1}' (versão '{2}') foi instalada com êxito. A entrada foi adici
Converta um programa baseado em arquivo em um programa baseado em projeto.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf
index b605c45c2d9e..6cda527f7daf 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf
@@ -2290,6 +2290,46 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
Преобразование программы на основе файла в программу на основе проекта.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf
index 7eb51cb17ed3..258e628a4f27 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf
@@ -2290,6 +2290,46 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
Dosya tabanlı bir programı proje tabanlı bir programa dönüştür.
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf
index 3d1c446d846d..5a0e30d879ac 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf
@@ -2290,6 +2290,46 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
将基于文件的程序转换为基于项目的程序。
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf
index af3ea934d94f..7d66f8e0a97d 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf
@@ -2290,6 +2290,46 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
將檔案型程式轉換成專案型程式。
+
+ Specify the output directory ({0}):
+ Specify the output directory ({0}):
+ {0} is the default value
+
+
+ Determines changes without actually modifying the file system
+ Determines changes without actually modifying the file system
+
+
+
+ Dry run: would remove file-level directives from file: {0}
+ Dry run: would remove file-level directives from file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would copy file '{0}' to '{1}'.
+ Dry run: would copy file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+
+
+ Dry run: would create directory: {0}
+ Dry run: would create directory: {0}
+ {0} is the directory full path.
+
+
+ Dry run: would create file: {0}
+ Dry run: would create file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would delete file: {0}
+ Dry run: would delete file: {0}
+ {0} is the file full path.
+
+
+ Dry run: would move file '{0}' to '{1}'.
+ Dry run: would move file '{0}' to '{1}'.
+ {0} and {1} are file full paths.
+ PROJECT_MANIFESTPROJECT_MANIFEST
diff --git a/src/Cli/dotnet/CommonOptions.cs b/src/Cli/dotnet/CommonOptions.cs
index 0e9de478c89b..92a43cc2ef58 100644
--- a/src/Cli/dotnet/CommonOptions.cs
+++ b/src/Cli/dotnet/CommonOptions.cs
@@ -240,23 +240,25 @@ public static Argument DefaultToCurrentDirectory(this Argument a
private static bool IsCIEnvironmentOrRedirected() =>
new Telemetry.CIEnvironmentDetectorForTelemetry().IsCIEnvironment() || Console.IsOutputRedirected;
+ public const string InteractiveOptionName = "--interactive";
+
///
/// A 'template' for interactive usage across the whole dotnet CLI. Use this as a base and then specialize it for your use cases.
/// Despite being a 'forwarded option' there is no default forwarding configured, so if you want forwarding you can add it on a per-command basis.
///
/// Whether the option accepts an boolean argument. If false, the option will be a flag.
///
- // If not set by a user, this will default to true if the user is not in a CI environment as detected by .
- // If this is set to function as a flag, then there is no simple user-provided way to circumvent the behavior.
- //
+ /// If not set by a user, this will default to true if the user is not in a CI environment as detected by .
+ /// If this is set to function as a flag, then there is no simple user-provided way to circumvent the behavior.
+ ///
public static ForwardedOption InteractiveOption(bool acceptArgument = false) =>
- new("--interactive")
- {
- Description = CliStrings.CommandInteractiveOptionDescription,
- Arity = acceptArgument ? ArgumentArity.ZeroOrOne : ArgumentArity.Zero,
- // this default is called when no tokens/options are passed on the CLI args
- DefaultValueFactory = (ar) => !IsCIEnvironmentOrRedirected()
- };
+ new(InteractiveOptionName)
+ {
+ Description = CliStrings.CommandInteractiveOptionDescription,
+ Arity = acceptArgument ? ArgumentArity.ZeroOrOne : ArgumentArity.Zero,
+ // this default is called when no tokens/options are passed on the CLI args
+ DefaultValueFactory = (ar) => !IsCIEnvironmentOrRedirected()
+ };
public static Option InteractiveMsBuildForwardOption = InteractiveOption(acceptArgument: true).ForwardAsSingle(b => $"--property:NuGetInteractive={(b ? "true" : "false")}");
diff --git a/src/Cli/dotnet/InteractiveConsole.cs b/src/Cli/dotnet/InteractiveConsole.cs
new file mode 100644
index 000000000000..2b6a6fdcbd58
--- /dev/null
+++ b/src/Cli/dotnet/InteractiveConsole.cs
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.CommandLine;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.DotNet.Cli.Commands;
+
+namespace Microsoft.DotNet.Cli;
+
+public static class InteractiveConsole
+{
+ ///
+ /// If you need to confirm an action, Escape means "cancel" and that is fine.
+ /// If you need an answer where the "no" might mean something dangerous, Escape should not be used as implicit "no".
+ ///
+ ///
+ /// if the user confirmed the action,
+ /// if the user declined it,
+ /// if the user could not answer because --no-interactive was specified.
+ ///
+ public static bool? Confirm(string message, ParseResult parseResult, bool acceptEscapeForFalse)
+ {
+ if (parseResult.GetValue(CommonOptions.YesOption))
+ {
+ return true;
+ }
+
+ if (!parseResult.GetValue(CommonOptions.InteractiveOptionName))
+ {
+ return null;
+ }
+
+ Console.Write(AddPromptOptions(message));
+
+ while (true)
+ {
+ var key = Console.ReadKey();
+ Console.WriteLine();
+
+ if (key.Key == ConsoleKey.Enter || KeyMatches(key, CliCommandStrings.ConfirmationPromptYesValue))
+ {
+ return true;
+ }
+
+ if ((acceptEscapeForFalse && key.Key == ConsoleKey.Escape) || KeyMatches(key, CliCommandStrings.ConfirmationPromptNoValue))
+ {
+ return false;
+ }
+
+ Console.Write(AddPromptOptions(string.Format(CliCommandStrings.ConfirmationPromptInvalidChoiceMessage, CliCommandStrings.ConfirmationPromptYesValue, CliCommandStrings.ConfirmationPromptNoValue)));
+ }
+
+ static string AddPromptOptions(string message)
+ {
+ return $"{message} [{CliCommandStrings.ConfirmationPromptYesValue}/{CliCommandStrings.ConfirmationPromptNoValue}] ({CliCommandStrings.ConfirmationPromptYesValue}): ";
+ }
+
+ static bool KeyMatches(ConsoleKeyInfo pressedKey, string valueKey)
+ {
+ // Apparently you can't do invariant case insensitive comparison on a char directly, so we have to convert it to a string.
+ // The resource string should be a single character, but we take the first character just to be sure.
+ return pressedKey.KeyChar.ToString().ToLowerInvariant().Equals(
+ valueKey.ToLowerInvariant().Substring(0, 1));
+ }
+ }
+
+ public delegate bool Validator(
+ string? answer,
+ out TResult? result,
+ [NotNullWhen(returnValue: false)] out string? error);
+
+ public static bool Ask(
+ string question,
+ ParseResult parseResult,
+ Validator validate,
+ out TResult? result)
+ {
+ if (!parseResult.GetValue(CommonOptions.InteractiveOptionName))
+ {
+ result = default;
+ return false;
+ }
+
+ while (true)
+ {
+ Console.Write(question);
+ Console.Write(' ');
+
+ string? answer = Console.ReadLine();
+ answer = string.IsNullOrWhiteSpace(answer) ? null : answer.Trim();
+ if (!validate(answer, out result, out var error))
+ {
+ Console.WriteLine(error);
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+}
diff --git a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs
index 229f81f857e0..1b88615bdcaf 100644
--- a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs
+++ b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs
@@ -33,7 +33,7 @@ public void SameAsTemplate()
new DirectoryInfo(dotnetProjectConvert)
.EnumerateFileSystemInfos().Select(d => d.Name).Order()
- .Should().BeEquivalentTo(["Program"]);
+ .Should().BeEquivalentTo(["Program", "Program.cs"]);
new DirectoryInfo(Path.Join(dotnetProjectConvert, "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -140,7 +140,7 @@ public void MultipleEntryPointFiles()
new DirectoryInfo(testInstance.Path)
.EnumerateFileSystemInfos().Select(d => d.Name).Order()
- .Should().BeEquivalentTo(["Program1", "Program2.cs"]);
+ .Should().BeEquivalentTo(["Program1", "Program1.cs", "Program2.cs"]);
new DirectoryInfo(Path.Join(testInstance.Path, "Program1"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -208,7 +208,7 @@ public void ExtensionCasing()
new DirectoryInfo(testInstance.Path)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Program"]);
+ .Should().BeEquivalentTo(["Program", "Program.CS"]);
new DirectoryInfo(Path.Join(testInstance.Path, "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -230,7 +230,10 @@ public void FileContent(string content)
new DirectoryInfo(testInstance.Path)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Program"]);
+ .Should().BeEquivalentTo(["Program", "Program.cs"]);
+
+ File.ReadAllText(Path.Join(testInstance.Path, "Program.cs"))
+ .Should().Be(content);
new DirectoryInfo(Path.Join(testInstance.Path, "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -255,7 +258,7 @@ public void NestedDirectory()
new DirectoryInfo(Path.Join(testInstance.Path, "app"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Program"]);
+ .Should().BeEquivalentTo(["Program", "Program.cs"]);
new DirectoryInfo(Path.Join(testInstance.Path, "app", "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -285,7 +288,7 @@ public void DefaultItems()
new DirectoryInfo(testInstance.Path)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Program", "Resources.resx", "Util.cs", "my.json", "subdir"]);
+ .Should().BeEquivalentTo(["Program", "Program.cs", "Resources.resx", "Util.cs", "my.json", "subdir"]);
new DirectoryInfo(Path.Join(testInstance.Path, "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -315,7 +318,7 @@ public void DefaultItems_MoreIncluded()
new DirectoryInfo(testInstance.Path)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Program", "Resources.resx", "Util.cs", "my.json"]);
+ .Should().BeEquivalentTo(["Program", "Program.cs", "Resources.resx", "Util.cs", "my.json"]);
new DirectoryInfo(Path.Join(testInstance.Path, "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -341,7 +344,7 @@ public void DefaultItems_MoreExcluded()
new DirectoryInfo(testInstance.Path)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Program", "Resources.resx", "Util.cs", "my.json"]);
+ .Should().BeEquivalentTo(["Program", "Program.cs", "Resources.resx", "Util.cs", "my.json"]);
new DirectoryInfo(Path.Join(testInstance.Path, "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -378,7 +381,7 @@ public void DefaultItems_ExcludedViaMetadata()
new DirectoryInfo(testInstance.Path)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Directory.Build.targets", "Program", "Resources.resx", "Util.cs", "my.json", "second.json"]);
+ .Should().BeEquivalentTo(["Directory.Build.targets", "Program", "Program.cs", "Resources.resx", "Util.cs", "my.json", "second.json"]);
// `second.json` is excluded from the conversion.
new DirectoryInfo(Path.Join(testInstance.Path, "Program"))
@@ -420,7 +423,7 @@ class Util { public static string GetText() => "Hi from Util"; }
new DirectoryInfo(testInstance.Path)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Directory.Build.props", "Program", "Util.cs"]);
+ .Should().BeEquivalentTo(["Directory.Build.props", "Program", "Program.cs", "Util.cs"]);
// Directory.Build.props is included as it's a None item.
new DirectoryInfo(Path.Join(testInstance.Path, "Program"))
@@ -471,7 +474,7 @@ class Util { public static string GetText() => "Hi from Util"; }
new DirectoryInfo(subdir)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Program", "Util.cs"]);
+ .Should().BeEquivalentTo(["Program", "Program.cs", "Util.cs"]);
new DirectoryInfo(Path.Join(subdir, "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -521,7 +524,7 @@ class Util { public static string GetText() => "Hi from Util"; }
new DirectoryInfo(subdir)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Program"]);
+ .Should().BeEquivalentTo(["Program", "Program.cs"]);
new DirectoryInfo(Path.Join(subdir, "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
@@ -585,10 +588,11 @@ public void ProcessingFails_Evaluation()
public void ProcessingSucceeds()
{
var testInstance = _testAssetsManager.CreateTestDirectory();
- File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """
+ var originalSource = """
#:package Humanizer@2.14.1
Console.WriteLine();
- """);
+ """;
+ File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), originalSource);
new DotnetCommand(Log, "project", "convert", "Program.cs")
.WithWorkingDirectory(testInstance.Path)
@@ -597,7 +601,10 @@ public void ProcessingSucceeds()
new DirectoryInfo(testInstance.Path)
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
- .Should().BeEquivalentTo(["Program"]);
+ .Should().BeEquivalentTo(["Program", "Program.cs"]);
+
+ File.ReadAllText(Path.Join(testInstance.Path, "Program.cs"))
+ .Should().Be(originalSource);
new DirectoryInfo(Path.Join(testInstance.Path, "Program"))
.EnumerateFileSystemInfos().Select(f => f.Name).Order()
diff --git a/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh b/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh
index 1e61c0df86d4..4745292a88c3 100644
--- a/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh
+++ b/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh
@@ -1178,7 +1178,7 @@ _testhost_project_convert() {
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()
- opts="--output --force --help"
+ opts="--output --force --interactive --dry-run --help"
if [[ $COMP_CWORD == "$1" ]]; then
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
diff --git a/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 b/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1
index 60fbc9bdda7f..74c47f83f124 100644
--- a/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1
+++ b/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1
@@ -680,6 +680,8 @@ Register-ArgumentCompleter -Native -CommandName 'testhost' -ScriptBlock {
[CompletionResult]::new('--output', '--output', [CompletionResultType]::ParameterName, "Location to place the generated output.")
[CompletionResult]::new('--output', '-o', [CompletionResultType]::ParameterName, "Location to place the generated output.")
[CompletionResult]::new('--force', '--force', [CompletionResultType]::ParameterName, "Force conversion even if there are malformed directives.")
+ [CompletionResult]::new('--interactive', '--interactive', [CompletionResultType]::ParameterName, "Allows the command to stop and wait for user input or action (for example to complete authentication).")
+ [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, "Determines changes without actually modifying the file system")
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, "Show command line help.")
[CompletionResult]::new('--help', '-h', [CompletionResultType]::ParameterName, "Show command line help.")
)
diff --git a/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh b/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh
index fa12d3005129..ebcf7c958169 100644
--- a/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh
+++ b/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh
@@ -676,6 +676,8 @@ _testhost() {
'--output=[Location to place the generated output.]: :_files' \
'-o=[Location to place the generated output.]: :_files' \
'--force[Force conversion even if there are malformed directives.]' \
+ '--interactive[Allows the command to stop and wait for user input or action (for example to complete authentication).]' \
+ '--dry-run[Determines changes without actually modifying the file system]' \
'--help[Show command line help.]' \
'-h[Show command line help.]' \
':file -- Path to the file-based program.: ' \