| @@ -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."); | ||||