* 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.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -13,8 +14,7 @@ namespace Discord.Commands
|
||||
Parameter,
|
||||
QuotedParameter
|
||||
}
|
||||
|
||||
public static async Task<ParseResult> ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos)
|
||||
public static async Task<ParseResult> ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos, IReadOnlyDictionary<char, char> aliasMap)
|
||||
{
|
||||
ParameterInfo curParam = null;
|
||||
StringBuilder argBuilder = new StringBuilder(input.Length);
|
||||
@@ -24,7 +24,27 @@ namespace Discord.Commands
|
||||
var argList = ImmutableArray.CreateBuilder<TypeReaderResult>();
|
||||
var paramList = ImmutableArray.CreateBuilder<TypeReaderResult>();
|
||||
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++)
|
||||
{
|
||||
@@ -74,9 +94,11 @@ namespace Discord.Commands
|
||||
argBuilder.Append(c);
|
||||
continue;
|
||||
}
|
||||
if (c == '\"')
|
||||
|
||||
if (IsOpenQuote(aliasMap, c))
|
||||
{
|
||||
curPart = ParserPart.QuotedParameter;
|
||||
matchQuote = GetMatch(aliasMap, c);
|
||||
continue;
|
||||
}
|
||||
curPart = ParserPart.Parameter;
|
||||
@@ -97,7 +119,7 @@ namespace Discord.Commands
|
||||
}
|
||||
else if (curPart == ParserPart.QuotedParameter)
|
||||
{
|
||||
if (c == '\"')
|
||||
if (c == matchQuote)
|
||||
{
|
||||
argString = argBuilder.ToString(); //Remove quotes
|
||||
lastArgEndPos = curPos + 1;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -32,6 +32,7 @@ namespace Discord.Commands
|
||||
internal readonly RunMode _defaultRunMode;
|
||||
internal readonly Logger _cmdLogger;
|
||||
internal readonly LogManager _logManager;
|
||||
internal readonly IReadOnlyDictionary<char, char> _quotationMarkAliasMap;
|
||||
|
||||
public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x);
|
||||
public IEnumerable<CommandInfo> Commands => _moduleDefs.SelectMany(x => x.Commands);
|
||||
@@ -45,6 +46,7 @@ namespace Discord.Commands
|
||||
_ignoreExtraArgs = config.IgnoreExtraArgs;
|
||||
_separatorChar = config.SeparatorChar;
|
||||
_defaultRunMode = config.DefaultRunMode;
|
||||
_quotationMarkAliasMap = (config.QuotationMarkAliasMap ?? new Dictionary<char, char>()).ToImmutableDictionary();
|
||||
if (_defaultRunMode == RunMode.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)
|
||||
{
|
||||
services = services ?? EmptyServiceProvider.Instance;
|
||||
|
||||
var searchResult = Search(context, input);
|
||||
if (!searchResult.IsSuccess)
|
||||
return searchResult;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
@@ -18,6 +19,10 @@ namespace Discord.Commands
|
||||
/// <summary> Determines whether RunMode.Sync commands should push exceptions up to the caller. </summary>
|
||||
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>
|
||||
public bool IgnoreExtraArgs { get; set; } = false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Discord.Commands.Builders;
|
||||
using Discord.Commands.Builders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -121,7 +121,8 @@ namespace Discord.Commands
|
||||
return ParseResult.FromError(preconditionResult);
|
||||
|
||||
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)
|
||||
|
||||
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