* Add ability to support different types of quotation marks * Added normal quotation mark to list of aliases, removed single quote mark * clean up leftover changes from testing * change quotation mark parsing to use a map of matching pairs * remove commented out code * Fix conventions of the command parser utility functions * change storage type of alias dictionary to be IReadOnlyDictionary * revert type of CommandServiceConfig QuotationMarkAliasMap to Dictionary * minor formatting changes to CommandParser * remove unnecessary whitespace * Move aliases outside of CommandInfo class * copy IReadOnlyDictionary to ImmutableDictionary * minor syntax changes in CommandServiceConfig * add newline before namespace for consistency * newline formatting tweak * simplification of GetMatch method for CommandParser * add more quote unicode punctuation pairs * add check for null value when building ImmutableDictionary * Move default alias map into a separate source file * Ensure that the collection passed into command service is not null
This commit is contained in:
committed by
Christopher F
parent
b52af7ae7c
commit
cee71ef35a
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -13,8 +14,7 @@ namespace Discord.Commands
|
|||||||
Parameter,
|
Parameter,
|
||||||
QuotedParameter
|
QuotedParameter
|
||||||
}
|
}
|
||||||
|
public static async Task<ParseResult> ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos, IReadOnlyDictionary<char, char> aliasMap)
|
||||||
public static async Task<ParseResult> ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos)
|
|
||||||
{
|
{
|
||||||
ParameterInfo curParam = null;
|
ParameterInfo curParam = null;
|
||||||
StringBuilder argBuilder = new StringBuilder(input.Length);
|
StringBuilder argBuilder = new StringBuilder(input.Length);
|
||||||
@@ -24,7 +24,27 @@ namespace Discord.Commands
|
|||||||
var argList = ImmutableArray.CreateBuilder<TypeReaderResult>();
|
var argList = ImmutableArray.CreateBuilder<TypeReaderResult>();
|
||||||
var paramList = ImmutableArray.CreateBuilder<TypeReaderResult>();
|
var paramList = ImmutableArray.CreateBuilder<TypeReaderResult>();
|
||||||
bool isEscaping = false;
|
bool isEscaping = false;
|
||||||
char c;
|
char c, matchQuote = '\0';
|
||||||
|
|
||||||
|
// local helper functions
|
||||||
|
bool IsOpenQuote(IReadOnlyDictionary<char, char> dict, char ch)
|
||||||
|
{
|
||||||
|
// 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 == '\"';
|
||||||
|
}
|
||||||
|
|
||||||
|
char GetMatch(IReadOnlyDictionary<char, char> dict, char ch)
|
||||||
|
{
|
||||||
|
// get the corresponding value for the key, if it exists
|
||||||
|
// 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 '\"';
|
||||||
|
}
|
||||||
|
|
||||||
for (int curPos = startPos; curPos <= endPos; curPos++)
|
for (int curPos = startPos; curPos <= endPos; curPos++)
|
||||||
{
|
{
|
||||||
@@ -74,9 +94,11 @@ namespace Discord.Commands
|
|||||||
argBuilder.Append(c);
|
argBuilder.Append(c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c == '\"')
|
|
||||||
|
if (IsOpenQuote(aliasMap, c))
|
||||||
{
|
{
|
||||||
curPart = ParserPart.QuotedParameter;
|
curPart = ParserPart.QuotedParameter;
|
||||||
|
matchQuote = GetMatch(aliasMap, c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
curPart = ParserPart.Parameter;
|
curPart = ParserPart.Parameter;
|
||||||
@@ -97,7 +119,7 @@ namespace Discord.Commands
|
|||||||
}
|
}
|
||||||
else if (curPart == ParserPart.QuotedParameter)
|
else if (curPart == ParserPart.QuotedParameter)
|
||||||
{
|
{
|
||||||
if (c == '\"')
|
if (c == matchQuote)
|
||||||
{
|
{
|
||||||
argString = argBuilder.ToString(); //Remove quotes
|
argString = argBuilder.ToString(); //Remove quotes
|
||||||
lastArgEndPos = curPos + 1;
|
lastArgEndPos = curPos + 1;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
@@ -32,6 +32,7 @@ namespace Discord.Commands
|
|||||||
internal readonly RunMode _defaultRunMode;
|
internal readonly RunMode _defaultRunMode;
|
||||||
internal readonly Logger _cmdLogger;
|
internal readonly Logger _cmdLogger;
|
||||||
internal readonly LogManager _logManager;
|
internal readonly LogManager _logManager;
|
||||||
|
internal readonly IReadOnlyDictionary<char, char> _quotationMarkAliasMap;
|
||||||
|
|
||||||
public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x);
|
public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x);
|
||||||
public IEnumerable<CommandInfo> Commands => _moduleDefs.SelectMany(x => x.Commands);
|
public IEnumerable<CommandInfo> Commands => _moduleDefs.SelectMany(x => x.Commands);
|
||||||
@@ -45,6 +46,7 @@ namespace Discord.Commands
|
|||||||
_ignoreExtraArgs = config.IgnoreExtraArgs;
|
_ignoreExtraArgs = config.IgnoreExtraArgs;
|
||||||
_separatorChar = config.SeparatorChar;
|
_separatorChar = config.SeparatorChar;
|
||||||
_defaultRunMode = config.DefaultRunMode;
|
_defaultRunMode = config.DefaultRunMode;
|
||||||
|
_quotationMarkAliasMap = (config.QuotationMarkAliasMap ?? new Dictionary<char, char>()).ToImmutableDictionary();
|
||||||
if (_defaultRunMode == RunMode.Default)
|
if (_defaultRunMode == RunMode.Default)
|
||||||
throw new InvalidOperationException("The default run mode cannot be set to Default.");
|
throw new InvalidOperationException("The default run mode cannot be set to Default.");
|
||||||
|
|
||||||
@@ -337,7 +339,6 @@ namespace Discord.Commands
|
|||||||
public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
||||||
{
|
{
|
||||||
services = services ?? EmptyServiceProvider.Instance;
|
services = services ?? EmptyServiceProvider.Instance;
|
||||||
|
|
||||||
var searchResult = Search(context, input);
|
var searchResult = Search(context, input);
|
||||||
if (!searchResult.IsSuccess)
|
if (!searchResult.IsSuccess)
|
||||||
return searchResult;
|
return searchResult;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
@@ -18,6 +19,10 @@ namespace Discord.Commands
|
|||||||
/// <summary> Determines whether RunMode.Sync commands should push exceptions up to the caller. </summary>
|
/// <summary> Determines whether RunMode.Sync commands should push exceptions up to the caller. </summary>
|
||||||
public bool ThrowOnError { get; set; } = true;
|
public bool ThrowOnError { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary> Collection of aliases that can wrap strings for command parsing.
|
||||||
|
/// represents the opening quotation mark and the value is the corresponding closing mark.</summary>
|
||||||
|
public Dictionary<char, char> QuotationMarkAliasMap { get; set; } = QuotationAliasUtils.GetDefaultAliasMap;
|
||||||
|
|
||||||
/// <summary> Determines whether extra parameters should be ignored. </summary>
|
/// <summary> Determines whether extra parameters should be ignored. </summary>
|
||||||
public bool IgnoreExtraArgs { get; set; } = false;
|
public bool IgnoreExtraArgs { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Discord.Commands.Builders;
|
using Discord.Commands.Builders;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
@@ -121,7 +121,8 @@ namespace Discord.Commands
|
|||||||
return ParseResult.FromError(preconditionResult);
|
return ParseResult.FromError(preconditionResult);
|
||||||
|
|
||||||
string input = searchResult.Text.Substring(startIndex);
|
string input = searchResult.Text.Substring(startIndex);
|
||||||
return await CommandParser.ParseArgsAsync(this, context, services, input, 0).ConfigureAwait(false);
|
|
||||||
|
return await CommandParser.ParseArgsAsync(this, context, _commandService._ignoreExtraArgs, services, input, 0, _commandService._quotationMarkAliasMap).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services)
|
public Task<IResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services)
|
||||||
|
|||||||
95
src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs
Normal file
95
src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Discord.Commands
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Utility methods for generating matching pairs of unicode quotation marks for CommandServiceConfig
|
||||||
|
/// </summary>
|
||||||
|
internal static class QuotationAliasUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Generates an IEnumerable of characters representing open-close pairs of
|
||||||
|
/// quotation punctuation.
|
||||||
|
/// </summary>
|
||||||
|
internal static Dictionary<char, char> 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<char, char> {
|
||||||
|
{'\"', '\"' },
|
||||||
|
{'«', '»' },
|
||||||
|
{'‘', '’' },
|
||||||
|
{'“', '”' },
|
||||||
|
{'„', '‟' },
|
||||||
|
{'‹', '›' },
|
||||||
|
{'‚', '‛' },
|
||||||
|
{'《', '》' },
|
||||||
|
{'〈', '〉' },
|
||||||
|
{'「', '」' },
|
||||||
|
{'『', '』' },
|
||||||
|
{'〝', '〞' },
|
||||||
|
{'﹁', '﹂' },
|
||||||
|
{'﹃', '﹄' },
|
||||||
|
{'"', '"' },
|
||||||
|
{''', ''' },
|
||||||
|
{'「', '」' },
|
||||||
|
{'(', ')' },
|
||||||
|
{'༺', '༻' },
|
||||||
|
{'༼', '༽' },
|
||||||
|
{'᚛', '᚜' },
|
||||||
|
{'⁅', '⁆' },
|
||||||
|
{'⌈', '⌉' },
|
||||||
|
{'⌊', '⌋' },
|
||||||
|
{'❨', '❩' },
|
||||||
|
{'❪', '❫' },
|
||||||
|
{'❬', '❭' },
|
||||||
|
{'❮', '❯' },
|
||||||
|
{'❰', '❱' },
|
||||||
|
{'❲', '❳' },
|
||||||
|
{'❴', '❵' },
|
||||||
|
{'⟅', '⟆' },
|
||||||
|
{'⟦', '⟧' },
|
||||||
|
{'⟨', '⟩' },
|
||||||
|
{'⟪', '⟫' },
|
||||||
|
{'⟬', '⟭' },
|
||||||
|
{'⟮', '⟯' },
|
||||||
|
{'⦃', '⦄' },
|
||||||
|
{'⦅', '⦆' },
|
||||||
|
{'⦇', '⦈' },
|
||||||
|
{'⦉', '⦊' },
|
||||||
|
{'⦋', '⦌' },
|
||||||
|
{'⦍', '⦎' },
|
||||||
|
{'⦏', '⦐' },
|
||||||
|
{'⦑', '⦒' },
|
||||||
|
{'⦓', '⦔' },
|
||||||
|
{'⦕', '⦖' },
|
||||||
|
{'⦗', '⦘' },
|
||||||
|
{'⧘', '⧙' },
|
||||||
|
{'⧚', '⧛' },
|
||||||
|
{'⧼', '⧽' },
|
||||||
|
{'⸂', '⸃' },
|
||||||
|
{'⸄', '⸅' },
|
||||||
|
{'⸉', '⸊' },
|
||||||
|
{'⸌', '⸍' },
|
||||||
|
{'⸜', '⸝' },
|
||||||
|
{'⸠', '⸡' },
|
||||||
|
{'⸢', '⸣' },
|
||||||
|
{'⸤', '⸥' },
|
||||||
|
{'⸦', '⸧' },
|
||||||
|
{'⸨', '⸩' },
|
||||||
|
{'【', '】'},
|
||||||
|
{'〔', '〕' },
|
||||||
|
{'〖', '〗' },
|
||||||
|
{'〘', '〙' },
|
||||||
|
{'〚', '〛' }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user