Skip to content

Commit 5254f54

Browse files
committed
Fix spell with "-" word
In fcitx4 actually we separate the candidate with two field (display, commit). And we didn't correctly return prefix+hint for custom dictionary. This fixes this, and also, since we handles the upper/lowercases in the dict level, the engine level case-fix does not make sense anymore. So push down to the dict. Especially we already has case fix one-run in the custom dict. Doesn't really make sense if we do that twice.
1 parent aeb1add commit 5254f54

10 files changed

+135
-83
lines changed

src/im/keyboard/keyboard.cpp

+14-59
Original file line numberDiff line numberDiff line change
@@ -45,53 +45,6 @@ const char imNamePrefix[] = "keyboard-";
4545
namespace fcitx {
4646

4747
namespace {
48-
enum class SpellType { AllLower, Mixed, FirstUpper, AllUpper };
49-
50-
SpellType guessSpellType(const std::string &input) {
51-
if (input.size() <= 1) {
52-
if (charutils::isupper(input[0])) {
53-
return SpellType::FirstUpper;
54-
}
55-
return SpellType::AllLower;
56-
}
57-
58-
if (std::all_of(input.begin(), input.end(),
59-
[](char c) { return charutils::isupper(c); })) {
60-
return SpellType::AllUpper;
61-
}
62-
63-
if (std::all_of(input.begin() + 1, input.end(),
64-
[](char c) { return charutils::islower(c); })) {
65-
if (charutils::isupper(input[0])) {
66-
return SpellType::FirstUpper;
67-
}
68-
return SpellType::AllLower;
69-
}
70-
71-
return SpellType::Mixed;
72-
}
73-
74-
std::string formatWord(const std::string &input, SpellType type) {
75-
if (type == SpellType::Mixed || type == SpellType::AllLower) {
76-
return input;
77-
}
78-
if (guessSpellType(input) != SpellType::AllLower) {
79-
return input;
80-
}
81-
std::string result;
82-
if (type == SpellType::AllUpper) {
83-
result.reserve(input.size());
84-
std::transform(input.begin(), input.end(), std::back_inserter(result),
85-
charutils::toupper);
86-
} else {
87-
// FirstUpper
88-
result = input;
89-
if (!result.empty()) {
90-
result[0] = charutils::toupper(result[0]);
91-
}
92-
}
93-
return result;
94-
}
9548

9649
std::string findBestLanguage(const IsoCodes &isocodes, const std::string &hint,
9750
const std::vector<std::string> &languages) {
@@ -155,20 +108,23 @@ std::string findBestLanguage(const IsoCodes &isocodes, const std::string &hint,
155108

156109
class KeyboardCandidateWord : public CandidateWord {
157110
public:
158-
KeyboardCandidateWord(KeyboardEngine *engine, Text text)
159-
: CandidateWord(std::move(text)), engine_(engine) {}
111+
KeyboardCandidateWord(KeyboardEngine *engine, Text text, std::string commit)
112+
: CandidateWord(std::move(text)), engine_(engine),
113+
commit_(std::move(commit)) {}
160114

161115
void select(InputContext *inputContext) const override {
162-
auto commit = text().toString();
163116
inputContext->inputPanel().reset();
164117
inputContext->updatePreedit();
165118
inputContext->updateUserInterface(UserInterfaceComponent::InputPanel);
166-
inputContext->commitString(commit);
119+
inputContext->commitString(commit_);
167120
engine_->resetState(inputContext);
168121
}
169122

123+
const std::string &stringForCommit() const { return commit_; }
124+
170125
private:
171126
KeyboardEngine *engine_;
127+
std::string commit_;
172128
};
173129

174130
class LongPressCandidateWord : public CandidateWord {
@@ -515,11 +471,11 @@ void KeyboardEngineState::setPreedit() {
515471

516472
void KeyboardEngineState::updateCandidate(const InputMethodEntry &entry) {
517473
inputContext_->inputPanel().reset();
518-
std::vector<std::string> results;
474+
std::vector<std::pair<std::string, std::string>> results;
519475
if (auto spell = engine_->spell()) {
520-
results =
521-
spell->call<ISpell::hint>(entry.languageCode(), buffer_.userInput(),
522-
engine_->config().pageSize.value());
476+
results = spell->call<ISpell::hintForDisplay>(
477+
entry.languageCode(), SpellProvider::Default, buffer_.userInput(),
478+
engine_->config().pageSize.value());
523479
}
524480
if (engine_->config().enableEmoji.value() && engine_->emoji()) {
525481
auto emojiResults = engine_->emoji()->call<IEmoji::query>(
@@ -535,16 +491,15 @@ void KeyboardEngineState::updateCandidate(const InputMethodEntry &entry) {
535491
while (i < emojiResults.size() &&
536492
static_cast<int>(results.size()) <
537493
engine_->config().pageSize.value()) {
538-
results.push_back(emojiResults[i]);
494+
results.emplace_back(emojiResults[i], emojiResults[i]);
539495
i++;
540496
}
541497
}
542498

543499
auto candidateList = std::make_unique<CommonCandidateList>();
544-
auto spellType = guessSpellType(buffer_.userInput());
545500
for (const auto &result : results) {
546501
candidateList->append<KeyboardCandidateWord>(
547-
engine_, Text(formatWord(result, spellType)));
502+
engine_, Text(result.first), result.second);
548503
}
549504
candidateList->setPageSize(*engine_->config().pageSize);
550505
candidateList->setSelectionKey(engine_->selectionKeys());
@@ -825,7 +780,7 @@ std::string KeyboardEngineState::currentSelection() const {
825780
dynamic_cast<const KeyboardCandidateWord *>(
826781
&candidateList->candidate(
827782
candidateList->cursorIndex()))) {
828-
return candidate->text().toString();
783+
return candidate->stringForCommit();
829784
}
830785
}
831786
return buffer_.userInput();

src/modules/spell/spell-custom-dict.cpp

+13-4
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,8 @@ int SpellCustomDict::getDistance(const char *word, int utf8Len,
416416
return -1;
417417
}
418418

419-
std::vector<std::string> SpellCustomDict::hint(const std::string &str,
420-
size_t limit) {
419+
std::vector<std::pair<std::string, std::string>>
420+
SpellCustomDict::hint(const std::string &str, size_t limit) {
421421
const char *word = str.c_str();
422422
const char *real_word = word;
423423
std::vector<std::string> result;
@@ -431,6 +431,9 @@ std::vector<std::string> SpellCustomDict::hint(const std::string &str,
431431
if (!real_word[0]) {
432432
return {};
433433
}
434+
435+
std::string_view prefix(word);
436+
prefix = prefix.substr(0, real_word - word);
434437
auto word_type = wordCheck(real_word);
435438
int word_len = fcitx_utf8_strlen(real_word);
436439
auto compare = [](const std::pair<const char *, int> &lhs,
@@ -455,9 +458,15 @@ std::vector<std::string> SpellCustomDict::hint(const std::string &str,
455458

456459
result.reserve(tops.size());
457460
for (auto &top : tops) {
458-
result.emplace_back(top.first);
461+
result.emplace_back(std::move(top.first));
459462
}
460463
hintComplete(result, word_type);
461-
return result;
464+
465+
std::vector<std::pair<std::string, std::string>> finalResult;
466+
finalResult.reserve(result.size());
467+
for (const auto &item : result) {
468+
finalResult.emplace_back(item, stringutils::concat(prefix, item));
469+
}
470+
return finalResult;
462471
}
463472
} // namespace fcitx

src/modules/spell/spell-custom-dict.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ class SpellCustomDict {
2121
static bool checkDict(const std::string &language);
2222
static std::string locateDictFile(const std::string &lang);
2323

24-
std::vector<std::string> hint(const std::string &str, size_t limit);
24+
std::vector<std::pair<std::string, std::string>>
25+
hint(const std::string &str, size_t limit);
2526

2627
protected:
2728
void loadDict(const std::string &lang);

src/modules/spell/spell-custom.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ bool fcitx::SpellCustom::loadDict(const std::string &language) {
3838
return false;
3939
}
4040

41-
std::vector<std::string> fcitx::SpellCustom::hint(const std::string &language,
42-
const std::string &str,
43-
size_t limit) {
41+
std::vector<std::pair<std::string, std::string>>
42+
fcitx::SpellCustom::hint(const std::string &language, const std::string &str,
43+
size_t limit) {
4444
if (!loadDict(language)) {
4545
return {};
4646
}

src/modules/spell/spell-custom.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ class SpellCustom : public SpellBackend {
2121

2222
bool checkDict(const std::string &language) override;
2323
void addWord(const std::string &language, const std::string &word) override;
24-
std::vector<std::string> hint(const std::string &language,
25-
const std::string &str,
26-
size_t limit) override;
24+
std::vector<std::pair<std::string, std::string>>
25+
hint(const std::string &language, const std::string &str,
26+
size_t limit) override;
2727

2828
private:
2929
bool loadDict(const std::string &language);

src/modules/spell/spell-enchant.cpp

+58-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,56 @@
1616
#include "fcitx/misc_p.h"
1717

1818
namespace fcitx {
19+
namespace {
20+
enum class SpellType { AllLower, Mixed, FirstUpper, AllUpper };
21+
22+
SpellType guessSpellType(const std::string &input) {
23+
if (input.size() <= 1) {
24+
if (charutils::isupper(input[0])) {
25+
return SpellType::FirstUpper;
26+
}
27+
return SpellType::AllLower;
28+
}
29+
30+
if (std::all_of(input.begin(), input.end(),
31+
[](char c) { return charutils::isupper(c); })) {
32+
return SpellType::AllUpper;
33+
}
34+
35+
if (std::all_of(input.begin() + 1, input.end(),
36+
[](char c) { return charutils::islower(c); })) {
37+
if (charutils::isupper(input[0])) {
38+
return SpellType::FirstUpper;
39+
}
40+
return SpellType::AllLower;
41+
}
42+
43+
return SpellType::Mixed;
44+
}
45+
46+
std::string formatWord(const std::string &input, SpellType type) {
47+
if (type == SpellType::Mixed || type == SpellType::AllLower) {
48+
return input;
49+
}
50+
if (guessSpellType(input) != SpellType::AllLower) {
51+
return input;
52+
}
53+
std::string result;
54+
if (type == SpellType::AllUpper) {
55+
result.reserve(input.size());
56+
std::transform(input.begin(), input.end(), std::back_inserter(result),
57+
charutils::toupper);
58+
} else {
59+
// FirstUpper
60+
result = input;
61+
if (!result.empty()) {
62+
result[0] = charutils::toupper(result[0]);
63+
}
64+
}
65+
return result;
66+
}
67+
68+
} // namespace
1969

2070
template <typename Callback>
2171
auto foreachLanguage(const std::string &lang, const std::string &systemLanguage,
@@ -66,9 +116,9 @@ SpellEnchant::SpellEnchant(Spell *spell)
66116

67117
SpellEnchant::~SpellEnchant() {}
68118

69-
std::vector<std::string> SpellEnchant::hint(const std::string &language,
70-
const std::string &word,
71-
size_t limit) {
119+
std::vector<std::pair<std::string, std::string>>
120+
SpellEnchant::hint(const std::string &language, const std::string &word,
121+
size_t limit) {
72122
if (word.empty() || !loadDict(language)) {
73123
return {};
74124
}
@@ -80,11 +130,14 @@ std::vector<std::string> SpellEnchant::hint(const std::string &language,
80130
return {};
81131
}
82132

83-
std::vector<std::string> result;
133+
std::vector<std::pair<std::string, std::string>> result;
84134
number = number > limit ? limit : number;
85135
result.reserve(number);
136+
auto spellType = guessSpellType(word);
86137
for (size_t i = 0; i < number; i++) {
87-
result.push_back(suggestions[i]);
138+
std::string hintWord = suggestions[i];
139+
hintWord = formatWord(hintWord, spellType);
140+
result.emplace_back(hintWord, hintWord);
88141
}
89142

90143
enchant_dict_free_string_list(dict_.get(), suggestions);

src/modules/spell/spell-enchant.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ class SpellEnchant : public SpellBackend {
2020

2121
bool checkDict(const std::string &language) override;
2222
void addWord(const std::string &language, const std::string &word) override;
23-
std::vector<std::string> hint(const std::string &language,
24-
const std::string &word,
25-
size_t limit) override;
23+
std::vector<std::pair<std::string, std::string>>
24+
hint(const std::string &language, const std::string &word,
25+
size_t limit) override;
2626

2727
private:
2828
bool loadDict(const std::string &language);

src/modules/spell/spell.cpp

+26-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ Spell::BackendMap::iterator Spell::findBackend(const std::string &language) {
4545

4646
Spell::BackendMap::iterator Spell::findBackend(const std::string &language,
4747
SpellProvider provider) {
48+
if (provider == SpellProvider::Default) {
49+
return findBackend(language);
50+
}
51+
4852
auto iter = backends_.find(provider);
4953
if (iter != backends_.end() && iter->second->checkDict(language)) {
5054
return iter;
@@ -66,14 +70,24 @@ void Spell::addWord(const std::string &language, const std::string &word) {
6670
iter->second->addWord(language, word);
6771
}
6872

73+
std::vector<std::string>
74+
takeSecond(std::vector<std::pair<std::string, std::string>> raw) {
75+
std::vector<std::string> result;
76+
result.reserve(raw.size());
77+
for (auto &item : raw) {
78+
result.emplace_back(std::move(item.second));
79+
}
80+
return result;
81+
}
82+
6983
std::vector<std::string> Spell::hint(const std::string &language,
7084
const std::string &word, size_t limit) {
7185
auto iter = findBackend(language);
7286
if (iter == backends_.end()) {
7387
return {};
7488
}
7589

76-
return iter->second->hint(language, word, limit);
90+
return takeSecond(iter->second->hint(language, word, limit));
7791
}
7892

7993
std::vector<std::string> Spell::hintWithProvider(const std::string &language,
@@ -85,6 +99,17 @@ std::vector<std::string> Spell::hintWithProvider(const std::string &language,
8599
return {};
86100
}
87101

102+
return takeSecond(iter->second->hint(language, word, limit));
103+
}
104+
105+
std::vector<std::pair<std::string, std::string>>
106+
Spell::hintForDisplay(const std::string &language, SpellProvider provider,
107+
const std::string &word, size_t limit) {
108+
auto iter = findBackend(language, provider);
109+
if (iter == backends_.end()) {
110+
return {};
111+
}
112+
88113
return iter->second->hint(language, word, limit);
89114
}
90115

src/modules/spell/spell.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,16 @@ class Spell final : public AddonInstance {
6666
SpellProvider provider,
6767
const std::string &word,
6868
size_t limit);
69+
std::vector<std::pair<std::string, std::string>>
70+
hintForDisplay(const std::string &language, SpellProvider provider,
71+
const std::string &word, size_t limit);
6972

7073
private:
7174
FCITX_ADDON_EXPORT_FUNCTION(Spell, checkDict);
7275
FCITX_ADDON_EXPORT_FUNCTION(Spell, addWord);
7376
FCITX_ADDON_EXPORT_FUNCTION(Spell, hint);
7477
FCITX_ADDON_EXPORT_FUNCTION(Spell, hintWithProvider);
78+
FCITX_ADDON_EXPORT_FUNCTION(Spell, hintForDisplay);
7579
SpellConfig config_;
7680
typedef std::unordered_map<SpellProvider, std::unique_ptr<SpellBackend>,
7781
EnumHash>
@@ -92,9 +96,9 @@ class SpellBackend {
9296
virtual bool checkDict(const std::string &language) = 0;
9397
virtual void addWord(const std::string &language,
9498
const std::string &word) = 0;
95-
virtual std::vector<std::string> hint(const std::string &language,
96-
const std::string &word,
97-
size_t limit) = 0;
99+
virtual std::vector<std::pair<std::string, std::string>>
100+
hint(const std::string &language, const std::string &word,
101+
size_t limit) = 0;
98102

99103
const SpellConfig &config() { return parent_->config(); }
100104

0 commit comments

Comments
 (0)