-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added DotNet global tool for code formatting. #29403
Added DotNet global tool for code formatting. #29403
Conversation
317c43d
to
9f1894a
Compare
src/Tools/dotnet-format/Program.cs
Outdated
|
||
MSBuildEnvironment.ApplyEnvironmentVariables(); | ||
|
||
return await CodeFormatter.FormatWorkspaceAsync(logger, workspacePath, isSolution, cancellationTokenSource.Token); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this directly calling a fullframework library? Did you try to install the tool and invoke locally? As long as that works it is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up changing the Workspaces.MSBuild project to multi-target. Only required a couple changes to code that was already conditionally compiling. I've tested on Win10 and Ubuntu 18.04.
src/Tools/dotnet-format/Program.cs
Outdated
.UseParseErrorReporting() | ||
.UseExceptionHandler() | ||
.AddOption(new[] { "-w", "--workspace" }, "The project or solution to format", a => a.WithDefaultValue(() => null).ExactlyOne()) | ||
.AddOption(new[] { "-q", "--quiet" }, "Suppresses all output except warnings and errors", a => a.ParseArgumentsAs<bool>((sr) => ArgumentParseResult.Success(true), new ArgumentArityValidator(0, 0))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be simpler. Just this should do what you want:
a.ParseArgumentsAs<bool>()
The arity is inferred from the type if you don't specify it.
Also, I think the following will always evaluate as true because you're not parsing the sr
value that you're receiving in the lambda:
a => a.ParseArgumentsAs<bool>((sr) => ArgumentParseResult.Success(true)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted an arity of 0 for those options since I was treating them as simple flags. This gives the behavior that my tool could be invoked like dotnet format -v .\SomePath\
and verbose would be set and the .\SomePath\
would get interpreted as the workspace. Just using a.ParseArgumentsAs<bool>()
would try to interpret .\SomePath\
as a bool
and error.
Thankfully it doesn't always return true. It has the desired behavior of only being true when the option is present.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default behavior for bool
is that the argument can be supplied or not (e.g. an arity of 0 - 1), and either way will work:
format -q
format -q true
format -q false
I'm wondering if you found a bug. I'll try to repro.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This indeed a bug in the parser.
9f1894a
to
9f69e72
Compare
{ | ||
internal class CodeFormatter | ||
{ | ||
public static async Task<int> FormatWorkspaceAsync(ILogger logger, string workspacePath, bool isSolution, CancellationToken token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't workspacePath
be solutionOrProjectPath
?
{ "AlwaysCompileMarkupFilesInSeparateDomain", bool.FalseString }, | ||
|
||
// Use the latest language version to force the full set of available analyzers to run on the project. | ||
{ "LangVersion", "latest" }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would think we would want to respect the language version of each project. I wouldn't want 7.3 features added if they break my project.
} | ||
|
||
var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); | ||
if (GeneratedCodeUtilities.IsGeneratedCode(syntaxTree, isCommentTrivia, cancellationToken)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we control this with an option? Some people may want to format generated code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably should not. Code generators have the option of applying the formatting themselves during the code generation process, but we should assume that once the tool completes the form is finalized.
@@ -0,0 +1,66 @@ | |||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copyright?
else | ||
{ | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all this code looks like an exact duplicate of code that is above.
} | ||
catch (Exception ex) | ||
{ | ||
throw new Exception($"Unable to write standard error output when running command {commandInfo.Name}: {ex.Message}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Localized message?
var buffer = new char[512]; | ||
while (true) | ||
{ | ||
var readCount = process.StandardOutput.Read(buffer, 0, 512); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
repeat of constant.
{ | ||
Name = "where", | ||
Arguments = commandName | ||
}, timeoutInMilliseconds: 1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
repeat of constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the need for a timeout?
{ | ||
using (var outputStream = new MemoryStream()) | ||
{ | ||
using (var outputStreamWriter = new StreamWriter(outputStream)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unnecessary indentation.
private static IEnumerable<string> FindSolutionFiles(string basePath) => Directory.EnumerateFileSystemEntries(basePath, "*.sln", SearchOption.TopDirectoryOnly); | ||
|
||
private static IEnumerable<string> FindProjectFiles(string basePath) => Directory.EnumerateFileSystemEntries(basePath, "*.*proj", SearchOption.TopDirectoryOnly) | ||
.Where(f => !".xproj".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
repeat of constant.
src/Tools/dotnet-format/Program.cs
Outdated
} | ||
catch (Exception ex) | ||
{ | ||
if (ex is TaskCanceledException || ex is OperationCanceledException) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TaskCanceledException is already an OperationCanceledException. So it's redundant to have both checks.
src/Tools/dotnet-format/Program.cs
Outdated
} | ||
|
||
logger.LogError(ex.ToString()); | ||
logger.LogError((string)"An unexpected error occurred"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this cast seems unnecessary.
src/Tools/dotnet-format/README.md
Outdated
@@ -0,0 +1,48 @@ | |||
dotnet-format | |||
============= | |||
`dotnet-format` is a code formatter for `dotnet` that reads style preferences from your `.editorconfig` file and applies them to a project or solution. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i missed it, but what code actually ensures there is a .editorconfig file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good point.
In the absence of an .editorconfig file it will use the default formatting options. This behavior might be useful to some users who do not wish to choose formatting options for their project but simply enforce some level of consistency (like Prettier). Something to consider.
<value>Formatting code files in workspace '{0}'.</value> | ||
</data> | ||
<data name="Warn_UnsupportedProject" xml:space="preserve"> | ||
<value>Could not format '{0}'. Format currently supports only C# and VisualBasic projects.</value> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Visual Basic" is two words.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Having a bit of a "Berenstain Bears" moment here. I guess I always assumed in the same way QuickBasic was one word, that Visual Basic was too and just never noticed otherwise.
Dumb question: why does this need to live in the Roslyn repo? It seems like it would be fine living elsewhere. |
build/Targets/Packages.props
Outdated
@@ -268,6 +271,8 @@ | |||
https://myget.org/F/vs-editor/api/v3/index.json; | |||
https://vside.myget.org/F/vssdk/api/v3/index.json; | |||
https://vside.myget.org/F/vs-impl/api/v3/index.json; | |||
https://dotnet.myget.org/F/system-commandline/api/v3/index.json; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extra blank line.
// AppDomains will likely not work due to https://github.com/Microsoft/MSBuildLocator/issues/16. | ||
{ "AlwaysCompileMarkupFilesInSeparateDomain", bool.FalseString }, | ||
|
||
// Use the latest language version to force the full set of available analyzers to run on the project. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need analyzers running if this is just updating code formatting?
a989e11
to
09900cf
Compare
09900cf
to
0bcddd8
Compare
Tool can be run on a project, solution, or folder containing a project or solution. Tool pulls formatting configuration from an .editorconfig if present, otherwise formats with roslyn defaults.
0bcddd8
to
6113fa0
Compare
|
||
// LICENSING NOTE: The license for this file is from the originating | ||
// source and not the general https://github.com/dotnet/roslyn license. | ||
// See https://github.com/Microsoft/MSBuildLocator/blob/6631a6dbf9be72b2426e260c99dc0f345e79b8e5/src/MSBuildLocator/MSBuildLocator.cs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ What would be the downside if we used MSBuild Locator via PackageReference
for now, and sent a pull request to that project to add x-plat support instead of coding it all here in a forked copy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like they have an open PR to support the .net core use case. I can add a comment that links to the PR and recommends moving to use their package once it gets in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC, that PR hit some significant roadblocks. Have you reached out to the right folks on the .NET Core SDK and MSBuild teams to determine whether the solution here is the right one? I think the MSBuild Locator PR has pretty much stalled out. At a minimum, I'd start with @nguerrera.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There were really two things with the locator PR:
-
It doesn't work on Mac (nor all flavors of Linux). This is a technical issue about how it is assuming it can P/Invoke to already loaded hostfxr.dll. This does not apply to the alternate approach of parsing dotnet -info.
-
It ties the locator to implementation details of the CLI. This applies to both approaches. See Support for locating .NET Core instances microsoft/MSBuildLocator#33 (comment)
As on the locator PR, I can let (2) go here if you agree to update this code if/when the layout changes. We do not consider the current placement of msbuild dlls in the CLI as a contract for tools outside the CLI.
@wli3, Not a lot has changed, but if you can just confirm that things still look OK I would be grateful. |
Is there an ETA for release? /cc @JoeRobich @jaredpar |
@MarcoRossignoli, If the request is to fail a build based on formatting issues, then there is a second PR for a Formatting Analyzer that you will be able to install through NuGet. You could configure the severity of that analyzer to fail your build and give you error locations. |
@JoeRobich thank's!I'll take a look! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pull request needs to be rewritten to avoid including dotnet-format.2.11.0-dev.nupkg in the source control history.
|
||
namespace Microsoft.CodeAnalysis.Tools.CodeFormatter | ||
{ | ||
internal class CodeFormatter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 static class
private readonly IConsole _console; | ||
private readonly LogLevel _logLevel; | ||
|
||
private readonly IReadOnlyDictionary<LogLevel, ConsoleColor> _logLevelColorMap = new Dictionary<LogLevel, ConsoleColor> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 static
private readonly IConsole _console; | ||
private readonly LogLevel _logLevel; | ||
|
||
private readonly IReadOnlyDictionary<LogLevel, ConsoleColor> _logLevelColorMap = new Dictionary<LogLevel, ConsoleColor> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 ImmutableDictionary<LogLevel, ConsoleColor>
{ | ||
internal class EditorConfigOptionsApplier | ||
{ | ||
private IReadOnlyList<(IOption, OptionStorageLocation, MethodInfo)> _formattingOptionsWithStorage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 readonly
{ | ||
internal class EditorConfigOptionsApplier | ||
{ | ||
private IReadOnlyList<(IOption, OptionStorageLocation, MethodInfo)> _formattingOptionsWithStorage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 ImmutableArray<>
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp2.1</TargetFramework> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❔ Why not netcoreapp2.0
like our other projects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, should be netcoreapp2.0
in dev16.0.x.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
global tools only support netcoreapp2.1 and above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
global tools only support netcoreapp2.1 and above
😕 Which part of it? I would expect anything that supports netcoreapp2.1 to also support netcoreapp2.0, and also netcoreapp1.0 for that matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: we are on netcoreapp2.1 now in master and netcoreapp2.0 is actually no longer even supported.
src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj
Show resolved
Hide resolved
For global tools packaging concern, the current state is good. And global tools only support netcoreapp2.1 and up (due to the dependency on the framework dependent apphost added in runtime 2.1) |
d9ae946
to
8571fde
Compare
@sharwell I removed the nupkg from the commit history and addressed the open feedback. Could you give it another look? |
Logged ubuntu_16_debug_prtest failure as #30374 |
retest ubuntu_16_debug_prtest please |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs to handle localized dotnet --info
output.
|
||
// LICENSING NOTE: The license for this file is from the originating | ||
// source and not the general https://github.com/dotnet/roslyn license. | ||
// See https://github.com/Microsoft/MSBuildLocator/blob/6631a6dbf9be72b2426e260c99dc0f345e79b8e5/src/MSBuildLocator/MSBuildLocator.cs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC, that PR hit some significant roadblocks. Have you reached out to the right folks on the .NET Core SDK and MSBuild teams to determine whether the solution here is the right one? I think the MSBuild Locator PR has pretty much stalled out. At a minimum, I'd start with @nguerrera.
src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! My meta feedback is that we need to get this code into the MSBuildLocator because this PR makes MSBuildWorkspace available to .NET Core applications but those applications still lack the connective tissue to make it work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not review the entire PR, only the place where I was mentioned. I approve this with the understanding that we will look for a more permanent home in MSBuildLocator and also that this approval does not mean that the MSBuild.dll location in the SDK cannot be changed in future versions. The tool here (or better, MSBuildLocator) will need to react to such changes.
Tool can be run on a project, solution, or folder containing a
project or solution. Tool pulls formatting configuration from an
.editorconfig if present, otherwise formats with roslyn defaults.