|
45 | 45 | Description = "Enable semantic analysis for advanced rules", |
46 | 46 | }; |
47 | 47 |
|
| 48 | +var summaryOption = new Option<bool>("--summary") |
| 49 | +{ |
| 50 | + Description = "Show a summary of diagnostics grouped by rule ID", |
| 51 | +}; |
| 52 | + |
48 | 53 | var versionOption = new Option<bool>("--version") |
49 | 54 | { |
50 | 55 | Description = "Show version information and exit", |
|
59 | 64 | listRulesOption, |
60 | 65 | showConfigOption, |
61 | 66 | semanticOption, |
| 67 | + summaryOption, |
62 | 68 | versionOption, |
63 | 69 | }; |
64 | 70 |
|
|
116 | 122 | string[]? excludePatterns = parseResult.GetValue(excludeOption); |
117 | 123 |
|
118 | 124 | bool semantic = parseResult.GetValue(semanticOption); |
| 125 | + bool summary = parseResult.GetValue(summaryOption); |
119 | 126 | #if !SEMANTIC |
120 | 127 | if (semantic) |
121 | 128 | { |
|
180 | 187 | ? allDiagnostics.Where(d => d.Severity >= minSeverity).ToList() |
181 | 188 | : allDiagnostics; |
182 | 189 |
|
183 | | - string output = formatter.Format(filteredDiagnostics); |
184 | | - |
185 | | - if (!string.IsNullOrEmpty(output)) |
| 190 | + if (summary) |
186 | 191 | { |
187 | | - Console.Write(output); |
| 192 | + PrintSummary(filteredDiagnostics, registry); |
| 193 | + } |
| 194 | + else |
| 195 | + { |
| 196 | + string output = formatter.Format(filteredDiagnostics); |
| 197 | + |
| 198 | + if (!string.IsNullOrEmpty(output)) |
| 199 | + { |
| 200 | + Console.Write(output); |
| 201 | + } |
188 | 202 | } |
189 | 203 |
|
190 | 204 | if (hasError) |
@@ -245,6 +259,39 @@ static int PrintRuleList() |
245 | 259 | return 0; |
246 | 260 | } |
247 | 261 |
|
| 262 | +static void PrintSummary(IReadOnlyList<LintDiagnostic> diagnostics, RuleRegistry registry) |
| 263 | +{ |
| 264 | + if (diagnostics.Count == 0) |
| 265 | + { |
| 266 | + return; |
| 267 | + } |
| 268 | + |
| 269 | + Dictionary<string, string> ruleNames = registry.Rules.ToDictionary(r => r.RuleId, r => r.Name); |
| 270 | + |
| 271 | + List<(string RuleId, string Name, int Count)> groups = diagnostics |
| 272 | + .GroupBy(d => d.RuleId) |
| 273 | + .Select(g => (RuleId: g.Key, Name: ruleNames.GetValueOrDefault(g.Key, ""), Count: g.Count())) |
| 274 | + .OrderByDescending(g => g.Count) |
| 275 | + .ThenBy(g => g.RuleId, StringComparer.Ordinal) |
| 276 | + .ToList(); |
| 277 | + |
| 278 | + int maxIdLen = Math.Max("Rule".Length, groups.Max(g => g.RuleId.Length)); |
| 279 | + int maxNameLen = Math.Max("Name".Length, groups.Max(g => g.Name.Length)); |
| 280 | + int maxCountLen = Math.Max("Count".Length, diagnostics.Count.ToString().Length); |
| 281 | + int lineWidth = maxIdLen + 2 + maxNameLen + 2 + maxCountLen; |
| 282 | + |
| 283 | + Console.WriteLine($"{"Rule".PadRight(maxIdLen)} {"Name".PadRight(maxNameLen)} {"Count".PadLeft(maxCountLen)}"); |
| 284 | + Console.WriteLine(new string('\u2500', lineWidth)); |
| 285 | + |
| 286 | + foreach ((string ruleId, string name, int count) in groups) |
| 287 | + { |
| 288 | + Console.WriteLine($"{ruleId.PadRight(maxIdLen)} {name.PadRight(maxNameLen)} {count.ToString().PadLeft(maxCountLen)}"); |
| 289 | + } |
| 290 | + |
| 291 | + Console.WriteLine(new string('\u2500', lineWidth)); |
| 292 | + Console.WriteLine($"{"Total".PadRight(maxIdLen)} {"".PadRight(maxNameLen)} {diagnostics.Count.ToString().PadLeft(maxCountLen)}"); |
| 293 | +} |
| 294 | + |
248 | 295 | static int PrintConfig(LintConfiguration config) |
249 | 296 | { |
250 | 297 | using var stream = new MemoryStream(); |
|
0 commit comments