158 lines
3.5 KiB
C++
158 lines
3.5 KiB
C++
|
|
#include "SlashCommand.h"
|
||
|
|
#include "CoreMinimal.h"
|
||
|
|
#include "Containers/StringView.h"
|
||
|
|
#include "Misc/Char.h"
|
||
|
|
#include "Misc/DefaultValueHelper.h"
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////
|
||
|
|
//
|
||
|
|
// SlashCommand
|
||
|
|
//
|
||
|
|
// A command line plus a cursor, exposed to blueprint.
|
||
|
|
//
|
||
|
|
//////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
|
||
|
|
UlxSlashCommand* UlxSlashCommand::MakeSlashCommand(const FString& CommandLine)
|
||
|
|
{
|
||
|
|
UlxSlashCommand* Result = NewObject<UlxSlashCommand>();
|
||
|
|
Result->CommandLine = CommandLine;
|
||
|
|
return Result;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
bool UlxSlashCommand::IsSlashCommand(const FString& Command)
|
||
|
|
{
|
||
|
|
if (Command.Len() < 2 || Command[0] != TEXT('/'))
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Every character after the slash must be alphanumeric.
|
||
|
|
for (int32 i = 1; i < Command.Len(); i++)
|
||
|
|
{
|
||
|
|
if (!FChar::IsAlnum(Command[i]))
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
FStringView UlxSlashCommand::FetchToken()
|
||
|
|
{
|
||
|
|
const TCHAR* Data = *CommandLine;
|
||
|
|
int32 Len = CommandLine.Len();
|
||
|
|
|
||
|
|
// Skip leading whitespace.
|
||
|
|
while (Cursor < Len && FChar::IsWhitespace(Data[Cursor]))
|
||
|
|
{
|
||
|
|
Cursor++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Read characters up to the next whitespace.
|
||
|
|
int32 Start = Cursor;
|
||
|
|
while (Cursor < Len && !FChar::IsWhitespace(Data[Cursor]))
|
||
|
|
{
|
||
|
|
Cursor++;
|
||
|
|
}
|
||
|
|
|
||
|
|
return FStringView(Data + Start, Cursor - Start);
|
||
|
|
}
|
||
|
|
|
||
|
|
ElxSuccessOrError UlxSlashCommand::CheckCommand(const FString& Literal, const FString& Prototype)
|
||
|
|
{
|
||
|
|
KnownCommands.Add(Literal);
|
||
|
|
|
||
|
|
// Checking the command always starts a fresh parse.
|
||
|
|
Cursor = 0;
|
||
|
|
FStringView Token = FetchToken();
|
||
|
|
if (Token.Equals(Literal, ESearchCase::IgnoreCase))
|
||
|
|
{
|
||
|
|
MatchingPrototypes.Add(Prototype);
|
||
|
|
return ElxSuccessOrError::Success;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ElxSuccessOrError::Error;
|
||
|
|
}
|
||
|
|
|
||
|
|
ElxSuccessOrError UlxSlashCommand::ReadToken(FString& Result)
|
||
|
|
{
|
||
|
|
FStringView Token = FetchToken();
|
||
|
|
if (Token.IsEmpty())
|
||
|
|
{
|
||
|
|
Result.Empty();
|
||
|
|
return ElxSuccessOrError::Error;
|
||
|
|
}
|
||
|
|
|
||
|
|
Result = FString(Token);
|
||
|
|
return ElxSuccessOrError::Success;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
ElxSuccessOrError UlxSlashCommand::ReadInteger(int32& Result)
|
||
|
|
{
|
||
|
|
// ParseInt validates the whole token and converts it, so "12abc"
|
||
|
|
// is rejected rather than read as a partial number.
|
||
|
|
FStringView Token = FetchToken();
|
||
|
|
if (!FDefaultValueHelper::ParseInt(FString(Token), Result))
|
||
|
|
{
|
||
|
|
Result = 0;
|
||
|
|
return ElxSuccessOrError::Error;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ElxSuccessOrError::Success;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
ElxSuccessOrError UlxSlashCommand::ReadFloat(double& Result)
|
||
|
|
{
|
||
|
|
// ParseDouble validates the whole token and converts it, so
|
||
|
|
// "12abc" is rejected rather than read as a partial number.
|
||
|
|
FStringView Token = FetchToken();
|
||
|
|
if (!FDefaultValueHelper::ParseDouble(FString(Token), Result))
|
||
|
|
{
|
||
|
|
Result = 0.0;
|
||
|
|
return ElxSuccessOrError::Error;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ElxSuccessOrError::Success;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
ElxSuccessOrError UlxSlashCommand::ReadRest(FString& Result)
|
||
|
|
{
|
||
|
|
const TCHAR* Data = *CommandLine;
|
||
|
|
int32 Len = CommandLine.Len();
|
||
|
|
|
||
|
|
// Everything from the cursor to the end of the line, trimmed.
|
||
|
|
Result = FString(FStringView(Data + Cursor, Len - Cursor));
|
||
|
|
Result.TrimStartAndEndInline();
|
||
|
|
Cursor = Len;
|
||
|
|
return ElxSuccessOrError::Success;
|
||
|
|
}
|
||
|
|
|
||
|
|
FString UlxSlashCommand::GetErrorMessage() const
|
||
|
|
{
|
||
|
|
if (MatchingPrototypes.Num() == 0)
|
||
|
|
{
|
||
|
|
TArray<FString> Commands;
|
||
|
|
for (const FString& Command : KnownCommands)
|
||
|
|
{
|
||
|
|
Commands.Add(Command);
|
||
|
|
}
|
||
|
|
Commands.Sort();
|
||
|
|
return FString(TEXT("No such slash command. Valid slash commands: ")) + FString::Join(Commands, TEXT(", "));
|
||
|
|
}
|
||
|
|
|
||
|
|
FString Result = TEXT("Invalid parameters. Valid parameters:\n");
|
||
|
|
for (const FString& Prototype : MatchingPrototypes)
|
||
|
|
{
|
||
|
|
Result += Prototype;
|
||
|
|
Result += TEXT("\n");
|
||
|
|
}
|
||
|
|
return Result;
|
||
|
|
}
|