From 64d6eae8e073f17723dd26fabf77750e3aafc6b5 Mon Sep 17 00:00:00 2001 From: Weng Xuetian Date: Mon, 30 Sep 2024 10:46:55 -0700 Subject: [PATCH] Improve ordering and spell checking in pinyin for upper case input (#193) Fix #194 --- im/pinyin/pinyin.cpp | 39 +++++++++++++++++++++++++++------------ test/testpinyin.cpp | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/im/pinyin/pinyin.cpp b/im/pinyin/pinyin.cpp index 439574c..16e5564 100644 --- a/im/pinyin/pinyin.cpp +++ b/im/pinyin/pinyin.cpp @@ -187,13 +187,18 @@ void PinyinEngine::updatePredict(InputContext *inputContext) { inputContext->updateUserInterface(UserInterfaceComponent::InputPanel); } -int englishNess(const std::string &input, bool sp) { +std::tuple englishNess(const std::string &input, bool sp) { const auto pys = stringutils::split(input, " "); constexpr int fullWeight = -2; constexpr int shortWeight = 3; constexpr int invalidWeight = 6; constexpr int defaultWeight = shortWeight; int weight = 0; + if (std::any_of(input.begin(), input.end(), charutils::isupper)) { + return {true, + std::max(1, (invalidWeight * pys.size() + 7) / 10)}; + } + for (const auto &py : pys) { if (sp) { if (py.size() == 2) { @@ -207,7 +212,7 @@ int englishNess(const std::string &input, bool sp) { } else { auto firstChr = py[0]; if (firstChr == '\'') { - return 0; + return {false, 0}; } if (firstChr == 'i' || firstChr == 'u' || firstChr == 'v') { weight += invalidWeight; @@ -223,9 +228,9 @@ int englishNess(const std::string &input, bool sp) { } if (weight < 0) { - return 0; + return {false, 0}; } - return (weight + 7) / 10; + return {false, (weight + 7) / 10}; } bool isStroke(const std::string &input) { @@ -488,7 +493,8 @@ void PinyinEngine::updateUI(InputContext *inputContext) { if (*config_.spellEnabled && spell() && parsedPyCursor >= selectedSentence.size() && selectedLength <= context.cursor()) { - int engNess = englishNess(parsedPy, context.useShuangpin()); + auto [hasUpper, engNess] = + englishNess(parsedPy, context.useShuangpin()); if (engNess) { parsedPyCursor -= selectedSentence.length(); parsedPy = parsedPy.substr( @@ -498,14 +504,23 @@ void PinyinEngine::updateUI(InputContext *inputContext) { : std::string::npos); auto results = spell()->call( "en", SpellProvider::Custom, pyBeforeCursor, engNess); - std::string bestSentence; - if (!candidates.empty()) { - bestSentence = candidates[0].toString(); + + // Our hint doesn't work well with mixed case, so, always put a + // word as is. + if (hasUpper && !pyBeforeCursor.empty()) { + if (std::find(results.begin(), results.end(), + pyBeforeCursor) == results.end()) { + if (!charutils::isupper(pyBeforeCursor[0])) { + results.insert(results.begin(), pyBeforeCursor); + } else { + results.push_back(pyBeforeCursor); + } + } } - int position = 1; - for (auto &result : results) { - if (customCandidateSet.count(result) || - context.candidatesToCursorSet().count(result)) { + + int position = hasUpper ? 0 : 1; + for (const auto &result : results) { + if (customCandidateSet.count(result)) { continue; } extraCandidates.push_back( diff --git a/test/testpinyin.cpp b/test/testpinyin.cpp index 7ae0992..2f1af79 100644 --- a/test/testpinyin.cpp +++ b/test/testpinyin.cpp @@ -193,6 +193,36 @@ void testSelectByChar(Instance *instance) { }); } +void testUppercase(Instance *instance) { + instance->eventDispatcher().schedule([instance]() { + auto *testfrontend = instance->addonManager().addon("testfrontend"); + auto uuid = + testfrontend->call("testapp"); + + testfrontend->call(uuid, Key("Control+space"), + false); + + testfrontend->call("Apple"); + + testfrontend->call(uuid, Key("A"), false); + testfrontend->call(uuid, Key("p"), false); + testfrontend->call(uuid, Key("p"), false); + testfrontend->call(uuid, Key("l"), false); + testfrontend->call(uuid, Key("e"), false); + testfrontend->call(uuid, Key("space"), false); + + testfrontend->call("iPhone"); + + testfrontend->call(uuid, Key("i"), false); + testfrontend->call(uuid, Key("P"), false); + testfrontend->call(uuid, Key("h"), false); + testfrontend->call(uuid, Key("o"), false); + testfrontend->call(uuid, Key("n"), false); + testfrontend->call(uuid, Key("e"), false); + testfrontend->call(uuid, Key("space"), false); + }); +} + void testForget(Instance *instance) { instance->eventDispatcher().schedule([instance]() { auto *testfrontend = instance->addonManager().addon("testfrontend"); @@ -372,13 +402,14 @@ int main() { char arg0[] = "testpinyin"; char arg1[] = "--disable=all"; char arg2[] = "--enable=testim,testfrontend,pinyin,punctuation," - "pinyinhelper"; + "pinyinhelper,spell"; char *argv[] = {arg0, arg1, arg2}; fcitx::Log::setLogRule("default=5,pinyin=5"); Instance instance(FCITX_ARRAY_SIZE(argv), argv); instance.addonManager().registerDefaultLoader(nullptr); testBasic(&instance); testSelectByChar(&instance); + testUppercase(&instance); testForget(&instance); testPin(&instance); testPunctuation(&instance);