Skip to content
This repository was archived by the owner on Jul 12, 2022. It is now read-only.

Commit 05039a8

Browse files
committed
Add support for cleaning up files without a solution or project. #91
The passed file, can now not only be an msbuild project or a solution file. But also an rsp file. The rsp file is the same file format as the commandline arguments that gets passed to csc.exe Closes #89
1 parent 4286c97 commit 05039a8

File tree

3 files changed

+113
-13
lines changed

3 files changed

+113
-13
lines changed

src/CodeFormatter/Program.cs

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System;
5-
using System.Collections.Generic;
65
using System.Collections.Immutable;
76
using System.IO;
87
using System.Linq;
98
using System.Reflection;
109
using System.Threading;
1110
using System.Threading.Tasks;
11+
using Microsoft.CodeAnalysis;
1212
using Microsoft.CodeAnalysis.MSBuild;
1313
using Microsoft.DotNet.CodeFormatting;
1414

@@ -19,18 +19,21 @@ internal static class Program
1919
private const string FileSwitch = "/file:";
2020
private const string ConfigSwitch = "/c:";
2121
private const string CopyrightSwitch = "/copyright:";
22+
private const string LanguageSwitch = "/lang:";
2223

2324
private static int Main(string[] args)
2425
{
2526
if (args.Length < 1)
2627
{
27-
Console.WriteLine("CodeFormatter <project or solution> [/file:<filename>] [/nocopyright] [/nounicode] [/tables] [/c:<config1,config2> [/copyright:file] [/verbose]");
28+
Console.WriteLine("CodeFormatter <project, solution or responsefile> [/file:<filename>] [/nocopyright] [/nounicode] [/tables] [/c:<config1,config2> [/copyright:file] [/lang:<language>] [/verbose]");
2829
Console.WriteLine(" <filename> - Only apply changes to files with specified name.");
2930
Console.WriteLine(" <configs> - Additional preprocessor configurations the formatter");
3031
Console.WriteLine(" should run under.");
3132
Console.WriteLine(" <copyright> - Specifies file containing copyright header.");
3233
Console.WriteLine(" Use ConvertTests to convert MSTest tests to xUnit.");
3334
Console.WriteLine(" <tables> - Let tables opt out of formatting by defining DOTNET_FORMATTER");
35+
Console.WriteLine(" <language> - Specifies the language to use when a responsefile is specified.");
36+
Console.WriteLine(" i.e. 'C#', 'Visual Basic', ... (default: 'C#')");
3437
Console.WriteLine(" <verbose> - Verbose output");
3538
Console.WriteLine(" <nounicode> - Do not convert unicode strings to escape sequences");
3639
Console.WriteLine(" <nocopyright>- Do not update the copyright message.");
@@ -40,14 +43,15 @@ private static int Main(string[] args)
4043
var projectOrSolutionPath = args[0];
4144
if (!File.Exists(projectOrSolutionPath))
4245
{
43-
Console.Error.WriteLine("Project or solution {0} doesn't exist.", projectOrSolutionPath);
46+
Console.Error.WriteLine("Project, solution or response file {0} doesn't exist.", projectOrSolutionPath);
4447
return -1;
4548
}
4649

4750
var fileNamesBuilder = ImmutableArray.CreateBuilder<string>();
4851
var ruleTypeBuilder = ImmutableArray.CreateBuilder<string>();
4952
var configBuilder = ImmutableArray.CreateBuilder<string[]>();
5053
var copyrightHeader = FormattingConstants.DefaultCopyrightHeader;
54+
var language = LanguageNames.CSharp;
5155
var convertUnicode = true;
5256
var allowTables = false;
5357
var verbose = false;
@@ -82,6 +86,10 @@ private static int Main(string[] args)
8286
return -1;
8387
}
8488
}
89+
else if (arg.StartsWith(LanguageSwitch, StringComparison.OrdinalIgnoreCase))
90+
{
91+
language = arg.Substring(LanguageSwitch.Length);
92+
}
8593
else if (comparer.Equals(arg, "/nocopyright"))
8694
{
8795
copyrightHeader = ImmutableArray<string>.Empty;
@@ -117,6 +125,7 @@ private static int Main(string[] args)
117125
fileNamesBuilder.ToImmutableArray(),
118126
configBuilder.ToImmutableArray(),
119127
copyrightHeader,
128+
language,
120129
allowTables,
121130
convertUnicode,
122131
verbose,
@@ -140,17 +149,17 @@ private static int Main(string[] args)
140149
}
141150

142151
private static async Task RunAsync(
143-
string projectOrSolutionPath,
152+
string projectSolutionOrRspPath,
144153
ImmutableArray<string> ruleTypes,
145154
ImmutableArray<string> fileNames,
146155
ImmutableArray<string[]> preprocessorConfigurations,
147156
ImmutableArray<string> copyrightHeader,
157+
string language,
148158
bool allowTables,
149159
bool convertUnicode,
150160
bool verbose,
151161
CancellationToken cancellationToken)
152162
{
153-
var workspace = MSBuildWorkspace.Create();
154163
var engine = FormattingEngine.Create(ruleTypes);
155164
engine.PreprocessorConfigurations = preprocessorConfigurations;
156165
engine.FileNames = fileNames;
@@ -159,18 +168,33 @@ private static async Task RunAsync(
159168
engine.ConvertUnicodeCharacters = convertUnicode;
160169
engine.Verbose = verbose;
161170

162-
Console.WriteLine(Path.GetFileName(projectOrSolutionPath));
163-
string extension = Path.GetExtension(projectOrSolutionPath);
164-
if (StringComparer.OrdinalIgnoreCase.Equals(extension, ".sln"))
171+
Console.WriteLine(Path.GetFileName(projectSolutionOrRspPath));
172+
string extension = Path.GetExtension(projectSolutionOrRspPath);
173+
if (StringComparer.OrdinalIgnoreCase.Equals(extension, ".rsp"))
174+
{
175+
using (var workspace = ResponseFileWorkspace.Create())
176+
{
177+
Project project = workspace.OpenCommandLineProject(projectSolutionOrRspPath, language);
178+
await engine.FormatProjectAsync(project, cancellationToken);
179+
}
180+
}
181+
else if (StringComparer.OrdinalIgnoreCase.Equals(extension, ".sln"))
165182
{
166-
var solution = await workspace.OpenSolutionAsync(projectOrSolutionPath, cancellationToken);
167-
await engine.FormatSolutionAsync(solution, cancellationToken);
183+
using (var workspace = MSBuildWorkspace.Create())
184+
{
185+
workspace.LoadMetadataForReferencedProjects = true;
186+
var solution = await workspace.OpenSolutionAsync(projectSolutionOrRspPath, cancellationToken);
187+
await engine.FormatSolutionAsync(solution, cancellationToken);
188+
}
168189
}
169190
else
170191
{
171-
workspace.LoadMetadataForReferencedProjects = true;
172-
var project = await workspace.OpenProjectAsync(projectOrSolutionPath, cancellationToken);
173-
await engine.FormatProjectAsync(project, cancellationToken);
192+
using (var workspace = MSBuildWorkspace.Create())
193+
{
194+
workspace.LoadMetadataForReferencedProjects = true;
195+
var project = await workspace.OpenProjectAsync(projectSolutionOrRspPath, cancellationToken);
196+
await engine.FormatProjectAsync(project, cancellationToken);
197+
}
174198
}
175199
}
176200
}

src/Microsoft.DotNet.CodeFormatting/Microsoft.DotNet.CodeFormatting.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
<Reference Include="System.Xml" />
8686
</ItemGroup>
8787
<ItemGroup>
88+
<Compile Include="ResponseFileWorkspace.cs" />
8889
<Compile Include="SyntaxUtil.cs" />
8990
<Compile Include="Filters\FilenameFilter.cs" />
9091
<Compile Include="Filters\UsableFileFilter.cs" />
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.IO;
6+
using System.Text;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.Host.Mef;
9+
using Microsoft.CodeAnalysis.Text;
10+
11+
namespace Microsoft.DotNet.CodeFormatting
12+
{
13+
public sealed class ResponseFileWorkspace : Workspace
14+
{
15+
private static Encoding s_utf8WithoutBom = new UTF8Encoding(false);
16+
17+
private ResponseFileWorkspace()
18+
: base(DesktopMefHostServices.DefaultServices, "Custom")
19+
{
20+
}
21+
22+
public static ResponseFileWorkspace Create()
23+
{
24+
return new ResponseFileWorkspace();
25+
}
26+
27+
public Project OpenCommandLineProject(string responseFile, string language)
28+
{
29+
// This line deserves better error handling, but the tools current model is just throwing exception for most errors.
30+
// Issue: #90
31+
string rspContents = File.ReadAllText(responseFile);
32+
33+
var projectInfo = CommandLineProject.CreateProjectInfo(
34+
projectName: Path.GetFileNameWithoutExtension(responseFile),
35+
language: language,
36+
commandLine: rspContents,
37+
baseDirectory: Path.GetDirectoryName(Path.GetFullPath(responseFile)),
38+
workspace: this);
39+
40+
this.OnProjectAdded(projectInfo);
41+
42+
return this.CurrentSolution.GetProject(projectInfo.Id);
43+
}
44+
45+
public override bool CanApplyChange(ApplyChangesKind feature)
46+
{
47+
return feature == ApplyChangesKind.ChangeDocument;
48+
}
49+
50+
protected override void ApplyDocumentTextChanged(DocumentId documentId, SourceText text)
51+
{
52+
var document = this.CurrentSolution.GetDocument(documentId);
53+
if (document != null)
54+
{
55+
try
56+
{
57+
using (var writer = new StreamWriter(document.FilePath, append: false, encoding: text.Encoding ?? s_utf8WithoutBom))
58+
{
59+
text.Write(writer);
60+
}
61+
}
62+
catch (IOException e)
63+
{
64+
this.OnWorkspaceFailed(new DocumentDiagnostic(WorkspaceDiagnosticKind.Failure, e.Message, documentId));
65+
}
66+
catch (UnauthorizedAccessException e)
67+
{
68+
this.OnWorkspaceFailed(new DocumentDiagnostic(WorkspaceDiagnosticKind.Failure, e.Message, documentId));
69+
}
70+
71+
this.OnDocumentTextChanged(documentId, text, PreservationMode.PreserveValue);
72+
}
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)