diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt index 24d1392569e7..969a697c36f1 100644 --- a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt +++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt @@ -69,7 +69,7 @@ static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.GetPropertyFromS static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.GetTempSubdirectory(string? dotNetSubdirectory = null) -> string! static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.GetTempSubpath(string! name, string? dotNetSubdirectory = null) -> string! static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.GetVirtualProjectPath(string! entryPointFilePath) -> string! -static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.IsValidEntryPointPath(string! entryPointFilePath) -> bool +static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.IsValidEntryPointPath(string! entryPointFilePath, bool requireFileToExist = true) -> bool static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.TryGetEntryPointFilePathFromVirtualProjectPath(string! projectPath, out string? entryPointFilePath) -> bool static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.WriteProjectFile(System.IO.TextWriter! writer, System.Collections.Immutable.ImmutableArray directives, System.Collections.Generic.IEnumerable<(string! name, string! value)>! defaultProperties, bool isVirtualProject, string? entryPointFilePath = null, string? artifactsPath = null, bool includeRuntimeConfigInformation = true, string? userSecretsId = null, System.Collections.Immutable.ImmutableArray explicitProjectItems = default(System.Collections.Immutable.ImmutableArray)) -> void static Microsoft.DotNet.Utilities.Extensions.ToHashSet(this System.Collections.Generic.IEnumerable! source, System.Collections.Generic.IEqualityComparer! comparer) -> System.Collections.Generic.HashSet! diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/VirtualProjectBuilder.cs b/src/Cli/Microsoft.DotNet.FileBasedPrograms/VirtualProjectBuilder.cs index e01aeaa06b80..fc9548c650e1 100644 --- a/src/Cli/Microsoft.DotNet.FileBasedPrograms/VirtualProjectBuilder.cs +++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/VirtualProjectBuilder.cs @@ -171,9 +171,9 @@ internal static string GetTempSubpath(string name, string? dotNetSubdirectory = return Path.Combine(GetTempSubdirectory(dotNetSubdirectory), name); } - public static bool IsValidEntryPointPath(string entryPointFilePath) + public static bool IsValidEntryPointPath(string entryPointFilePath, bool requireFileToExist = true) { - if (!File.Exists(entryPointFilePath)) + if (requireFileToExist && !File.Exists(entryPointFilePath)) { return false; } @@ -183,6 +183,12 @@ public static bool IsValidEntryPointPath(string entryPointFilePath) return true; } + // If we haven't checked file existence yet, do it before opening the file. + if (!requireFileToExist && !File.Exists(entryPointFilePath)) + { + return false; + } + // Check if the first two characters are #! try { diff --git a/src/Dotnet.Format/dotnet-format/CodeFormatter.cs b/src/Dotnet.Format/dotnet-format/CodeFormatter.cs index 35cc03c7eb0a..a207d1589c1c 100644 --- a/src/Dotnet.Format/dotnet-format/CodeFormatter.cs +++ b/src/Dotnet.Format/dotnet-format/CodeFormatter.cs @@ -35,15 +35,17 @@ public static async Task FormatWorkspaceAsync( var workspaceStopwatch = Stopwatch.StartNew(); - using var workspace = formatOptions.WorkspaceType == WorkspaceType.Folder + using var loadedWorkspace = formatOptions.WorkspaceType == WorkspaceType.Folder ? OpenFolderWorkspace(formatOptions.WorkspaceFilePath, formatOptions.FileMatcher) : await OpenMSBuildWorkspaceAsync(formatOptions.WorkspaceFilePath, formatOptions.WorkspaceType, formatOptions.NoRestore, formatOptions.FixCategory != FixCategory.Whitespace, formatOptions.BinaryLogPath, logWorkspaceWarnings, logger, formatOptions.TargetFramework, cancellationToken); - if (workspace is null) + if (loadedWorkspace is null) { return new WorkspaceFormatResult(filesFormatted: 0, fileCount: 0, exitCode: 1); } + var workspace = loadedWorkspace.Workspace; + if (formatOptions.LogLevel <= LogLevel.Debug) { foreach (var project in workspace.CurrentSolution.Projects) @@ -58,13 +60,12 @@ public static async Task FormatWorkspaceAsync( var loadWorkspaceMS = workspaceStopwatch.ElapsedMilliseconds; logger.LogTrace(Resources.Complete_in_0_ms, loadWorkspaceMS); - var projectPath = formatOptions.WorkspaceType == WorkspaceType.Project ? formatOptions.WorkspaceFilePath : string.Empty; var solution = workspace.CurrentSolution; logger.LogTrace(Resources.Determining_formattable_files); var (fileCount, formatableFiles) = await DetermineFormattableFilesAsync( - solution, projectPath, formatOptions, logger, cancellationToken); + solution, loadedWorkspace.ProjectId, formatOptions, logger, cancellationToken); var determineFilesMS = workspaceStopwatch.ElapsedMilliseconds - loadWorkspaceMS; logger.LogTrace(Resources.Complete_in_0_ms, determineFilesMS); @@ -110,14 +111,14 @@ public static async Task FormatWorkspaceAsync( return new WorkspaceFormatResult(documentIdsWithErrors.Length, fileCount, exitCode); } - private static Workspace OpenFolderWorkspace(string workspacePath, SourceFileMatcher fileMatcher) + private static LoadedWorkspace OpenFolderWorkspace(string workspacePath, SourceFileMatcher fileMatcher) { var folderWorkspace = FolderWorkspace.Create(); folderWorkspace.OpenFolder(workspacePath, fileMatcher); - return folderWorkspace; + return new LoadedWorkspace(folderWorkspace, ProjectId: null); } - private static async Task OpenMSBuildWorkspaceAsync( + private static async Task OpenMSBuildWorkspaceAsync( string solutionOrProjectPath, WorkspaceType workspaceType, bool noRestore, @@ -165,11 +166,13 @@ private static async Task RunCodeFormattersAsync( internal static async Task<(int, ImmutableArray)> DetermineFormattableFilesAsync( Solution solution, - string projectPath, + ProjectId? projectId, FormatOptions formatOptions, ILogger logger, CancellationToken cancellationToken) { + Debug.Assert((formatOptions.WorkspaceType is WorkspaceType.Project) == (projectId is not null)); + var totalFileCount = solution.Projects.Sum(project => project.DocumentIds.Count); var projectFileCount = 0; @@ -187,7 +190,7 @@ private static async Task RunCodeFormattersAsync( } // If a project is used as a workspace, then ignore other referenced projects. - if (!string.IsNullOrEmpty(projectPath) && !project.FilePath.Equals(projectPath, StringComparison.OrdinalIgnoreCase)) + if (projectId != null && project.Id != projectId) { logger.LogDebug(Resources.Skipping_referenced_project_0, project.Name); continue; diff --git a/src/Dotnet.Format/dotnet-format/Commands/FormatCommandCommon.cs b/src/Dotnet.Format/dotnet-format/Commands/FormatCommandCommon.cs index b8b51242ec24..8847516bd7a9 100644 --- a/src/Dotnet.Format/dotnet-format/Commands/FormatCommandCommon.cs +++ b/src/Dotnet.Format/dotnet-format/Commands/FormatCommandCommon.cs @@ -20,9 +20,9 @@ internal static class FormatCommandCommon private static string[] VerbosityLevels => new[] { "q", "quiet", "m", "minimal", "n", "normal", "d", "detailed", "diag", "diagnostic" }; private static string[] SeverityLevels => new[] { "info", "warn", "error", "hidden" }; - public static readonly Argument SlnOrProjectArgument = new Argument(Resources.SolutionOrProjectArgumentName) + public static readonly Argument SlnOrProjectArgument = new Argument(Resources.SolutionOrProjectOrFileArgumentName) { - Description = Resources.SolutionOrProjectArgumentDescription, + Description = Resources.SolutionOrProjectOrFileArgumentDescription, Arity = ArgumentArity.ZeroOrOne }.DefaultToCurrentDirectory(); diff --git a/src/Dotnet.Format/dotnet-format/Resources.resx b/src/Dotnet.Format/dotnet-format/Resources.resx index b54bf224c6a8..d7ef293f9ad5 100644 --- a/src/Dotnet.Format/dotnet-format/Resources.resx +++ b/src/Dotnet.Format/dotnet-format/Resources.resx @@ -132,8 +132,8 @@ Failed to save formatting changes. - - The file '{0}' does not appear to be a valid project or solution file. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. Multiple MSBuild project files found in '{0}'. Specify which to use with the <workspace> argument. @@ -336,11 +336,11 @@ Cannot specify the '--folder' option with '--framework'. - - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. Accepts a file path which if provided will produce a json report in the given directory. diff --git a/src/Dotnet.Format/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs b/src/Dotnet.Format/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs index eb50bf5545c2..af35c5311d49 100644 --- a/src/Dotnet.Format/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs +++ b/src/Dotnet.Format/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs @@ -6,6 +6,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // See https://github.com/aspnet/DotNetTools/blob/261b27b70027871143540af10a5cba57ce07ff97/src/dotnet-watch/Internal/MsBuildProjectFinder.cs +using Microsoft.DotNet.FileBasedPrograms; + namespace Microsoft.CodeAnalysis.Tools.Workspaces { internal class MSBuildWorkspaceFinder @@ -62,10 +64,12 @@ private static (bool isSolution, string workspacePath) FindFile(string workspace var isProject = !isSolution && workspaceExtension.EndsWith("proj", StringComparison.OrdinalIgnoreCase) && !workspaceExtension.Equals(DnxProjectExtension, StringComparison.OrdinalIgnoreCase); + var isFileBasedApp = !isSolution && !isProject + && VirtualProjectBuilder.IsValidEntryPointPath(workspacePath, requireFileToExist: false); - if (!isSolution && !isProject) + if (!isSolution && !isProject && !isFileBasedApp) { - throw new FileNotFoundException(string.Format(Resources.The_file_0_does_not_appear_to_be_a_valid_project_or_solution_file, Path.GetFileName(workspacePath))); + throw new FileNotFoundException(string.Format(Resources.The_file_0_does_not_appear_to_be_a_valid_project_solution_file_or_file_based_app, Path.GetFileName(workspacePath))); } if (!File.Exists(workspacePath)) diff --git a/src/Dotnet.Format/dotnet-format/Workspaces/MSBuildWorkspaceLoader.cs b/src/Dotnet.Format/dotnet-format/Workspaces/MSBuildWorkspaceLoader.cs index 561fe20188c3..11acc7bdb657 100644 --- a/src/Dotnet.Format/dotnet-format/Workspaces/MSBuildWorkspaceLoader.cs +++ b/src/Dotnet.Format/dotnet-format/Workspaces/MSBuildWorkspaceLoader.cs @@ -12,7 +12,7 @@ internal static class MSBuildWorkspaceLoader // Used in tests for locking around MSBuild invocations internal static readonly SemaphoreSlim Guard = new SemaphoreSlim(1, 1); - public static async Task LoadAsync( + public static async Task LoadAsync( string solutionOrProjectPath, WorkspaceType workspaceType, string? binaryLogPath, @@ -35,6 +35,7 @@ internal static class MSBuildWorkspaceLoader } var workspace = MSBuildWorkspace.Create(properties); + ProjectId? projectId = null; Build.Framework.ILogger? binlog = null; if (binaryLogPath is not null) @@ -54,7 +55,8 @@ internal static class MSBuildWorkspaceLoader { try { - await workspace.OpenProjectAsync(solutionOrProjectPath, msbuildLogger: binlog, cancellationToken: cancellationToken); + var project = await workspace.OpenProjectAsync(solutionOrProjectPath, msbuildLogger: binlog, cancellationToken: cancellationToken); + projectId = project.Id; } catch (InvalidOperationException) { @@ -66,7 +68,7 @@ internal static class MSBuildWorkspaceLoader LogWorkspaceDiagnostics(logger, logWorkspaceWarnings, workspace.Diagnostics); - return workspace; + return new LoadedWorkspace(workspace, projectId); static void LogWorkspaceDiagnostics(ILogger logger, bool logWorkspaceWarnings, ImmutableList diagnostics) { @@ -94,4 +96,10 @@ static void LogWorkspaceDiagnostics(ILogger logger, bool logWorkspaceWarnings, I } } } + + /// Set to the project if the workspace is loaded in "project" mode. + internal sealed record LoadedWorkspace(Workspace Workspace, ProjectId? ProjectId) : IDisposable + { + public void Dispose() => Workspace.Dispose(); + } } diff --git a/src/Dotnet.Format/dotnet-format/dotnet-format.csproj b/src/Dotnet.Format/dotnet-format/dotnet-format.csproj index 8a30d41239a7..7119eb4699ba 100644 --- a/src/Dotnet.Format/dotnet-format/dotnet-format.csproj +++ b/src/Dotnet.Format/dotnet-format/dotnet-format.csproj @@ -40,6 +40,10 @@ + + + + diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.cs.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.cs.xlf index bff1473b47a3..a78c4e0776f4 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.cs.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.cs.xlf @@ -292,14 +292,14 @@ Přeskočí se odkazovaný projekt {0}. - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - Soubor projektu nebo řešení, se kterým se má operace provést. Pokud soubor není zadaný, příkaz ho bude hledat v aktuálním adresáři. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ Verze modulu runtime dotnet je {0}. - - The file '{0}' does not appear to be a valid project or solution file. - Soubor {0} zřejmě není platný soubor projektu nebo řešení. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.de.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.de.xlf index 3076b304bc15..a2d90f8d59b0 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.de.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.de.xlf @@ -292,14 +292,14 @@ Überspringen von referenziertem Projekt "{0}". - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - Das Projekt oder die Projektmappendatei, die verwendet werden soll. Wenn keine Datei angegeben ist, durchsucht der Befehl das aktuelle Verzeichnis nach einer Datei. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ Die Dotnet-Laufzeitversion ist „{0}“. - - The file '{0}' does not appear to be a valid project or solution file. - Die Datei "{0}" ist weder ein gültiges Projekt noch eine Projektmappendatei. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.es.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.es.xlf index 66c03769e6de..91b3e5e47601 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.es.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.es.xlf @@ -292,14 +292,14 @@ Omitiendo projecto al que se hace referencia "{0}". - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - El archivo de proyecto o solución donde operar. Si no se especifica un archivo, el comando buscará uno en el directorio actual. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ La versión del entorno de ejecución de dotnet es "{0}". - - The file '{0}' does not appear to be a valid project or solution file. - El archivo "{0}" no parece ser un proyecto o archivo de solución válido. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.fr.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.fr.xlf index 579b3cd75ca9..c15e36381a9d 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.fr.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.fr.xlf @@ -292,14 +292,14 @@ Saut du projet référencé '{0}'. - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - Fichier projet ou solution à utiliser. Si vous ne spécifiez pas de fichier, la commande en recherche un dans le répertoire actuel. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ La version du runtime dotnet est «{0}». - - The file '{0}' does not appear to be a valid project or solution file. - Le fichier '{0}' ne semble pas être un fichier projet ou solution valide. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.it.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.it.xlf index fffc9751af7b..b3bc91fd34c0 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.it.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.it.xlf @@ -292,14 +292,14 @@ Il progetto di riferimento '{0}' verrà ignorato. - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - File di progetto o di soluzione su cui intervenire. Se non si specifica un file, il comando ne cercherà uno nella directory corrente. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ La versione del runtime dotnet è '{0}'. - - The file '{0}' does not appear to be a valid project or solution file. - Il file '{0}' non sembra essere un file di progetto o di soluzione valido. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.ja.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.ja.xlf index 94261f079d21..6744ed6cf329 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.ja.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.ja.xlf @@ -292,14 +292,14 @@ 参照プロジェクト '{0}' をスキップしています。 - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - 利用するプロジェクト ファイルまたはソリューション ファイル。指定しない場合、コマンドは現在のディレクトリを検索します。 + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ dotnet ランタイム バージョンは '{0}' です。 - - The file '{0}' does not appear to be a valid project or solution file. - ファイル '{0}' が、有効なプロジェクト ファイルまたはソリューション ファイルではない可能性があります。 + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.ko.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.ko.xlf index 82065400c1b8..8ad3e4538b72 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.ko.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.ko.xlf @@ -292,14 +292,14 @@ 참조된 프로젝트 '{0}'을(를) 건너뜁니다. - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - 수행할 프로젝트 또는 솔루션 파일입니다. 파일을 지정하지 않으면 명령이 현재 디렉토리에서 파일을 검색합니다. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ dotnet 런타임 버전은 '{0}'입니다. - - The file '{0}' does not appear to be a valid project or solution file. - '{0}' 파일은 유효한 프로젝트 또는 솔루션 파일이 아닌 것 같습니다. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.pl.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.pl.xlf index b9d39f2b54cb..4f23c5f29162 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.pl.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.pl.xlf @@ -292,14 +292,14 @@ Pomijanie przywoływanego projektu „{0}”. - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - Plik projektu lub rozwiązania, dla którego ma zostać wykonana operacja. Jeśli plik nie zostanie podany, polecenie wyszuka go w bieżącym katalogu. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ Wersja środowiska uruchomieniowego dotnet to „{0}”. - - The file '{0}' does not appear to be a valid project or solution file. - Plik „{0}” prawdopodobnie nie jest prawidłowym plikiem projektu lub rozwiązania. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.pt-BR.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.pt-BR.xlf index 55d09806aade..15e93236a0f2 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.pt-BR.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.pt-BR.xlf @@ -292,14 +292,14 @@ Ignorando o projeto referenciado '{0}'. - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - O arquivo de solução ou projeto para operar. Se um arquivo não for especificado, o comando pesquisará um no diretório atual. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ A versão do dotnet runtime é '{0}'. - - The file '{0}' does not appear to be a valid project or solution file. - O arquivo '{0}' parece não ser um projeto válido ou o arquivo de solução. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.ru.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.ru.xlf index 01b87364661c..ed8d9d1f2ffd 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.ru.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.ru.xlf @@ -292,14 +292,14 @@ Пропуск указанного проекта "{0}". - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - Файл проекта или решения. Если файл не указан, команда будет искать его в текущем каталоге. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ Версия среды выполнения dotnet: "{0}". - - The file '{0}' does not appear to be a valid project or solution file. - Файл "{0}" не является допустимым файлом проекта или решения. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.tr.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.tr.xlf index 591a716f4bb5..966fa3c741be 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.tr.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.tr.xlf @@ -292,14 +292,14 @@ Atlama projesi '{0}' başvuru. - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - Üzerinde işlem yapılacak proje veya çözüm dosyası. Bir dosya belirtilmezse komut geçerli dizinde dosya arar. + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ Dotnet çalışma zamanı sürümü '{0}'. - - The file '{0}' does not appear to be a valid project or solution file. - '{0}' dosyası geçerli proje veya çözüm dosyası gibi görünmüyor. + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.zh-Hans.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.zh-Hans.xlf index 46bd577ef432..11ac76cad0ce 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.zh-Hans.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.zh-Hans.xlf @@ -292,14 +292,14 @@ 正在跳过引用的项目“{0}”。 - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - 要操作的项目或解决方案文件。如果没有指定文件,则命令将在当前目录里搜索一个文件。 + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ dotnet 运行时版本为 '{0}'。 - - The file '{0}' does not appear to be a valid project or solution file. - 文件“{0}”似乎不是有效的项目或解决方案文件。 + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Dotnet.Format/dotnet-format/xlf/Resources.zh-Hant.xlf b/src/Dotnet.Format/dotnet-format/xlf/Resources.zh-Hant.xlf index 92a98b25e489..09c037f5a931 100644 --- a/src/Dotnet.Format/dotnet-format/xlf/Resources.zh-Hant.xlf +++ b/src/Dotnet.Format/dotnet-format/xlf/Resources.zh-Hant.xlf @@ -292,14 +292,14 @@ 跳過參考的專案 '{0}’。 - - The project or solution file to operate on. If a file is not specified, the command will search the current directory for one. - 要操作的專案或解決方案。若未指定檔案,命令就會在目前的目錄中搜尋一個檔案。 + + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. + The project or solution or C# (file-based program) file to operate on. If a file is not specified, the command will search the current directory for a project or solution. - - PROJECT | SOLUTION - PROJECT | SOLUTION + + PROJECT | SOLUTION | FILE + PROJECT | SOLUTION | FILE @@ -332,9 +332,9 @@ .NET 執行階段版本為 '{0}'。 - - The file '{0}' does not appear to be a valid project or solution file. - 檔案 '{0}' 似乎不是有效的專案或解決方案檔。 + + The file '{0}' does not appear to be a valid project, solution file, or file-based app. + The file '{0}' does not appear to be a valid project, solution file, or file-based app. diff --git a/src/Microsoft.DotNet.ProjectTools/PublicAPI.Unshipped.txt b/src/Microsoft.DotNet.ProjectTools/PublicAPI.Unshipped.txt index d221a6c4975c..4d4131a39f6f 100644 --- a/src/Microsoft.DotNet.ProjectTools/PublicAPI.Unshipped.txt +++ b/src/Microsoft.DotNet.ProjectTools/PublicAPI.Unshipped.txt @@ -33,6 +33,6 @@ static Microsoft.DotNet.FileBasedPrograms.BuildServiceExtensions.Wrap(this Micro static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.CreateProjectInstanceAsync(Microsoft.DotNet.FileBasedPrograms.IBuildService! buildService, string! entryPointFilePath, string! targetFramework, Microsoft.DotNet.FileBasedPrograms.IProjectCollection! projectCollection, System.Action! errorReporter) -> System.Threading.Tasks.ValueTask static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.GetPropertyFromSourceFile(string! sourceFilePath, string! propertyName) -> string? static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.GetVirtualProjectPath(string! entryPointFilePath) -> string! -static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.IsValidEntryPointPath(string! entryPointFilePath) -> bool +static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.IsValidEntryPointPath(string! entryPointFilePath, bool requireFileToExist = true) -> bool static Microsoft.DotNet.FileBasedPrograms.VirtualProjectBuilder.TryGetEntryPointFilePathFromVirtualProjectPath(string! projectPath, out string? entryPointFilePath) -> bool static Microsoft.DotNet.ProjectTools.LaunchSettings.TryFindLaunchSettingsFile(string! projectOrEntryPointFilePath, string? launchProfile, System.Action! report) -> string? diff --git a/test/TestAssets/dotnet-format/for_code_formatter/file_based_app/formatted.cs b/test/TestAssets/dotnet-format/for_code_formatter/file_based_app/formatted.cs new file mode 100644 index 000000000000..52b2bd58fff4 --- /dev/null +++ b/test/TestAssets/dotnet-format/for_code_formatter/file_based_app/formatted.cs @@ -0,0 +1 @@ +Console.WriteLine(); diff --git a/test/TestAssets/dotnet-format/for_code_formatter/file_based_app/unformatted.cs b/test/TestAssets/dotnet-format/for_code_formatter/file_based_app/unformatted.cs new file mode 100644 index 000000000000..5022f20bcd47 --- /dev/null +++ b/test/TestAssets/dotnet-format/for_code_formatter/file_based_app/unformatted.cs @@ -0,0 +1 @@ + Console.WriteLine( ); diff --git a/test/dotnet-format.UnitTests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs b/test/dotnet-format.UnitTests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs index e690ef393913..5898259b3da5 100644 --- a/test/dotnet-format.UnitTests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs +++ b/test/dotnet-format.UnitTests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs @@ -34,12 +34,12 @@ public async Task InitializeAsync() // Load the analyzer_project into a MSBuildWorkspace. var workspacePath = Path.Combine(TestProjectsPathHelper.GetProjectsDirectory(), s_analyzerProjectFilePath); - var analyzerWorkspace = await MSBuildWorkspaceLoader.LoadAsync(workspacePath, WorkspaceType.Project, binaryLogPath: null, logWorkspaceWarnings: true, logger, targetFramework: null, CancellationToken.None); + using var loadedWorkspace = await MSBuildWorkspaceLoader.LoadAsync(workspacePath, WorkspaceType.Project, binaryLogPath: null, logWorkspaceWarnings: true, logger, targetFramework: null, CancellationToken.None); TestOutputHelper.WriteLine(logger.GetLog()); // From this project we can get valid AnalyzerReferences to add to our test project. - _analyzerReferencesProject = analyzerWorkspace.CurrentSolution.Projects.Single(); + _analyzerReferencesProject = loadedWorkspace.Workspace.CurrentSolution.Projects.Single(); } catch { diff --git a/test/dotnet-format.UnitTests/CodeFormatterTests.cs b/test/dotnet-format.UnitTests/CodeFormatterTests.cs index 2f06e493d574..6745a1221e5b 100644 --- a/test/dotnet-format.UnitTests/CodeFormatterTests.cs +++ b/test/dotnet-format.UnitTests/CodeFormatterTests.cs @@ -23,6 +23,10 @@ public class CodeFormatterTests private static readonly string s_unformattedProgramFilePath = Path.Combine(s_unformattedProjectPath, "program.cs"); private static readonly string s_unformattedSolutionFilePath = Path.Combine("for_code_formatter", "unformatted_solution", "unformatted_solution.sln"); + private static readonly string s_fileBasedAppsDirectoryPath = Path.Combine("for_code_formatter", "file_based_app"); + private static readonly string s_formattedFileBasedAppPath = Path.Combine(s_fileBasedAppsDirectoryPath, "formatted.cs"); + private static readonly string s_unformattedFileBasedAppPath = Path.Combine(s_fileBasedAppsDirectoryPath, "unformatted.cs"); + private static readonly string s_fSharpProjectPath = Path.Combine("for_code_formatter", "fsharp_project"); private static readonly string s_fSharpProjectFilePath = Path.Combine(s_fSharpProjectPath, "fsharp_project.fsproj"); @@ -80,6 +84,19 @@ await TestFormatWorkspaceAsync( expectedFileCount: 3); } + [TestMethod] + public async Task NoFilesFormattedInFormattedFileBasedApp() + { + await TestFormatWorkspaceAsync( + s_formattedFileBasedAppPath, + include: EmptyFilesList, + exclude: EmptyFilesList, + includeGenerated: false, + expectedExitCode: 0, + expectedFilesFormatted: 0, + expectedFileCount: 4); + } + [TestMethod] public async Task FilesFormattedInUnformattedProject() { @@ -93,6 +110,19 @@ await TestFormatWorkspaceAsync( expectedFileCount: 6); } + [TestMethod] + public async Task FilesFormattedInUnformattedFileBasedApp() + { + await TestFormatWorkspaceAsync( + s_unformattedFileBasedAppPath, + include: EmptyFilesList, + exclude: EmptyFilesList, + includeGenerated: false, + expectedExitCode: 0, + expectedFilesFormatted: 1, + expectedFileCount: 4); + } + [TestMethod] public async Task NoFilesFormattedInUnformattedProjectWhenFixingCodeStyle() { @@ -705,7 +735,7 @@ internal async Task TestFormatWorkspaceAsync( } else { - workspaceType = workspacePath.EndsWith("proj") + workspaceType = workspacePath.EndsWith("proj") || workspacePath.EndsWith(".cs") ? WorkspaceType.Project : WorkspaceType.Solution; } diff --git a/test/dotnet-format.UnitTests/MSBuild/MSBuildWorkspaceFinderTests.cs b/test/dotnet-format.UnitTests/MSBuild/MSBuildWorkspaceFinderTests.cs index 185d3c7deb29..1025b7fbd267 100644 --- a/test/dotnet-format.UnitTests/MSBuild/MSBuildWorkspaceFinderTests.cs +++ b/test/dotnet-format.UnitTests/MSBuild/MSBuildWorkspaceFinderTests.cs @@ -29,6 +29,20 @@ public void ThrowsException_CannotFindMSBuildProjectFile() Assert.StartsWith(exceptionMessageStart, exception.Message); } + [TestMethod] + public void ThrowsException_CannotFindFileBasedApp() + { + var testInstance = TestAssetsManager + .CopyTestAsset(testProjectName: "for_workspace_finder/no_project_or_solution", testAssetSubdirectory: "dotnet-format") + .WithSource(); + var filePath = Path.Combine(testInstance.Path, "nonexistent.cs"); + var exceptionMessageStart = string.Format( + Resources.The_project_file_0_does_not_exist, + filePath).Replace('/', Path.DirectorySeparatorChar); + var exception = Assert.ThrowsExactly(() => MSBuildWorkspaceFinder.FindWorkspace(filePath, filePath)); + Assert.StartsWith(exceptionMessageStart, exception.Message); + } + [TestMethod] public void ThrowsException_MultipleMSBuildProjectFiles() { diff --git a/test/dotnet-format.UnitTests/MSBuild/MSBuildWorkspaceLoaderTests.cs b/test/dotnet-format.UnitTests/MSBuild/MSBuildWorkspaceLoaderTests.cs index f2961de0a1d2..6ceecf70c4d6 100644 --- a/test/dotnet-format.UnitTests/MSBuild/MSBuildWorkspaceLoaderTests.cs +++ b/test/dotnet-format.UnitTests/MSBuild/MSBuildWorkspaceLoaderTests.cs @@ -133,7 +133,8 @@ private static async Task AssertProjectLoadsCleanlyAsync(string projectFilePath, { var binaryLogPath = Path.ChangeExtension(projectFilePath, ".binlog"); - using var workspace = (MSBuildWorkspace)await MSBuildWorkspaceLoader.LoadAsync(projectFilePath, WorkspaceType.Project, binaryLogPath, logWorkspaceWarnings: true, logger, targetFramework: null, CancellationToken.None); + using var loadedWorkspace = await MSBuildWorkspaceLoader.LoadAsync(projectFilePath, WorkspaceType.Project, binaryLogPath, logWorkspaceWarnings: true, logger, targetFramework: null, CancellationToken.None); + var workspace = (MSBuildWorkspace)loadedWorkspace.Workspace; Assert.IsEmpty(workspace.Diagnostics); diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests_BuildCommands.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests_BuildCommands.cs index dfddefa8e1f1..27debf9ca339 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests_BuildCommands.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests_BuildCommands.cs @@ -862,6 +862,25 @@ public void Clean() dllFile.Should().NotExist(); } + [TestMethod] + public void Format() + { + var testInstance = TestAssetsManager.CreateTestDirectory(); + var programFile = Path.Join(testInstance.Path, "app.cs"); + File.WriteAllText(programFile, """ + class C {} + """); + + new DotnetCommand(Log, "format", "app.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + File.ReadAllText(programFile).Should().Be(""" + class C { } + """); + } + [TestMethod] [OSCondition(ConditionMode.Exclude, OperatingSystems.Windows)] [UnsupportedOSPlatform("windows")]