Skip to content

Commit

Permalink
Fix globbing to include/exclude projects (dotnet#1183)
Browse files Browse the repository at this point in the history
  • Loading branch information
josefpihrt authored and JochemHarmes committed Oct 30, 2023
1 parent cc84acf commit 0749542
Show file tree
Hide file tree
Showing 17 changed files with 118 additions and 165 deletions.
2 changes: 1 addition & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +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)).
- [CLI] Add glob pattern matching (`--include` or/and `--exclude`) ([#1178](https://github.com/josefpihrt/roslynator/pull/1178), [#1183](https://github.com/josefpihrt/roslynator/pull/1183)).

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public DocumentationWriter(MarkdownWriter writer) : base(writer)

public override void WriteOptionDescription(CommandOption option)
{
string description = option.FullDescription;
string description = option.Description;

if (!string.IsNullOrEmpty(description))
{
Expand All @@ -49,5 +49,8 @@ public override void WriteOptionDescription(CommandOption option)
_writer.WriteString(description);
}
}

if (!string.IsNullOrEmpty(option.AdditionalDescription))
_writer.WriteRaw(option.AdditionalDescription);
}
}
17 changes: 17 additions & 0 deletions src/CommandLine/CommandLineHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ namespace Roslynator.CommandLine;

internal static class CommandLineHelpers
{
public static bool IsGlobPatternForFileOrFolder(string pattern)
{
return !IsGlobPatternForProject(pattern)
&& !IsGlobPatternForSolution(pattern);
}

public static bool IsGlobPatternForProject(string pattern)
{
return pattern.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase)
|| pattern.EndsWith(".vbproj", StringComparison.OrdinalIgnoreCase);
}

public static bool IsGlobPatternForSolution(string pattern)
{
return pattern.EndsWith(".sln", StringComparison.OrdinalIgnoreCase);
}

public static void WaitForKeyPress(string message = null)
{
if (Console.IsInputRedirected)
Expand Down
2 changes: 0 additions & 2 deletions src/CommandLine/Commands/AnalyzeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ public override async Task<AnalyzeCommandResult> ExecuteAsync(ProjectOrSolution
{
Solution solution = projectOrSolution.AsSolution();

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

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

Expand Down
2 changes: 0 additions & 2 deletions src/CommandLine/Commands/FixCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ public override async Task<FixCommandResult> ExecuteAsync(ProjectOrSolution proj

CultureInfo culture = (Options.Culture is not null) ? CultureInfo.GetCultureInfo(Options.Culture) : null;

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

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

Expand Down
37 changes: 18 additions & 19 deletions src/CommandLine/Commands/MSBuildWorkspaceCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.Extensions.FileSystemGlobbing;
using static Roslynator.Logger;

namespace Roslynator.CommandLine;
Expand All @@ -34,7 +35,7 @@ public string Language

public abstract Task<TCommandResult> ExecuteAsync(ProjectOrSolution projectOrSolution, CancellationToken cancellationToken = default);

public async Task<CommandStatus> ExecuteAsync(IEnumerable<string> paths, string msbuildPath = null, IEnumerable<string> properties = null)
public async Task<CommandStatus> ExecuteAsync(IEnumerable<PathInfo> paths, string msbuildPath = null, IEnumerable<string> properties = null)
{
if (paths is null)
throw new ArgumentNullException(nameof(paths));
Expand Down Expand Up @@ -67,12 +68,25 @@ public async Task<CommandStatus> ExecuteAsync(IEnumerable<string> paths, string
var status = CommandStatus.Success;
var results = new List<TCommandResult>();

foreach (string path in paths)
foreach (PathInfo path in paths)
{
if (path.Origin == PathOrigin.PipedInput)
{
Matcher matcher = (string.Equals(Path.GetExtension(path.Path), ".sln", StringComparison.OrdinalIgnoreCase))
? ProjectFilter.SolutionMatcher
: ProjectFilter.Matcher;

if (matcher?.Match(path.Path).HasMatches == false)
{
WriteLine($"Skip '{path.Path}'", ConsoleColors.DarkGray, Verbosity.Normal);
continue;
}
}

TCommandResult result;
try
{
result = await ExecuteAsync(path, workspace, cancellationToken);
result = await ExecuteAsync(path.Path, workspace, cancellationToken);

if (result is null)
{
Expand Down Expand Up @@ -134,11 +148,6 @@ private async Task<TCommandResult> ExecuteAsync(string path, MSBuildWorkspace wo
if (!File.Exists(path))
throw new FileNotFoundException($"Project or solution file not found: {path}");

TCommandResult result = await ExecuteAsync(path, workspace, ConsoleProgressReporter.Default, cancellationToken);

if (result is not null)
return result;

ProjectOrSolution projectOrSolution = await OpenProjectOrSolutionAsync(path, workspace, ConsoleProgressReporter.Default, cancellationToken);

Solution solution = projectOrSolution.AsSolution();
Expand Down Expand Up @@ -207,15 +216,6 @@ protected virtual void WorkspaceFailed(object sender, WorkspaceDiagnosticEventAr
WriteLine($" {e.Diagnostic.Message}", e.Diagnostic.Kind.GetColors(), Verbosity.Detailed);
}

protected virtual Task<TCommandResult> ExecuteAsync(
string path,
MSBuildWorkspace workspace,
IProgress<ProjectLoadProgress> progress = null,
CancellationToken cancellationToken = default)
{
return Task.FromResult(default(TCommandResult));
}

private static async Task<ProjectOrSolution> OpenProjectOrSolutionAsync(
string path,
MSBuildWorkspace workspace,
Expand Down Expand Up @@ -353,8 +353,7 @@ private static bool TryGetVisualStudioInstance(out VisualStudioInstance result)

private protected bool IsMatch(Project project)
{
return ProjectFilter.IsMatch(project)
&& FileSystemFilter?.IsMatch(project.FilePath) != false;
return ProjectFilter.IsMatch(project);
}

private protected async Task<ImmutableArray<Compilation>> GetCompilationsAsync(
Expand Down
8 changes: 4 additions & 4 deletions src/CommandLine/Commands/MigrateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ internal class MigrateCommand
",
RegexOptions.IgnorePatternWhitespace);

public MigrateCommand(ImmutableArray<string> paths, string identifier, Version version, bool dryRun)
public MigrateCommand(ImmutableArray<PathInfo> paths, string identifier, Version version, bool dryRun)
{
Paths = paths;
Identifier = identifier;
Version = version;
DryRun = dryRun;
}

public ImmutableArray<string> Paths { get; }
public ImmutableArray<PathInfo> Paths { get; }

public string Identifier { get; }

Expand Down Expand Up @@ -90,9 +90,9 @@ private CommandStatus Execute(CancellationToken cancellationToken)
{
var status = CommandStatus.Success;

foreach (string path in Paths)
foreach (PathInfo path in Paths)
{
CommandStatus status2 = ExecutePath(path, cancellationToken);
CommandStatus status2 = ExecutePath(path.Path, cancellationToken);

if (status != CommandStatus.Success)
status = status2;
Expand Down
2 changes: 0 additions & 2 deletions src/CommandLine/Commands/RenameSymbolCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ public override async Task<RenameSymbolCommandResult> ExecuteAsync(ProjectOrSolu
{
AssemblyResolver.Register();

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

SymbolRenameState renamer = null;

if (projectOrSolution.IsProject)
Expand Down
2 changes: 0 additions & 2 deletions src/CommandLine/Commands/SpellcheckCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ public override async Task<SpellcheckCommandResult> ExecuteAsync(ProjectOrSoluti

CultureInfo culture = (Options.Culture is not null) ? CultureInfo.GetCultureInfo(Options.Culture) : null;

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

return await FixAsync(projectOrSolution, options, culture, cancellationToken);
}

Expand Down
48 changes: 44 additions & 4 deletions src/CommandLine/Options/MSBuildCommandLineOptions.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using CommandLine;
using Microsoft.Extensions.FileSystemGlobbing;

namespace Roslynator.CommandLine;

// Files, IgnoredFiles
public abstract class MSBuildCommandLineOptions : BaseCommandLineOptions
{
[AdditionalDescription(" For further information about the syntax see [reference documentation](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.filesystemglobbing.matcher?view=dotnet-plat-ext-7.0#remarks).")]
[Option(
longName: "include",
HelpText = "Space separated list of glob patterns to include files/folders.",
HelpText = "Space separated list of glob patterns to include files, folders, solutions or projects.",
MetaValue = "<GLOB>")]
public IEnumerable<string> Include { get; set; }

[AdditionalDescription(" For further information about the syntax see [reference documentation](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.filesystemglobbing.matcher?view=dotnet-plat-ext-7.0#remarks).")]
[Option(
longName: "exclude",
HelpText = "Space separated list of glob patterns to exclude files/folders.",
HelpText = "Space separated list of glob patterns to exclude files, folders, solutions or projects.",
MetaValue = "<GLOB>")]
public IEnumerable<string> Exclude { get; set; }

Expand Down Expand Up @@ -81,7 +84,44 @@ internal bool TryGetProjectFilter(out ProjectFilter projectFilter)
return false;
}

projectFilter = new ProjectFilter(Projects, IgnoredProjects, language);
Matcher projectMatcher = CreateMatcher(p => CommandLineHelpers.IsGlobPatternForProject(p));
Matcher solutionMatcher = CreateMatcher(p => CommandLineHelpers.IsGlobPatternForSolution(p));

projectFilter = new ProjectFilter(projectMatcher, solutionMatcher, Projects, IgnoredProjects, language);
return true;
}

private Matcher CreateMatcher(Func<string, bool> patternPredicate)
{
if (!Include.Any()
&& !Exclude.Any())
{
return null;
}

string[] include = Include.Where(patternPredicate).ToArray();
string[] exclude = Exclude.Where(patternPredicate).ToArray();

Matcher matcher = null;

if (include.Any()
|| exclude.Any())
{
matcher = new Matcher((FileSystemHelpers.IsCaseSensitive) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);

if (include.Any())
{
matcher.AddIncludePatterns(include);
}
else
{
matcher.AddInclude("**");
}

if (exclude.Any())
matcher.AddExcludePatterns(exclude);
}

return matcher;
}
}
5 changes: 5 additions & 0 deletions src/CommandLine/PathInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Roslynator.CommandLine;

internal readonly record struct PathInfo(string Path, PathOrigin Origin);
10 changes: 10 additions & 0 deletions src/CommandLine/PathOrigin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Roslynator.CommandLine;

internal enum PathOrigin
{
Argument,
PipedInput,
CurrentDirectory,
}
15 changes: 14 additions & 1 deletion src/CommandLine/ProjectFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.FileSystemGlobbing;

namespace Roslynator.CommandLine;

internal readonly struct ProjectFilter
{
public ProjectFilter(
Matcher matcher,
Matcher solutionMatcher,
IEnumerable<string> names,
IEnumerable<string> ignoredNames,
string language)
Expand All @@ -21,11 +24,17 @@ namespace Roslynator.CommandLine;
throw new ArgumentException($"Cannot specify both '{nameof(names)}' and '{nameof(ignoredNames)}'.", nameof(names));
}

Matcher = matcher;
SolutionMatcher = solutionMatcher;
Names = names?.Select(f => ProjectName.Create(f)).ToImmutableHashSet() ?? ImmutableHashSet<ProjectName>.Empty;
IgnoredNames = ignoredNames?.Select(f => ProjectName.Create(f)).ToImmutableHashSet() ?? ImmutableHashSet<ProjectName>.Empty;
Language = language;
}

public Matcher Matcher { get; }

public Matcher SolutionMatcher { get; }

public ImmutableHashSet<ProjectName> Names { get; }

public ImmutableHashSet<ProjectName> IgnoredNames { get; }
Expand All @@ -36,7 +45,8 @@ public bool IsDefault
{
get
{
return Names is null
return Matcher is null
&& Names is null
&& IgnoredNames is null
&& Language is null;
}
Expand All @@ -50,6 +60,9 @@ public bool IsMatch(Project project)
return false;
}

if (Matcher?.Match(project.FilePath).HasMatches == false)
return false;

if (Names?.Count > 0)
return IsMatch(project.Name, Names);

Expand Down
49 changes: 0 additions & 49 deletions src/CommandLine/docs/analyze-assembly-command.md

This file was deleted.

0 comments on commit 0749542

Please sign in to comment.