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

Commit 1378bd1

Browse files
committed
Merge pull request #132 from jaredpar/cmdline
Enable / Disable rules at the command line
2 parents 740d26f + ecb3ecd commit 1378bd1

28 files changed

+671
-221
lines changed

src/CodeFormatter/CodeFormatter.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
<Reference Include="System.Xml" />
9999
</ItemGroup>
100100
<ItemGroup>
101+
<Compile Include="CommandLineParser.cs" />
101102
<Compile Include="Program.cs" />
102103
<Compile Include="Properties\AssemblyInfo.cs" />
103104
</ItemGroup>
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.DotNet.CodeFormatting;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Collections.Immutable;
6+
using System.Diagnostics;
7+
using System.IO;
8+
using System.Linq;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
12+
namespace CodeFormatter
13+
{
14+
public enum Operation
15+
{
16+
Format,
17+
ListRules,
18+
}
19+
20+
public sealed class CommandLineOptions
21+
{
22+
public static readonly CommandLineOptions ListRules = new CommandLineOptions(
23+
Operation.ListRules,
24+
ImmutableArray<string[]>.Empty,
25+
ImmutableArray<string>.Empty,
26+
ImmutableDictionary<string, bool>.Empty,
27+
ImmutableArray<string>.Empty,
28+
ImmutableArray<string>.Empty,
29+
null,
30+
allowTables: false,
31+
verbose: false);
32+
33+
public readonly Operation Operation;
34+
public readonly ImmutableArray<string[]> PreprocessorConfigurations;
35+
public readonly ImmutableArray<string> CopyrightHeader;
36+
public readonly ImmutableDictionary<string, bool> RuleMap;
37+
public readonly ImmutableArray<string> FormatTargets;
38+
public readonly ImmutableArray<string> FileNames;
39+
public readonly string Language;
40+
public readonly bool AllowTables;
41+
public readonly bool Verbose;
42+
43+
public CommandLineOptions(
44+
Operation operation,
45+
ImmutableArray<string[]> preprocessorConfigurations,
46+
ImmutableArray<string> copyrightHeader,
47+
ImmutableDictionary<string, bool> ruleMap,
48+
ImmutableArray<string> formatTargets,
49+
ImmutableArray<string> fileNames,
50+
string language,
51+
bool allowTables,
52+
bool verbose)
53+
{
54+
Operation = operation;
55+
PreprocessorConfigurations = preprocessorConfigurations;
56+
CopyrightHeader = copyrightHeader;
57+
RuleMap = ruleMap;
58+
FileNames = fileNames;
59+
FormatTargets = formatTargets;
60+
Language = language;
61+
AllowTables = allowTables;
62+
Verbose = verbose;
63+
}
64+
}
65+
66+
public sealed class CommandLineParseResult
67+
{
68+
private readonly CommandLineOptions _options;
69+
private readonly string _error;
70+
71+
public bool IsSuccess
72+
{
73+
get { return _options != null; }
74+
}
75+
76+
public bool IsError
77+
{
78+
get { return !IsSuccess; }
79+
}
80+
81+
public CommandLineOptions Options
82+
{
83+
get
84+
{
85+
Debug.Assert(IsSuccess);
86+
return _options;
87+
}
88+
}
89+
90+
public string Error
91+
{
92+
get
93+
{
94+
Debug.Assert(IsError);
95+
return _error;
96+
}
97+
}
98+
99+
private CommandLineParseResult(CommandLineOptions options = null, string error = null)
100+
{
101+
_options = options;
102+
_error = error;
103+
}
104+
105+
public static CommandLineParseResult CreateSuccess(CommandLineOptions options)
106+
{
107+
return new CommandLineParseResult(options: options);
108+
}
109+
110+
public static CommandLineParseResult CreateError(string error)
111+
{
112+
return new CommandLineParseResult(error: error);
113+
}
114+
}
115+
116+
public static class CommandLineParser
117+
{
118+
private const string FileSwitch = "/file:";
119+
private const string ConfigSwitch = "/c:";
120+
private const string CopyrightSwitch = "/copyright:";
121+
private const string LanguageSwitch = "/lang:";
122+
private const string RuleEnabledSwitch1 = "/rule+:";
123+
private const string RuleEnabledSwitch2 = "/rule:";
124+
private const string RuleDisabledSwitch = "/rule-:";
125+
private const string Usage =
126+
@"CodeFormatter [/file:<filename>] [/lang:<language>] [/c:<config>[,<config>...]>]
127+
[/copyright:<file> | /nocopyright] [/tables] [/nounicode]
128+
[/rule(+|-):rule1,rule2,...] [/verbose]
129+
<project, solution or response file>
130+
131+
/file - Only apply changes to files with specified name
132+
/lang - Specifies the language to use when a responsefile is
133+
specified. i.e. 'C#', 'Visual Basic', ... (default: 'C#')
134+
/c - Additional preprocessor configurations the formatter
135+
should run under.
136+
/copyright - Specifies file containing copyright header.
137+
Use ConvertTests to convert MSTest tests to xUnit.
138+
/nocopyright - Do not update the copyright message.
139+
/tables - Let tables opt out of formatting by defining
140+
DOTNET_FORMATTER
141+
/nounicode - Do not convert unicode strings to escape sequences
142+
/rule(+|-) - Enable (default) or disable the specified rule
143+
/rules - List the available rules
144+
/verbose - Verbose output
145+
";
146+
147+
public static void PrintUsage()
148+
{
149+
Console.WriteLine(Usage);
150+
}
151+
152+
public static bool TryParse(string[] args, out CommandLineOptions options)
153+
{
154+
var result = Parse(args);
155+
options = result.IsSuccess ? result.Options : null;
156+
return result.IsSuccess;
157+
}
158+
159+
public static CommandLineParseResult Parse(string[] args)
160+
{
161+
var comparer = StringComparer.OrdinalIgnoreCase;
162+
var comparison = StringComparison.OrdinalIgnoreCase;
163+
var formatTargets = new List<string>();
164+
var fileNames = new List<string>();
165+
var configBuilder = ImmutableArray.CreateBuilder<string[]>();
166+
var copyrightHeader = FormattingDefaults.DefaultCopyrightHeader;
167+
var ruleMap = ImmutableDictionary<string, bool>.Empty;
168+
var language = LanguageNames.CSharp;
169+
var allowTables = false;
170+
var verbose = false;
171+
172+
for (int i = 0; i < args.Length; i++)
173+
{
174+
string arg = args[i];
175+
if (arg.StartsWith(ConfigSwitch, StringComparison.OrdinalIgnoreCase))
176+
{
177+
var all = arg.Substring(ConfigSwitch.Length);
178+
var configs = all.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
179+
configBuilder.Add(configs);
180+
}
181+
else if (arg.StartsWith(CopyrightSwitch, StringComparison.OrdinalIgnoreCase))
182+
{
183+
var fileName = arg.Substring(CopyrightSwitch.Length);
184+
try
185+
{
186+
copyrightHeader = ImmutableArray.CreateRange(File.ReadAllLines(fileName));
187+
}
188+
catch (Exception ex)
189+
{
190+
string error = string.Format("Could not read {0}{1}{2}",
191+
fileName,
192+
Environment.NewLine,
193+
ex.Message);
194+
return CommandLineParseResult.CreateError(error);
195+
}
196+
}
197+
else if (arg.StartsWith(LanguageSwitch, StringComparison.OrdinalIgnoreCase))
198+
{
199+
language = arg.Substring(LanguageSwitch.Length);
200+
}
201+
else if (comparer.Equals(arg, "/nocopyright"))
202+
{
203+
ruleMap = ruleMap.SetItem(FormattingDefaults.CopyrightRuleName, false);
204+
}
205+
else if (comparer.Equals(arg, "/nounicode"))
206+
{
207+
ruleMap = ruleMap.SetItem(FormattingDefaults.UnicodeLiteralsRuleName, false);
208+
}
209+
else if (comparer.Equals(arg, "/verbose"))
210+
{
211+
verbose = true;
212+
}
213+
else if (arg.StartsWith(FileSwitch, comparison))
214+
{
215+
fileNames.Add(arg.Substring(FileSwitch.Length));
216+
}
217+
else if (arg.StartsWith(RuleEnabledSwitch1, comparison))
218+
{
219+
UpdateRuleMap(ref ruleMap, arg.Substring(RuleEnabledSwitch1.Length), enabled: true);
220+
}
221+
else if (arg.StartsWith(RuleEnabledSwitch2, comparison))
222+
{
223+
UpdateRuleMap(ref ruleMap, arg.Substring(RuleEnabledSwitch2.Length), enabled: true);
224+
}
225+
else if (arg.StartsWith(RuleDisabledSwitch, comparison))
226+
{
227+
UpdateRuleMap(ref ruleMap, arg.Substring(RuleDisabledSwitch.Length), enabled: false);
228+
}
229+
else if (comparer.Equals(arg, "/tables"))
230+
{
231+
allowTables = true;
232+
}
233+
else if (comparer.Equals(arg, "/rules"))
234+
{
235+
return CommandLineParseResult.CreateSuccess(CommandLineOptions.ListRules);
236+
}
237+
else
238+
{
239+
formatTargets.Add(arg);
240+
}
241+
}
242+
243+
if (formatTargets.Count == 0)
244+
{
245+
return CommandLineParseResult.CreateError("Must specify at least one project / solution / rsp to format");
246+
}
247+
248+
var options = new CommandLineOptions(
249+
Operation.Format,
250+
configBuilder.ToImmutableArray(),
251+
copyrightHeader,
252+
ruleMap,
253+
formatTargets.ToImmutableArray(),
254+
fileNames.ToImmutableArray(),
255+
language,
256+
allowTables,
257+
verbose);
258+
return CommandLineParseResult.CreateSuccess(options);
259+
}
260+
261+
private static void UpdateRuleMap(ref ImmutableDictionary<string, bool> ruleMap, string data, bool enabled)
262+
{
263+
foreach (var current in data.Split(','))
264+
{
265+
ruleMap = ruleMap.SetItem(current, enabled);
266+
}
267+
}
268+
}
269+
}

0 commit comments

Comments
 (0)