Files
Lu Fan(SonnyCalcr) 4375d80414 feat: 支持导入自定义辅助码,内置“加加”更名为“拼音加加” (#477)
* feat(engine): 支持 helpcodes/custom 下的自定义辅助码

custom 目录里的每个 .txt 都是一套方案,方案名为 custom/<文件名>。
文件开头的 "# name:" / "# name_en:" 注释给出中英文显示名,缺省用文件名;
兼容 BOM、CRLF 和全角冒号。方案名不能跳出 custom 目录。

* feat(engine): WebView 契约新增 openHelpcodeDirectory

设置页请求打开自定义辅助码目录,仅允许 settings 页发送。同步 ui-html/webview2/shared。

* feat(server): 设置页列出自定义辅助码并可打开其目录

配置快照带上 helpcode.custom_schemas 与 helpcode.custom_directory,每次都会重新扫描;
方案文件被删除或改名后回退到 lantian。两个设置宿主都会处理 openHelpcodeDirectory。

* feat(ui-html): 辅助码设置显示拼音加加与自定义方案

内置方案“加加”改名为“拼音加加”。自定义方案追加在内置方案之后;新增说明区,
提供“打开目录”和“刷新方案”两个按钮,刷新完成后弹出提示。

* feat(installer): 升级与迁移数据目录时保留自定义辅助码

升级清理时只清掉 helpcodes 里的内置表,保留 custom 子目录;更换数据目录时一并迁移;
安装时建出 helpcodes\custom。
2026-09-26 20:21:55 +08:00

119 lines
4.5 KiB
C++

#pragma once
#include <string>
#include <filesystem>
#include <memory>
#include <unordered_map>
#include <vector>
namespace HelpcodeUtils
{
using Keymap = std::unordered_map<std::string, std::string>;
using SharedKeymap = std::shared_ptr<const Keymap>;
SharedKeymap load_helpcode_keymap(const std::filesystem::path &resources, const std::string &schema);
std::string selected_helpcode_schema();
const Keymap &helpcode_keymap();
bool is_supported_helpcode_schema(const std::string &schema);
bool select_helpcode_schema(const std::string &schema);
// User tables dropped into <resources>/helpcodes/custom/*.txt are schemas named
// "custom/<file stem>". Leading "# name: ..." / "# name_en: ..." comment lines give display names.
inline constexpr char kCustomHelpcodeSchemaPrefix[] = "custom/";
struct CustomHelpcodeSchema
{
std::string schema;
std::string name; // Chinese display name; empty when the file does not declare one
std::string name_en; // English display name; empty when the file does not declare one
std::string file_stem;
};
std::filesystem::path custom_helpcode_directory(const std::filesystem::path &resources);
std::vector<CustomHelpcodeSchema> list_custom_helpcode_schemas(const std::filesystem::path &resources);
// Built-in schemas are always available; a custom schema only while its file exists.
bool is_helpcode_schema_available(const std::filesystem::path &resources, const std::string &schema);
std::string get_first_han_char(const std::string &words);
std::string get_last_han_char(const std::string &words);
std::string::size_type count_han_chars(const std::string &words);
std::string::size_type count_utf8_chars(const std::string &text);
std::string compute_helpcodes(const std::string &words, bool uppercase_all = false, const Keymap *keymap = nullptr);
bool is_quanpin_single_help_mode(const std::string &pinyin_with_cases);
bool is_quanpin_double_help_mode(const std::string &pinyin_with_cases);
enum class SingleHelpcodeMatch
{
None,
First,
Last,
Both,
};
SingleHelpcodeMatch match_single_helpcode(const std::string &word, const std::string &help_code,
const Keymap *keymap = nullptr);
bool matches_double_helpcodes(const std::string &word, const std::string &help_codes, const Keymap *keymap = nullptr);
template <typename TWordItem>
std::vector<TWordItem> reorder_candidates_with_single_helpcode(const std::vector<TWordItem> &candidate_list,
const std::string &help_code,
const Keymap *keymap = nullptr)
{
if (help_code.size() != 1)
{
return candidate_list;
}
std::vector<TWordItem> first_helpcode_matched_list;
std::vector<TWordItem> last_helpcode_matched_list;
std::vector<TWordItem> left_helpcode_matched_list;
std::vector<TWordItem> result_list;
for (const auto &cand : candidate_list)
{
switch (match_single_helpcode(cand.word, help_code, keymap))
{
case SingleHelpcodeMatch::First:
first_helpcode_matched_list.push_back(cand);
break;
case SingleHelpcodeMatch::Last:
last_helpcode_matched_list.push_back(cand);
break;
case SingleHelpcodeMatch::Both:
first_helpcode_matched_list.push_back(cand);
break;
case SingleHelpcodeMatch::None:
left_helpcode_matched_list.push_back(cand);
break;
}
}
result_list.insert(result_list.end(), first_helpcode_matched_list.begin(), first_helpcode_matched_list.end());
result_list.insert(result_list.end(), last_helpcode_matched_list.begin(), last_helpcode_matched_list.end());
result_list.insert(result_list.end(), left_helpcode_matched_list.begin(), left_helpcode_matched_list.end());
return result_list;
}
template <typename TWordItem>
std::vector<TWordItem> filter_candidates_with_double_helpcodes(const std::vector<TWordItem> &candidate_list,
const std::string &help_codes,
const Keymap *keymap = nullptr)
{
std::vector<TWordItem> filtered_list;
if (help_codes.size() != 2)
{
return filtered_list;
}
for (const auto &cand : candidate_list)
{
if (matches_double_helpcodes(cand.word, help_codes, keymap))
{
filtered_list.push_back(cand);
}
}
return filtered_list;
}
} // namespace HelpcodeUtils