Files
Lu Fan(SonnyCalcr) 23bfdd4d51 feat(engine,server): 符号混输放开拼音读音,emoji 混输改为整串精确匹配 (#1050)
- 符号混输不再挡读得成拼音的编码:混输本来就只认整串精确匹配,xi → ξ 也照样混入;单字母编码仍不混。
- 构建器读取 msime-dictionary 新增的 custom/symbols.txt(拼音<TAB>符号),把 dian → · 、、
  shuminghao → 《》 〈〉 等标点读音写进 symbol_code,书名号、引号、括号成对给出。
- emoji 混输改用新的 query_mixed_emoji,只认整串精确等于关键词,不再按前缀带出 emoji;双拼下只查
  展开后的全拼,并和符号一样只在光标位于末尾时查询。Shift+E 的 emoji 模式仍按前缀查。
- 同步更新设置页说明、出厂与开发配置注释和 README。
2026-10-18 11:23:26 +08:00

112 lines
4.2 KiB
C++

#include "../contracts/assets/assets.h"
#include "emoji_query.h"
#include "local_database.h"
#include "../core/data_path.h"
#include "../shuangpin/shuangpin_query.h"
#include <algorithm>
#include <cctype>
#include <memory>
#include <vector>
namespace metasequoia::local_modes
{
namespace
{
bool valid_code(const std::string &code)
{
return !code.empty() && std::all_of(code.begin(), code.end(), [](unsigned char character) {
return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || character == '\'';
});
}
std::string lower_ascii(std::string text)
{
std::transform(text.begin(), text.end(), text.begin(),
[](unsigned char character) { return static_cast<char>(std::tolower(character)); });
return text;
}
LocalQueryResult query_failure(const char *diagnostic)
{
return {{}, std::string(diagnostic)};
}
LocalQueryResult run_emoji_query(const std::string &code, SchemeType scheme, const std::filesystem::path &database_path,
int limit, const ShuangpinProfile &profile, bool exact_only)
{
if (!valid_code(code) || limit <= 0)
{
return {};
}
if (database_path.empty())
{
return query_failure("Emoji database is unavailable.");
}
const std::string lower = lower_ascii(code);
std::vector<RankedLookup> lookups{{lower, true}};
if (scheme == SchemeType::Shuangpin)
{
const std::string quanpin = shuangpin::normalize_input(lower, profile);
if (!quanpin.empty() && quanpin != lower)
{
// 双拼下精确命中只认展开后的全拼:原始码恰好等于某个简拼或英文键(如 xl → 😀)时,
// 不能压过用户想打的全拼前缀;只要精确命中时原始码干脆不查。
if (exact_only)
lookups.clear();
else
lookups.front().exact_ranks = false;
lookups.push_back({quanpin, true});
}
}
const std::shared_ptr<sqlite3> database = open_local_database(database_path);
if (!database)
{
return query_failure("Emoji database is unavailable.");
}
// 精确命中的键排在更长的键之前。只按目录顺序(sort_order)排会把短码挤下去——
// ku 会先出 kuxiao(苦笑)→😅,而 哭→😢 的目录位置更靠后;精确行排在 LIMIT 之前,不会被前缀行截掉。
constexpr const char *kPrefixSql = "SELECT emoji,sort_order,key IS ?1 FROM emoji_pinyin WHERE key>=?1 AND key<?2 "
"ORDER BY key IS NOT ?1,sort_order LIMIT ?3";
constexpr const char *kExactSql = "SELECT emoji,sort_order,1 FROM emoji_pinyin WHERE key=?1 AND key<?2 "
"ORDER BY sort_order LIMIT ?3";
const auto entries = query_ranked_entries(database.get(), exact_only ? kExactSql : kPrefixSql, lookups, limit);
if (!entries)
{
return query_failure("Emoji database could not be queried.");
}
const std::size_t count = entries->size();
LocalQueryResult result;
result.candidates.reserve(count);
for (std::size_t index = 0; index < count; ++index)
{
result.candidates.emplace_back(lower, (*entries)[index], static_cast<std::int64_t>(count - index),
CandidateSource::Emoji);
}
return result;
}
} // namespace
LocalQueryResult query_emoji(const std::string &code, SchemeType scheme, int limit, const ShuangpinProfile &profile)
{
return query_emoji(code, scheme, data_file_path(metasequoia::assets::other_dictionary), limit, profile);
}
LocalQueryResult query_emoji(const std::string &code, SchemeType scheme, const std::filesystem::path &database_path,
int limit, const ShuangpinProfile &profile)
{
return run_emoji_query(code, scheme, database_path, limit, profile, false);
}
LocalQueryResult query_mixed_emoji(const std::string &code, SchemeType scheme,
const std::filesystem::path &database_path, int limit,
const ShuangpinProfile &profile)
{
return run_emoji_query(code, scheme, database_path, limit, profile, true);
}
} // namespace metasequoia::local_modes