Skip to content

Commit

Permalink
[CLI] Include/exclude files using glob patterns (dotnet#1178)
Browse files Browse the repository at this point in the history
  • Loading branch information
josefpihrt authored and JochemHarmes committed Oct 30, 2023
1 parent 1c6069f commit 908ac64
Show file tree
Hide file tree
Showing 38 changed files with 398 additions and 141 deletions.
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Remove empty namespace declaration ([RCS1072](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1072))
- Remove empty region directive ([RCS1091](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1091))
- Remove empty destructor ([RCS1106](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1106))
- [CLI] Add glob pattern matching (`--include` or/and `--exclude`) ([#1178](https://github.com/josefpihrt/roslynator/pull/1178)).

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context)
{
SyntaxTree tree = context.Tree;

if (_options.FileSystemFilter?.IsMatch(tree.FilePath) == false)
return;

SyntaxNode root = tree.GetRoot(context.CancellationToken);

var analysisContext = new SpellingAnalysisContext(
Expand Down
74 changes: 73 additions & 1 deletion src/CommandLine/Commands/AbstractLinesOfCodeCommand`1.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,82 @@
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Roslynator.CodeMetrics;
using Roslynator.Host.Mef;
using static Roslynator.Logger;

namespace Roslynator.CommandLine;

internal abstract class AbstractLinesOfCodeCommand<TResult> : MSBuildWorkspaceCommand<LinesOfCodeCommandResult>
{
protected AbstractLinesOfCodeCommand(in ProjectFilter projectFilter) : base(projectFilter)
protected AbstractLinesOfCodeCommand(in ProjectFilter projectFilter, FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
}

public async Task<ImmutableDictionary<ProjectId, CodeMetricsInfo>> CountLinesAsync(
IEnumerable<Project> projects,
LinesOfCodeKind kind,
CodeMetricsOptions options = null,
CancellationToken cancellationToken = default)
{
var codeMetrics = new ConcurrentBag<(ProjectId projectId, CodeMetricsInfo codeMetrics)>();

#if NETFRAMEWORK
await Task.CompletedTask;

Parallel.ForEach(
projects,
project =>
{
ICodeMetricsService service = MefWorkspaceServices.Default.GetService<ICodeMetricsService>(project.Language);
CodeMetricsInfo projectMetrics = (service is not null)
? service.CountLinesAsync(project, kind, FileSystemFilter, options, cancellationToken).Result
: CodeMetricsInfo.NotAvailable;
codeMetrics.Add((project.Id, codeMetrics: projectMetrics));
});
#else
await Parallel.ForEachAsync(
projects,
cancellationToken,
async (project, cancellationToken) =>
{
ICodeMetricsService service = MefWorkspaceServices.Default.GetService<ICodeMetricsService>(project.Language);
CodeMetricsInfo projectMetrics = (service is not null)
? await service.CountLinesAsync(project, kind, FileSystemFilter, options, cancellationToken)
: CodeMetricsInfo.NotAvailable;
codeMetrics.Add((project.Id, codeMetrics: projectMetrics));
});
#endif
return codeMetrics.ToImmutableDictionary(f => f.projectId, f => f.codeMetrics);
}

protected static void WriteLinesOfCode(Solution solution, ImmutableDictionary<ProjectId, CodeMetricsInfo> projectsMetrics)
{
int maxDigits = projectsMetrics.Max(f => f.Value.CodeLineCount).ToString("n0").Length;
int maxNameLength = projectsMetrics.Max(f => solution.GetProject(f.Key).Name.Length);

foreach (KeyValuePair<ProjectId, CodeMetricsInfo> kvp in projectsMetrics
.OrderByDescending(f => f.Value.CodeLineCount)
.ThenBy(f => solution.GetProject(f.Key).Name))
{
Project project = solution.GetProject(kvp.Key);
CodeMetricsInfo codeMetrics = kvp.Value;

string count = (codeMetrics.CodeLineCount >= 0)
? codeMetrics.CodeLineCount.ToString("n0").PadLeft(maxDigits)
: "-";

WriteLine($"{count} {project.Name.PadRight(maxNameLength)} {project.Language}", Verbosity.Normal);
}
}
}
5 changes: 3 additions & 2 deletions src/CommandLine/Commands/AnalyzeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Roslynator.CommandLine;

internal class AnalyzeCommand : MSBuildWorkspaceCommand<AnalyzeCommandResult>
{
public AnalyzeCommand(AnalyzeCommandLineOptions options, DiagnosticSeverity severityLevel, in ProjectFilter projectFilter) : base(projectFilter)
public AnalyzeCommand(AnalyzeCommandLineOptions options, DiagnosticSeverity severityLevel, in ProjectFilter projectFilter, FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
Options = options;
SeverityLevel = severityLevel;
Expand All @@ -31,6 +31,7 @@ public override async Task<AnalyzeCommandResult> ExecuteAsync(ProjectOrSolution
AssemblyResolver.Register();

var codeAnalyzerOptions = new CodeAnalyzerOptions(
fileSystemFilter: FileSystemFilter,
ignoreAnalyzerReferences: Options.IgnoreAnalyzerReferences,
ignoreCompilerDiagnostics: Options.IgnoreCompilerDiagnostics,
reportNotConfigurable: Options.ReportNotConfigurable,
Expand Down Expand Up @@ -80,7 +81,7 @@ public override async Task<AnalyzeCommandResult> ExecuteAsync(ProjectOrSolution

var projectFilter = new ProjectFilter(Options.Projects, Options.IgnoredProjects, Language);

results = await codeAnalyzer.AnalyzeSolutionAsync(solution, f => projectFilter.IsMatch(f), cancellationToken);
results = await codeAnalyzer.AnalyzeSolutionAsync(solution, f => IsMatch(f), cancellationToken);
}

return new AnalyzeCommandResult(
Expand Down
3 changes: 2 additions & 1 deletion src/CommandLine/Commands/FindSymbolsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ internal class FindSymbolsCommand : MSBuildWorkspaceCommand<CommandResult>
public FindSymbolsCommand(
FindSymbolsCommandLineOptions options,
SymbolFinderOptions symbolFinderOptions,
in ProjectFilter projectFilter) : base(projectFilter)
in ProjectFilter projectFilter,
FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
Options = options;
SymbolFinderOptions = symbolFinderOptions;
Expand Down
11 changes: 6 additions & 5 deletions src/CommandLine/Commands/FixCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ internal class FixCommand : MSBuildWorkspaceCommand<FixCommandResult>
IEnumerable<KeyValuePair<string, string>> diagnosticFixMap,
IEnumerable<KeyValuePair<string, string>> diagnosticFixerMap,
FixAllScope fixAllScope,
in ProjectFilter projectFilter) : base(projectFilter)
in ProjectFilter projectFilter,
FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
Options = options;
SeverityLevel = severityLevel;
Expand All @@ -47,6 +48,7 @@ public override async Task<FixCommandResult> ExecuteAsync(ProjectOrSolution proj
AssemblyResolver.Register();

var codeFixerOptions = new CodeFixerOptions(
fileSystemFilter: FileSystemFilter,
severityLevel: SeverityLevel,
ignoreCompilerErrors: Options.IgnoreCompilerErrors,
ignoreAnalyzerReferences: Options.IgnoreAnalyzerReferences,
Expand All @@ -69,14 +71,13 @@ public override async Task<FixCommandResult> ExecuteAsync(ProjectOrSolution proj

var projectFilter = new ProjectFilter(Options.Projects, Options.IgnoredProjects, Language);

return await FixAsync(projectOrSolution, analyzerAssemblies, codeFixerOptions, projectFilter, culture, cancellationToken);
return await FixAsync(projectOrSolution, analyzerAssemblies, codeFixerOptions, culture, cancellationToken);
}

private static async Task<FixCommandResult> FixAsync(
private async Task<FixCommandResult> FixAsync(
ProjectOrSolution projectOrSolution,
IEnumerable<AnalyzerAssembly> analyzerAssemblies,
CodeFixerOptions codeFixerOptions,
ProjectFilter projectFilter,
IFormatProvider formatProvider = null,
CancellationToken cancellationToken = default)
{
Expand Down Expand Up @@ -114,7 +115,7 @@ public override async Task<FixCommandResult> ExecuteAsync(ProjectOrSolution proj

CodeFixer codeFixer = GetCodeFixer(solution);

results = await codeFixer.FixSolutionAsync(f => projectFilter.IsMatch(f), cancellationToken);
results = await codeFixer.FixSolutionAsync(f => IsMatch(f), cancellationToken);
}

WriteProjectFixResults(results, codeFixerOptions, formatProvider);
Expand Down
34 changes: 29 additions & 5 deletions src/CommandLine/Commands/FormatCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Roslynator.CommandLine;

internal class FormatCommand : MSBuildWorkspaceCommand<FormatCommandResult>
{
public FormatCommand(FormatCommandLineOptions options, in ProjectFilter projectFilter) : base(projectFilter)
public FormatCommand(FormatCommandLineOptions options, in ProjectFilter projectFilter, FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
Options = options;
}
Expand All @@ -30,20 +30,18 @@ public override async Task<FormatCommandResult> ExecuteAsync(ProjectOrSolution p
{
ImmutableArray<DocumentId> formattedDocuments;

var options = new CodeFormatterOptions(fileSystemFilter: FileSystemFilter, includeGeneratedCode: Options.IncludeGeneratedCode);

if (projectOrSolution.IsProject)
{
Project project = projectOrSolution.AsProject();

var options = new CodeFormatterOptions(includeGeneratedCode: Options.IncludeGeneratedCode);

formattedDocuments = await FormatProjectAsync(project, options, cancellationToken);
}
else
{
Solution solution = projectOrSolution.AsSolution();

var options = new CodeFormatterOptions(includeGeneratedCode: Options.IncludeGeneratedCode);

formattedDocuments = await FormatSolutionAsync(solution, options, cancellationToken);
}

Expand All @@ -60,6 +58,9 @@ private async Task<ImmutableArray<DocumentId>> FormatSolutionAsync(Solution solu

var changedDocuments = new ConcurrentBag<ImmutableArray<DocumentId>>();

#if NETFRAMEWORK
await Task.CompletedTask;

Parallel.ForEach(
FilterProjects(solution),
project =>
Expand All @@ -80,6 +81,29 @@ private async Task<ImmutableArray<DocumentId>> FormatSolutionAsync(Solution solu
WriteLine($" Done analyzing '{project.Name}'", Verbosity.Normal);
});
#else
await Parallel.ForEachAsync(
FilterProjects(solution),
cancellationToken,
async (project, cancellationToken) =>
{
WriteLine($" Analyze '{project.Name}'", Verbosity.Minimal);
ISyntaxFactsService syntaxFacts = MefWorkspaceServices.Default.GetService<ISyntaxFactsService>(project.Language);
Project newProject = CodeFormatter.FormatProjectAsync(project, syntaxFacts, options, cancellationToken).Result;
ImmutableArray<DocumentId> formattedDocuments = await CodeFormatter.GetFormattedDocumentsAsync(project, newProject, syntaxFacts);
if (formattedDocuments.Any())
{
changedDocuments.Add(formattedDocuments);
LogHelpers.WriteFormattedDocuments(formattedDocuments, project, solutionDirectory);
}
WriteLine($" Done analyzing '{project.Name}'", Verbosity.Normal);
});
#endif

if (!changedDocuments.IsEmpty)
{
Expand Down
6 changes: 4 additions & 2 deletions src/CommandLine/Commands/GenerateDocCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ internal class GenerateDocCommand : MSBuildWorkspaceCommand<CommandResult>
FilesLayout filesLayout,
bool groupByCommonNamespace,
InheritanceStyle inheritanceStyle,
in ProjectFilter projectFilter) : base(projectFilter)
in ProjectFilter projectFilter,
FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
Options = options;
Depth = depth;
Expand Down Expand Up @@ -118,7 +119,8 @@ public override async Task<CommandResult> ExecuteAsync(ProjectOrSolution project
IgnoredTitleParts = IgnoredTitleParts,
IncludeContainingNamespaceFilter = IncludeContainingNamespaceFilter,
FilesLayout = FilesLayout,
ScrollToContent = (DocumentationHost == DocumentationHost.GitHub) && Options.ScrollToContent
ScrollToContent = (DocumentationHost == DocumentationHost.GitHub) && Options.ScrollToContent,
FileSystemFilter = FileSystemFilter,
};

if (Options.IgnoredNames is not null)
Expand Down
4 changes: 3 additions & 1 deletion src/CommandLine/Commands/GenerateDocRootCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ internal class GenerateDocRootCommand : MSBuildWorkspaceCommand<CommandResult>
DocumentationHost documentationHost,
FilesLayout filesLayout,
bool groupByCommonNamespace,
in ProjectFilter projectFilter) : base(projectFilter)
in ProjectFilter projectFilter,
FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
Options = options;
Depth = depth;
Expand Down Expand Up @@ -73,6 +74,7 @@ public override async Task<CommandResult> ExecuteAsync(ProjectOrSolution project
IncludeContainingNamespaceFilter = IncludeContainingNamespaceFilter,
ScrollToContent = (DocumentationHost == DocumentationHost.GitHub) && Options.ScrollToContent,
FilesLayout = FilesLayout,
FileSystemFilter = FileSystemFilter,
};

if (Options.IgnoredNames is not null)
Expand Down
5 changes: 3 additions & 2 deletions src/CommandLine/Commands/GenerateSourceReferencesCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ internal class GenerateSourceReferencesCommand : MSBuildWorkspaceCommand<Command
GenerateSourceReferencesCommandLineOptions options,
DocumentationDepth depth,
Visibility visibility,
in ProjectFilter projectFilter) : base(projectFilter)
in ProjectFilter projectFilter,
FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
Options = options;
Depth = depth;
Expand All @@ -39,7 +40,7 @@ public override async Task<CommandResult> ExecuteAsync(ProjectOrSolution project
{
AssemblyResolver.Register();

var filter = new SymbolFilterOptions(Visibility.ToVisibilityFilter());
var filter = new SymbolFilterOptions(FileSystemFilter, Visibility.ToVisibilityFilter());

WriteLine($"Save source references to '{Options.Output}'.", Verbosity.Minimal);

Expand Down
3 changes: 2 additions & 1 deletion src/CommandLine/Commands/ListReferencesCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ internal class ListReferencesCommand : MSBuildWorkspaceCommand<CommandResult>
ListReferencesCommandLineOptions options,
MetadataReferenceDisplay display,
MetadataReferenceFilter filter,
in ProjectFilter projectFilter) : base(projectFilter)
in ProjectFilter projectFilter,
FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
Options = options;
Display = display;
Expand Down
3 changes: 2 additions & 1 deletion src/CommandLine/Commands/ListSymbolsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ internal class ListSymbolsCommand : MSBuildWorkspaceCommand<CommandResult>
WrapListOptions wrapListOptions,
SymbolDefinitionListLayout layout,
SymbolDefinitionPartFilter ignoredParts,
in ProjectFilter projectFilter) : base(projectFilter)
in ProjectFilter projectFilter,
FileSystemFilter fileSystemFilter) : base(projectFilter, fileSystemFilter)
{
Options = options;
SymbolFilterOptions = symbolFilterOptions;
Expand Down

0 comments on commit 908ac64

Please sign in to comment.