1 /*
2  * SPDX-FileCopyrightText: 2017-2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #ifndef _TABLE_TABLEDICTRESOLVER_H_
8 #define _TABLE_TABLEDICTRESOLVER_H_
9 
10 #include <fcitx-config/configuration.h>
11 #include <fcitx-config/enum.h>
12 #include <fcitx-utils/i18n.h>
13 #include <fcitx-utils/log.h>
14 #include <fcitx/candidatelist.h>
15 #include <libime/core/userlanguagemodel.h>
16 #include <libime/table/tablebaseddictionary.h>
17 #include <tuple>
18 
19 namespace fcitx {
20 
21 FCITX_CONFIG_ENUM(OrderPolicy, No, Freq, Fast);
22 FCITX_CONFIG_ENUM_NAME(CandidateLayoutHint, "Not set", "Vertical",
23                        "Horizontal");
24 FCITX_CONFIG_ENUM_I18N_ANNOTATION(CandidateLayoutHint, N_("Not set"),
25                                   N_("Vertical"), N_("Horizontal"));
26 
27 struct NoSaveAnnotation {
skipDescriptionNoSaveAnnotation28     bool skipDescription() { return true; }
skipSaveNoSaveAnnotation29     bool skipSave() { return true; }
dumpDescriptionNoSaveAnnotation30     void dumpDescription(RawConfig &) const {}
31 };
32 
33 template <typename T>
34 using NoSaveOption = OptionWithAnnotation<T, NoSaveAnnotation>;
35 
36 FCITX_CONFIGURATION(
37     TableConfig, NoSaveOption<std::string> file{this, "File", _("File")};
38     KeyListOption prevPage{
39         this,
40         "PrevPage",
41         _("Prev page"),
42         {Key(FcitxKey_Up)},
43         KeyListConstrain(KeyConstrainFlag::AllowModifierLess)};
44     KeyListOption nextPage{
45         this,
46         "NextPage",
47         _("Next page"),
48         {Key(FcitxKey_Down)},
49         KeyListConstrain(KeyConstrainFlag::AllowModifierLess)};
50     KeyListOption prevCandidate{
51         this,
52         "PrevCandidate",
53         _("Prev Candidate"),
54         {Key("Left")},
55         KeyListConstrain(KeyConstrainFlag::AllowModifierLess)};
56     KeyListOption nextCandidate{
57         this,
58         "NextCandidate",
59         _("Next Candidate"),
60         {Key("Right")},
61         KeyListConstrain(KeyConstrainFlag::AllowModifierLess)};
62     KeyListOption secondCandidate{
63         this,
64         "SecondCandidate",
65         _("Select Second Candidate"),
66         {},
67         KeyListConstrain({KeyConstrainFlag::AllowModifierLess,
68                           KeyConstrainFlag::AllowModifierOnly})};
69     KeyListOption thirdCandidate{
70         this,
71         "ThirdCandidate",
72         _("Select Third Candidate"),
73         {},
74         KeyListConstrain({KeyConstrainFlag::AllowModifierLess,
75                           KeyConstrainFlag::AllowModifierOnly})};
76     NoSaveOption<KeyList> selection{
77         this,
78         "Selection",
79         _("Selection"),
80         {Key(FcitxKey_1), Key(FcitxKey_2), Key(FcitxKey_3), Key(FcitxKey_4),
81          Key(FcitxKey_5), Key(FcitxKey_6), Key(FcitxKey_7), Key(FcitxKey_8),
82          Key(FcitxKey_9), Key(FcitxKey_0)}};
83     Option<int, IntConstrain> pageSize{this, "PageSize", _("Page size"), 5,
84                                        IntConstrain(3, 10)};
85     // Fcitx 4 default behavior.
86     Option<bool> commitAfterSelect{this, "CommitAfterSelect",
87                                    _("Commit after auto select candidates"),
88                                    true};
89     Option<bool> commitWhenDeactivate{
90         this, "CommitWhenDeactivate",
91         _("Commit buffer when deactivating input method"), true};
92     OptionWithAnnotation<bool, ToolTipAnnotation> commitInvalidSegment{
93         this,
94         "CommitInvalidSegment",
95         _("Commit Invalid Segment"),
96         false,
97         {},
98         {},
99         ToolTipAnnotation(
100             _("Commit the segment that does not exist in the table. This "
101               "option is useful for some latin/compose based table."))};
102     Option<bool> useFullWidth{this, "UseFullWidth", _("Use full width"), true};
103     Option<bool> ignorePunc{this, "IgnorePunc",
104                             _("Ignore built in punctuation"), false};
105     Option<bool> firstCandidateAsPreedit{this, "FirstCandidateAsPreedit",
106                                          _("First candidate as Preedit"),
107                                          false};
108     Option<Key, KeyConstrain> quickphrase{
109         this,
110         "QuickPhraseKey",
111         _("Key to trigger quickphrase"),
112         Key{},
113         {KeyConstrainFlag::AllowModifierLess}};
114     OptionWithAnnotation<std::string, ToolTipAnnotation> quickphraseText{
115         this,
116         "QuickPhraseText",
117         _("Text to trigger quickphrase"),
118         "",
119         {},
120         {},
121         {_("When a new character is typed, if it matches any character in this "
122            "string, all the existing text will be put into quickphrase "
123            "buffer.")}};
124     NoSaveOption<std::string> icon{this, "Icon", _("Icon")};
125     Option<int> noSortInputLength{this, "NoSortInputLength",
126                                   _("Don't sort word shorter than")};
127     Option<OrderPolicy> orderPolicy{this, "OrderPolicy", _("Order policy")};
128     OptionWithAnnotation<bool, ToolTipAnnotation> useSystemLanguageModel{
129         this,
130         "UseSystemLanguageModel",
131         _("Use system language model"),
132         true,
133         {},
134         {},
135         ToolTipAnnotation(_("When available, system language model will be "
136                             "loaded for given language and provide ability to "
137                             "give better context based order."))};
138     OptionWithAnnotation<bool, ToolTipAnnotation> useContextBasedOrder{
139         this,
140         "UseContextRelatedOrder",
141         _("Sort candidates based on current context"),
142         true,
143         {},
144         {},
145         ToolTipAnnotation(
146             _("Order the candidate based on the word you already typed. You "
147               "may want to disable this option if you want the candidate order "
148               "to be stable regardless of the history."))};
149     Option<Key, KeyConstrain> matchingKey{
150         this,
151         "MatchingKey",
152         _("Wildcard matching Key"),
153         Key(),
154         {KeyConstrainFlag::AllowModifierLess}};
155     Option<Key, KeyConstrain> pinyinKey{this,
156                                         "PinyinKey",
157                                         _("Prefix key to trigger Pinyin"),
158                                         Key(),
159                                         {KeyConstrainFlag::AllowModifierLess}};
160 
161     Option<bool> autoSelect{this, "AutoSelect", _("Auto select candidate")};
162     OptionWithAnnotation<int, ToolTipAnnotation> autoSelectLength{
163         this,
164         "AutoSelectLength",
165         _("Length limit of selecting the only candidate"),
166         0,
167         {},
168         {},
169         ToolTipAnnotation(
170             _("When current input length is greater than or equal to the limit "
171               "and there is only one candidate available, automatically select "
172               "this candidate. -1 Means the maximum code length of the table. "
173               "0 means this behavior is disabled."))};
174     OptionWithAnnotation<std::string, ToolTipAnnotation> autoSelectRegex{
175         this,
176         "AutoSelectRegex",
177         _("Regular expression for selecting the only candidate"),
178         "",
179         {},
180         {},
181         ToolTipAnnotation(_("When current input matches the regular expression "
182                             "and there is only one candidate available, "
183                             "automatically select this candidate."))};
184     OptionWithAnnotation<int, ToolTipAnnotation> noMatchAutoSelectLength{
185         this,
186         "NoMatchAutoSelectLength",
187         _("Auto select when no match is found"),
188         0,
189         {},
190         {},
191         ToolTipAnnotation(
192             _("When typing a new character, if the current input length is "
193               "greater than or equal to the limit, and the existing segment "
194               "with the new character does not have any matched entries in the "
195               "table, select the current candidate and then type in the new "
196               "character. -1 means the maximum code length of the table. 0 "
197               "means this behavior is disabled."))};
198     OptionWithAnnotation<std::string, ToolTipAnnotation> noMatchAutoSelectRegex{
199         this,
200         "NoMatchAutoSelectRegex",
201         _("Regular expression for auto select when no match is found"),
202         "",
203         {},
204         {},
205         ToolTipAnnotation(
206             _("When typing a new character, if the current input matches the "
207               "regular expression and the existing segment "
208               "with the new character does not have any matched entries in the "
209               "table, select the current candidate and then type in the new "
210               "character."))};
211     NoSaveOption<KeyList> endKey{this, "EndKey", _("End key")};
212     OptionWithAnnotation<int, ToolTipAnnotation> autoPhraseLength{
213         this,
214         "AutoPhraseLength",
215         _("Auto phrase length"),
216         -1,
217         {},
218         {},
219         ToolTipAnnotation(
220             _("After committing every character, learn the new phrase based on "
221               "the input history. If certain word is seen for multiple times "
222               "or selected by user once, it will be added as user phrase. -1 "
223               "means the maximum code length of the table. 0 means disable "
224               "this feature. "))};
225     Option<std::string> markerForAutoPhrase{
226         this, "MarkerForAutoPhrase",
227         _("Marker for auto phrase in the candidates"), "*"};
228     OptionWithAnnotation<int, ToolTipAnnotation> saveAutoPhraseAfter{
229         this,
230         "SaveAutoPhraseAfter",
231         _("Save auto phrase after being typed for ... times"),
232         -1,
233         {},
234         {},
235         ToolTipAnnotation(
236             _("If the value less than 0, means this feature is disabled. If "
237               "auto phrase is enabled, selecting auto phrase candidate "
238               "explicitly will still add the word as a new user phrase."))};
239     Option<bool> exactMatch{this, "ExactMatch", _("Exact Match")};
240     Option<bool> learning{this, "Learning", _("Learning"), true};
241     Option<bool> hint{this, "Hint", _("Display Hint for word")};
242     Option<bool> displayCustomHint{this, "DisplayCustomHint",
243                                    _("Display custom hint")};
244     Option<std::string> hintSeparator{
245         this, "HintSeparator", _("Separator text between hint and candidate"),
246         " ~ "};
247     OptionWithAnnotation<CandidateLayoutHint, CandidateLayoutHintI18NAnnotation>
248         candidateLayoutHint{this, "CandidateLayoutHint",
249                             _("Candidate List orientation"),
250                             CandidateLayoutHint::NotSet};
251     NoSaveOption<std::vector<std::string>> autoRuleSet{this, "AutoRuleSet",
252                                                        _("Auto rule set")};);
253 
254 FCITX_CONFIGURATION(PartialIMInfo, HiddenOption<std::string> languageCode{
255                                        this, "LangCode", "Language Code"};);
256 
257 FCITX_CONFIGURATION(TableConfigRoot,
258                     Option<TableConfig> config{this, "Table", "Table"};
259                     Option<PartialIMInfo, NoConstrain<PartialIMInfo>,
260                            DefaultMarshaller<PartialIMInfo>, NoSaveAnnotation>
261                         im{this, "InputMethod", "InputMethod"};);
262 
263 struct TableData {
264     TableConfigRoot root;
265     std::unique_ptr<libime::TableBasedDictionary> dict;
266     std::unique_ptr<libime::UserLanguageModel> model;
267 };
268 
269 class TableIME {
270 public:
271     TableIME(libime::LanguageModelResolver *lmResolver);
272 
273     const TableConfig &config(const std::string &name);
274 
275     std::tuple<libime::TableBasedDictionary *, libime::UserLanguageModel *,
276                const TableConfig *>
277     requestDict(const std::string &name);
278     void saveDict(const std::string &name);
279     void saveAll();
280     void updateConfig(const std::string &name, const RawConfig &config);
281 
282     void releaseUnusedDict(const std::unordered_set<std::string> &names);
283 
284 private:
285     libime::LanguageModelResolver *lm_;
286     std::unordered_map<std::string, TableData> tables_;
287 };
288 
289 FCITX_DECLARE_LOG_CATEGORY(table_logcategory);
290 
291 #define TABLE_DEBUG() FCITX_LOGC(::fcitx::table_logcategory, Debug)
292 #define TABLE_ERROR() FCITX_LOGC(::fcitx::table_logcategory, Error)
293 } // namespace fcitx
294 
295 #endif // _TABLE_TABLEDICTRESOLVER_H_
296