From 605630f46c9392d5e7690c184ea44c6f65b470aa Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sun, 28 Jan 2018 04:12:58 -0800 Subject: [PATCH 01/20] Add ability to support different types of quotation marks --- src/Discord.Net.Commands/CommandParser.cs | 17 +++++++++++++++-- src/Discord.Net.Commands/CommandService.cs | 4 +++- .../CommandServiceConfig.cs | 3 +++ src/Discord.Net.Commands/Info/CommandInfo.cs | 4 +++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 28e36d54d9..1b3f6067b4 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -13,6 +13,19 @@ private enum ParserPart Parameter, QuotedParameter } + + /// + /// Checks to see if the supplied character is a quotation mark + /// from either the default " character, or the list of aliases + /// if they are provided. + /// + /// + /// + private static bool isQuotationChar(char c, char[] aliases) + { + if (aliases == null) return c == '\"'; + return Array.Exists(aliases, x => x == c); + } public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos) { @@ -74,7 +87,7 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma argBuilder.Append(c); continue; } - if (c == '\"') + if (isQuotationChar(c, command._quotationAliases)) { curPart = ParserPart.QuotedParameter; continue; @@ -97,7 +110,7 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma } else if (curPart == ParserPart.QuotedParameter) { - if (c == '\"') + if (isQuotationChar(c, command._quotationAliases)) { argString = argBuilder.ToString(); //Remove quotes lastArgEndPos = curPos + 1; diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index cf2b932779..99d333ee55 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -32,6 +32,7 @@ public class CommandService internal readonly RunMode _defaultRunMode; internal readonly Logger _cmdLogger; internal readonly LogManager _logManager; + internal readonly char[] _quotationMarkAliases; public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); @@ -44,6 +45,7 @@ public CommandService(CommandServiceConfig config) _throwOnError = config.ThrowOnError; _separatorChar = config.SeparatorChar; _defaultRunMode = config.DefaultRunMode; + _quotationMarkAliases = config.QuotationMarkAliases; if (_defaultRunMode == RunMode.Default) throw new InvalidOperationException("The default run mode cannot be set to Default."); @@ -276,7 +278,7 @@ public Task ExecuteAsync(ICommandContext context, int argPos, IServiceP public async Task ExecuteAsync(ICommandContext context, string input, IServiceProvider services = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { services = services ?? EmptyServiceProvider.Instance; - + var searchResult = Search(context, input); if (!searchResult.IsSuccess) return searchResult; diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index b53b0248cc..86e05e6198 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -15,5 +15,8 @@ public class CommandServiceConfig /// Determines whether RunMode.Sync commands should push exceptions up to the caller. public bool ThrowOnError { get; set; } = true; + + /// List of aliases for string parsing + public char[] QuotationMarkAliases { get; set; } = new char[] { '“', '”', '\'', '«', '»', '‹', '›' }; } } diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 6bb621f941..e563f904d0 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -19,6 +19,7 @@ public class CommandInfo private static readonly ConcurrentDictionary, object>> _arrayConverters = new ConcurrentDictionary, object>>(); private readonly Func _action; + internal readonly char[] _quotationAliases; public ModuleInfo Module { get; } public string Name { get; } @@ -64,6 +65,7 @@ internal CommandInfo(CommandBuilder builder, ModuleInfo module, CommandService s HasVarArgs = builder.Parameters.Count > 0 ? builder.Parameters[builder.Parameters.Count - 1].IsMultiple : false; _action = builder.Callback; + _quotationAliases = service._quotationMarkAliases; } public async Task CheckPreconditionsAsync(ICommandContext context, IServiceProvider services = null) @@ -107,7 +109,7 @@ async Task CheckGroups(IEnumerable pr return PreconditionResult.FromSuccess(); } - public async Task ParseAsync(ICommandContext context, int startIndex, SearchResult searchResult, PreconditionResult preconditionResult = null, IServiceProvider services = null) + public async Task ParseAsync(ICommandContext context, int startIndex, SearchResult searchResult, PreconditionResult preconditionResult = null, IServiceProvider services = null, char[] quotationAliases = null) { services = services ?? EmptyServiceProvider.Instance; From 384b0ca622aecd1a48e0fdce871eee1b4cbe02e2 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sun, 28 Jan 2018 04:26:20 -0800 Subject: [PATCH 02/20] Added normal quotation mark to list of aliases, removed single quote mark --- src/Discord.Net.Commands/CommandServiceConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index c3f56a8da3..4401df9653 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -17,7 +17,7 @@ public class CommandServiceConfig public bool ThrowOnError { get; set; } = true; /// List of aliases for string parsing - public char[] QuotationMarkAliases { get; set; } = new char[] { '“', '”', '\'', '«', '»', '‹', '›' }; + public char[] QuotationMarkAliases { get; set; } = new char[] { '\"', '“', '”', '\'', '«', '»', '‹', '›' }; /// Determines whether extra parameters should be ignored. public bool IgnoreExtraArgs { get; set; } = false; From 4f6e29be8ca7ebcbd99a9ec20f475345c8e2eb0f Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sun, 28 Jan 2018 04:29:18 -0800 Subject: [PATCH 03/20] clean up leftover changes from testing --- src/Discord.Net.Commands/CommandService.cs | 1 - src/Discord.Net.Commands/CommandServiceConfig.cs | 2 +- src/Discord.Net.Commands/Info/CommandInfo.cs | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 5825f17f90..67d721dbbd 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -279,7 +279,6 @@ public Task ExecuteAsync(ICommandContext context, int argPos, IServiceP public async Task ExecuteAsync(ICommandContext context, string input, IServiceProvider services = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { services = services ?? EmptyServiceProvider.Instance; - var searchResult = Search(context, input); if (!searchResult.IsSuccess) return searchResult; diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 4401df9653..3985a39a75 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -17,7 +17,7 @@ public class CommandServiceConfig public bool ThrowOnError { get; set; } = true; /// List of aliases for string parsing - public char[] QuotationMarkAliases { get; set; } = new char[] { '\"', '“', '”', '\'', '«', '»', '‹', '›' }; + public char[] QuotationMarkAliases { get; set; } = new char[] { '\"', '“', '”', '«', '»', '‹', '›' }; /// Determines whether extra parameters should be ignored. public bool IgnoreExtraArgs { get; set; } = false; diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 442598d4f0..781a0212f1 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -1,4 +1,4 @@ -using Discord.Commands.Builders; +using Discord.Commands.Builders; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -66,8 +66,8 @@ internal CommandInfo(CommandBuilder builder, ModuleInfo module, CommandService s HasVarArgs = builder.Parameters.Count > 0 ? builder.Parameters[builder.Parameters.Count - 1].IsMultiple : false; _action = builder.Callback; - _commandService = service; _quotationAliases = service._quotationMarkAliases; + _commandService = service; } public async Task CheckPreconditionsAsync(ICommandContext context, IServiceProvider services = null) @@ -111,7 +111,7 @@ async Task CheckGroups(IEnumerable pr return PreconditionResult.FromSuccess(); } - public async Task ParseAsync(ICommandContext context, int startIndex, SearchResult searchResult, PreconditionResult preconditionResult = null, IServiceProvider services = null, char[] quotationAliases = null) + public async Task ParseAsync(ICommandContext context, int startIndex, SearchResult searchResult, PreconditionResult preconditionResult = null, IServiceProvider services = null) { services = services ?? EmptyServiceProvider.Instance; From 3e54e6fa044fa683cd9168bb0ccef972476613d0 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sun, 28 Jan 2018 16:41:09 -0800 Subject: [PATCH 04/20] change quotation mark parsing to use a map of matching pairs --- src/Discord.Net.Commands/CommandParser.cs | 46 +++++++++++++------ src/Discord.Net.Commands/CommandService.cs | 4 +- .../CommandServiceConfig.cs | 28 +++++++++-- src/Discord.Net.Commands/Info/CommandInfo.cs | 4 +- 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 96b946444d..bc535ff748 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Text; using System.Threading.Tasks; @@ -14,19 +15,32 @@ private enum ParserPart QuotedParameter } - /// - /// Checks to see if the supplied character is a quotation mark - /// from either the default " character, or the list of aliases - /// if they are provided. - /// - /// - /// - private static bool isQuotationChar(char c, char[] aliases) + private static bool isOpenQuote(Dictionary map, char c) { - if (aliases == null) return c == '\"'; - return Array.Exists(aliases, x => x == c); + // determine if the map contains the key for this value + if(map != null) + { + return map.ContainsKey(c); + } + // or if the value is a normal quote " + return c == '\"'; + } + + // get the corresponding matching quote for the key + private static char getMatch(Dictionary map, char c) + { + if (map != null) + { + char value; + if( map.TryGetValue(c, out value)) + { + return value; + } + } + + return '\"'; } - + public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos) { ParameterInfo curParam = null; @@ -37,7 +51,7 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma var argList = ImmutableArray.CreateBuilder(); var paramList = ImmutableArray.CreateBuilder(); bool isEscaping = false; - char c; + char c, matchQuote = '\0'; for (int curPos = startPos; curPos <= endPos; curPos++) { @@ -87,9 +101,11 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma argBuilder.Append(c); continue; } - if (isQuotationChar(c, command._quotationAliases)) + + if(isOpenQuote(command._quotationAliases, c)) { curPart = ParserPart.QuotedParameter; + matchQuote = getMatch(command._quotationAliases, c); continue; } curPart = ParserPart.Parameter; @@ -110,7 +126,9 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma } else if (curPart == ParserPart.QuotedParameter) { - if (isQuotationChar(c, command._quotationAliases)) + //if (findQuotationChar(c, false, out matchingQuotation)) + //if( matchingQuotation != null && matchingQuotation.Right == c) + if(c == matchQuote) { argString = argBuilder.ToString(); //Remove quotes lastArgEndPos = curPos + 1; diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 67d721dbbd..02f1da5c3b 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -32,7 +32,7 @@ public class CommandService internal readonly RunMode _defaultRunMode; internal readonly Logger _cmdLogger; internal readonly LogManager _logManager; - internal readonly char[] _quotationMarkAliases; + internal readonly Dictionary _quotationMarkAliasMap; public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); @@ -46,7 +46,7 @@ public CommandService(CommandServiceConfig config) _ignoreExtraArgs = config.IgnoreExtraArgs; _separatorChar = config.SeparatorChar; _defaultRunMode = config.DefaultRunMode; - _quotationMarkAliases = config.QuotationMarkAliases; + _quotationMarkAliasMap = config.QuotationMarkAliasMap; if (_defaultRunMode == RunMode.Default) throw new InvalidOperationException("The default run mode cannot be set to Default."); diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 3985a39a75..6de61f9393 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -1,4 +1,5 @@ -namespace Discord.Commands +using System.Collections.Generic; +namespace Discord.Commands { public class CommandServiceConfig { @@ -16,8 +17,29 @@ public class CommandServiceConfig /// Determines whether RunMode.Sync commands should push exceptions up to the caller. public bool ThrowOnError { get; set; } = true; - /// List of aliases for string parsing - public char[] QuotationMarkAliases { get; set; } = new char[] { '\"', '“', '”', '«', '»', '‹', '›' }; + /// Collection of aliases that can wrap strings for command parsing. + /// represents the opening quotation mark and the value is the corresponding closing mark. + public Dictionary QuotationMarkAliasMap { get; set; } + = new Dictionary() + { + {'\"', '\"' }, + {'«', '»' }, + {'‘', '’' }, + {'“', '”' }, + {'„', '‟' }, + {'‹', '›' }, + {'‚', '‛' }, + {'《', '》' }, + {'〈', '〉' }, + {'「', '」' }, + {'『', '』' }, + {'〝', '〞' }, + {'﹁', '﹂' }, + {'﹃', '﹄' }, + {'"', '"' }, + {''', ''' }, + {'「', '」' } + }; /// Determines whether extra parameters should be ignored. public bool IgnoreExtraArgs { get; set; } = false; diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 781a0212f1..4dee863b5b 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -20,7 +20,7 @@ public class CommandInfo private readonly CommandService _commandService; private readonly Func _action; - internal readonly char[] _quotationAliases; + internal readonly Dictionary _quotationAliases; public ModuleInfo Module { get; } public string Name { get; } @@ -66,7 +66,7 @@ internal CommandInfo(CommandBuilder builder, ModuleInfo module, CommandService s HasVarArgs = builder.Parameters.Count > 0 ? builder.Parameters[builder.Parameters.Count - 1].IsMultiple : false; _action = builder.Callback; - _quotationAliases = service._quotationMarkAliases; + _quotationAliases = service._quotationMarkAliasMap; _commandService = service; } From 2e27d268a9cb44513cd6f0769648f2bcc7bce155 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sun, 28 Jan 2018 16:50:58 -0800 Subject: [PATCH 05/20] remove commented out code --- src/Discord.Net.Commands/CommandParser.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index bc535ff748..b9408f9635 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -126,8 +126,6 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma } else if (curPart == ParserPart.QuotedParameter) { - //if (findQuotationChar(c, false, out matchingQuotation)) - //if( matchingQuotation != null && matchingQuotation.Right == c) if(c == matchQuote) { argString = argBuilder.ToString(); //Remove quotes From 6633a5cca5c7fe2c0d19d6e568bdc6826f5598f3 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Mon, 29 Jan 2018 00:23:20 -0800 Subject: [PATCH 06/20] Fix conventions of the command parser utility functions --- src/Discord.Net.Commands/CommandParser.cs | 57 +++++++++++------------ 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index b9408f9635..2e055802f4 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -14,33 +14,7 @@ private enum ParserPart Parameter, QuotedParameter } - - private static bool isOpenQuote(Dictionary map, char c) - { - // determine if the map contains the key for this value - if(map != null) - { - return map.ContainsKey(c); - } - // or if the value is a normal quote " - return c == '\"'; - } - - // get the corresponding matching quote for the key - private static char getMatch(Dictionary map, char c) - { - if (map != null) - { - char value; - if( map.TryGetValue(c, out value)) - { - return value; - } - } - - return '\"'; - } - + public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos) { ParameterInfo curParam = null; @@ -53,6 +27,31 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma bool isEscaping = false; char c, matchQuote = '\0'; + // local helper functions + bool IsOpenQuote(IReadOnlyDictionary dict, char ch) + { + // return if the key is contained in the dictionary if it exists + if (dict != null) + return dict.ContainsKey(ch); + // or otherwise if it is the default double quote + return c == '\"'; + } + + char GetMatch(IReadOnlyDictionary dict, char ch) + { + // get the corresponding value for the key, if it exists + if (dict != null) + { + char value; + if (dict.TryGetValue(c, out value)) + { + return value; + } + } + // or get the default pair of the default double quote + return '\"'; + } + for (int curPos = startPos; curPos <= endPos; curPos++) { if (curPos < endPos) @@ -102,10 +101,10 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma continue; } - if(isOpenQuote(command._quotationAliases, c)) + if(IsOpenQuote(command._quotationAliases, c)) { curPart = ParserPart.QuotedParameter; - matchQuote = getMatch(command._quotationAliases, c); + matchQuote = GetMatch(command._quotationAliases, c); continue; } curPart = ParserPart.Parameter; From d42936c568fa6acbe0b835bfa8f42c9ebdaefa06 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Mon, 29 Jan 2018 00:43:48 -0800 Subject: [PATCH 07/20] change storage type of alias dictionary to be IReadOnlyDictionary --- src/Discord.Net.Commands/CommandService.cs | 2 +- src/Discord.Net.Commands/CommandServiceConfig.cs | 2 +- src/Discord.Net.Commands/Info/CommandInfo.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 02f1da5c3b..99bdd3d40c 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -32,7 +32,7 @@ public class CommandService internal readonly RunMode _defaultRunMode; internal readonly Logger _cmdLogger; internal readonly LogManager _logManager; - internal readonly Dictionary _quotationMarkAliasMap; + internal readonly IReadOnlyDictionary _quotationMarkAliasMap; public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 6de61f9393..143b6fbb1d 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -19,7 +19,7 @@ public class CommandServiceConfig /// Collection of aliases that can wrap strings for command parsing. /// represents the opening quotation mark and the value is the corresponding closing mark. - public Dictionary QuotationMarkAliasMap { get; set; } + public IReadOnlyDictionary QuotationMarkAliasMap { get; set; } = new Dictionary() { {'\"', '\"' }, diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 4dee863b5b..a96e7decc8 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -20,7 +20,7 @@ public class CommandInfo private readonly CommandService _commandService; private readonly Func _action; - internal readonly Dictionary _quotationAliases; + internal readonly IReadOnlyDictionary _quotationAliases; public ModuleInfo Module { get; } public string Name { get; } From 46e9cf64f3d3635de872720c308e922b13ab8114 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Mon, 29 Jan 2018 02:29:31 -0800 Subject: [PATCH 08/20] revert type of CommandServiceConfig QuotationMarkAliasMap to Dictionary --- src/Discord.Net.Commands/CommandServiceConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 143b6fbb1d..6de61f9393 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -19,7 +19,7 @@ public class CommandServiceConfig /// Collection of aliases that can wrap strings for command parsing. /// represents the opening quotation mark and the value is the corresponding closing mark. - public IReadOnlyDictionary QuotationMarkAliasMap { get; set; } + public Dictionary QuotationMarkAliasMap { get; set; } = new Dictionary() { {'\"', '\"' }, From 9c58d0ee01808fd9c665ba0083a5f92ab64c8ad0 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Mon, 29 Jan 2018 15:48:22 -0800 Subject: [PATCH 09/20] minor formatting changes to CommandParser --- src/Discord.Net.Commands/CommandParser.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 2e055802f4..924e80e41b 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -42,8 +42,7 @@ char GetMatch(IReadOnlyDictionary dict, char ch) // get the corresponding value for the key, if it exists if (dict != null) { - char value; - if (dict.TryGetValue(c, out value)) + if (dict.TryGetValue(c, out var value)) { return value; } @@ -101,7 +100,7 @@ char GetMatch(IReadOnlyDictionary dict, char ch) continue; } - if(IsOpenQuote(command._quotationAliases, c)) + if (IsOpenQuote(command._quotationAliases, c)) { curPart = ParserPart.QuotedParameter; matchQuote = GetMatch(command._quotationAliases, c); @@ -125,7 +124,7 @@ char GetMatch(IReadOnlyDictionary dict, char ch) } else if (curPart == ParserPart.QuotedParameter) { - if(c == matchQuote) + if (c == matchQuote) { argString = argBuilder.ToString(); //Remove quotes lastArgEndPos = curPos + 1; From 23ba23b133bbc687036c9bce95f54982e724f271 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Mon, 29 Jan 2018 15:50:22 -0800 Subject: [PATCH 10/20] remove unnecessary whitespace --- src/Discord.Net.Commands/CommandParser.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 924e80e41b..81bbe1783c 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -13,8 +13,7 @@ private enum ParserPart None, Parameter, QuotedParameter - } - + } public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos) { ParameterInfo curParam = null; From 1a8aa96f9b5f9a1dbb54670b5d14508253657d9b Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Mon, 29 Jan 2018 15:55:47 -0800 Subject: [PATCH 11/20] Move aliases outside of CommandInfo class --- src/Discord.Net.Commands/CommandParser.cs | 6 +++--- src/Discord.Net.Commands/Info/CommandInfo.cs | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 81bbe1783c..3b9783674b 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -14,7 +14,7 @@ private enum ParserPart Parameter, QuotedParameter } - public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos) + public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos, IReadOnlyDictionary aliasMap) { ParameterInfo curParam = null; StringBuilder argBuilder = new StringBuilder(input.Length); @@ -99,10 +99,10 @@ char GetMatch(IReadOnlyDictionary dict, char ch) continue; } - if (IsOpenQuote(command._quotationAliases, c)) + if (IsOpenQuote(aliasMap, c)) { curPart = ParserPart.QuotedParameter; - matchQuote = GetMatch(command._quotationAliases, c); + matchQuote = GetMatch(aliasMap, c); continue; } curPart = ParserPart.Parameter; diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index a96e7decc8..3f69b8ae31 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -20,7 +20,6 @@ public class CommandInfo private readonly CommandService _commandService; private readonly Func _action; - internal readonly IReadOnlyDictionary _quotationAliases; public ModuleInfo Module { get; } public string Name { get; } @@ -66,7 +65,6 @@ internal CommandInfo(CommandBuilder builder, ModuleInfo module, CommandService s HasVarArgs = builder.Parameters.Count > 0 ? builder.Parameters[builder.Parameters.Count - 1].IsMultiple : false; _action = builder.Callback; - _quotationAliases = service._quotationMarkAliasMap; _commandService = service; } @@ -121,7 +119,7 @@ public async Task ParseAsync(ICommandContext context, int startInde return ParseResult.FromError(preconditionResult); string input = searchResult.Text.Substring(startIndex); - return await CommandParser.ParseArgsAsync(this, context, _commandService._ignoreExtraArgs, services, input, 0).ConfigureAwait(false); + return await CommandParser.ParseArgsAsync(this, context, _commandService._ignoreExtraArgs, services, input, 0, _commandService._quotationMarkAliasMap).ConfigureAwait(false); } public Task ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services) From f0fe5d603df8ed68c1f428e32a7d048105f5df51 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Mon, 29 Jan 2018 15:57:47 -0800 Subject: [PATCH 12/20] copy IReadOnlyDictionary to ImmutableDictionary --- src/Discord.Net.Commands/CommandService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 99bdd3d40c..7faccb016b 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -46,7 +46,7 @@ public CommandService(CommandServiceConfig config) _ignoreExtraArgs = config.IgnoreExtraArgs; _separatorChar = config.SeparatorChar; _defaultRunMode = config.DefaultRunMode; - _quotationMarkAliasMap = config.QuotationMarkAliasMap; + _quotationMarkAliasMap = config.QuotationMarkAliasMap.ToImmutableDictionary(); if (_defaultRunMode == RunMode.Default) throw new InvalidOperationException("The default run mode cannot be set to Default."); From 8187f35975031971b17818aae516fdfaf5d2eb36 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Mon, 29 Jan 2018 15:58:08 -0800 Subject: [PATCH 13/20] minor syntax changes in CommandServiceConfig --- src/Discord.Net.Commands/CommandServiceConfig.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 6de61f9393..85a11b7693 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -20,8 +20,7 @@ public class CommandServiceConfig /// Collection of aliases that can wrap strings for command parsing. /// represents the opening quotation mark and the value is the corresponding closing mark. public Dictionary QuotationMarkAliasMap { get; set; } - = new Dictionary() - { + = new Dictionary { {'\"', '\"' }, {'«', '»' }, {'‘', '’' }, From befb65efdeb062fc96b7f5183fd79fedecea353f Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Tue, 30 Jan 2018 11:26:21 -0800 Subject: [PATCH 14/20] add newline before namespace for consistency --- src/Discord.Net.Commands/CommandServiceConfig.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 85a11b7693..5b13842e64 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; + namespace Discord.Commands { public class CommandServiceConfig From 2cb6750e25bcd70720c07fcc44feb72caccce122 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Thu, 1 Feb 2018 07:50:41 -0800 Subject: [PATCH 15/20] newline formatting tweak --- src/Discord.Net.Commands/CommandParser.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 3b9783674b..64d975bf54 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -13,7 +13,8 @@ private enum ParserPart None, Parameter, QuotedParameter - } + } + public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos, IReadOnlyDictionary aliasMap) { ParameterInfo curParam = null; From 7fbd0ab08b1f10066971392e4235798f4e5fa248 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sat, 3 Feb 2018 00:48:19 -0800 Subject: [PATCH 16/20] simplification of GetMatch method for CommandParser --- src/Discord.Net.Commands/CommandParser.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 64d975bf54..9b6c1cc030 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -40,13 +40,8 @@ bool IsOpenQuote(IReadOnlyDictionary dict, char ch) char GetMatch(IReadOnlyDictionary dict, char ch) { // get the corresponding value for the key, if it exists - if (dict != null) - { - if (dict.TryGetValue(c, out var value)) - { - return value; - } - } + if (dict != null && dict.TryGetValue(c, out var value)) + return value; // or get the default pair of the default double quote return '\"'; } From ac394c21326014b7b3cdcef07038556e52e92603 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sat, 3 Feb 2018 01:03:34 -0800 Subject: [PATCH 17/20] add more quote unicode punctuation pairs --- .../CommandServiceConfig.cs | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 5b13842e64..91656cf721 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -38,7 +38,56 @@ public class CommandServiceConfig {'﹃', '﹄' }, {'"', '"' }, {''', ''' }, - {'「', '」' } + {'「', '」' }, + {'(', ')' }, + {'༺', '༻' }, + {'༼', '༽' }, + {'᚛', '᚜' }, + {'⁅', '⁆' }, + {'⌈', '⌉' }, + {'⌊', '⌋' }, + {'❨', '❩' }, + {'❪', '❫' }, + {'❬', '❭' }, + {'❮', '❯' }, + {'❰', '❱' }, + {'❲', '❳' }, + {'❴', '❵' }, + {'⟅', '⟆' }, + {'⟦', '⟧' }, + {'⟨', '⟩' }, + {'⟪', '⟫' }, + {'⟬', '⟭' }, + {'⟮', '⟯' }, + {'⦃', '⦄' }, + {'⦅', '⦆' }, + {'⦇', '⦈' }, + {'⦉', '⦊' }, + {'⦋', '⦌' }, + {'⦍', '⦎' }, + {'⦏', '⦐' }, + {'⦑', '⦒' }, + {'⦓', '⦔' }, + {'⦕', '⦖' }, + {'⦗', '⦘' }, + {'⧘', '⧙' }, + {'⧚', '⧛' }, + {'⧼', '⧽' }, + {'⸂', '⸃' }, + {'⸄', '⸅' }, + {'⸉', '⸊' }, + {'⸌', '⸍' }, + {'⸜', '⸝' }, + {'⸠', '⸡' }, + {'⸢', '⸣' }, + {'⸤', '⸥' }, + {'⸦', '⸧' }, + {'⸨', '⸩' }, + {'【', '】'}, + {'〔', '〕' }, + {'〖', '〗' }, + {'〘', '〙' }, + {'〚', '〛' } }; /// Determines whether extra parameters should be ignored. From 12d5a70087d7009681287d4b906fd6981b1a7c60 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sat, 3 Feb 2018 01:16:46 -0800 Subject: [PATCH 18/20] add check for null value when building ImmutableDictionary --- src/Discord.Net.Commands/CommandService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 7faccb016b..9f06639006 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -46,7 +46,7 @@ public CommandService(CommandServiceConfig config) _ignoreExtraArgs = config.IgnoreExtraArgs; _separatorChar = config.SeparatorChar; _defaultRunMode = config.DefaultRunMode; - _quotationMarkAliasMap = config.QuotationMarkAliasMap.ToImmutableDictionary(); + _quotationMarkAliasMap = config.QuotationMarkAliasMap?.ToImmutableDictionary(); if (_defaultRunMode == RunMode.Default) throw new InvalidOperationException("The default run mode cannot be set to Default."); From fd3533f7bb02aeed8d7ff7ad92febecf88b24c92 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sun, 4 Feb 2018 17:23:59 -0800 Subject: [PATCH 19/20] Move default alias map into a separate source file --- .../CommandServiceConfig.cs | 72 +------------- .../Utilities/QuotationAliasUtils.cs | 95 +++++++++++++++++++ 2 files changed, 97 insertions(+), 70 deletions(-) create mode 100644 src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 91656cf721..9ab89c1bb2 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Discord.Commands { @@ -20,75 +20,7 @@ public class CommandServiceConfig /// Collection of aliases that can wrap strings for command parsing. /// represents the opening quotation mark and the value is the corresponding closing mark. - public Dictionary QuotationMarkAliasMap { get; set; } - = new Dictionary { - {'\"', '\"' }, - {'«', '»' }, - {'‘', '’' }, - {'“', '”' }, - {'„', '‟' }, - {'‹', '›' }, - {'‚', '‛' }, - {'《', '》' }, - {'〈', '〉' }, - {'「', '」' }, - {'『', '』' }, - {'〝', '〞' }, - {'﹁', '﹂' }, - {'﹃', '﹄' }, - {'"', '"' }, - {''', ''' }, - {'「', '」' }, - {'(', ')' }, - {'༺', '༻' }, - {'༼', '༽' }, - {'᚛', '᚜' }, - {'⁅', '⁆' }, - {'⌈', '⌉' }, - {'⌊', '⌋' }, - {'❨', '❩' }, - {'❪', '❫' }, - {'❬', '❭' }, - {'❮', '❯' }, - {'❰', '❱' }, - {'❲', '❳' }, - {'❴', '❵' }, - {'⟅', '⟆' }, - {'⟦', '⟧' }, - {'⟨', '⟩' }, - {'⟪', '⟫' }, - {'⟬', '⟭' }, - {'⟮', '⟯' }, - {'⦃', '⦄' }, - {'⦅', '⦆' }, - {'⦇', '⦈' }, - {'⦉', '⦊' }, - {'⦋', '⦌' }, - {'⦍', '⦎' }, - {'⦏', '⦐' }, - {'⦑', '⦒' }, - {'⦓', '⦔' }, - {'⦕', '⦖' }, - {'⦗', '⦘' }, - {'⧘', '⧙' }, - {'⧚', '⧛' }, - {'⧼', '⧽' }, - {'⸂', '⸃' }, - {'⸄', '⸅' }, - {'⸉', '⸊' }, - {'⸌', '⸍' }, - {'⸜', '⸝' }, - {'⸠', '⸡' }, - {'⸢', '⸣' }, - {'⸤', '⸥' }, - {'⸦', '⸧' }, - {'⸨', '⸩' }, - {'【', '】'}, - {'〔', '〕' }, - {'〖', '〗' }, - {'〘', '〙' }, - {'〚', '〛' } - }; + public Dictionary QuotationMarkAliasMap { get; set; } = QuotationAliasUtils.GetDefaultAliasMap; /// Determines whether extra parameters should be ignored. public bool IgnoreExtraArgs { get; set; } = false; diff --git a/src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs b/src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs new file mode 100644 index 0000000000..15a08b9b3b --- /dev/null +++ b/src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Globalization; + +namespace Discord.Commands +{ + /// + /// Utility methods for generating matching pairs of unicode quotation marks for CommandServiceConfig + /// + internal static class QuotationAliasUtils + { + /// + /// Generates an IEnumerable of characters representing open-close pairs of + /// quotation punctuation. + /// + internal static Dictionary GetDefaultAliasMap + { + get + { + // Output of a gist provided by https://gist.github.com/ufcpp + // https://gist.github.com/ufcpp/5b2cf9a9bf7d0b8743714a0b88f7edc5 + // This was not used for the implementation because of incompatibility with netstandard1.1 + return new Dictionary { + {'\"', '\"' }, + {'«', '»' }, + {'‘', '’' }, + {'“', '”' }, + {'„', '‟' }, + {'‹', '›' }, + {'‚', '‛' }, + {'《', '》' }, + {'〈', '〉' }, + {'「', '」' }, + {'『', '』' }, + {'〝', '〞' }, + {'﹁', '﹂' }, + {'﹃', '﹄' }, + {'"', '"' }, + {''', ''' }, + {'「', '」' }, + {'(', ')' }, + {'༺', '༻' }, + {'༼', '༽' }, + {'᚛', '᚜' }, + {'⁅', '⁆' }, + {'⌈', '⌉' }, + {'⌊', '⌋' }, + {'❨', '❩' }, + {'❪', '❫' }, + {'❬', '❭' }, + {'❮', '❯' }, + {'❰', '❱' }, + {'❲', '❳' }, + {'❴', '❵' }, + {'⟅', '⟆' }, + {'⟦', '⟧' }, + {'⟨', '⟩' }, + {'⟪', '⟫' }, + {'⟬', '⟭' }, + {'⟮', '⟯' }, + {'⦃', '⦄' }, + {'⦅', '⦆' }, + {'⦇', '⦈' }, + {'⦉', '⦊' }, + {'⦋', '⦌' }, + {'⦍', '⦎' }, + {'⦏', '⦐' }, + {'⦑', '⦒' }, + {'⦓', '⦔' }, + {'⦕', '⦖' }, + {'⦗', '⦘' }, + {'⧘', '⧙' }, + {'⧚', '⧛' }, + {'⧼', '⧽' }, + {'⸂', '⸃' }, + {'⸄', '⸅' }, + {'⸉', '⸊' }, + {'⸌', '⸍' }, + {'⸜', '⸝' }, + {'⸠', '⸡' }, + {'⸢', '⸣' }, + {'⸤', '⸥' }, + {'⸦', '⸧' }, + {'⸨', '⸩' }, + {'【', '】'}, + {'〔', '〕' }, + {'〖', '〗' }, + {'〘', '〙' }, + {'〚', '〛' } + }; + } + } + } +} From 820f9e36483d63df64ae651f862d0226c4c8e52a Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sat, 12 May 2018 13:48:02 -0700 Subject: [PATCH 20/20] Ensure that the collection passed into command service is not null --- src/Discord.Net.Commands/CommandParser.cs | 9 +++++---- src/Discord.Net.Commands/CommandService.cs | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 92e1a95c87..9ce4e1469e 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Text; @@ -29,8 +29,8 @@ public static async Task ParseArgsAsync(CommandInfo command, IComma // local helper functions bool IsOpenQuote(IReadOnlyDictionary dict, char ch) { - // return if the key is contained in the dictionary if it exists - if (dict != null) + // return if the key is contained in the dictionary if it is populated + if (dict.Count != 0) return dict.ContainsKey(ch); // or otherwise if it is the default double quote return c == '\"'; @@ -39,7 +39,8 @@ bool IsOpenQuote(IReadOnlyDictionary dict, char ch) char GetMatch(IReadOnlyDictionary dict, char ch) { // get the corresponding value for the key, if it exists - if (dict != null && dict.TryGetValue(c, out var value)) + // and if the dictionary is populated + if (dict.Count != 0 && dict.TryGetValue(c, out var value)) return value; // or get the default pair of the default double quote return '\"'; diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 5287cb0ad2..82a5d66ad1 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; @@ -46,7 +46,7 @@ public CommandService(CommandServiceConfig config) _ignoreExtraArgs = config.IgnoreExtraArgs; _separatorChar = config.SeparatorChar; _defaultRunMode = config.DefaultRunMode; - _quotationMarkAliasMap = config.QuotationMarkAliasMap?.ToImmutableDictionary(); + _quotationMarkAliasMap = (config.QuotationMarkAliasMap ?? new Dictionary()).ToImmutableDictionary(); if (_defaultRunMode == RunMode.Default) throw new InvalidOperationException("The default run mode cannot be set to Default.");