diff --git a/src/libime/pinyin/pinyincorrectionprofile.cpp b/src/libime/pinyin/pinyincorrectionprofile.cpp index a3053b6..4b4c383 100644 --- a/src/libime/pinyin/pinyincorrectionprofile.cpp +++ b/src/libime/pinyin/pinyincorrectionprofile.cpp @@ -47,6 +47,7 @@ getProfileMapping(BuiltinPinyinCorrectionProfile profile) { class PinyinCorrectionProfilePrivate { public: PinyinMap pinyinMap_; + std::unordered_map> correctionMap_; }; PinyinCorrectionProfile::PinyinCorrectionProfile( @@ -57,6 +58,7 @@ PinyinCorrectionProfile::PinyinCorrectionProfile( const std::unordered_map> &mapping) : d_ptr(std::make_unique()) { FCITX_D(); + d->correctionMap_ = mapping; // Fill with the original pinyin map. d->pinyinMap_ = getPinyinMapV2(); if (mapping.empty()) { @@ -93,4 +95,9 @@ const PinyinMap &PinyinCorrectionProfile::pinyinMap() const { return d->pinyinMap_; } -} // namespace libime \ No newline at end of file +const std::unordered_map> & +PinyinCorrectionProfile::correctionMap() const { + FCITX_D(); + return d->correctionMap_; +} +} // namespace libime diff --git a/src/libime/pinyin/pinyincorrectionprofile.h b/src/libime/pinyin/pinyincorrectionprofile.h index 73100e3..6db0fbf 100644 --- a/src/libime/pinyin/pinyincorrectionprofile.h +++ b/src/libime/pinyin/pinyincorrectionprofile.h @@ -64,6 +64,15 @@ class LIBIMEPINYIN_EXPORT PinyinCorrectionProfile { * @see getPinyinMapV2 */ const PinyinMap &pinyinMap() const; + /** + * Return the correction mapping. + * + * E.g. w may be corrected to q,e, the mapping will contain {'w': ['q', + * 'e']}. + * + * @see getPinyinMapV2 + */ + const std::unordered_map> &correctionMap() const; private: FCITX_DECLARE_PRIVATE(PinyinCorrectionProfile); @@ -72,4 +81,4 @@ class LIBIMEPINYIN_EXPORT PinyinCorrectionProfile { } // namespace libime -#endif \ No newline at end of file +#endif diff --git a/src/libime/pinyin/shuangpinprofile.cpp b/src/libime/pinyin/shuangpinprofile.cpp index a3ee00e..7e127ad 100644 --- a/src/libime/pinyin/shuangpinprofile.cpp +++ b/src/libime/pinyin/shuangpinprofile.cpp @@ -4,14 +4,30 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "shuangpinprofile.h" +#include "pinyincorrectionprofile.h" #include "pinyindata.h" #include "pinyinencoder.h" #include "shuangpindata.h" +#include #include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include +#include +#include #include +#include #include +#include +#include namespace libime { @@ -29,9 +45,321 @@ class ShuangpinProfilePrivate { ShuangpinProfile::ValidInputSetType validInputs_; ShuangpinProfile::ValidInputSetType validInitials_; ShuangpinProfile::TableType spTable_; + + void buildShuangpinTable(const PinyinCorrectionProfile *correctionProfile) { + // Set up valid inputs. + for (char c = 'a'; c <= 'z'; c++) { + validInputs_.insert(c); + } + for (const auto &p : initialMap_) { + validInputs_.insert(p.first); + } + std::unordered_map singleCharFinal; + for (const auto &p : finalMap_) { + validInputs_.insert(p.first); + if (PinyinEncoder::finalToString(p.second).size() == 1) { + singleCharFinal[p.second] = p.first; + } + } + + for (const auto &p : initialFinalMap_) { + for (auto c : p.first) { + validInputs_.insert(c); + } + } + + std::set initialChars; + for (auto zero : zeroS_) { + if (zero != '*') { + validInputs_.insert(zero); + initialChars.insert(zero); + } + } + + // Collect all initial and final chars. + // Add single char initial to initialChars. + for (auto c = PinyinEncoder::firstInitial; + c <= PinyinEncoder::lastInitial; c++) { + const auto &initialString = + PinyinEncoder::initialToString(static_cast(c)); + if (initialString.size() == 1) { + initialChars.insert(initialString[0]); + } + } + // Add char in map to initialChars. + for (auto &p : initialMap_) { + initialChars.insert(p.first); + } + + // Collect all final chars. + // Add single char final to finalChars. + std::set finalChars; + for (auto c = PinyinEncoder::firstFinal; c <= PinyinEncoder::lastFinal; + c++) { + auto f = static_cast(c); + const auto &finalString = PinyinEncoder::finalToString(f); + if (finalString.size() == 1 && !singleCharFinal.count(f)) { + finalChars.insert(finalString[0]); + singleCharFinal[f] = finalString[0]; + } + } + // Add final in map to finalChars + for (auto &p : finalMap_) { + finalChars.insert(p.first); + } + + for (const auto &[final, chr] : singleCharFinal) { + auto [begin, end] = finalMap_.equal_range(chr); + if (std::find_if(begin, end, [final = final](const auto &item) { + return item.second == final; + }) == end) { + finalMap_.emplace(chr, final); + } + } + + auto addPinyinToList = + [](std::multimap &pys, + PinyinInitial i, PinyinFinal f, PinyinFuzzyFlags flags) { + PinyinSyllable s(i, f); + if (flags == PinyinFuzzyFlag::None) { + + auto iter = pys.find(s); + // We replace fuzzy with non-fuzzy. + if (iter != pys.end() && + iter->second != PinyinFuzzyFlag::None) { + pys.erase(s); + iter = pys.end(); + } + if (iter == pys.end()) { + pys.emplace(s, flags); + } + } else { + auto iterPair = pys.equal_range(s); + // no match + if (iterPair.first != iterPair.second) { + if (iterPair.first->second == PinyinFuzzyFlag::None) { + return; + } + // check dup + for (auto i = iterPair.first; i != iterPair.second; + i++) { + if (i->second == flags) { + return; + } + } + } + + pys.emplace(s, flags); + } + }; + + auto addPinyin = + [addPinyinToList]( + std::multimap &pys, + const std::string &py) { + const auto &map = getPinyinMapV2(); + auto iterPair = map.equal_range(py); + if (iterPair.first != iterPair.second) { + for (const auto &item : boost::make_iterator_range( + iterPair.first, iterPair.second)) { + // Shuangpin should not consider advanced typo, since + // it's swapping character order and will leads to wrong + // entry. Common typo also have "ng->gn" is ok. + if (item.flags().test(PinyinFuzzyFlag::AdvancedTypo)) { + continue; + } + addPinyinToList(pys, item.initial(), item.final(), + item.flags()); + } + } + }; + + // Special handling for Ziranma & Xiaohe style. + if (zeroS_.find('*') != std::string::npos) { + // length 1: aeiou, repeat it once: e.g. aa + // length 2: keep same as quanpin + // length 3: use the initial of quanpin and the one in the table. + for (auto c : finalChars) { + // If c is in final map. + auto finalIterPair = finalMap_.equal_range(c); + for (auto &item : boost::make_iterator_range( + finalIterPair.first, finalIterPair.second)) { + if (PinyinEncoder::isValidInitialFinal(PinyinInitial::Zero, + item.second)) { + std::string input; + const auto &finalString = + PinyinEncoder::finalToString(item.second); + if (finalString.size() == 1) { + input = std::string{c, c}; + } else { + auto final = PinyinEncoder::stringToFinal( + std::string{finalString[0]}); + if (final != PinyinFinal::Invalid) { + auto singleCharFinalIter = + singleCharFinal.find(final); + if (singleCharFinalIter != + singleCharFinal.end()) { + input = std::string{ + singleCharFinalIter->second, c}; + } + } + } + spTable_[input].emplace( + PinyinSyllable{PinyinInitial::Zero, item.second}, + PinyinFuzzyFlag::None); + } + } + } + } + + // Enumerate the combinition of initial + final + for (auto c1 : initialChars) { + for (auto c2 : finalChars) { + std::string input{c1, c2}; + auto &pys = spTable_[input]; + + std::vector initials; + std::vector finals; + auto initialIterPair = initialMap_.equal_range(c1); + if (initialIterPair.first != initialIterPair.second) { + for (auto &item : boost::make_iterator_range( + initialIterPair.first, initialIterPair.second)) { + initials.push_back(item.second); + } + } + auto initial = PinyinEncoder::stringToInitial(std::string{c1}); + if (initial != PinyinInitial::Invalid) { + initials.push_back(initial); + } + + if (zeroS_.find(c1) != std::string::npos) { + initials.push_back(PinyinInitial::Zero); + } + + auto finalIterPair = finalMap_.equal_range(c2); + for (auto &item : boost::make_iterator_range( + finalIterPair.first, finalIterPair.second)) { + finals.push_back(item.second); + } + + for (auto i : initials) { + for (auto f : finals) { + auto py = PinyinEncoder::initialToString(i) + + PinyinEncoder::finalToString(f); + addPinyin(pys, py); + } + } + + if (pys.empty()) { + spTable_.erase(input); + } + } + } + + // Populate initial final map. + for (const auto &p : initialFinalMap_) { + auto &pys = spTable_[p.first]; + auto py = PinyinEncoder::initialToString(p.second.first) + + PinyinEncoder::finalToString(p.second.second); + addPinyin(pys, py); + } + + // Add non-existent 2 char pinyin to the map. + for (const auto &p : getPinyinMapV2()) { + // Don't add "ng" as two char direct pinyin. + if (p.pinyin() == "ng") { + continue; + } + + if (p.pinyin().size() == 2 && p.initial() == PinyinInitial::Zero && + (!spTable_.count(p.pinyin()) || + zeroS_.find('*') != std::string::npos)) { + auto &pys = spTable_[p.pinyin()]; + pys.emplace(PinyinSyllable{p.initial(), p.final()}, p.flags()); + } + } + + // Add partial pinyin to the table. + for (char c : validInputs_) { + std::string input{c}; + auto &pys = spTable_[input]; + auto initial = PinyinEncoder::stringToInitial(std::string{c}); + if (initial != PinyinInitial::Invalid) { + addPinyinToList(pys, initial, PinyinFinal::Invalid, + PinyinFuzzyFlag::None); + } + auto initialIterPair = initialMap_.equal_range(c); + for (auto &item : boost::make_iterator_range( + initialIterPair.first, initialIterPair.second)) { + addPinyinToList(pys, item.second, PinyinFinal::Invalid, + PinyinFuzzyFlag::None); + } + + // Add single char final to partial pinyin. + auto [begin, end] = finalMap_.equal_range(c); + for (auto &item : boost::make_iterator_range(begin, end)) { + const auto final = item.second; + if (PinyinEncoder::finalToString(final).size() == 1 && + PinyinEncoder::isValidInitialFinal(PinyinInitial::Zero, + final) && + pys.empty()) { + addPinyinToList(pys, PinyinInitial::Zero, final, + PinyinFuzzyFlag::None); + } + } + + if (pys.empty()) { + spTable_.erase(input); + } + } + + std::vector> + newEntries; + + if (correctionProfile != nullptr) { + const auto &correctionMap = correctionProfile->correctionMap(); + for (const auto &[input, pys] : spTable_) { + for (size_t i = 0; i < input.size(); i++) { + auto chr = input[i]; + auto swap = correctionMap.find(chr); + if (swap == correctionMap.end() || swap->second.empty()) { + continue; + } + std::string newInput = input; + for (auto sub : swap->second) { + newInput[i] = sub; + for (const auto &x : pys) { + newEntries.emplace_back( + newInput, x.first, + x.second | PinyinFuzzyFlag::Correction); + } + newInput[i] = chr; + } + } + } + } + + for (const auto &[input, syllable, flags] : newEntries) { + auto &pys = spTable_[input]; + pys.emplace(syllable, flags); + } + + for (const auto &sp : spTable_) { + assert(!sp.first.empty() && sp.first.size() <= 2); + validInitials_.insert(sp.first[0]); + } + } }; ShuangpinProfile::ShuangpinProfile(ShuangpinBuiltinProfile profile) + : ShuangpinProfile::ShuangpinProfile(profile, nullptr) {} + +ShuangpinProfile::ShuangpinProfile(std::istream &in) + : ShuangpinProfile::ShuangpinProfile(in, nullptr) {} + +ShuangpinProfile::ShuangpinProfile( + ShuangpinBuiltinProfile profile, + const PinyinCorrectionProfile *correctionProfile) : d_ptr(std::make_unique()) { FCITX_D(); const SP_C *c = nullptr; @@ -83,10 +411,11 @@ ShuangpinProfile::ShuangpinProfile(ShuangpinBuiltinProfile profile) PinyinEncoder::stringToInitial(s[i].strQP)); } - buildShuangpinTable(); + d->buildShuangpinTable(correctionProfile); } -ShuangpinProfile::ShuangpinProfile(std::istream &in) +ShuangpinProfile::ShuangpinProfile( + std::istream &in, const PinyinCorrectionProfile *correctionProfile) : d_ptr(std::make_unique()) { FCITX_D(); std::string line; @@ -156,281 +485,13 @@ ShuangpinProfile::ShuangpinProfile(std::istream &in) } } - buildShuangpinTable(); + d->buildShuangpinTable(correctionProfile); } FCITX_DEFINE_DPTR_COPY_AND_DEFAULT_DTOR_AND_MOVE(ShuangpinProfile) -void ShuangpinProfile::buildShuangpinTable() { - FCITX_D(); - // Set up valid inputs. - for (char c = 'a'; c <= 'z'; c++) { - d->validInputs_.insert(c); - } - for (const auto &p : d->initialMap_) { - d->validInputs_.insert(p.first); - } - std::unordered_map singleCharFinal; - for (const auto &p : d->finalMap_) { - d->validInputs_.insert(p.first); - if (PinyinEncoder::finalToString(p.second).size() == 1) { - singleCharFinal[p.second] = p.first; - } - } - - for (const auto &p : d->initialFinalMap_) { - for (auto c : p.first) { - d->validInputs_.insert(c); - } - } - - std::set initialChars; - for (auto zero : d->zeroS_) { - if (zero != '*') { - d->validInputs_.insert(zero); - initialChars.insert(zero); - } - } - - // Collect all initial and final chars. - // Add single char initial to initialChars. - for (auto c = PinyinEncoder::firstInitial; c <= PinyinEncoder::lastInitial; - c++) { - const auto &initialString = - PinyinEncoder::initialToString(static_cast(c)); - if (initialString.size() == 1) { - initialChars.insert(initialString[0]); - } - } - // Add char in map to initialChars. - for (auto &p : d->initialMap_) { - initialChars.insert(p.first); - } - - // Collect all final chars. - // Add single char final to finalChars. - std::set finalChars; - for (auto c = PinyinEncoder::firstFinal; c <= PinyinEncoder::lastFinal; - c++) { - auto f = static_cast(c); - const auto &finalString = PinyinEncoder::finalToString(f); - if (finalString.size() == 1 && !singleCharFinal.count(f)) { - finalChars.insert(finalString[0]); - singleCharFinal[f] = finalString[0]; - } - } - // Add final in map to finalChars - for (auto &p : d->finalMap_) { - finalChars.insert(p.first); - } - - for (const auto &[final, chr] : singleCharFinal) { - auto [begin, end] = d->finalMap_.equal_range(chr); - if (std::find_if(begin, end, [final = final](const auto &item) { - return item.second == final; - }) == end) { - d->finalMap_.emplace(chr, final); - } - } - - auto addPinyinToList = - [](std::multimap &pys, - PinyinInitial i, PinyinFinal f, PinyinFuzzyFlags flags) { - PinyinSyllable s(i, f); - if (flags == PinyinFuzzyFlag::None) { - - auto iter = pys.find(s); - // We replace fuzzy with non-fuzzy. - if (iter != pys.end() && - iter->second != PinyinFuzzyFlag::None) { - pys.erase(s); - iter = pys.end(); - } - if (iter == pys.end()) { - pys.emplace(s, flags); - } - } else { - auto iterPair = pys.equal_range(s); - // no match - if (iterPair.first != iterPair.second) { - if (iterPair.first->second == PinyinFuzzyFlag::None) { - return; - } - // check dup - for (auto i = iterPair.first; i != iterPair.second; i++) { - if (i->second == flags) { - return; - } - } - } - - pys.emplace(s, flags); - } - }; - - auto addPinyin = [addPinyinToList]( - std::multimap &pys, - const std::string &py) { - const auto &map = getPinyinMapV2(); - auto iterPair = map.equal_range(py); - if (iterPair.first != iterPair.second) { - for (const auto &item : - boost::make_iterator_range(iterPair.first, iterPair.second)) { - // Shuangpin should not consider advanced typo, since it's - // swapping character order and will leads to wrong entry. - // Common typo also have "ng->gn" is ok. - if (item.flags().test(PinyinFuzzyFlag::AdvancedTypo)) { - continue; - } - addPinyinToList(pys, item.initial(), item.final(), - item.flags()); - } - } - }; - - // Special handling for Ziranma & Xiaohe style. - if (d->zeroS_.find('*') != std::string::npos) { - // length 1: aeiou, repeat it once: e.g. aa - // length 2: keep same as quanpin - // length 3: use the initial of quanpin and the one in the table. - for (auto c : finalChars) { - // If c is in final map. - auto finalIterPair = d->finalMap_.equal_range(c); - for (auto &item : boost::make_iterator_range( - finalIterPair.first, finalIterPair.second)) { - if (PinyinEncoder::isValidInitialFinal(PinyinInitial::Zero, - item.second)) { - std::string input; - const auto &finalString = - PinyinEncoder::finalToString(item.second); - if (finalString.size() == 1) { - input = std::string{c, c}; - } else { - auto final = PinyinEncoder::stringToFinal( - std::string{finalString[0]}); - if (final != PinyinFinal::Invalid) { - auto singleCharFinalIter = - singleCharFinal.find(final); - if (singleCharFinalIter != singleCharFinal.end()) { - input = - std::string{singleCharFinalIter->second, c}; - } - } - } - d->spTable_[input].emplace( - PinyinSyllable{PinyinInitial::Zero, item.second}, - PinyinFuzzyFlag::None); - } - } - } - } - - // Enumerate the combinition of initial + final - for (auto c1 : initialChars) { - for (auto c2 : finalChars) { - std::string input{c1, c2}; - auto &pys = d->spTable_[input]; - - std::vector initials; - std::vector finals; - auto initialIterPair = d->initialMap_.equal_range(c1); - if (initialIterPair.first != initialIterPair.second) { - for (auto &item : boost::make_iterator_range( - initialIterPair.first, initialIterPair.second)) { - initials.push_back(item.second); - } - } - auto initial = PinyinEncoder::stringToInitial(std::string{c1}); - if (initial != PinyinInitial::Invalid) { - initials.push_back(initial); - } - - if (d->zeroS_.find(c1) != std::string::npos) { - initials.push_back(PinyinInitial::Zero); - } - - auto finalIterPair = d->finalMap_.equal_range(c2); - for (auto &item : boost::make_iterator_range( - finalIterPair.first, finalIterPair.second)) { - finals.push_back(item.second); - } - - for (auto i : initials) { - for (auto f : finals) { - auto py = PinyinEncoder::initialToString(i) + - PinyinEncoder::finalToString(f); - addPinyin(pys, py); - } - } - - if (pys.empty()) { - d->spTable_.erase(input); - } - } - } - - // Populate initial final map. - for (const auto &p : d->initialFinalMap_) { - auto &pys = d->spTable_[p.first]; - auto py = PinyinEncoder::initialToString(p.second.first) + - PinyinEncoder::finalToString(p.second.second); - addPinyin(pys, py); - } - - // Add non-existent 2 char pinyin to the map. - for (const auto &p : getPinyinMapV2()) { - // Don't add "ng" as two char direct pinyin. - if (p.pinyin() == "ng") { - continue; - } - - if (p.pinyin().size() == 2 && p.initial() == PinyinInitial::Zero && - (!d->spTable_.count(p.pinyin()) || - d->zeroS_.find('*') != std::string::npos)) { - auto &pys = d->spTable_[p.pinyin()]; - pys.emplace(PinyinSyllable{p.initial(), p.final()}, p.flags()); - } - } - - // Add partial pinyin to the table. - for (char c : d->validInputs_) { - std::string input{c}; - auto &pys = d->spTable_[input]; - auto initial = PinyinEncoder::stringToInitial(std::string{c}); - if (initial != PinyinInitial::Invalid) { - addPinyinToList(pys, initial, PinyinFinal::Invalid, - PinyinFuzzyFlag::None); - } - auto initialIterPair = d->initialMap_.equal_range(c); - for (auto &item : boost::make_iterator_range(initialIterPair.first, - initialIterPair.second)) { - addPinyinToList(pys, item.second, PinyinFinal::Invalid, - PinyinFuzzyFlag::None); - } - - // Add single char final to partial pinyin. - auto [begin, end] = d->finalMap_.equal_range(c); - for (auto &item : boost::make_iterator_range(begin, end)) { - const auto final = item.second; - if (PinyinEncoder::finalToString(final).size() == 1 && - PinyinEncoder::isValidInitialFinal(PinyinInitial::Zero, - final) && - pys.empty()) { - addPinyinToList(pys, PinyinInitial::Zero, final, - PinyinFuzzyFlag::None); - } - } - - if (pys.empty()) { - d->spTable_.erase(input); - } - } - - for (const auto &sp : d->spTable_) { - assert(!sp.first.empty() && sp.first.size() <= 2); - d->validInitials_.insert(sp.first[0]); - } -} +// Deprecated, keep to only keep ABI stable. +void ShuangpinProfile::buildShuangpinTable() {} const ShuangpinProfile::TableType &ShuangpinProfile::table() const { FCITX_D(); diff --git a/src/libime/pinyin/shuangpinprofile.h b/src/libime/pinyin/shuangpinprofile.h index 922bd0a..12c44ea 100644 --- a/src/libime/pinyin/shuangpinprofile.h +++ b/src/libime/pinyin/shuangpinprofile.h @@ -9,6 +9,7 @@ #include "libimepinyin_export.h" #include #include +#include #include #include #include @@ -37,6 +38,11 @@ class LIBIMEPINYIN_EXPORT ShuangpinProfile { explicit ShuangpinProfile(ShuangpinBuiltinProfile profile); explicit ShuangpinProfile(std::istream &in); + explicit ShuangpinProfile(ShuangpinBuiltinProfile profile, + const PinyinCorrectionProfile *correctionProfile); + explicit ShuangpinProfile(std::istream &in, + const PinyinCorrectionProfile *correctionProfile); + FCITX_DECLARE_VIRTUAL_DTOR_COPY_AND_MOVE(ShuangpinProfile) const TableType &table() const; diff --git a/test/testpinyinencoder.cpp b/test/testpinyinencoder.cpp index 0c30638..6a5fd5e 100644 --- a/test/testpinyinencoder.cpp +++ b/test/testpinyinencoder.cpp @@ -6,6 +6,7 @@ #include "libime/pinyin/pinyincorrectionprofile.h" #include "libime/pinyin/pinyinencoder.h" +#include "libime/pinyin/shuangpinprofile.h" #include #include #include @@ -251,5 +252,24 @@ int main() { dfs(graph, {"suan", "g"}); } + { + ShuangpinProfile sp(ShuangpinBuiltinProfile::Xiaohe); + PinyinCorrectionProfile profile(BuiltinPinyinCorrectionProfile::Qwerty); + + auto graph = PinyinEncoder::parseUserShuangpin( + "xnqiee", sp, PinyinFuzzyFlag::Correction); + dfs(graph, {"xn", "qi", "ee"}); + graph = PinyinEncoder::parseUserShuangpin("znwiee", sp, + PinyinFuzzyFlag::Correction); + dfs(graph, {"z", "nw", "ie", "e"}); + + graph = PinyinEncoder::parseUserShuangpin("wokeyityxxboli", sp, + PinyinFuzzyFlag::Correction); + dfs(graph, {"wo", "ke", "yi", "ty", "xx", "bo", "li"}); + graph = PinyinEncoder::parseUserShuangpin("wokeyityxzboli", sp, + PinyinFuzzyFlag::Correction); + dfs(graph, {"wo", "ke", "yi", "ty", "x", "z", "bo", "li"}); + } + return 0; } diff --git a/test/testpinyinime_unit.cpp b/test/testpinyinime_unit.cpp index 3cdb76a..e4a5ec5 100644 --- a/test/testpinyinime_unit.cpp +++ b/test/testpinyinime_unit.cpp @@ -6,6 +6,7 @@ #include "libime/core/userlanguagemodel.h" #include "libime/pinyin/pinyincontext.h" +#include "libime/pinyin/pinyincorrectionprofile.h" #include "libime/pinyin/pinyindictionary.h" #include "libime/pinyin/pinyinencoder.h" #include "libime/pinyin/pinyinime.h" @@ -26,7 +27,8 @@ int main() { ime.dict()->load(PinyinDictionary::SystemDict, LIBIME_BINARY_DIR "/data/sc.dict", PinyinDictFormat::Binary); - ime.setFuzzyFlags(PinyinFuzzyFlag::Inner); + PinyinFuzzyFlags flags = PinyinFuzzyFlag::Inner; + ime.setFuzzyFlags(flags); ime.setScoreFilter(1.0f); ime.setShuangpinProfile( std::make_shared(ShuangpinBuiltinProfile::Xiaohe)); @@ -54,5 +56,29 @@ int main() { c.learn(); FCITX_ASSERT(ime.dict()->lookupWord(PinyinDictionary::UserDict, "ni'hao'zhong'guo", "你好中国")); + + c.setUseShuangpin(true); + + c.type("bkqilb"); + FCITX_ASSERT(c.candidates().size() == c.candidateSet().size()); + FCITX_ASSERT(c.candidateSet().count("冰淇淋")); + c.clear(); + + c.type("bkqiln"); + FCITX_ASSERT(c.candidates().size() == c.candidateSet().size()); + FCITX_ASSERT(!c.candidateSet().count("冰淇淋")); + c.clear(); + + ime.setCorrectionProfile(std::make_shared( + BuiltinPinyinCorrectionProfile::Qwerty)); + ime.setShuangpinProfile(std::make_shared( + ShuangpinBuiltinProfile::Xiaohe, ime.correctionProfile().get())); + ime.setFuzzyFlags(flags | PinyinFuzzyFlag::Correction); + + c.type("bkqiln"); + FCITX_ASSERT(c.candidates().size() == c.candidateSet().size()); + FCITX_ASSERT(c.candidateSet().count("冰淇淋")); + c.clear(); + return 0; }