Files
msime-windows/server/tests/src/test_shuangpin_preedit_display.cpp
T
fanlusky 45c473158a feat(engine,server,windows,ui-html): 双拼预编辑可显示转换后的全拼
外观里行内预编辑与候选窗预编辑各加一个「双拼显示转换后的全拼」开关,默认关闭。
开启后在双拼方案下:行内原始按键显示不带分词符号的全拼,拼音分词显示带分词符号的
全拼;候选窗显示带分词符号的全拼。

- 引擎新增 get_shuangpin_preedit_forms,同时给出原串切分与全拼切分,不受
  shuangpin_preedit_mode 影响。
- 原始按键样式下 TSF 不等 Server 回包,开关开启且为双拼时 Server 下发的实际样式
  改为 pinyin,回包里再按用户样式去掉分词符号;换方案时重新下发。
- 转换后字母数与按键不再对应,Server 在预编辑末尾附光标映射表
  (engine/contracts/preedit_caret_map.h),DLL 用它定位光标和处理方向键,
  无表或长度不符时回落到原有字母映射。
2026-10-02 16:14:50 +08:00

80 lines
3.3 KiB
C++

#include "tests/includes/test_framework.h"
#include "engine/contracts/preedit_caret_map.h"
#include "utils/shuangpin_preedit_display.h"
#include <cstddef>
#include <string>
#include <vector>
TEST_CASE(ShuangpinQuanpinPreeditKeepsSeparatorsForPinyinStyle)
{
const auto preedit = BuildShuangpinQuanpinPreedit("nihc", "ni'hc", "ni'hao", /*keep_separators=*/true);
REQUIRE_EQ(preedit.text, std::string("ni'hao"));
// n|i|h|c: 两键中间停在全拼音节的同一偏移,音节末尾停在全拼音节末尾。
REQUIRE(preedit.caret_map == std::vector<std::size_t>({0, 1, 2, 4, 6}));
}
TEST_CASE(ShuangpinQuanpinPreeditDropsSeparatorsForRawStyle)
{
const auto preedit = BuildShuangpinQuanpinPreedit("nihc", "ni'hc", "ni'hao", /*keep_separators=*/false);
REQUIRE_EQ(preedit.text, std::string("nihao"));
REQUIRE(preedit.caret_map == std::vector<std::size_t>({0, 1, 2, 3, 5}));
}
TEST_CASE(ShuangpinQuanpinPreeditFollowsManualSeparator)
{
// 用户手动敲的分词符号也算在原串里,光标停在它后面时落到下一个全拼音节开头。
const auto preedit = BuildShuangpinQuanpinPreedit("ni'hc", "ni'hc", "ni'hao", /*keep_separators=*/true);
REQUIRE_EQ(preedit.text, std::string("ni'hao"));
REQUIRE(preedit.caret_map == std::vector<std::size_t>({0, 1, 2, 3, 4, 6}));
const auto raw = BuildShuangpinQuanpinPreedit("ni'hc", "ni'hc", "ni'hao", /*keep_separators=*/false);
REQUIRE_EQ(raw.text, std::string("nihao"));
REQUIRE(raw.caret_map == std::vector<std::size_t>({0, 1, 2, 2, 3, 5}));
}
TEST_CASE(ShuangpinQuanpinPreeditKeepsTrailingInitial)
{
const auto preedit = BuildShuangpinQuanpinPreedit("vgh", "vg'h", "zheng'h", /*keep_separators=*/true);
REQUIRE_EQ(preedit.text, std::string("zheng'h"));
REQUIRE(preedit.caret_map == std::vector<std::size_t>({0, 1, 5, 7}));
}
TEST_CASE(ShuangpinQuanpinPreeditFallsBackWhenSegmentsDiffer)
{
const auto preedit = BuildShuangpinQuanpinPreedit("abcd", "ab'cd", "abc", /*keep_separators=*/true);
REQUIRE_EQ(preedit.text, std::string("abc"));
REQUIRE_EQ(preedit.caret_map.size(), std::size_t(5));
REQUIRE_EQ(preedit.caret_map.front(), std::size_t(0));
REQUIRE_EQ(preedit.caret_map.back(), std::size_t(3));
}
TEST_CASE(PreeditCaretMapRoundTrips)
{
std::wstring payload = FanyImePreeditCaretMap::Encode(L"你好nihao", {2, 3, 4, 5, 7});
REQUIRE(payload.find(L'\t') == std::wstring::npos);
std::vector<std::size_t> caret_map;
FanyImePreeditCaretMap::Decode(payload, caret_map);
REQUIRE(payload == L"你好nihao");
REQUIRE(caret_map == std::vector<std::size_t>({2, 3, 4, 5, 7}));
}
TEST_CASE(PreeditCaretMapIgnoresPlainAndMalformedPayloads)
{
std::vector<std::size_t> caret_map{1};
std::wstring plain = L"ni'hao";
FanyImePreeditCaretMap::Decode(plain, caret_map);
REQUIRE(plain == L"ni'hao");
REQUIRE(caret_map.empty());
std::wstring malformed = std::wstring(L"nihao") + FanyImePreeditCaretMap::kMarker + L"0,x";
FanyImePreeditCaretMap::Decode(malformed, caret_map);
REQUIRE(malformed == L"nihao");
REQUIRE(caret_map.empty());
std::wstring out_of_range = std::wstring(L"ni") + FanyImePreeditCaretMap::kMarker + L"0,9";
FanyImePreeditCaretMap::Decode(out_of_range, caret_map);
REQUIRE(out_of_range == L"ni");
REQUIRE(caret_map.empty());
}