@@ -3,7 +3,7 @@ | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
[AttributeUsage(AttributeTargets.Parameter)] | [AttributeUsage(AttributeTargets.Parameter)] | ||||
public class UnparsedAttribute : Attribute | |||||
public class RemainderAttribute : Attribute | |||||
{ | { | ||||
} | } | ||||
} | } |
@@ -87,16 +87,16 @@ namespace Discord.Commands | |||||
if (reader == null) | if (reader == null) | ||||
throw new InvalidOperationException($"{type.FullName} is not supported as a command parameter, are you missing a TypeReader?"); | throw new InvalidOperationException($"{type.FullName} is not supported as a command parameter, are you missing a TypeReader?"); | ||||
bool isUnparsed = parameter.GetCustomAttribute<UnparsedAttribute>() != null; | |||||
if (isUnparsed && i != parameters.Length - 1) | |||||
throw new InvalidOperationException("Unparsed parameters must be the last parameter in a command."); | |||||
bool isRemainder = parameter.GetCustomAttribute<RemainderAttribute>() != null; | |||||
if (isRemainder && i != parameters.Length - 1) | |||||
throw new InvalidOperationException("Remainder parameters must be the last parameter in a command."); | |||||
string name = parameter.Name; | string name = parameter.Name; | ||||
string description = typeInfo.GetCustomAttribute<DescriptionAttribute>()?.Text; | string description = typeInfo.GetCustomAttribute<DescriptionAttribute>()?.Text; | ||||
bool isOptional = parameter.IsOptional; | bool isOptional = parameter.IsOptional; | ||||
object defaultValue = parameter.HasDefaultValue ? parameter.DefaultValue : null; | object defaultValue = parameter.HasDefaultValue ? parameter.DefaultValue : null; | ||||
paramBuilder.Add(new CommandParameter(name, description, reader, isOptional, isUnparsed, defaultValue)); | |||||
paramBuilder.Add(new CommandParameter(name, description, reader, isOptional, isRemainder, defaultValue)); | |||||
} | } | ||||
return paramBuilder.ToImmutable(); | return paramBuilder.ToImmutable(); | ||||
} | } | ||||
@@ -12,15 +12,15 @@ namespace Discord.Commands | |||||
public string Name { get; } | public string Name { get; } | ||||
public string Description { get; } | public string Description { get; } | ||||
public bool IsOptional { get; } | public bool IsOptional { get; } | ||||
public bool IsUnparsed { get; } | |||||
public bool IsRemainder { get; } | |||||
internal object DefaultValue { get; } | internal object DefaultValue { get; } | ||||
public CommandParameter(string name, string description, TypeReader reader, bool isOptional, bool isUnparsed, object defaultValue) | |||||
public CommandParameter(string name, string description, TypeReader reader, bool isOptional, bool isRemainder, object defaultValue) | |||||
{ | { | ||||
_reader = reader; | _reader = reader; | ||||
Name = name; | Name = name; | ||||
IsOptional = isOptional; | IsOptional = isOptional; | ||||
IsUnparsed = isUnparsed; | |||||
IsRemainder = isRemainder; | |||||
DefaultValue = defaultValue; | DefaultValue = defaultValue; | ||||
} | } | ||||
@@ -30,6 +30,6 @@ namespace Discord.Commands | |||||
} | } | ||||
public override string ToString() => Name; | public override string ToString() => Name; | ||||
private string DebuggerDisplay => $"{Name}{(IsOptional ? " (Optional)" : "")}{(IsUnparsed ? " (Unparsed)" : "")}"; | |||||
private string DebuggerDisplay => $"{Name}{(IsOptional ? " (Optional)" : "")}{(IsRemainder ? " (Remainder)" : "")}"; | |||||
} | } | ||||
} | } |
@@ -42,14 +42,14 @@ namespace Discord.Commands | |||||
} | } | ||||
} | } | ||||
//Are we escaping the next character? | //Are we escaping the next character? | ||||
if (c == '\\' && (curParam == null || !curParam.IsUnparsed)) | |||||
if (c == '\\' && (curParam == null || !curParam.IsRemainder)) | |||||
{ | { | ||||
isEscaping = true; | isEscaping = true; | ||||
continue; | continue; | ||||
} | } | ||||
//If we're processing an unparsed parameter, ignore all other logic | |||||
if (curParam != null && curParam.IsUnparsed && curPos != endPos) | |||||
//If we're processing an remainder parameter, ignore all other logic | |||||
if (curParam != null && curParam.IsRemainder && curPos != endPos) | |||||
{ | { | ||||
argBuilder.Append(c); | argBuilder.Append(c); | ||||
continue; | continue; | ||||
@@ -65,7 +65,7 @@ namespace Discord.Commands | |||||
else | else | ||||
{ | { | ||||
curParam = command.Parameters.Count > argList.Count ? command.Parameters[argList.Count] : null; | curParam = command.Parameters.Count > argList.Count ? command.Parameters[argList.Count] : null; | ||||
if (curParam != null && curParam.IsUnparsed) | |||||
if (curParam != null && curParam.IsRemainder) | |||||
{ | { | ||||
argBuilder.Append(c); | argBuilder.Append(c); | ||||
continue; | continue; | ||||
@@ -118,8 +118,13 @@ namespace Discord.Commands | |||||
} | } | ||||
} | } | ||||
if (curParam != null && curParam.IsUnparsed) | |||||
argList.Add(argBuilder.ToString()); | |||||
if (curParam != null && curParam.IsRemainder) | |||||
{ | |||||
var typeReaderResult = await curParam.Parse(context, argBuilder.ToString()).ConfigureAwait(false); | |||||
if (!typeReaderResult.IsSuccess) | |||||
return ParseResult.FromError(typeReaderResult); | |||||
argList.Add(typeReaderResult.Value); | |||||
} | |||||
if (isEscaping) | if (isEscaping) | ||||
return ParseResult.FromError(CommandError.ParseFailed, "Input text may not end on an incomplete escape."); | return ParseResult.FromError(CommandError.ParseFailed, "Input text may not end on an incomplete escape."); | ||||