diff --git a/src/libime/pinyin/pinyincontext.cpp b/src/libime/pinyin/pinyincontext.cpp index 3c25c6e..dac2997 100644 --- a/src/libime/pinyin/pinyincontext.cpp +++ b/src/libime/pinyin/pinyincontext.cpp @@ -17,17 +17,21 @@ #include #include #include +#include #include +#include namespace libime { struct SelectedPinyin { - SelectedPinyin(size_t s, WordNode word, std::string encodedPinyin) + SelectedPinyin(size_t s, WordNode word, std::string encodedPinyin, + bool custom) : offset_(s), word_(std::move(word)), - encodedPinyin_(std::move(encodedPinyin)) {} + encodedPinyin_(std::move(encodedPinyin)), custom_(custom) {} size_t offset_; WordNode word_; std::string encodedPinyin_; + bool custom_; }; class PinyinContextPrivate : public fcitx::QPtrHolder { @@ -132,51 +136,52 @@ class PinyinContextPrivate : public fcitx::QPtrHolder { } } - void select(const SentenceResult &sentence) { + template + void selectHelper(const FillSentence &fillSentence) { FCITX_Q(); - auto offset = q->selectedLength(); - selected_.emplace_back(); auto &selection = selected_.back(); - for (const auto &p : sentence.sentence()) { - selection.emplace_back( - offset + p->to()->index(), - WordNode{p->word(), ime_->model()->index(p->word())}, - static_cast(p)->encodedPinyin()); - } + fillSentence(selection); // add some special code for handling separator at the end - auto remain = std::string_view(q->userInput()).substr(offset); + auto remain = + std::string_view(q->userInput()).substr(q->selectedLength()); if (!remain.empty()) { if (std::all_of(remain.begin(), remain.end(), [](char c) { return c == '\''; })) { - selection.emplace_back(q->size(), WordNode("", 0), ""); + selection.emplace_back(q->size(), WordNode("", 0), "", false); } } q->update(); } - void selectCustom(size_t inputLength, std::string_view segment) { + void select(const SentenceResult &sentence) { FCITX_Q(); auto offset = q->selectedLength(); - - selected_.emplace_back(); - - auto &selection = selected_.back(); - selection.emplace_back(offset + inputLength, - WordNode{segment, ime_->model()->index(segment)}, - ""); - // add some special code for handling separator at the end - auto remain = std::string_view(q->userInput()).substr(offset); - if (!remain.empty()) { - if (std::all_of(remain.begin(), remain.end(), - [](char c) { return c == '\''; })) { - selection.emplace_back(q->size(), WordNode("", 0), ""); + selectHelper([offset, &sentence, + this](std::vector &selection) { + for (const auto &p : sentence.sentence()) { + selection.emplace_back( + offset + p->to()->index(), + WordNode{p->word(), ime_->model()->index(p->word())}, + static_cast(p)->encodedPinyin(), + false); } - } + }); + } - q->update(); + void selectCustom(size_t inputLength, std::string_view segment, + std::string_view encodedPinyin) { + FCITX_Q(); + auto offset = q->selectedLength(); + selectHelper([this, offset, &segment, inputLength, + &encodedPinyin](std::vector &selection) { + auto index = ime_->model()->index(segment); + selection.emplace_back(offset + inputLength, + WordNode{segment, index}, + std::string(encodedPinyin), true); + }); } }; @@ -387,12 +392,16 @@ void PinyinContext::selectCandidatesToCursor(size_t idx) { d->select(candidates[idx]); } -void PinyinContext::selectCustom(size_t inputLength, std::string_view segment) { +void PinyinContext::selectCustom(size_t inputLength, std::string_view segment, + std::string_view encodedPinyin) { FCITX_D(); if (inputLength == 0 && selectedLength() + inputLength > size()) { throw std::out_of_range("Invalid input length"); } - d->selectCustom(inputLength, segment); + if (encodedPinyin.size() % 2 != 0) { + throw std::invalid_argument("Invalid encoded pinyin"); + } + d->selectCustom(inputLength, segment, encodedPinyin); } bool PinyinContext::cancelTill(size_t pos) { @@ -870,12 +879,27 @@ bool PinyinContext::learnWord() { if (d->selected_.size() == 1 && d->selected_[0].size() == 1) { return false; } + bool hasCustom = false; + for (auto &s : d->selected_) { + for (auto &item : s) { + if (item.custom_) { + hasCustom = true; + break; + } + } + if (hasCustom) { + break; + } + } for (auto &s : d->selected_) { bool first = true; for (auto &item : s) { if (!item.word_.word().empty()) { // We can't learn non pinyin word. - if (item.encodedPinyin_.size() != 2) { + if (item.encodedPinyin_.empty()) { + return false; + } + if (item.encodedPinyin_.size() != 2 && !hasCustom) { return false; } if (first) { diff --git a/src/libime/pinyin/pinyincontext.h b/src/libime/pinyin/pinyincontext.h index aae6e85..a2a0eb8 100644 --- a/src/libime/pinyin/pinyincontext.h +++ b/src/libime/pinyin/pinyincontext.h @@ -65,9 +65,11 @@ class LIBIMEPINYIN_EXPORT PinyinContext : public InputBuffer { * * @param inputLength the length of characters to match in the input * @param segment segment + * @param encodedPinyin whether this segment has a pinyin * @since 1.1.7 */ - void selectCustom(size_t inputLength, std::string_view segment); + void selectCustom(size_t inputLength, std::string_view segment, + std::string_view encodedPinyin = ""); /// Whether the input is fully selected. bool selected() const; diff --git a/test/testpinyincontext.cpp b/test/testpinyincontext.cpp index bf07617..b0b97d1 100644 --- a/test/testpinyincontext.cpp +++ b/test/testpinyincontext.cpp @@ -10,11 +10,13 @@ #include "libime/pinyin/pinyincontext.h" #include "libime/pinyin/pinyindecoder.h" #include "libime/pinyin/pinyindictionary.h" +#include "libime/pinyin/pinyinencoder.h" #include "libime/pinyin/pinyinime.h" #include "testdir.h" #include #include #include +#include using namespace libime; @@ -205,6 +207,30 @@ int main() { FCITX_ASSERT(c.selected()); FCITX_ASSERT(c.selectedSentence() == "Hello你What"); + + } + + { + c.clear(); + c.type("shounihao"); + auto shouPinyin = PinyinEncoder::encodeFullPinyin("shou"); + c.selectCustom(4, "✋", std::string_view(shouPinyin.data(), shouPinyin.size())); + size_t i = 0; + for (const auto &candidate : c.candidatesToCursor()) { + if (candidate.toString() == "你好") { + break; + } + i++; + } + FCITX_ASSERT(i < c.candidatesToCursor().size()); + c.selectCandidatesToCursor(i); + + FCITX_ASSERT(c.selected()); + c.learn(); + + c.clear(); + c.type("shounihao"); + FCITX_ASSERT(c.candidatesToCursorSet().count("✋你好") > 0); } return 0;