diff --git a/README.md b/README.md index dcc20622b8..5fcdde9fc5 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Options: --check Terminates with a non-zero exit code if any files were formatted. --files A comma separated list of relative file paths to format. All files are formatted if empty. --version Display version information + --report Writes a json file to the given directory. Defaults to 'format-report.json' if no filename given. ``` Add `format` after `dotnet` and before the command arguments that you want to run: @@ -62,6 +63,7 @@ Add `format` after `dotnet` and before the command arguments that you want to ru | dotnet **format** -v diag | Formats with very verbose logging. | | dotnet **format** --files Programs.cs,Utility\Logging.cs | Formats the files Program.cs and Utility\Logging.cs | | dotnet **format** --check --dry-run | Formats but does not save. Returns a non-zero exit code if any files would have been changed. | +| dotnet **format** --report <report-path> | Formats and saves a json report file to the given directory. | ### How To Uninstall diff --git a/eng/Versions.props b/eng/Versions.props index acdeea81aa..7d40e5a2fd 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -23,6 +23,7 @@ 1.1.20180503.2 2.9.6 $(MicrosoftNETCoreCompilersPackageVersion) + 4.7.0 true diff --git a/src/CodeFormatter.cs b/src/CodeFormatter.cs index 33bbf55c94..d116c89d37 100644 --- a/src/CodeFormatter.cs +++ b/src/CodeFormatter.cs @@ -6,12 +6,13 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.MSBuild; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Tools.Utilities; using Microsoft.CodeAnalysis.Tools.Formatters; +using Microsoft.CodeAnalysis.Tools.Utilities; using Microsoft.CodeAnalysis.Tools.Workspaces; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.CodingConventions; @@ -33,7 +34,7 @@ public static async Task FormatWorkspaceAsync( ILogger logger, CancellationToken cancellationToken) { - var (workspaceFilePath, workspaceType, logLevel, saveFormattedFiles, _, filesToFormat) = options; + var (workspaceFilePath, workspaceType, logLevel, saveFormattedFiles, _, filesToFormat, reportPath) = options; var logWorkspaceWarnings = logLevel == LogLevel.Trace; logger.LogInformation(string.Format(Resources.Formatting_code_files_in_workspace_0, workspaceFilePath)); @@ -66,8 +67,9 @@ public static async Task FormatWorkspaceAsync( logger.LogTrace(Resources.Running_formatters); + var formattedFiles = new List(); var formattedSolution = await RunCodeFormattersAsync( - solution, formatableFiles, options, logger, cancellationToken).ConfigureAwait(false); + solution, formatableFiles, options, logger, formattedFiles, cancellationToken).ConfigureAwait(false); var formatterRanMS = workspaceStopwatch.ElapsedMilliseconds - loadWorkspaceMS - determineFilesMS; logger.LogTrace(Resources.Complete_in_0_ms, formatterRanMS); @@ -93,6 +95,20 @@ public static async Task FormatWorkspaceAsync( exitCode = 1; } + if (exitCode == 0 && !string.IsNullOrWhiteSpace(reportPath)) + { + var reportFilePath = GetReportFilePath(reportPath); + + logger.LogInformation(Resources.Writing_formatting_report_to_0, reportFilePath); + var seralizerOptions = new JsonSerializerOptions + { + WriteIndented = true + }; + var formattedFilesJson = JsonSerializer.Serialize(formattedFiles, seralizerOptions); + + File.WriteAllText(reportFilePath, formattedFilesJson); + } + logger.LogDebug(Resources.Formatted_0_of_1_files, filesFormatted, fileCount); logger.LogInformation(Resources.Format_complete_in_0_ms, workspaceStopwatch.ElapsedMilliseconds); @@ -101,6 +117,23 @@ public static async Task FormatWorkspaceAsync( } } + private static string GetReportFilePath(string reportPath) + { + var defaultReportName = "format-report.json"; + if (reportPath.EndsWith(".json")) + { + return reportPath; + } + else if (reportPath == ".") + { + return Path.Combine(Environment.CurrentDirectory, defaultReportName); + } + else + { + return Path.Combine(reportPath, defaultReportName); + } + } + private static async Task OpenWorkspaceAsync( string workspacePath, WorkspaceType workspaceType, @@ -192,13 +225,14 @@ private static async Task RunCodeFormattersAsync( ImmutableArray<(DocumentId, OptionSet, ICodingConventionsSnapshot)> formattableDocuments, FormatOptions options, ILogger logger, + List formattedFiles, CancellationToken cancellationToken) { var formattedSolution = solution; foreach (var codeFormatter in s_codeFormatters) { - formattedSolution = await codeFormatter.FormatAsync(formattedSolution, formattableDocuments, options, logger, cancellationToken).ConfigureAwait(false); + formattedSolution = await codeFormatter.FormatAsync(formattedSolution, formattableDocuments, options, logger, formattedFiles, cancellationToken).ConfigureAwait(false); } return formattedSolution; diff --git a/src/FileChange.cs b/src/FileChange.cs new file mode 100644 index 0000000000..d149ee2240 --- /dev/null +++ b/src/FileChange.cs @@ -0,0 +1,21 @@ +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Tools +{ + public class FileChange + { + public int LineNumber { get; } + + public int CharNumber { get; } + + public string FormatDescription { get; } + + public FileChange(LinePosition changePosition, string formatDescription) + { + // LinePosition is zero based so we need to increment to report numbers people expect. + LineNumber = changePosition.Line + 1; + CharNumber = changePosition.Character + 1; + FormatDescription = formatDescription; + } + } +} diff --git a/src/FormatOptions.cs b/src/FormatOptions.cs index 0c4a27a358..ec2d2a8fbd 100644 --- a/src/FormatOptions.cs +++ b/src/FormatOptions.cs @@ -13,6 +13,7 @@ internal class FormatOptions public bool SaveFormattedFiles { get; } public bool ChangesAreErrors { get; } public ImmutableHashSet FilesToFormat { get; } + public string ReportPath { get; } public FormatOptions( string workspaceFilePath, @@ -20,7 +21,8 @@ public FormatOptions( LogLevel logLevel, bool saveFormattedFiles, bool changesAreErrors, - ImmutableHashSet filesToFormat) + ImmutableHashSet filesToFormat, + string reportPath) { WorkspaceFilePath = workspaceFilePath; WorkspaceType = workspaceType; @@ -28,6 +30,7 @@ public FormatOptions( SaveFormattedFiles = saveFormattedFiles; ChangesAreErrors = changesAreErrors; FilesToFormat = filesToFormat; + ReportPath = reportPath; } public void Deconstruct( @@ -36,7 +39,8 @@ public void Deconstruct( out LogLevel logLevel, out bool saveFormattedFiles, out bool changesAreErrors, - out ImmutableHashSet filesToFormat) + out ImmutableHashSet filesToFormat, + out string reportPath) { workspaceFilePath = WorkspaceFilePath; workspaceType = WorkspaceType; @@ -44,6 +48,7 @@ public void Deconstruct( saveFormattedFiles = SaveFormattedFiles; changesAreErrors = ChangesAreErrors; filesToFormat = FilesToFormat; + reportPath = ReportPath; } } } diff --git a/src/FormattedFile.cs b/src/FormattedFile.cs new file mode 100644 index 0000000000..b68a88ccaf --- /dev/null +++ b/src/FormattedFile.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Microsoft.CodeAnalysis.Tools +{ + public class FormattedFile + { + public DocumentId DocumentId { get; } + + public string FileName { get; } + + public string FilePath { get; } + + public IEnumerable FileChanges { get; } + + public FormattedFile(Document document, IEnumerable fileChanges) + { + DocumentId = document.Id; + FileName = document.Name; + FilePath = document.FilePath; + FileChanges = fileChanges; + } + } +} diff --git a/src/Formatters/DocumentFormatter.cs b/src/Formatters/DocumentFormatter.cs index 279ba2fc69..a5b36a1b01 100644 --- a/src/Formatters/DocumentFormatter.cs +++ b/src/Formatters/DocumentFormatter.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. +using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Threading; @@ -26,10 +27,11 @@ public async Task FormatAsync( ImmutableArray<(DocumentId, OptionSet, ICodingConventionsSnapshot)> formattableDocuments, FormatOptions formatOptions, ILogger logger, + List formattedFiles, CancellationToken cancellationToken) { var formattedDocuments = FormatFiles(solution, formattableDocuments, formatOptions, logger, cancellationToken); - return await ApplyFileChangesAsync(solution, formattedDocuments, formatOptions, logger, cancellationToken).ConfigureAwait(false); + return await ApplyFileChangesAsync(solution, formattedDocuments, formatOptions, logger, formattedFiles, cancellationToken).ConfigureAwait(false); } /// @@ -90,10 +92,11 @@ protected abstract Task FormatFileAsync( /// Applies the changed to each formatted . /// private async Task ApplyFileChangesAsync( - Solution solution, + Solution solution, ImmutableArray<(Document, Task<(SourceText originalText, SourceText formattedText)>)> formattedDocuments, FormatOptions formatOptions, ILogger logger, + List formattedFiles, CancellationToken cancellationToken) { var formattedSolution = solution; @@ -111,11 +114,8 @@ private async Task ApplyFileChangesAsync( continue; } - if (!formatOptions.SaveFormattedFiles || formatOptions.LogLevel == LogLevel.Trace) - { - // Log formatting changes as errors when we are doing a dry-run. - LogFormattingChanges(formatOptions.WorkspaceFilePath, document.FilePath, originalText, formattedText, formatOptions.ChangesAreErrors, logger); - } + var fileChanges = GetFileChanges(formatOptions, formatOptions.WorkspaceFilePath, document.FilePath, originalText, formattedText, formatOptions.ChangesAreErrors, logger); + formattedFiles.Add(new FormattedFile(document, fileChanges)); formattedSolution = formattedSolution.WithDocumentText(document.Id, formattedText, PreservationMode.PreserveIdentity); } @@ -123,26 +123,38 @@ private async Task ApplyFileChangesAsync( return formattedSolution; } - private void LogFormattingChanges(string workspacePath, string filePath, SourceText originalText, SourceText formattedText, bool changesAreErrors, ILogger logger) + private IEnumerable GetFileChanges(FormatOptions formatOptions, string workspacePath, string filePath, SourceText originalText, SourceText formattedText, bool changesAreErrors, ILogger logger) { + var fileChanges = new List(); var workspaceFolder = Path.GetDirectoryName(workspacePath); var changes = formattedText.GetChangeRanges(originalText); foreach (var change in changes) { - // LinePosition is zero based so we need to increment to report numbers people expect. var changePosition = originalText.Lines.GetLinePosition(change.Span.Start); - var formatMessage = $"{Path.GetRelativePath(workspaceFolder, filePath)}({changePosition.Line + 1},{changePosition.Character + 1}): {FormatWarningDescription}"; + var fileChange = new FileChange(changePosition, FormatWarningDescription); + fileChanges.Add(fileChange); - if (changesAreErrors) - { - logger.LogError(formatMessage); - } - else + if (!formatOptions.SaveFormattedFiles || formatOptions.LogLevel == LogLevel.Trace) { - logger.LogWarning(formatMessage); + LogFormattingChanges(filePath, changesAreErrors, logger, workspaceFolder, fileChange); } } + + return fileChanges; + } + + private static void LogFormattingChanges(string filePath, bool changesAreErrors, ILogger logger, string workspaceFolder, FileChange fileChange) + { + var formatMessage = $"{Path.GetRelativePath(workspaceFolder, filePath)}({fileChange.LineNumber},{fileChange.CharNumber}): {fileChange.FormatDescription}"; + if (changesAreErrors) + { + logger.LogError(formatMessage); + } + else + { + logger.LogWarning(formatMessage); + } } } } diff --git a/src/Formatters/ICodeFormatter.cs b/src/Formatters/ICodeFormatter.cs index 85e301af9f..6770875d99 100644 --- a/src/Formatters/ICodeFormatter.cs +++ b/src/Formatters/ICodeFormatter.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -19,6 +20,7 @@ Task FormatAsync( ImmutableArray<(DocumentId, OptionSet, ICodingConventionsSnapshot)> formattableDocuments, FormatOptions options, ILogger logger, + List formattedFiles, CancellationToken cancellationToken); } } diff --git a/src/Program.cs b/src/Program.cs index 270b231b9e..d3bc20acab 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -36,13 +36,14 @@ private static async Task Main(string[] args) .AddOption(new Option(new[] { "--dry-run" }, Resources.Format_files_but_do_not_save_changes_to_disk, new Argument())) .AddOption(new Option(new[] { "--check" }, Resources.Terminate_with_a_non_zero_exit_code_if_any_files_were_formatted, new Argument())) .AddOption(new Option(new[] { "--files" }, Resources.A_comma_separated_list_of_relative_file_paths_to_format_All_files_are_formatted_if_empty, new Argument(() => null))) + .AddOption(new Option(new[] { "--report" }, Resources.Accepts_a_file_path_which_if_provided_will_produce_a_format_report_json_file_in_the_given_directory, new Argument(() => null))) .UseVersionOption() .Build(); return await parser.InvokeAsync(args).ConfigureAwait(false); } - public static async Task Run(string folder, string workspace, string verbosity, bool dryRun, bool check, string files, IConsole console = null) + public static async Task Run(string folder, string workspace, string verbosity, bool dryRun, bool check, string files, string report, IConsole console = null) { // Setup logging. var serviceCollection = new ServiceCollection(); @@ -120,7 +121,8 @@ public static async Task Run(string folder, string workspace, string verbos logLevel, saveFormattedFiles: !dryRun, changesAreErrors: check, - filesToFormat); + filesToFormat, + reportPath: report); var formatResult = await CodeFormatter.FormatWorkspaceAsync( formatOptions, diff --git a/src/Resources.resx b/src/Resources.resx index a7331817aa..7a7a5a25b4 100644 --- a/src/Resources.resx +++ b/src/Resources.resx @@ -210,4 +210,10 @@ Cannot specify both folder and workspace options. + + Accepts a file path, which if provided, will produce a json report in the given directory. + + + Writing formatting report to: '{0}'. + \ No newline at end of file diff --git a/src/dotnet-format.csproj b/src/dotnet-format.csproj index 0db3144261..2117266fc2 100644 --- a/src/dotnet-format.csproj +++ b/src/dotnet-format.csproj @@ -37,6 +37,7 @@ + @@ -45,7 +46,7 @@ - + diff --git a/src/xlf/Resources.cs.xlf b/src/xlf/Resources.cs.xlf index 1ec437e41c..a69cba18b9 100644 --- a/src/xlf/Resources.cs.xlf +++ b/src/xlf/Resources.cs.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.de.xlf b/src/xlf/Resources.de.xlf index b356ce8fd4..eebd3920c3 100644 --- a/src/xlf/Resources.de.xlf +++ b/src/xlf/Resources.de.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.es.xlf b/src/xlf/Resources.es.xlf index 8b37b5fcd2..ba69ba3c3d 100644 --- a/src/xlf/Resources.es.xlf +++ b/src/xlf/Resources.es.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.fr.xlf b/src/xlf/Resources.fr.xlf index 5345117728..5524303813 100644 --- a/src/xlf/Resources.fr.xlf +++ b/src/xlf/Resources.fr.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.it.xlf b/src/xlf/Resources.it.xlf index d472a97f7f..d4344edda3 100644 --- a/src/xlf/Resources.it.xlf +++ b/src/xlf/Resources.it.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.ja.xlf b/src/xlf/Resources.ja.xlf index 53f5fe83e0..52fec6f7e3 100644 --- a/src/xlf/Resources.ja.xlf +++ b/src/xlf/Resources.ja.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.ko.xlf b/src/xlf/Resources.ko.xlf index a7ef729016..d9152c013e 100644 --- a/src/xlf/Resources.ko.xlf +++ b/src/xlf/Resources.ko.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.pl.xlf b/src/xlf/Resources.pl.xlf index 069c24b4fe..078cca4c95 100644 --- a/src/xlf/Resources.pl.xlf +++ b/src/xlf/Resources.pl.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.pt-BR.xlf b/src/xlf/Resources.pt-BR.xlf index 26e4a79595..6bf7888b1f 100644 --- a/src/xlf/Resources.pt-BR.xlf +++ b/src/xlf/Resources.pt-BR.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.ru.xlf b/src/xlf/Resources.ru.xlf index 5192bf1779..5c5d2c581f 100644 --- a/src/xlf/Resources.ru.xlf +++ b/src/xlf/Resources.ru.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.tr.xlf b/src/xlf/Resources.tr.xlf index fcf937673d..5c9cc06534 100644 --- a/src/xlf/Resources.tr.xlf +++ b/src/xlf/Resources.tr.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.zh-Hans.xlf b/src/xlf/Resources.zh-Hans.xlf index c978ddb51c..01656d5dd0 100644 --- a/src/xlf/Resources.zh-Hans.xlf +++ b/src/xlf/Resources.zh-Hans.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/src/xlf/Resources.zh-Hant.xlf b/src/xlf/Resources.zh-Hant.xlf index 05476acc16..592725dae2 100644 --- a/src/xlf/Resources.zh-Hant.xlf +++ b/src/xlf/Resources.zh-Hant.xlf @@ -7,6 +7,11 @@ A comma separated list of relative file paths to format. All files are formatted if empty. + + Accepts a file path, which if provided, will produce a json report in the given directory. + Accepts a file path, which if provided, will produce a json report in the given directory. + + Add final newline. Add final newline. @@ -157,6 +162,11 @@ Warnings were encountered while loading the workspace. Set the verbosity option to the 'diagnostic' level to log warnings. + + Writing formatting report to: '{0}'. + Writing formatting report to: '{0}'. + + \ No newline at end of file diff --git a/tests/CodeFormatterTests.cs b/tests/CodeFormatterTests.cs index fc61b20614..1bd6acf413 100644 --- a/tests/CodeFormatterTests.cs +++ b/tests/CodeFormatterTests.cs @@ -260,7 +260,8 @@ public async Task TestFormatWorkspaceAsync(string workspaceFilePath, IEn LogLevel.Trace, saveFormattedFiles: false, changesAreErrors: false, - filesToFormat); + filesToFormat, + reportPath: string.Empty); var formatResult = await CodeFormatter.FormatWorkspaceAsync(formatOptions, logger, CancellationToken.None); Assert.Equal(expectedExitCode, formatResult.ExitCode); diff --git a/tests/Formatters/AbstractFormatterTests.cs b/tests/Formatters/AbstractFormatterTests.cs index 436aa720ce..401a9ba642 100644 --- a/tests/Formatters/AbstractFormatterTests.cs +++ b/tests/Formatters/AbstractFormatterTests.cs @@ -91,11 +91,12 @@ private protected async Task TestAsync(string testCode, string expec logLevel: LogLevel.Trace, saveFormattedFiles: false, changesAreErrors: false, - filesToFormat: ImmutableHashSet.Create(document.FilePath)); + filesToFormat: ImmutableHashSet.Create(document.FilePath), + reportPath: string.Empty); var filesToFormat = await GetOnlyFileToFormatAsync(solution, editorConfig); - var formattedSolution = await Formatter.FormatAsync(solution, filesToFormat, formatOptions, Logger, default); + var formattedSolution = await Formatter.FormatAsync(solution, filesToFormat, formatOptions, Logger, new List(), default); var formattedDocument = GetOnlyDocument(formattedSolution); var formattedText = await formattedDocument.GetTextAsync(); @@ -151,7 +152,7 @@ private protected async Task TestAsync(string testCode, string expec /// Additional documents to include in the project. /// Additional metadata references to include in the project. /// A solution containing a project with the specified sources and additional files. - private Solution GetSolution((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences) + private protected Solution GetSolution((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences) { var project = CreateProject(sources, additionalFiles, additionalMetadataReferences, Language); return project.Solution; diff --git a/tests/Formatters/FormattedFilesTests.cs b/tests/Formatters/FormattedFilesTests.cs new file mode 100644 index 0000000000..cd50fac8d2 --- /dev/null +++ b/tests/Formatters/FormattedFilesTests.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Tools.Formatters; +using Microsoft.CodeAnalysis.Tools.Tests.Utilities; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Microsoft.CodeAnalysis.Tools.Tests.Formatters +{ + public class FormattedFilesTests : CSharpFormatterTests + { + private protected override ICodeFormatter Formatter => new FinalNewlineFormatter(); + + private readonly Dictionary editorConfig = new Dictionary() + { + ["insert_final_newline"] = "true", + ["end_of_line"] = "lf", + }; + + [Fact] + public async Task ReturnsItem_WhenFileFormatted() + { + var testCode = "class C\n{\n}"; + + var result = await TestFormattedFiles(testCode); + + Assert.Single(result); + } + + [Fact] + public async Task ReturnsEmptyList_WhenNoFilesFormatted() + { + var testCode = "class C\n{\n}\n"; + + var result = await TestFormattedFiles(testCode); + + Assert.Empty(result); + } + + private async Task> TestFormattedFiles(string testCode) + { + var text = SourceText.From(testCode, Encoding.UTF8); + TestState.Sources.Add(text); + + var solution = GetSolution(TestState.Sources.ToArray(), TestState.AdditionalFiles.ToArray(), TestState.AdditionalReferences.ToArray()); + var project = solution.Projects.Single(); + var document = project.Documents.Single(); + var formatOptions = new FormatOptions( + workspaceFilePath: project.FilePath, + workspaceType: WorkspaceType.Folder, + logLevel: LogLevel.Trace, + saveFormattedFiles: false, + changesAreErrors: false, + filesToFormat: ImmutableHashSet.Create(document.FilePath), + reportPath: string.Empty); + + var filesToFormat = await GetOnlyFileToFormatAsync(solution, editorConfig); + + var formattedFiles = new List(); + await Formatter.FormatAsync(solution, filesToFormat, formatOptions, new TestLogger(), formattedFiles, default); + + return formattedFiles; + } + } +}