diff --git a/RimeWithWeasel/RimeWithWeasel.cpp b/RimeWithWeasel/RimeWithWeasel.cpp index b50407621..d4c1e85ed 100644 --- a/RimeWithWeasel/RimeWithWeasel.cpp +++ b/RimeWithWeasel/RimeWithWeasel.cpp @@ -475,22 +475,18 @@ void RimeWithWeaselHandler::_GetCandidateInfo(CandidateInfo& cinfo, cinfo.comments.resize(ctx.menu.num_candidates); cinfo.labels.resize(ctx.menu.num_candidates); for (int i = 0; i < ctx.menu.num_candidates; ++i) { - cinfo.candies[i].str = std::regex_replace( - string_to_wstring(ctx.menu.candidates[i].text, CP_UTF8), - std::wregex(L"\\r\\n|\\n|\\r"), L"\r"); + cinfo.candies[i].str = + escape_string(string_to_wstring(ctx.menu.candidates[i].text, CP_UTF8)); if (ctx.menu.candidates[i].comment) { - cinfo.comments[i].str = std::regex_replace( - string_to_wstring(ctx.menu.candidates[i].comment, CP_UTF8), - std::wregex(L"\\r\\n|\\n|\\r"), L"\r"); + cinfo.comments[i].str = escape_string( + string_to_wstring(ctx.menu.candidates[i].comment, CP_UTF8)); } if (RIME_STRUCT_HAS_MEMBER(ctx, ctx.select_labels) && ctx.select_labels) { cinfo.labels[i].str = - std::regex_replace(string_to_wstring(ctx.select_labels[i], CP_UTF8), - std::wregex(L"\\r\\n|\\n|\\r"), L"\r"); + escape_string(string_to_wstring(ctx.select_labels[i], CP_UTF8)); } else if (ctx.menu.select_keys) { cinfo.labels[i].str = - std::regex_replace(std::wstring(1, ctx.menu.select_keys[i]), - std::wregex(L"\\r\\n|\\n|\\r"), L"\r"); + escape_string(std::wstring(1, ctx.menu.select_keys[i])); } else { cinfo.labels[i].str = std::to_wstring((i + 1) % 10); } @@ -782,7 +778,9 @@ bool RimeWithWeaselHandler::_Respond(WeaselSessionId ipc_id, EatLine eat) { RIME_STRUCT(RimeCommit, commit); if (RimeGetCommit(session_id, &commit)) { actions.insert("commit"); - messages.push_back(std::string("commit=") + commit.text + '\n'); + + std::string commit_text = escape_string(commit.text); + messages.push_back(std::string("commit=") + commit_text + '\n'); RimeFreeCommit(&commit); } @@ -821,7 +819,8 @@ bool RimeWithWeaselHandler::_Respond(WeaselSessionId ipc_id, EatLine eat) { case UIStyle::PREVIEW: if (ctx.commit_text_preview != NULL) { std::string first = ctx.commit_text_preview; - messages.push_back(std::string("ctx.preedit=") + first + '\n'); + messages.push_back(std::string("ctx.preedit=") + + escape_string(first) + '\n'); messages.push_back( std::string("ctx.preedit.cursor=") + std::to_string(utf8towcslen(first.c_str(), 0)) + ',' + @@ -834,7 +833,8 @@ bool RimeWithWeaselHandler::_Respond(WeaselSessionId ipc_id, EatLine eat) { // no preview, fall back to composition case UIStyle::COMPOSITION: messages.push_back(std::string("ctx.preedit=") + - ctx.composition.preedit + '\n'); + escape_string(ctx.composition.preedit) + + '\n'); if (ctx.composition.sel_start <= ctx.composition.sel_end) { messages.push_back( std::string("ctx.preedit.cursor=") + @@ -852,8 +852,9 @@ bool RimeWithWeaselHandler::_Respond(WeaselSessionId ipc_id, EatLine eat) { case UIStyle::PREVIEW_ALL: CandidateInfo cinfo; _GetCandidateInfo(cinfo, ctx); - std::string topush = - std::string("ctx.preedit=") + ctx.composition.preedit + " ["; + std::string topush = std::string("ctx.preedit=") + + escape_string(ctx.composition.preedit) + + " ["; for (auto i = 0; i < ctx.menu.num_candidates; i++) { std::string label = session_status.style.label_font_point > 0 @@ -872,12 +873,11 @@ bool RimeWithWeaselHandler::_Respond(WeaselSessionId ipc_id, EatLine eat) { CP_UTF8); std::string prefix = (i != ctx.menu.highlighted_candidate_index) ? "" : mark_text; - topush += " " + prefix + label + - std::string(ctx.menu.candidates[i].text) + " " + comment; + topush += " " + prefix + escape_string(label) + + escape_string(ctx.menu.candidates[i].text) + " " + + escape_string(comment); } messages.push_back(topush + " ]\n"); - // messages.push_back(std::string("ctx.preedit=") + - // ctx.composition.preedit + '\n'); if (ctx.composition.sel_start <= ctx.composition.sel_end) { messages.push_back( std::string("ctx.preedit.cursor=") + diff --git a/WeaselIPC/Committer.cpp b/WeaselIPC/Committer.cpp index 74ac0d94e..42a28278d 100644 --- a/WeaselIPC/Committer.cpp +++ b/WeaselIPC/Committer.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "Deserializer.h" #include "Committer.h" +#include using namespace weasel; @@ -16,6 +17,7 @@ void Committer::Store(Deserializer::KeyType const& key, std::wstring const& value) { if (!m_pTarget->p_commit) return; - if (key.size() == 1) - *m_pTarget->p_commit = value; + if (key.size() == 1) { + *m_pTarget->p_commit = unescape_string(value); + } } diff --git a/WeaselIPC/ContextUpdater.cpp b/WeaselIPC/ContextUpdater.cpp index 271beecca..72d36f820 100644 --- a/WeaselIPC/ContextUpdater.cpp +++ b/WeaselIPC/ContextUpdater.cpp @@ -44,7 +44,7 @@ void ContextUpdater::_StoreText(Text& target, std::wstring const& value) { if (k.size() == 2) { target.clear(); - target.str = value; + target.str = unescape_string(value); return; } if (k.size() == 3) { @@ -78,6 +78,13 @@ void ContextUpdater::_StoreCand(Deserializer::KeyType k, boost::archive::text_wiarchive ia(ss); ia >> cinfo; + + for (auto& cand : cinfo.candies) + cand.str = unescape_string(cand.str); + for (auto& lalel : cinfo.labels) + lalel.str = unescape_string(lalel.str); + for (auto& comment : cinfo.comments) + comment.str = unescape_string(comment.str); } // StatusUpdater diff --git a/include/WeaselUtility.h b/include/WeaselUtility.h index 88cb0cfbd..9d4f9101d 100644 --- a/include/WeaselUtility.h +++ b/include/WeaselUtility.h @@ -84,5 +84,78 @@ inline std::string wstring_to_string(const std::wstring& wstr, return res; } +template +struct EscapeChar { + static const CharT escape; + static const CharT linefeed; + static const CharT tab; + static const CharT linefeed_escape; + static const CharT tab_escape; +}; + +template <> +const char EscapeChar::escape = '\\'; +template <> +const char EscapeChar::linefeed = '\n'; +template <> +const char EscapeChar::tab = '\t'; +template <> +const char EscapeChar::linefeed_escape = 'n'; +template <> +const char EscapeChar::tab_escape = 't'; + +template <> +const wchar_t EscapeChar::escape = L'\\'; +template <> +const wchar_t EscapeChar::linefeed = L'\n'; +template <> +const wchar_t EscapeChar::tab = L'\t'; +template <> +const wchar_t EscapeChar::linefeed_escape = L'n'; +template <> +const wchar_t EscapeChar::tab_escape = L't'; + +template +inline std::basic_string escape_string( + const std::basic_string input) { + using Esc = EscapeChar; + std::basic_stringstream res; + for (auto p = input.begin(); p != input.end(); ++p) { + if (*p == Esc::escape) { + res << Esc::escape << Esc::escape; + } else if (*p == Esc::linefeed) { + res << Esc::escape << Esc::linefeed_escape; + } else if (*p == Esc::tab) { + res << Esc::escape << Esc::tab_escape; + } else { + res << *p; + } + } + return res.str(); +} + +template +inline std::basic_string unescape_string( + const std::basic_string& input) { + using Esc = EscapeChar; + std::basic_stringstream res; + for (auto p = input.begin(); p != input.end(); ++p) { + if (*p == Esc::escape) { + if (++p == input.end()) { + break; + } else if (*p == Esc::linefeed_escape) { + res << Esc::linefeed; + } else if (*p == Esc::tab_escape) { + res << Esc::tab; + } else { // \a => a + res << *p; + } + } else { + res << *p; + } + } + return res.str(); +} + // resource std::string GetCustomResource(const char* name, const char* type);