aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Server/Console/ServerCliParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Minecraft.Server/Console/ServerCliParser.cpp')
-rw-r--r--Minecraft.Server/Console/ServerCliParser.cpp116
1 files changed, 116 insertions, 0 deletions
diff --git a/Minecraft.Server/Console/ServerCliParser.cpp b/Minecraft.Server/Console/ServerCliParser.cpp
new file mode 100644
index 00000000..5888153f
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCliParser.cpp
@@ -0,0 +1,116 @@
+#include "stdafx.h"
+
+#include "ServerCliParser.h"
+
+namespace ServerRuntime
+{
+ static void TokenizeLine(const std::string &line, std::vector<std::string> *tokens, bool *trailingSpace)
+ {
+ std::string current;
+ bool inQuotes = false;
+ bool escaped = false;
+
+ tokens->clear();
+ *trailingSpace = false;
+
+ for (size_t i = 0; i < line.size(); ++i)
+ {
+ char ch = line[i];
+ if (escaped)
+ {
+ // Keep escaped character literally (e.g. \" or \ ).
+ current.push_back(ch);
+ escaped = false;
+ continue;
+ }
+
+ if (ch == '\\')
+ {
+ escaped = true;
+ continue;
+ }
+
+ if (ch == '"')
+ {
+ // Double quotes group spaces into one token.
+ inQuotes = !inQuotes;
+ continue;
+ }
+
+ if (!inQuotes && (ch == ' ' || ch == '\t'))
+ {
+ if (!current.empty())
+ {
+ tokens->push_back(current);
+ current.clear();
+ }
+ continue;
+ }
+
+ current.push_back(ch);
+ }
+
+ if (!current.empty())
+ {
+ tokens->push_back(current);
+ }
+
+ if (!line.empty())
+ {
+ char tail = line[line.size() - 1];
+ // Trailing space means completion targets the next token slot.
+ *trailingSpace = (!inQuotes && (tail == ' ' || tail == '\t'));
+ }
+ }
+
+ ServerCliParsedLine ServerCliParser::Parse(const std::string &line)
+ {
+ ServerCliParsedLine parsed;
+ parsed.raw = line;
+ TokenizeLine(line, &parsed.tokens, &parsed.trailingSpace);
+ return parsed;
+ }
+
+ ServerCliCompletionContext ServerCliParser::BuildCompletionContext(const std::string &line)
+ {
+ ServerCliCompletionContext context;
+ context.parsed = Parse(line);
+
+ if (context.parsed.tokens.empty())
+ {
+ context.currentTokenIndex = 0;
+ context.prefix.clear();
+ context.linePrefix.clear();
+ return context;
+ }
+
+ if (context.parsed.trailingSpace)
+ {
+ // Cursor is after a separator, so complete a new token.
+ context.currentTokenIndex = context.parsed.tokens.size();
+ context.prefix.clear();
+ }
+ else
+ {
+ // Cursor is inside current token, so complete by its prefix.
+ context.currentTokenIndex = context.parsed.tokens.size() - 1;
+ context.prefix = context.parsed.tokens.back();
+ }
+
+ for (size_t i = 0; i < context.currentTokenIndex; ++i)
+ {
+ // linePrefix is the immutable left side reused by completion output.
+ if (!context.linePrefix.empty())
+ {
+ context.linePrefix.push_back(' ');
+ }
+ context.linePrefix += context.parsed.tokens[i];
+ }
+ if (!context.linePrefix.empty())
+ {
+ context.linePrefix.push_back(' ');
+ }
+
+ return context;
+ }
+}