#include "command_registry.h" #include namespace dingosdk::console { std::vector completion_tokens(std::string_view input, int cursor) { const int size = static_cast(input.size()); const int limit = std::clamp(cursor, 0, size); std::vector tokens; int position = 0; const auto separator = [](char c) { return c == ' ' || c == '\t'; }; while (position <= limit) { while (position < limit && separator(input[position])) ++position; CompletionToken token; token.begin = position; char quote = 0; bool escaped = false; // Scan the whole token, but decode only its prefix before the cursor. // This matches execution's escapes and single/double/concatenated quotes. for (; position < size; ++position) { const char c = input[position]; if (!escaped && !quote && separator(c)) break; const bool before_cursor = position < limit; if (escaped) { if (before_cursor) token.text += c; escaped = false; } else if (c == '\\') escaped = true; else if (quote) { if (c == quote) quote = 0; else if (before_cursor) token.text += c; } else if (c == '\'' || c == '"') { quote = c; token.quoted = true; } else if (before_cursor) token.text += c; } token.end = position; tokens.push_back(std::move(token)); if (position >= limit) break; ++position; } return tokens; } std::string lower(std::string_view text) { std::string result(text); for (auto &c : result) if (c >= 'A' && c <= 'Z') c = static_cast(c + ('a' - 'A')); return result; } bool equal(std::string_view a, std::string_view b) { return a.size() == b.size() && ascii_case_insensitive_starts_with(a, b); } const char *group_name(Group group) noexcept { constexpr std::array names{"Console", "Movement", "Gameplay", "World", "Graphics", "Progression", "Objects", "Engine"}; const auto i = static_cast(group); return i < names.size() ? names[i] : "Console"; } std::string value_text(const Value &value) { return std::visit( [](const auto &v) -> std::string { using T = std::decay_t; if constexpr (std::is_same_v) return v; else if constexpr (std::is_same_v) return v ? "1" : "0"; else { std::array text{}; const auto result = std::to_chars(text.data(), text.data() + text.size(), v); return result.ec == std::errc{} ? std::string(text.data(), result.ptr) : "?"; } }, value); } std::optional parse_value(Type type, std::string_view text) { if (type == Type::text) return std::string(text); if (type == Type::boolean) { if (text == "1" || equal(text, "true") || equal(text, "on")) return true; if (text == "0" || equal(text, "false") || equal(text, "off")) return false; return std::nullopt; } const auto parse = [&]() -> std::optional { T value{}; const auto result = std::from_chars(text.data(), text.data() + text.size(), value); if (result.ec != std::errc{} || result.ptr != text.data() + text.size()) return std::nullopt; if constexpr (std::is_floating_point_v) if (!std::isfinite(value)) return std::nullopt; return value; }; switch (type) { case Type::integer: return parse.operator()(); case Type::unsigned_integer: return parse.operator()(); case Type::number: return parse.operator()(); default: return std::nullopt; } } } // namespace dingosdk::console