diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dc2c015..6efaa65f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add option `--function` which replaces `--modify` ([#87](https://github.com/josefpihrt/orang/pull/87)). + +### Changed + +- Make option `--modify` obsolete ([#87](https://github.com/josefpihrt/orang/pull/87)). + ### Fixed - [CLI] Fix reading of redirected input ([PR](https://github.com/dotnet/roslynator/pull/91)) diff --git a/src/CommandLine/Aggregation/AggregateManager.cs b/src/CommandLine/Aggregation/AggregateManager.cs index db7f3960..45239b7b 100644 --- a/src/CommandLine/Aggregation/AggregateManager.cs +++ b/src/CommandLine/Aggregation/AggregateManager.cs @@ -181,8 +181,11 @@ public void WriteAggregatedValues(CancellationToken cancellationToken) { int groupCount = valuesMap[en.Current].Count; - _logger.Write(" ", Colors.Message_OK, Verbosity.Minimal); - _logger.Write(groupCount.ToString("n0"), Colors.Message_OK, Verbosity.Minimal); + if (groupCount > 1) + { + _logger.Write(" ", Colors.Message_OK, Verbosity.Minimal); + _logger.Write(groupCount.ToString("n0"), Colors.Message_OK, Verbosity.Minimal); + } } _logger.WriteLine(Verbosity.Minimal); diff --git a/src/CommandLine/CommandLineExtensions.cs b/src/CommandLine/CommandLineExtensions.cs index 9902a43e..fb8c4ab2 100644 --- a/src/CommandLine/CommandLineExtensions.cs +++ b/src/CommandLine/CommandLineExtensions.cs @@ -10,6 +10,15 @@ namespace Orang; internal static class CommandLineExtensions { + public static void WriteDeprecatedWarning( + this Logger logger, + string message, + ConsoleColors colors = default, + Verbosity verbosity = Verbosity.Minimal) + { + logger.ConsoleOut.ErrorWriter.WriteLine(message, (colors.IsDefault) ? Colors.Message_Warning : colors, verbosity); + } + public static void WriteWarning( this Logger logger, string message, diff --git a/src/CommandLine/CommandLineOptions/CommonCopyCommandLineOptions.cs b/src/CommandLine/CommandLineOptions/CommonCopyCommandLineOptions.cs index d7c65003..e3f2cd5b 100644 --- a/src/CommandLine/CommandLineOptions/CommonCopyCommandLineOptions.cs +++ b/src/CommandLine/CommandLineOptions/CommonCopyCommandLineOptions.cs @@ -10,7 +10,10 @@ namespace Orang.CommandLine; internal abstract class CommonCopyCommandLineOptions : CommonFindCommandLineOptions { - public override ContentDisplayStyle DefaultContentDisplayStyle => ContentDisplayStyle.Omit; + public override ContentDisplayStyle GetDefaultContentDisplayStyle() + { + return ContentDisplayStyle.Omit; + } [Option( longName: OptionNames.Compare, diff --git a/src/CommandLine/CommandLineOptions/CommonFindCommandLineOptions.cs b/src/CommandLine/CommandLineOptions/CommonFindCommandLineOptions.cs index 4d50ab3f..3ce42364 100644 --- a/src/CommandLine/CommandLineOptions/CommonFindCommandLineOptions.cs +++ b/src/CommandLine/CommandLineOptions/CommonFindCommandLineOptions.cs @@ -11,8 +11,6 @@ namespace Orang.CommandLine; [OptionValueProvider(nameof(Highlight), OptionValueProviderNames.FindHighlightOptions)] internal abstract class CommonFindCommandLineOptions : FileSystemCommandLineOptions { - public abstract ContentDisplayStyle DefaultContentDisplayStyle { get; } - [Option( shortName: OptionShortNames.Content, longName: OptionNames.Content, @@ -88,6 +86,9 @@ public bool TryParse(CommonFindCommandOptions options, ParseContext context) out ContentDisplayStyle contentDisplayStyle2, provider: OptionValueProviders.ContentDisplayStyleProvider)) { + if (!VerifyContentDisplayStyle(contentDisplayStyle2, context)) + return false; + contentDisplayStyle = contentDisplayStyle2; } else @@ -178,7 +179,7 @@ public bool TryParse(CommonFindCommandOptions options, ParseContext context) options.Format = new OutputDisplayFormat( contentDisplayStyle: contentDisplayStyle - ?? ((ReferenceEquals(contentFilter, Matcher.EntireInput)) ? ContentDisplayStyle.AllLines : DefaultContentDisplayStyle), + ?? ((ReferenceEquals(contentFilter, Matcher.EntireInput)) ? ContentDisplayStyle.AllLines : GetDefaultContentDisplayStyle()), pathDisplayStyle: pathDisplayStyle ?? PathDisplayStyle.Full, lineOptions: lineDisplayOptions, lineContext: lineContext, @@ -196,4 +197,11 @@ public bool TryParse(CommonFindCommandOptions options, ParseContext context) return true; } + + public abstract ContentDisplayStyle GetDefaultContentDisplayStyle(); + + public virtual bool VerifyContentDisplayStyle(ContentDisplayStyle contentDisplayStyle, ParseContext context) + { + return true; + } } diff --git a/src/CommandLine/CommandLineOptions/FileSystemCommandLineOptions.cs b/src/CommandLine/CommandLineOptions/FileSystemCommandLineOptions.cs index e502201b..f25a3075 100644 --- a/src/CommandLine/CommandLineOptions/FileSystemCommandLineOptions.cs +++ b/src/CommandLine/CommandLineOptions/FileSystemCommandLineOptions.cs @@ -221,7 +221,7 @@ public bool TryParse(FileSystemCommandOptions options, ParseContext context) if (IncludeDirectory.Any()) { - context.WriteWarning($"Option '{OptionNames.GetHelpText(OptionNames.IncludeDirectory)}' has been deprecated " + context.WriteDeprecatedWarning($"Option '{OptionNames.GetHelpText(OptionNames.IncludeDirectory)}' has been deprecated " + "and will be removed in future versions. " + $"Use options '{OptionNames.GetHelpText(OptionNames.Include)}/{OptionNames.GetHelpText(OptionNames.Exclude)}' instead."); } diff --git a/src/CommandLine/CommandLineOptions/FindCommandLineOptions.cs b/src/CommandLine/CommandLineOptions/FindCommandLineOptions.cs index a5e9fe57..db8e0354 100644 --- a/src/CommandLine/CommandLineOptions/FindCommandLineOptions.cs +++ b/src/CommandLine/CommandLineOptions/FindCommandLineOptions.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Text.RegularExpressions; using CommandLine; using Orang.CommandLine.Annotations; using Orang.FileSystem; @@ -14,8 +15,6 @@ namespace Orang.CommandLine; [CommandGroup("Main", 0)] internal sealed class FindCommandLineOptions : CommonFindCommandLineOptions { - public override ContentDisplayStyle DefaultContentDisplayStyle => ContentDisplayStyle.Line; - [Option( longName: OptionNames.Ask, HelpText = "Ask for permission after each file or value.", @@ -28,10 +27,18 @@ internal sealed class FindCommandLineOptions : CommonFindCommandLineOptions MetaValue = MetaValues.PipeMode)] public string Pipe { get; set; } = null!; + [Option( + longName: OptionNames.Function, + HelpText = "Space separated list of functions to modify a list of matches.", + MetaValue = MetaValues.Function)] + [AdditionalDescription(" All matches from all files are evaluated at once.")] + public IEnumerable Function { get; set; } = null!; + [Option( longName: OptionNames.Modify, - HelpText = "Functions to modify results.", + HelpText = "[deprecated] Modify results according to specified values.", MetaValue = MetaValues.ModifyOptions)] + [HideFromHelp] public IEnumerable Modify { get; set; } = null!; [Option( @@ -109,28 +116,69 @@ public bool TryParse(FindCommandOptions options, ParseContext context) } } - if (!context.TryParseModifyOptions( - Modify, - OptionNames.Modify, - out ModifyOptions? modifyOptions, - out bool aggregateOnly)) + options.ModifyOptions = ModifyOptions.Default; + + if (Modify.Any()) { - return false; + context.WriteDeprecatedWarning($"Option '{OptionNames.GetHelpText(OptionNames.Modify)}' has been deprecated " + + "and will be removed in future versions. " + + $"Use option '{OptionNames.GetHelpText(OptionNames.Function)}' instead."); + + if (!context.TryParseModifyOptions( + Modify, + OptionNames.Modify, + out ModifyOptions? modifyOptions, + out bool aggregateOnly)) + { + return false; + } + + options.ModifyOptions = modifyOptions; + options.AggregateOnly = aggregateOnly; + } + + if (Function.Any()) + { + if (Modify.Any()) + { + context.WriteError($"Options '{OptionNames.GetHelpText(OptionNames.Modify)}' and " + + $"'{OptionNames.GetHelpText(OptionNames.Function)}' cannot be used both at the same time."); + + return false; + } + + if (options.ContentFilter is null) + { + context.WriteError($"Option '{OptionNames.GetHelpText(OptionNames.Content)}' is required when " + + $"option '{OptionNames.GetHelpText(OptionNames.Function)}' is used."); + + return false; + } + + if (!context.TryParseFunctions( + Function, + OptionNames.Function, + out ModifyFunctions? functions, + out ValueSortProperty sortProperty)) + { + return false; + } + + options.ModifyOptions = new ModifyOptions( + functions.Value, + aggregate: true, + ignoreCase: (options.ContentFilter.Regex.Options & RegexOptions.IgnoreCase) != 0, + cultureInvariant: (options.ContentFilter.Regex.Options & RegexOptions.CultureInvariant) != 0, + sortProperty: sortProperty); + + options.AggregateOnly = true; } OutputDisplayFormat format = options.Format; ContentDisplayStyle contentDisplayStyle = format.ContentDisplayStyle; PathDisplayStyle pathDisplayStyle = format.PathDisplayStyle; - if (modifyOptions.HasAnyFunction - && contentDisplayStyle == ContentDisplayStyle.ValueDetail) - { - contentDisplayStyle = ContentDisplayStyle.Value; - } - options.Input = input; - options.ModifyOptions = modifyOptions; - options.AggregateOnly = aggregateOnly; options.Split = Split; options.Format = new OutputDisplayFormat( @@ -144,4 +192,24 @@ public bool TryParse(FindCommandOptions options, ParseContext context) return true; } + + public override ContentDisplayStyle GetDefaultContentDisplayStyle() + { + return (Function.Any()) ? ContentDisplayStyle.Value : ContentDisplayStyle.Line; + } + + public override bool VerifyContentDisplayStyle(ContentDisplayStyle contentDisplayStyle, ParseContext context) + { + if (Function.Any() + && contentDisplayStyle != ContentDisplayStyle.Value) + { + context.WriteError($"When option '{OptionNames.GetHelpText(OptionNames.Function)}' is used " + + $"option '{OptionNames.GetHelpText(OptionNames.ContentMode)}' must be set to " + + $"'{OptionValues.ContentDisplayStyle_Value.HelpValue}' (or unspecified)."); + + return false; + } + + return true; + } } diff --git a/src/CommandLine/CommandLineOptions/HelpCommandLineOptions.cs b/src/CommandLine/CommandLineOptions/HelpCommandLineOptions.cs index c4cbcb4a..9fa40dfe 100644 --- a/src/CommandLine/CommandLineOptions/HelpCommandLineOptions.cs +++ b/src/CommandLine/CommandLineOptions/HelpCommandLineOptions.cs @@ -42,10 +42,10 @@ internal sealed class HelpCommandLineOptions : AbstractCommandLineOptions public bool TryParse(HelpCommandOptions options, ParseContext context) { if (Filter.Any()) - context.WriteWarning($"Option '{OptionNames.GetHelpText(OptionNames.Filter)}' has been deprecated."); + context.WriteDeprecatedWarning($"Option '{OptionNames.GetHelpText(OptionNames.Filter)}' has been deprecated."); if (Online) - context.WriteWarning($"Option '{OptionNames.GetHelpText(OptionNames.Online)}' has been deprecated."); + context.WriteDeprecatedWarning($"Option '{OptionNames.GetHelpText(OptionNames.Online)}' has been deprecated."); options.Command = Command.ToArray(); options.Manual = Manual; diff --git a/src/CommandLine/CommandLineOptions/RegexCommandLineOptions.cs b/src/CommandLine/CommandLineOptions/RegexCommandLineOptions.cs index efeb58cb..c6ddb009 100644 --- a/src/CommandLine/CommandLineOptions/RegexCommandLineOptions.cs +++ b/src/CommandLine/CommandLineOptions/RegexCommandLineOptions.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using CommandLine; using Orang.CommandLine.Annotations; using Orang.Text.RegularExpressions; @@ -18,6 +19,14 @@ internal abstract class RegexCommandLineOptions : CommonRegexCommandLineOptions MetaName = ArgumentMetaNames.Path)] public string Path { get; set; } = null!; + [Option( + shortName: OptionShortNames.Content, + longName: OptionNames.Content, + Required = true, + HelpText = "Regular expression for the input string.", + MetaValue = MetaValues.Regex)] + public IEnumerable Content { get; set; } = null!; + [Option( shortName: OptionShortNames.Input, longName: OptionNames.Input, @@ -25,10 +34,18 @@ internal abstract class RegexCommandLineOptions : CommonRegexCommandLineOptions MetaValue = MetaValues.Input)] public IEnumerable Input { get; set; } = null!; + [Option( + longName: OptionNames.Function, + HelpText = "Space separated list of functions to modify a list of matches.", + MetaValue = MetaValues.Function)] + [AdditionalDescription(" All matches from all files are evaluated at once.")] + public IEnumerable Function { get; set; } = null!; + [Option( longName: OptionNames.Modify, - HelpText = "Functions to modify results.", + HelpText = "[deprecated] Modify results according to specified values.", MetaValue = MetaValues.ModifyOptions)] + [HideFromHelp] public IEnumerable Modify { get; set; } = null!; public bool TryParse(RegexCommandOptions options, ParseContext context) @@ -115,23 +132,64 @@ public bool TryParse(RegexCommandOptions options, ParseContext context) if (ContentSeparator is not null) separator = ContentSeparator; #endif - if (!context.TryParseModifyOptions( - Modify, - OptionNames.Modify, - out ModifyOptions? modifyOptions, - out bool aggregateOnly)) + + options.ModifyOptions = ModifyOptions.Default; + + if (Modify.Any()) { - return false; + context.WriteDeprecatedWarning($"Option '{OptionNames.GetHelpText(OptionNames.Modify)}' has been deprecated " + + "and will be removed in future versions. " + + $"Use option '{OptionNames.GetHelpText(OptionNames.Function)}' instead."); + + if (!context.TryParseModifyOptions( + Modify, + OptionNames.Modify, + out ModifyOptions? modifyOptions, + out bool aggregateOnly)) + { + return false; + } + + options.ModifyOptions = modifyOptions; + + if (aggregateOnly) + context.Logger.ConsoleOut.Verbosity = Orang.Verbosity.Minimal; + + if (modifyOptions.HasAnyFunction + && contentDisplayStyle == ContentDisplayStyle.ValueDetail) + { + contentDisplayStyle = ContentDisplayStyle.Value; + } } - if (modifyOptions.HasAnyFunction - && contentDisplayStyle == ContentDisplayStyle.ValueDetail) + if (Function.Any()) { - contentDisplayStyle = ContentDisplayStyle.Value; - } + if (Modify.Any()) + { + context.WriteError($"Options '{OptionNames.GetHelpText(OptionNames.Modify)}' and " + + $"'{OptionNames.GetHelpText(OptionNames.Function)}' cannot be used both at the same time."); + + return false; + } + + if (!context.TryParseFunctions( + Function, + OptionNames.Function, + out ModifyFunctions? functions, + out ValueSortProperty sortProperty)) + { + return false; + } + + options.ModifyOptions = new ModifyOptions( + functions.Value, + aggregate: true, + ignoreCase: (options.Matcher.Regex.Options & RegexOptions.IgnoreCase) != 0, + cultureInvariant: (options.Matcher.Regex.Options & RegexOptions.CultureInvariant) != 0, + sortProperty: sortProperty); - if (aggregateOnly) context.Logger.ConsoleOut.Verbosity = Orang.Verbosity.Minimal; + } options.Format = new OutputDisplayFormat( contentDisplayStyle: contentDisplayStyle ?? ContentDisplayStyle.Value, @@ -144,8 +202,6 @@ public bool TryParse(RegexCommandOptions options, ParseContext context) #else separator: Environment.NewLine); #endif - - options.ModifyOptions = modifyOptions; options.Input = input; return true; diff --git a/src/CommandLine/CommandLineOptions/RegexMatchCommandLineOptions.cs b/src/CommandLine/CommandLineOptions/RegexMatchCommandLineOptions.cs index b74b5d45..b9745f3a 100644 --- a/src/CommandLine/CommandLineOptions/RegexMatchCommandLineOptions.cs +++ b/src/CommandLine/CommandLineOptions/RegexMatchCommandLineOptions.cs @@ -13,14 +13,6 @@ namespace Orang.CommandLine; [CommandAlias("regex match")] internal sealed class RegexMatchCommandLineOptions : RegexCommandLineOptions { - [Option( - shortName: OptionShortNames.Content, - longName: OptionNames.Content, - Required = true, - HelpText = "Regular expression for the input string.", - MetaValue = MetaValues.Regex)] - public IEnumerable Content { get; set; } = null!; - [Option( shortName: OptionShortNames.MaxCount, longName: OptionNames.MaxCount, diff --git a/src/CommandLine/CommandLineOptions/RegexSplitCommandLineOptions.cs b/src/CommandLine/CommandLineOptions/RegexSplitCommandLineOptions.cs index 3ec34eb9..7de57d48 100644 --- a/src/CommandLine/CommandLineOptions/RegexSplitCommandLineOptions.cs +++ b/src/CommandLine/CommandLineOptions/RegexSplitCommandLineOptions.cs @@ -15,14 +15,6 @@ namespace Orang.CommandLine; [CommandAlias("regex split")] internal sealed class RegexSplitCommandLineOptions : RegexCommandLineOptions { - [Option( - shortName: OptionShortNames.Content, - longName: OptionNames.Content, - Required = true, - HelpText = "Regular expression for the input string.", - MetaValue = MetaValues.Regex)] - public IEnumerable Content { get; set; } = null!; - [Option( shortName: OptionShortNames.MaxCount, longName: OptionNames.MaxCount, diff --git a/src/CommandLine/CommandUtility.cs b/src/CommandLine/CommandUtility.cs index c7fd56bd..ce721deb 100644 --- a/src/CommandLine/CommandUtility.cs +++ b/src/CommandLine/CommandUtility.cs @@ -131,7 +131,7 @@ private static void ReplaceArgs(string commandName, string commandAlias, Logger { if (commandAlias is not null) { - logger.WriteWarning($"Command '{args[0]}' has been deprecated " + logger.WriteDeprecatedWarning($"Command '{args[0]}' has been deprecated " + $"and will be removed in future versions. Use command '{commandAlias}' instead."); } diff --git a/src/CommandLine/Commands/FindCommand`1.cs b/src/CommandLine/Commands/FindCommand`1.cs index c0046364..aace4047 100644 --- a/src/CommandLine/Commands/FindCommand`1.cs +++ b/src/CommandLine/Commands/FindCommand`1.cs @@ -27,10 +27,11 @@ public override bool CanEndProgress { get { - return !Options.OmitPath - || (ContentFilter is not null - && !Options.OmitContent - && _logger.ConsoleOut.Verbosity > Verbosity.Minimal); + return !Options.AggregateOnly + && (!Options.OmitPath + || (ContentFilter is not null + && !Options.OmitContent + && _logger.ConsoleOut.Verbosity > Verbosity.Minimal)); } } diff --git a/src/CommandLine/DiagnosticWriter.cs b/src/CommandLine/DiagnosticWriter.cs index e9162c7c..fcf2af63 100644 --- a/src/CommandLine/DiagnosticWriter.cs +++ b/src/CommandLine/DiagnosticWriter.cs @@ -270,12 +270,12 @@ internal void WriteFindCommand(FindCommandOptions options) options.SizePredicate, options.CreationTimePredicate, options.ModifiedTimePredicate); + WriteFunctions("function", options.ModifyOptions?.Functions); WriteOption("highlight options", options.HighlightOptions); WriteInput(options.Input); WriteOption("line number", options.Format.Includes(LineDisplayOptions.IncludeLineNumber)); WriteOption("max matching files", options.MaxMatchingFiles); WriteOption("max matches in file", options.MaxMatchesInFile); - WriteModify("modify", options.ModifyOptions); WriteFilter("name filter", options.NameFilter, options.NamePart); WriteOption("path mode", options.Format.PathDisplayStyle); WritePaths("paths", options.Paths); @@ -922,67 +922,19 @@ private void WriteReplaceModify(string name, Replacer replacer) WriteLine(); } - private void WriteModify(string name, ModifyOptions options) + private void WriteFunctions(string name, ModifyFunctions? functions) { WriteName(name); - if (options.Aggregate - || options.CultureInvariant - || options.IgnoreCase - || options.Functions != ModifyFunctions.None - || options.SortProperty != ValueSortProperty.None) + if (functions is not null + && functions != ModifyFunctions.None) { - if (options.Aggregate) - { - WriteLine(); - WriteIndent(); - WriteName("aggregate"); - WriteValue(options.Aggregate); - } - - if (options.CultureInvariant) - { - WriteLine(); - WriteIndent(); - WriteName("culture invariant"); - WriteValue(options.CultureInvariant); - } - - if (options.IgnoreCase) - { - WriteLine(); - WriteIndent(); - WriteName("ignore case"); - WriteValue(options.IgnoreCase); - } - - if (options.SortProperty != ValueSortProperty.None) - { - WriteLine(); - WriteIndent(); - WriteName("sort by"); - WriteValue(options.SortProperty.ToString()); - } - - WriteLine(); - WriteIndent(); - WriteName("functions"); - - if (options.Functions != ModifyFunctions.None) - { - WriteValue(options.Functions.ToString()); - } - else - { - WriteNullValue(); - } + WriteValue(functions.ToString()); } else { WriteNullValue(); } - - WriteLine(); } private void WriteValue(bool value) diff --git a/src/CommandLine/FunctionFlags.cs b/src/CommandLine/FunctionFlags.cs new file mode 100644 index 00000000..5cf0ace8 --- /dev/null +++ b/src/CommandLine/FunctionFlags.cs @@ -0,0 +1,15 @@ +// Copyright (c) Josef Pihrt. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Orang.CommandLine; + +[Flags] +internal enum FunctionFlags +{ + None = 0, + Distinct = 1, + Sort = 1 << 1, + SortDescending = 1 << 2, + Group = 1 << 5, +} diff --git a/src/CommandLine/ModifyFlags.cs b/src/CommandLine/ModifyFlags.cs index b0de33ae..26972734 100644 --- a/src/CommandLine/ModifyFlags.cs +++ b/src/CommandLine/ModifyFlags.cs @@ -14,7 +14,6 @@ internal enum ModifyFlags Except = 1 << 3, Intersect = 1 << 4, Group = 1 << 5, - Except_Intersect_Group = Except | Intersect | Group, RemoveEmpty = 1 << 6, RemoveWhiteSpace = 1 << 7, TrimStart = 1 << 8, diff --git a/src/CommandLine/Names/MetaValues.cs b/src/CommandLine/Names/MetaValues.cs index aea968f0..b85f3710 100644 --- a/src/CommandLine/Names/MetaValues.cs +++ b/src/CommandLine/Names/MetaValues.cs @@ -16,6 +16,7 @@ internal static class MetaValues public const string ExtensionOptions = ""; public const string FilePath = ""; public const string FileProperties = ""; + public const string Function = ""; public const string Highlight = ""; public const string Indent = ""; public const string Input = " []"; diff --git a/src/CommandLine/Names/OptionNames.cs b/src/CommandLine/Names/OptionNames.cs index 11b0e514..8f336a92 100644 --- a/src/CommandLine/Names/OptionNames.cs +++ b/src/CommandLine/Names/OptionNames.cs @@ -39,6 +39,7 @@ internal static class OptionNames public const string Fixes = "fixes"; public const string Flat = "flat"; public const string FromFile = "from-file"; + public const string Function = "function"; public const string Help = "help"; public const string Highlight = "highlight"; public const string CharGroup = "char-group"; diff --git a/src/CommandLine/OptionValueProviders.cs b/src/CommandLine/OptionValueProviders.cs index 6457e8e7..89b24cd1 100644 --- a/src/CommandLine/OptionValueProviders.cs +++ b/src/CommandLine/OptionValueProviders.cs @@ -297,6 +297,15 @@ internal static class OptionValueProviders OptionValues.TrimStart ); + public static OptionValueProvider FunctionFlagsProvider { get; } = new( + MetaValues.Function, + OptionValues.FunctionFlags_Sort, + OptionValues.FunctionFlags_SortDescending, + SimpleOptionValue.Create(ModifyFlags.Distinct, shortValue: "", description: "Return distinct values."), + OptionValues.ModifyFlags_Group, + OptionValues.SortBy + ); + public static OptionValueProvider HighlightOptionsProvider { get; } = new( MetaValues.Highlight, OptionValues.HighlightOptions_None, diff --git a/src/CommandLine/OptionValues.cs b/src/CommandLine/OptionValues.cs index 1f43e309..0a9d4a90 100644 --- a/src/CommandLine/OptionValues.cs +++ b/src/CommandLine/OptionValues.cs @@ -45,6 +45,9 @@ internal static class OptionValues public static readonly SimpleOptionValue ModifyFlags_Group = SimpleOptionValue.Create(ModifyFlags.Group, shortValue: "", description: "Group matching files by matched values."); public static readonly SimpleOptionValue ModifyFlags_Count = SimpleOptionValue.Create(ModifyFlags.Count, shortValue: "", description: "Show number of values in a group."); + public static readonly SimpleOptionValue FunctionFlags_Sort = SimpleOptionValue.Create(FunctionFlags.Sort, shortValue: "", description: "Sort values in an ascending order."); + public static readonly SimpleOptionValue FunctionFlags_SortDescending = SimpleOptionValue.Create(FunctionFlags.SortDescending, shortValue: "", description: "Sort values in a descending order."); + public static readonly SimpleOptionValue ModifyOptions_Ascending = SimpleOptionValue.Create(ModifyFlags.Ascending, description: "Sort values in an ascending order."); public static readonly SimpleOptionValue ModifyOptions_Descending = SimpleOptionValue.Create(ModifyFlags.Descending, description: "Sort values in a descending order."); diff --git a/src/CommandLine/ParseContext.cs b/src/CommandLine/ParseContext.cs index 08642b84..a67857b8 100644 --- a/src/CommandLine/ParseContext.cs +++ b/src/CommandLine/ParseContext.cs @@ -33,9 +33,9 @@ public ParseContext(Logger logger) Logger = logger; } - public void WriteWarning(string message) + public void WriteDeprecatedWarning(string message) { - Logger.WriteWarning(message); + Logger.WriteDeprecatedWarning(message); } public void WriteError(Exception exception) @@ -320,6 +320,94 @@ void AddDescriptor(SortProperty p, SortDirection d) } } + public bool TryParseFunctions( + IEnumerable values, + string optionName, + [NotNullWhen(true)] out ModifyFunctions? functions, + [NotNullWhen(true)] out ValueSortProperty sortProperty) + { + functions = null; + sortProperty = ValueSortProperty.None; + List? options = null; + + foreach (string value in values) + { + int index = value.IndexOf('='); + + if (index >= 0) + { + string key = value.Substring(0, index); + string value2 = value.Substring(index + 1); + + if (OptionValues.SortBy.IsKeyOrShortKey(key)) + { + if (!TryParseAsEnum( + value2, + optionName, + out sortProperty, + provider: OptionValueProviders.ValueSortPropertyProvider)) + { + return false; + } + } + else + { + WriteOptionError(value, optionName, OptionValueProviders.FunctionFlagsProvider); + return false; + } + } + else + { + (options ??= new List()).Add(value); + } + } + + var modifyFlags = FunctionFlags.None; + + if (options is not null + && !TryParseAsEnumFlags( + options, + optionName, + out modifyFlags, + provider: OptionValueProviders.FunctionFlagsProvider)) + { + return false; + } + + functions = ModifyFunctions.None; + + if ((modifyFlags & FunctionFlags.Sort) != 0) + functions |= ModifyFunctions.SortAscending; + + if ((modifyFlags & FunctionFlags.SortDescending) != 0) + functions |= ModifyFunctions.SortDescending; + + if ((functions & ModifyFunctions.Sort) == ModifyFunctions.Sort) + { + Logger.WriteError($"Option '{optionName}' cannot use both '{OptionValues.ModifyOptions_Ascending.HelpValue}' " + + $"and '{OptionValues.ModifyOptions_Descending.HelpValue}' values."); + + return false; + } + + if ((modifyFlags & FunctionFlags.Distinct) != 0) + functions |= ModifyFunctions.Distinct; + + if ((modifyFlags & FunctionFlags.Group) != 0) + { + functions |= ModifyFunctions.Group; + functions |= ModifyFunctions.Count; + } + + if (sortProperty != ValueSortProperty.None + && (functions & ModifyFunctions.Sort) == 0) + { + functions |= ModifyFunctions.SortAscending; + } + + return true; + } + public bool TryParseModifyOptions( IEnumerable values, string optionName, @@ -376,12 +464,12 @@ public bool TryParseModifyOptions( return false; } - ModifyFlags except_Intersect_Group = modifyFlags & ModifyFlags.Except_Intersect_Group; + ModifyFlags exceptIntersectGroup = modifyFlags & (ModifyFlags.Except | ModifyFlags.Intersect | ModifyFlags.Group); - if (except_Intersect_Group != ModifyFlags.None - && except_Intersect_Group != ModifyFlags.Except - && except_Intersect_Group != ModifyFlags.Intersect - && except_Intersect_Group != ModifyFlags.Group) + if (exceptIntersectGroup != ModifyFlags.None + && exceptIntersectGroup != ModifyFlags.Except + && exceptIntersectGroup != ModifyFlags.Intersect + && exceptIntersectGroup != ModifyFlags.Group) { Logger.WriteError($"Values '{OptionValues.ModifyFlags_Except.HelpValue}', " + $"'{OptionValues.ModifyFlags_Intersect.HelpValue}' and "