diff --git a/src/bot/base_commands.cpp b/src/bot/base_commands.cpp index 25b708e..d2d16c4 100644 --- a/src/bot/base_commands.cpp +++ b/src/bot/base_commands.cpp @@ -24,21 +24,22 @@ namespace jessibot { namespace io { using namespace jessilib::io; +using namespace std::literals; command quit_command{ [](command_context& context) { using namespace jessilib::io; - text quit_text{ "Closing jessibot", text::property::bold, color{ 0xFF0000 } }; // TODO: localize + text quit_text{ u8"Closing jessibot", text::property::bold, color{ 0xFF0000 } }; // TODO: localize context.publicReply(formatted_message{ "{}", quit_text }); notify_shutdown(); -}, "quit" }; +}, u8"quit" }; // ISSUE: help command has no way to know what commands exist for the given context command help_command{ [](command_context& context) { auto details = context.details(); - auto table_name = details["table"].get(); + auto table_name = details[u8"table"s].get(); if (table_name.empty()) { - text error_text{ "ERROR", text::property::bold, color{ 0xFF0000 } }; // TODO: localize + text error_text{ u8"ERROR", text::property::bold, color{ 0xFF0000 } }; // TODO: localize context.publicReply(formatted_message{ "{} command context is missing permission table name", error_text }); return; } @@ -54,7 +55,7 @@ command help_command{ [](command_context& context) { context.publicReply(formatted_message{ "{}", in_command->label() }); return true; }); -}, "help" }; +}, u8"help" }; } // namespace io } // namespace jessibot diff --git a/src/bot/console/console.cpp b/src/bot/console/console.cpp index ef1138b..055ec9e 100644 --- a/src/bot/console/console.cpp +++ b/src/bot/console/console.cpp @@ -30,13 +30,13 @@ namespace io { void console_input_loop() { using namespace jessilib::io; - std::string input; + std::wstring input; auto shutdown_future = get_shutdown_future(); while (shutdown_future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { - std::getline(std::cin, input); // TODO: use a non-bloicking call and poll running periodically - jessibot::io::console_command_context context{ input }; + std::getline(std::wcin, input); // TODO: use a non-bloicking call and poll running periodically? + jessibot::io::console_command_context context{ jessilib::string_cast(input) }; if (!command_manager::instance().execute_command(context)) { - text error_text{ "ERROR", text::property::bold, color{ 0xFF0000 }}; + text error_text{ u8"ERROR", text::property::bold, color{ 0xFF0000 }}; text keyword_text{ context.keyword(), text::property::bold, color{ 0x0000FF }}; auto result = process_message(formatted_message{"{} Command \"{}\" not found", error_text, keyword_text}); std::cout << result << std::endl; diff --git a/src/bot/console/console_command_context.cpp b/src/bot/console/console_command_context.cpp index e3cd05e..3974f1b 100644 --- a/src/bot/console/console_command_context.cpp +++ b/src/bot/console/console_command_context.cpp @@ -27,7 +27,9 @@ namespace io { /** Reply */ bool console_command_context::privateReply(const jessilib::io::formatted_message& in_message) { auto result = jessilib::io::process_message(in_message); - std::cout << result << std::endl; + // TODO check locale before printing to see if console is using UTF-8; if so we can just chuck this straight to cout + // instead of leveraging to wchar_t + std::wcout << jessilib::string_cast(result) << std::endl; return true; } @@ -39,7 +41,7 @@ bool console_command_context::publicReply(const jessilib::io::formatted_message& /** Additional contextual details */ jessilib::object console_command_context::details() const { static jessilib::object s_details { - jessilib::object::map_type{ { "table", "console" } } + jessilib::object::map_type{ { u8"table", u8"console" } } }; return s_details; diff --git a/src/bot/main.cpp b/src/bot/main.cpp index 623f3d1..b7d361d 100644 --- a/src/bot/main.cpp +++ b/src/bot/main.cpp @@ -24,7 +24,7 @@ int main(int argc, char** argv) { jessilib::app_parameters parameters{ argc, argv }; - if (parameters.has_switch("echoParameters")) { + if (parameters.has_switch(u8"echoParameters")) { // TODO: Write pretty JSON serializer based on JSON serializer std::cout << std::endl << jessilib::json_parser{}.serialize(parameters) << std::endl; } diff --git a/src/common/io/command.cpp b/src/common/io/command.cpp index 981ad99..647b7c2 100644 --- a/src/common/io/command.cpp +++ b/src/common/io/command.cpp @@ -22,7 +22,7 @@ namespace jessilib { namespace io { -command::command(callback_t in_callback, std::string_view in_label) +command::command(callback_t in_callback, std::u8string_view in_label) : m_callback{ std::move(in_callback) }, m_label{ std::move(in_label) } { // Register command @@ -34,7 +34,7 @@ command::~command() { command_manager::instance().unregister_command(*this); } -std::string_view command::label() const { +std::u8string_view command::label() const { return m_label; } @@ -44,7 +44,7 @@ void command::execute(command_context& in_context) const { command test_command{ [](command_context&) { // do stuff -}, "test" }; +}, u8"test" }; } // namespace io } // namespace jessilib diff --git a/src/common/io/command_context.cpp b/src/common/io/command_context.cpp index 6a2cff9..e3e01ba 100644 --- a/src/common/io/command_context.cpp +++ b/src/common/io/command_context.cpp @@ -21,52 +21,54 @@ namespace jessilib { namespace io { -command_context::command_context(std::string_view in_input) - : m_input{ in_input } { +command_context::command_context(string_type in_input) + : m_input{ std::move(in_input) } { + std::u8string_view input_view = m_input; + // Strip leading whitespace - std::size_t pos = in_input.find_first_not_of(' '); + std::size_t pos = input_view.find_first_not_of(u8' '); if (pos != std::string_view::npos) { - in_input.remove_prefix(pos); + input_view.remove_prefix(pos); // Whitespace is stripped; parse keyword from input - pos = in_input.find(' '); + pos = input_view.find(u8' '); if (pos == std::string_view::npos) { - pos = in_input.size(); + pos = input_view.size(); } - m_keyword = in_input.substr(0, pos); - in_input.remove_prefix(pos); + m_keyword = input_view.substr(0, pos); + input_view.remove_prefix(pos); // Strip leading whitespace and parse parameter from remaining input - pos = in_input.find_first_not_of(' '); + pos = input_view.find_first_not_of(u8' '); if (pos != std::string_view::npos) { - in_input.remove_prefix(pos); - m_parameter = in_input; + input_view.remove_prefix(pos); + m_parameter = input_view; } } } -command_context::command_context(std::string_view in_input, std::string_view in_keyword, std::string_view in_parameter) +command_context::command_context(string_type in_input, string_type in_keyword, string_type in_parameter) : m_input{ std::move(in_input) }, m_keyword{ std::move(in_keyword) }, m_parameter{ std::move(in_parameter) } { // Empty ctor body } -std::string_view command_context::input() const { +const command_context::string_type& command_context::input() const { return m_input; } -std::string_view command_context::keyword() const { +const command_context::string_type& command_context::keyword() const { return m_keyword; } -std::string_view command_context::parameter() const { +const command_context::string_type& command_context::parameter() const { return m_parameter; } -std::vector command_context::paramaters() const { - std::vector result; - std::string_view parameter = m_parameter; +std::vector command_context::paramaters() const { + std::vector result; + std::u8string_view parameter = m_parameter; // Strip leading whitespace std::size_t pos = parameter.find_first_not_of(' '); diff --git a/src/common/io/message.cpp b/src/common/io/message.cpp index 4a54770..2572086 100644 --- a/src/common/io/message.cpp +++ b/src/common/io/message.cpp @@ -65,11 +65,11 @@ void text::set_color_bg(color in_color) { /** Text */ -const std::string& text::string() const { +const std::u8string& text::string() const { return m_string; } -void text::set_string(std::string_view in_string) { +void text::set_string(std::u8string_view in_string) { m_string = in_string; } diff --git a/src/include/jessilib/io/ansi/ansi_text.hpp b/src/include/jessilib/io/ansi/ansi_text.hpp index b0230d8..35ee08c 100644 --- a/src/include/jessilib/io/ansi/ansi_text.hpp +++ b/src/include/jessilib/io/ansi/ansi_text.hpp @@ -100,7 +100,7 @@ inline std::string text_to_string(const ansi::text_wrapper& } // Append textual string - result += in_text.string(); + result += jessilib::ustring_to_mbstring(std::u8string_view{in_text.string()}).second; // Reset (if needed) if (in_text.properties() != text::property::normal) { diff --git a/src/include/jessilib/io/command.hpp b/src/include/jessilib/io/command.hpp index 43ee7c9..a4dd86a 100644 --- a/src/include/jessilib/io/command.hpp +++ b/src/include/jessilib/io/command.hpp @@ -28,7 +28,7 @@ namespace io { class basic_command { public: virtual ~basic_command() = default; - virtual std::string_view label() const = 0; + virtual std::u8string_view label() const = 0; virtual void execute(command_context& in_context) const = 0; }; // class basic_command @@ -45,21 +45,21 @@ public: command& operator=(command&&) = delete; // Instantiates and self-registers a command - command(callback_t in_callback, std::string_view in_label); + command(callback_t in_callback, std::u8string_view in_label); // Cleans up and unregisters the command virtual ~command(); // Unique label associated with command - virtual std::string_view label() const override; + virtual std::u8string_view label() const override; // Executes the command virtual void execute(command_context& in_context) const override; private: callback_t m_callback; - std::string_view m_label; + std::u8string_view m_label; }; // class command } // namespace io -} // namespace jessilib \ No newline at end of file +} // namespace jessilib diff --git a/src/include/jessilib/io/command_context.hpp b/src/include/jessilib/io/command_context.hpp index 7df7f82..0a14f12 100644 --- a/src/include/jessilib/io/command_context.hpp +++ b/src/include/jessilib/io/command_context.hpp @@ -27,14 +27,15 @@ namespace io { class command_context { public: - command_context(std::string_view in_input); - command_context(std::string_view in_input, std::string_view in_keyword, std::string_view in_parameter); + using string_type = std::u8string; + command_context(string_type in_input); + command_context(string_type in_input, string_type in_keyword, string_type in_parameter); /** User input */ - std::string_view input() const; - std::string_view keyword() const; - std::string_view parameter() const; - std::vector paramaters() const; + const string_type& input() const; + const string_type& keyword() const; + const string_type& parameter() const; + std::vector paramaters() const; /** Reply */ virtual bool privateReply(const formatted_message& in_message) = 0; // Reply to invoker privately (i.e: PM) @@ -45,9 +46,9 @@ public: virtual std::string getText(std::string_view tag) const = 0; // Get localized text private: - std::string_view m_input; - std::string_view m_keyword; - std::string_view m_parameter; + string_type m_input; + string_type m_keyword; + string_type m_parameter; }; // class command_context } // namespace io diff --git a/src/include/jessilib/io/message.hpp b/src/include/jessilib/io/message.hpp index 0e6cb2a..fbe9395 100644 --- a/src/include/jessilib/io/message.hpp +++ b/src/include/jessilib/io/message.hpp @@ -23,6 +23,7 @@ #include #include #include +#include "jessilib/unicode.hpp" #include "color.hpp" namespace jessilib { @@ -114,11 +115,11 @@ public: void set_color_bg(color in_color); /** Text */ - const std::string& string() const; - void set_string(std::string_view in_string); + const std::u8string& string() const; + void set_string(std::u8string_view in_string); private: - std::string m_string; + std::u8string m_string; property m_properties{ property::normal }; color m_color{}; color m_color_bg{}; diff --git a/src/include/jessilib/unicode.hpp b/src/include/jessilib/unicode.hpp index 30277ce..e78d182 100644 --- a/src/include/jessilib/unicode.hpp +++ b/src/include/jessilib/unicode.hpp @@ -220,7 +220,9 @@ std::pair ustring_to_mbstring(std::basic_string_view i std::mbstate_t mbstate{}; decode_result decode; - while ((decode = decode_codepoint(in_string).units != 0)) { + while ((decode = decode_codepoint(in_string)).units != 0) { + in_string.remove_prefix(decode.units); + char buffer[MB_CUR_MAX]; // MB_LEN_MAX size_t bytes_written = std::c32rtomb(buffer, decode.codepoint, &mbstate); if (bytes_written > MB_CUR_MAX) {