From f6fdc7a0a785db4ce1aa0edd00d9065ba0bba70c Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Fri, 25 Aug 2023 15:45:44 +0200 Subject: [PATCH] Fix globbing to include/exclude projects (#1183) --- ChangeLog.md | 2 +- .../DocumentationWriter.cs | 5 +- src/CommandLine/CommandLineHelpers.cs | 17 ++++ src/CommandLine/Commands/AnalyzeCommand.cs | 2 - src/CommandLine/Commands/FixCommand.cs | 2 - .../Commands/MSBuildWorkspaceCommand.cs | 37 ++++----- src/CommandLine/Commands/MigrateCommand.cs | 8 +- .../Commands/RenameSymbolCommand.cs | 2 - src/CommandLine/Commands/SpellcheckCommand.cs | 2 - .../Options/MSBuildCommandLineOptions.cs | 48 ++++++++++- src/CommandLine/PathInfo.cs | 5 ++ src/CommandLine/PathOrigin.cs | 10 +++ src/CommandLine/Program.cs | 82 +++++++++++-------- src/CommandLine/ProjectFilter.cs | 15 +++- .../docs/analyze-assembly-command.md | 49 ----------- src/CommandLine/docs/list-vs-command.md | 23 ------ src/CommandLine/docs/sln-list-command.md | 54 ------------ tools/reinstall_cli_debug.ps1 | 2 +- 18 files changed, 167 insertions(+), 198 deletions(-) create mode 100644 src/CommandLine/PathInfo.cs create mode 100644 src/CommandLine/PathOrigin.cs delete mode 100644 src/CommandLine/docs/analyze-assembly-command.md delete mode 100644 src/CommandLine/docs/list-vs-command.md delete mode 100644 src/CommandLine/docs/sln-list-command.md diff --git a/ChangeLog.md b/ChangeLog.md index be3ee251fb..bebc5db2a6 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 diff --git a/src/CommandLine.DocumentationGenerator/DocumentationWriter.cs b/src/CommandLine.DocumentationGenerator/DocumentationWriter.cs index b379c13fc8..84799f83a8 100644 --- a/src/CommandLine.DocumentationGenerator/DocumentationWriter.cs +++ b/src/CommandLine.DocumentationGenerator/DocumentationWriter.cs @@ -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)) { @@ -49,5 +49,8 @@ public override void WriteOptionDescription(CommandOption option) _writer.WriteString(description); } } + + if (!string.IsNullOrEmpty(option.AdditionalDescription)) + _writer.WriteRaw(option.AdditionalDescription); } } diff --git a/src/CommandLine/CommandLineHelpers.cs b/src/CommandLine/CommandLineHelpers.cs index 28409c676f..d74d5874bd 100644 --- a/src/CommandLine/CommandLineHelpers.cs +++ b/src/CommandLine/CommandLineHelpers.cs @@ -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) diff --git a/src/CommandLine/Commands/AnalyzeCommand.cs b/src/CommandLine/Commands/AnalyzeCommand.cs index ededeed91b..6bc4ba9254 100644 --- a/src/CommandLine/Commands/AnalyzeCommand.cs +++ b/src/CommandLine/Commands/AnalyzeCommand.cs @@ -79,8 +79,6 @@ public override async Task ExecuteAsync(ProjectOrSolution { Solution solution = projectOrSolution.AsSolution(); - var projectFilter = new ProjectFilter(Options.Projects, Options.IgnoredProjects, Language); - results = await codeAnalyzer.AnalyzeSolutionAsync(solution, f => IsMatch(f), cancellationToken); } diff --git a/src/CommandLine/Commands/FixCommand.cs b/src/CommandLine/Commands/FixCommand.cs index 71bee423be..4f5c9c7db4 100644 --- a/src/CommandLine/Commands/FixCommand.cs +++ b/src/CommandLine/Commands/FixCommand.cs @@ -69,8 +69,6 @@ public override async Task 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); } diff --git a/src/CommandLine/Commands/MSBuildWorkspaceCommand.cs b/src/CommandLine/Commands/MSBuildWorkspaceCommand.cs index 3310eb29fe..2a4008bbfb 100644 --- a/src/CommandLine/Commands/MSBuildWorkspaceCommand.cs +++ b/src/CommandLine/Commands/MSBuildWorkspaceCommand.cs @@ -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; @@ -34,7 +35,7 @@ public string Language public abstract Task ExecuteAsync(ProjectOrSolution projectOrSolution, CancellationToken cancellationToken = default); - public async Task ExecuteAsync(IEnumerable paths, string msbuildPath = null, IEnumerable properties = null) + public async Task ExecuteAsync(IEnumerable paths, string msbuildPath = null, IEnumerable properties = null) { if (paths is null) throw new ArgumentNullException(nameof(paths)); @@ -67,12 +68,25 @@ public async Task ExecuteAsync(IEnumerable paths, string var status = CommandStatus.Success; var results = new List(); - 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) { @@ -134,11 +148,6 @@ private async Task 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(); @@ -207,15 +216,6 @@ protected virtual void WorkspaceFailed(object sender, WorkspaceDiagnosticEventAr WriteLine($" {e.Diagnostic.Message}", e.Diagnostic.Kind.GetColors(), Verbosity.Detailed); } - protected virtual Task ExecuteAsync( - string path, - MSBuildWorkspace workspace, - IProgress progress = null, - CancellationToken cancellationToken = default) - { - return Task.FromResult(default(TCommandResult)); - } - private static async Task OpenProjectOrSolutionAsync( string path, MSBuildWorkspace workspace, @@ -353,8 +353,7 @@ private protected IEnumerable FilterProjects( private protected bool IsMatch(Project project) { - return ProjectFilter.IsMatch(project) - && FileSystemFilter?.IsMatch(project.FilePath) != false; + return ProjectFilter.IsMatch(project); } private protected async Task> GetCompilationsAsync( diff --git a/src/CommandLine/Commands/MigrateCommand.cs b/src/CommandLine/Commands/MigrateCommand.cs index 40fb6c6998..cf07c3c6b0 100644 --- a/src/CommandLine/Commands/MigrateCommand.cs +++ b/src/CommandLine/Commands/MigrateCommand.cs @@ -34,7 +34,7 @@ internal class MigrateCommand ", RegexOptions.IgnorePatternWhitespace); - public MigrateCommand(ImmutableArray paths, string identifier, Version version, bool dryRun) + public MigrateCommand(ImmutableArray paths, string identifier, Version version, bool dryRun) { Paths = paths; Identifier = identifier; @@ -42,7 +42,7 @@ public MigrateCommand(ImmutableArray paths, string identifier, Version v DryRun = dryRun; } - public ImmutableArray Paths { get; } + public ImmutableArray Paths { get; } public string Identifier { get; } @@ -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; diff --git a/src/CommandLine/Commands/RenameSymbolCommand.cs b/src/CommandLine/Commands/RenameSymbolCommand.cs index 3f6a0c09b7..0e64630f86 100644 --- a/src/CommandLine/Commands/RenameSymbolCommand.cs +++ b/src/CommandLine/Commands/RenameSymbolCommand.cs @@ -53,8 +53,6 @@ public override async Task ExecuteAsync(ProjectOrSolu { AssemblyResolver.Register(); - var projectFilter = new ProjectFilter(Options.Projects, Options.IgnoredProjects, Language); - SymbolRenameState renamer = null; if (projectOrSolution.IsProject) diff --git a/src/CommandLine/Commands/SpellcheckCommand.cs b/src/CommandLine/Commands/SpellcheckCommand.cs index 1ec31f90ec..5ef680ca0a 100644 --- a/src/CommandLine/Commands/SpellcheckCommand.cs +++ b/src/CommandLine/Commands/SpellcheckCommand.cs @@ -71,8 +71,6 @@ public override async Task 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); } diff --git a/src/CommandLine/Options/MSBuildCommandLineOptions.cs b/src/CommandLine/Options/MSBuildCommandLineOptions.cs index 4b2b77cf79..2fd1fafdd5 100644 --- a/src/CommandLine/Options/MSBuildCommandLineOptions.cs +++ b/src/CommandLine/Options/MSBuildCommandLineOptions.cs @@ -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 = "")] public IEnumerable 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 = "")] public IEnumerable Exclude { get; set; } @@ -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 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; + } } diff --git a/src/CommandLine/PathInfo.cs b/src/CommandLine/PathInfo.cs new file mode 100644 index 0000000000..7cea644318 --- /dev/null +++ b/src/CommandLine/PathInfo.cs @@ -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); diff --git a/src/CommandLine/PathOrigin.cs b/src/CommandLine/PathOrigin.cs new file mode 100644 index 0000000000..328f4475f7 --- /dev/null +++ b/src/CommandLine/PathOrigin.cs @@ -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, +} diff --git a/src/CommandLine/Program.cs b/src/CommandLine/Program.cs index 80ba30b708..a78cb5ebad 100644 --- a/src/CommandLine/Program.cs +++ b/src/CommandLine/Program.cs @@ -4,12 +4,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; using CommandLine; using CommandLine.Text; @@ -110,7 +108,6 @@ private static int Main(string[] args) typeof(SpellcheckCommandLineOptions), #if DEBUG typeof(FindSymbolsCommandLineOptions), - typeof(SlnListCommandLineOptions), #endif }); @@ -317,7 +314,7 @@ private static async Task FixAsync(FixCommandLineOptions options) if (!options.TryGetProjectFilter(out ProjectFilter projectFilter)) return ExitCodes.Error; - if (!TryParsePaths(options.Paths, out ImmutableArray paths)) + if (!TryParsePaths(options.Paths, out ImmutableArray paths)) return ExitCodes.Error; var command = new FixCommand( @@ -327,7 +324,7 @@ private static async Task FixAsync(FixCommandLineOptions options) diagnosticFixerMap: diagnosticFixerMap, fixAllScope: fixAllScope, projectFilter: projectFilter, - fileSystemFilter: FileSystemFilter.CreateOrDefault(options.Include, options.Exclude)); + fileSystemFilter: CreateFileSystemFilter(options)); CommandStatus status = await command.ExecuteAsync(paths, options.MSBuildPath, options.Properties); @@ -342,10 +339,10 @@ private static async Task AnalyzeAsync(AnalyzeCommandLineOptions options) if (!options.TryGetProjectFilter(out ProjectFilter projectFilter)) return ExitCodes.Error; - if (!TryParsePaths(options.Paths, out ImmutableArray paths)) + if (!TryParsePaths(options.Paths, out ImmutableArray paths)) return ExitCodes.Error; - var command = new AnalyzeCommand(options, severityLevel, projectFilter, FileSystemFilter.CreateOrDefault(options.Include, options.Exclude)); + var command = new AnalyzeCommand(options, severityLevel, projectFilter, CreateFileSystemFilter(options)); CommandStatus status = await command.ExecuteAsync(paths, options.MSBuildPath, options.Properties); @@ -376,7 +373,7 @@ private static async Task FindSymbolsAsync(FindSymbolsCommandLineOptions op if (!TryParseOptionValueAsEnumFlags(options.WithoutFlags, OptionNames.WithoutFlags, out SymbolFlags withoutFlags, SymbolFlags.None)) return ExitCodes.Error; - if (!TryParsePaths(options.Paths, out ImmutableArray paths)) + if (!TryParsePaths(options.Paths, out ImmutableArray paths)) return ExitCodes.Error; ImmutableArray.Builder rules = ImmutableArray.CreateBuilder(); @@ -393,7 +390,7 @@ private static async Task FindSymbolsAsync(FindSymbolsCommandLineOptions op if (withoutFlags != SymbolFlags.None) rules.AddRange(SymbolFilterRuleFactory.FromFlags(withoutFlags, invert: true)); - FileSystemFilter fileSystemFilter = FileSystemFilter.CreateOrDefault(options.Include, options.Exclude); + FileSystemFilter fileSystemFilter = CreateFileSystemFilter(options); var symbolFinderOptions = new SymbolFinderOptions( fileSystemFilter, @@ -420,7 +417,7 @@ private static async Task RenameSymbolAsync(RenameSymbolCommandLineOptions if (!options.TryGetProjectFilter(out ProjectFilter projectFilter)) return ExitCodes.Error; - if (!TryParsePaths(options.Paths, out ImmutableArray paths)) + if (!TryParsePaths(options.Paths, out ImmutableArray paths)) return ExitCodes.Error; if (!TryParseOptionValueAsEnum(options.OnError, OptionNames.OnError, out CliCompilationErrorResolution errorResolution, defaultValue: CliCompilationErrorResolution.None)) @@ -470,7 +467,7 @@ private static async Task RenameSymbolAsync(RenameSymbolCommandLineOptions var command = new RenameSymbolCommand( options: options, projectFilter: projectFilter, - fileSystemFilter: FileSystemFilter.CreateOrDefault(options.Include, options.Exclude), + fileSystemFilter: CreateFileSystemFilter(options), scopeFilter: scopeFilter, errorResolution: errorResolution, ignoredCompilerDiagnostics: options.IgnoredCompilerDiagnostics, @@ -518,7 +515,7 @@ private static async Task ListSymbolsAsync(ListSymbolsCommandLineOptions op if (!TryParseOptionValueAsEnumFlags(options.Visibility, OptionNames.Visibility, out VisibilityFilter visibilityFilter, SymbolFilterOptions.Default.Visibility)) return ExitCodes.Error; - if (!TryParsePaths(options.Paths, out ImmutableArray paths)) + if (!TryParsePaths(options.Paths, out ImmutableArray paths)) return ExitCodes.Error; ImmutableArray rules = (ignoredSymbols.Any()) @@ -529,7 +526,7 @@ private static async Task ListSymbolsAsync(ListSymbolsCommandLineOptions op IgnoredAttributeNameFilterRule.Default, new IgnoredAttributeNameFilterRule(ignoredAttributes)); - FileSystemFilter fileSystemFilter = FileSystemFilter.CreateOrDefault(options.Include, options.Exclude); + FileSystemFilter fileSystemFilter = CreateFileSystemFilter(options); var symbolFilterOptions = new SymbolFilterOptions( fileSystemFilter: fileSystemFilter, @@ -572,7 +569,7 @@ private static async Task FormatAsync(FormatCommandLineOptions options) if (!options.TryGetProjectFilter(out ProjectFilter projectFilter)) return ExitCodes.Error; - if (!TryParsePaths(options.Paths, out ImmutableArray paths)) + if (!TryParsePaths(options.Paths, out ImmutableArray paths)) return ExitCodes.Error; if (options.EndOfLine is not null) @@ -581,7 +578,7 @@ private static async Task FormatAsync(FormatCommandLineOptions options) CommandLineHelpers.WaitForKeyPress(); } - var command = new FormatCommand(options, projectFilter, FileSystemFilter.CreateOrDefault(options.Include, options.Exclude)); + var command = new FormatCommand(options, projectFilter, CreateFileSystemFilter(options)); IEnumerable properties = options.Properties; @@ -625,7 +622,7 @@ private static async Task SpellcheckAsync(SpellcheckCommandLineOptions opti } } - if (!TryParsePaths(options.Paths, out ImmutableArray paths)) + if (!TryParsePaths(options.Paths, out ImmutableArray paths)) return ExitCodes.Error; var loadOptions = WordListLoadOptions.DetectNonWords; @@ -644,7 +641,7 @@ private static async Task SpellcheckAsync(SpellcheckCommandLineOptions opti var command = new SpellcheckCommand( options, projectFilter, - FileSystemFilter.CreateOrDefault(options.Include, options.Exclude), + CreateFileSystemFilter(options), data, visibility, scopeFilter); @@ -659,10 +656,10 @@ private static async Task PhysicalLinesOfCodeAsync(PhysicalLinesOfCodeComma if (!options.TryGetProjectFilter(out ProjectFilter projectFilter)) return ExitCodes.Error; - if (!TryParsePaths(options.Paths, out ImmutableArray paths)) + if (!TryParsePaths(options.Paths, out ImmutableArray paths)) return ExitCodes.Error; - var command = new PhysicalLinesOfCodeCommand(options, projectFilter, FileSystemFilter.CreateOrDefault(options.Include, options.Exclude)); + var command = new PhysicalLinesOfCodeCommand(options, projectFilter, CreateFileSystemFilter(options)); CommandStatus status = await command.ExecuteAsync(paths, options.MSBuildPath, options.Properties); @@ -674,10 +671,10 @@ private static async Task LogicalLinesOrCodeAsync(LogicalLinesOfCodeCommand if (!options.TryGetProjectFilter(out ProjectFilter projectFilter)) return ExitCodes.Error; - if (!TryParsePaths(options.Paths, out ImmutableArray paths)) + if (!TryParsePaths(options.Paths, out ImmutableArray paths)) return ExitCodes.Error; - var command = new LogicalLinesOfCodeCommand(options, projectFilter, FileSystemFilter.CreateOrDefault(options.Include, options.Exclude)); + var command = new LogicalLinesOfCodeCommand(options, projectFilter, CreateFileSystemFilter(options)); CommandStatus status = await command.ExecuteAsync(paths, options.MSBuildPath, options.Properties); @@ -734,7 +731,7 @@ private static async Task GenerateDocAsync(GenerateDocCommandLineOptions op if (!options.TryGetProjectFilter(out ProjectFilter projectFilter)) return ExitCodes.Error; - if (!TryParsePaths(options.Path, out ImmutableArray paths)) + if (!TryParsePaths(options.Path, out ImmutableArray paths)) return ExitCodes.Error; var command = new GenerateDocCommand( @@ -754,7 +751,7 @@ private static async Task GenerateDocAsync(GenerateDocCommandLineOptions op groupByCommonNamespace: options.GroupByCommonNamespace, inheritanceStyle: inheritanceStyle, projectFilter: projectFilter, - fileSystemFilter: FileSystemFilter.CreateOrDefault(options.Include, options.Exclude)); + fileSystemFilter: CreateFileSystemFilter(options)); CommandStatus status = await command.ExecuteAsync(paths, options.MSBuildPath, options.Properties); @@ -786,7 +783,7 @@ private static async Task GenerateDocRootAsync(GenerateDocRootCommandLineOp if (!options.TryGetProjectFilter(out ProjectFilter projectFilter)) return ExitCodes.Error; - if (!TryParsePaths(options.Path, out ImmutableArray paths)) + if (!TryParsePaths(options.Path, out ImmutableArray paths)) return ExitCodes.Error; var command = new GenerateDocRootCommand( @@ -799,7 +796,7 @@ private static async Task GenerateDocRootAsync(GenerateDocRootCommandLineOp filesLayout, options.GroupByCommonNamespace, projectFilter, - FileSystemFilter.CreateOrDefault(options.Include, options.Exclude)); + CreateFileSystemFilter(options)); CommandStatus status = await command.ExecuteAsync(paths, options.MSBuildPath, options.Properties); @@ -816,7 +813,7 @@ private static int Migrate(MigrateCommandLineOptions options) return ExitCodes.Error; } - if (!TryParsePaths(options.Path, out ImmutableArray paths)) + if (!TryParsePaths(options.Path, out ImmutableArray paths)) return ExitCodes.Error; if (!TryParseVersion(options.Version, out Version version)) @@ -844,21 +841,21 @@ private static int Migrate(MigrateCommandLineOptions options) return GetExitCode(status); } - private static bool TryParsePaths(string value, out ImmutableArray paths) + private static bool TryParsePaths(string value, out ImmutableArray paths) { return TryParsePaths(ImmutableArray.Create(value), out paths); } - private static bool TryParsePaths(IEnumerable values, out ImmutableArray paths) + private static bool TryParsePaths(IEnumerable values, out ImmutableArray paths) { - paths = ImmutableArray.Empty; + paths = ImmutableArray.Empty; if (values.Any()) { if (!TryEnsureFullPath(values, out ImmutableArray paths2)) return false; - paths = paths.AddRange(paths2); + paths = paths.AddRange(ImmutableArray.CreateRange(paths2, f => new PathInfo(f, PathOrigin.Argument))); } if (Console.IsInputRedirected) @@ -870,7 +867,7 @@ private static bool TryParsePaths(IEnumerable values, out ImmutableArray return false; } - paths = paths.AddRange(paths2); + paths = paths.AddRange(ImmutableArray.CreateRange(paths2, f => new PathInfo(f, PathOrigin.PipedInput))); } if (!paths.IsEmpty) @@ -901,12 +898,12 @@ private static bool TryParsePaths(IEnumerable values, out ImmutableArray return false; } - paths = ImmutableArray.Create(solutionPath); + paths = ImmutableArray.Create(new PathInfo(solutionPath, PathOrigin.CurrentDirectory)); return true; } else if (projectPath is not null) { - paths = ImmutableArray.Create(projectPath); + paths = ImmutableArray.Create(new PathInfo(projectPath, PathOrigin.CurrentDirectory)); return true; } else @@ -963,4 +960,23 @@ private static class ExitCodes public const int NotSuccess = 1; public const int Error = 2; } + + private static FileSystemFilter CreateFileSystemFilter(MSBuildCommandLineOptions options) + { + string[] include = options.Include.Where(p => CommandLineHelpers.IsGlobPatternForFileOrFolder(p)).ToArray(); + string[] exclude = options.Exclude.Where(p => CommandLineHelpers.IsGlobPatternForFileOrFolder(p)).ToArray(); + + FileSystemFilter filter = FileSystemFilter.CreateOrDefault(include, exclude); + + if (filter is not null) + { + foreach (string pattern in include) + WriteLine($"Glob to include files/folders: {pattern}", ConsoleColors.DarkGray, Verbosity.Diagnostic); + + foreach (string pattern in exclude) + WriteLine($"Glob to exclude files/folders: {pattern}", ConsoleColors.DarkGray, Verbosity.Diagnostic); + } + + return filter; + } } diff --git a/src/CommandLine/ProjectFilter.cs b/src/CommandLine/ProjectFilter.cs index 650ee70abc..61e2020e3b 100644 --- a/src/CommandLine/ProjectFilter.cs +++ b/src/CommandLine/ProjectFilter.cs @@ -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 names, IEnumerable ignoredNames, string language) @@ -21,11 +24,17 @@ public ProjectFilter( 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.Empty; IgnoredNames = ignoredNames?.Select(f => ProjectName.Create(f)).ToImmutableHashSet() ?? ImmutableHashSet.Empty; Language = language; } + public Matcher Matcher { get; } + + public Matcher SolutionMatcher { get; } + public ImmutableHashSet Names { get; } public ImmutableHashSet IgnoredNames { get; } @@ -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; } @@ -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); diff --git a/src/CommandLine/docs/analyze-assembly-command.md b/src/CommandLine/docs/analyze-assembly-command.md deleted file mode 100644 index b2e6d8f45a..0000000000 --- a/src/CommandLine/docs/analyze-assembly-command.md +++ /dev/null @@ -1,49 +0,0 @@ - -# `analyze-assembly` Command - -Searches file or directory for analyzer assemblies. - -## Synopsis - -```shell -roslynator analyze-assembly -[--additional-paths] -[--language] -[--file-log] -[--file-log-verbosity] -[--no-analyzers] -[--no-fixers] -[-v|--verbosity] -``` - -## Arguments - -**`PATH`** - -The path to file or directory to analyze. - -### Optional Options - -**`--additional-paths`** - -Defines additional paths to search. - -**`--language`** `{cs[harp]|v[isual-]b[asic])}` - -Defines project language. - -**`--no-analyzers`** - -Indicates whether to search for DiagnosticAnalyzers. - -**`--no-fixers`** - -Indicates whether to search for CodeFixProviders. - -**`-v|--verbosity`** `{q[uiet]|m[inimal]|n[ormal]|d[etailed]|diag[nostic]}` - -Defines the amount of information to display in the log. - -## See Also - -* [Roslynator Command-Line Interface](README.md) diff --git a/src/CommandLine/docs/list-vs-command.md b/src/CommandLine/docs/list-vs-command.md deleted file mode 100644 index 656be8d8a7..0000000000 --- a/src/CommandLine/docs/list-vs-command.md +++ /dev/null @@ -1,23 +0,0 @@ - -# `list-vs` Command - -Lists Visual Studio installations. - -## Synopsis - -```shell -roslynator list-vs -[--file-log] -[--file-log-verbosity] -[-v|--verbosity] -``` - -### Optional Options - -**`-v|--verbosity`** `{q[uiet]|m[inimal]|n[ormal]|d[etailed]|diag[nostic]}` - -Defines the amount of information to display in the log. - -## See Also - -* [Roslynator Command-Line Interface](README.md) diff --git a/src/CommandLine/docs/sln-list-command.md b/src/CommandLine/docs/sln-list-command.md deleted file mode 100644 index dbef8024a3..0000000000 --- a/src/CommandLine/docs/sln-list-command.md +++ /dev/null @@ -1,54 +0,0 @@ - -# `sln-list` Command - -Gets an information about specified solution and its projects. - -## Synopsis - -```shell -roslynator sln-list -[--ignored-projects] -[--language] -[--file-log] -[--file-log-verbosity] -[--msbuild-path] -[--projects] -[-p|--properties] -[-v|--verbosity] -``` - -## Arguments - -**`SOLUTION`** - -The solution to open. - -### Optional Options - -**`--ignored-projects`** - -Defines project names that should not be fixed. - -**`--language`** `{cs[harp]|v[isual-]b[asic])}` - -Defines project language. - -**`--msbuild-path`** - -Defines a path to MSBuild. This option must be specified if there are multiple locations of MSBuild (usually multiple installations of Visual Studio). - -**`--projects`** - -Defines projects that should be analyzed. - -**`-p|--properties`** `` - -Defines one or more MSBuild properties. - -**`-v|--verbosity`** `{q[uiet]|m[inimal]|n[ormal]|d[etailed]|diag[nostic]}` - -Defines the amount of information to display in the log. - -## See Also - -* [Roslynator Command-Line Interface](README.md) diff --git a/tools/reinstall_cli_debug.ps1 b/tools/reinstall_cli_debug.ps1 index 30f2791d15..65ef9da8bc 100644 --- a/tools/reinstall_cli_debug.ps1 +++ b/tools/reinstall_cli_debug.ps1 @@ -6,6 +6,6 @@ dotnet pack "../src/CommandLine/CommandLine.csproj" -c Debug -v minimal ` dotnet tool uninstall roslynator.dotnet.cli -g -dotnet tool install roslynator.dotnet.cli -g --add-source "../src/CommandLine/bin/Debug" +dotnet tool install roslynator.dotnet.cli -g --add-source "../src/CommandLine/bin/Debug" --version 1.0.0 Write-Host DONE