2828#import " McBopomofoLM.h"
2929#import " UTF8Helper.h"
3030#import " UserOverrideModel.h"
31+ #import " contextual_user_model.h"
3132#import " reading_grid.h"
3233
3334#import < algorithm>
@@ -60,6 +61,9 @@ @implementation KeyHandler {
6061 // user override model
6162 McBopomofo::UserOverrideModel *_userOverrideModel;
6263
64+ // contextual user model (KN backoff)
65+ Formosa::Gramambular2::ContextualUserModel *_contextualUserModel;
66+
6367 Formosa::Gramambular2::ReadingGrid *_grid;
6468 Formosa::Gramambular2::ReadingGrid::WalkResult _latestWalk;
6569
@@ -104,6 +108,7 @@ - (void)setInputMode:(NSString *)value
104108 std::shared_ptr<Formosa::Gramambular2::LanguageModel> lm (_emptySharedPtr, _languageModel);
105109 _grid = new Formosa::Gramambular2::ReadingGrid (lm);
106110 _grid->setReadingSeparator (" -" );
111+ _grid->setUserModel (_contextualUserModel);
107112 }
108113
109114 if (!_bpmfReadingBuffer->isEmpty ()) {
@@ -128,11 +133,13 @@ - (instancetype)init
128133 _languageModel = [LanguageModelManager languageModelMcBopomofo ];
129134 _languageModel->setPhraseReplacementEnabled (Preferences.phraseReplacementEnabled );
130135 _userOverrideModel = [LanguageModelManager userOverrideModel ];
136+ _contextualUserModel = [LanguageModelManager contextualUserModel ];
131137
132138 // This returns a shared_ptr that in turn points to an unmanaged object.
133139 std::shared_ptr<Formosa::Gramambular2::LanguageModel> lm (_emptySharedPtr, _languageModel);
134140 _grid = new Formosa::Gramambular2::ReadingGrid (lm);
135141 _grid->setReadingSeparator (" -" );
142+ _grid->setUserModel (_contextualUserModel);
136143
137144 _inputMode = InputModeBopomofo;
138145 }
@@ -171,26 +178,48 @@ - (void)syncWithPreferences
171178- (void )fixNodeWithReading : (NSString *)reading value : (NSString *)value originalCursorIndex : (size_t )originalCursorIndex useMoveCursorAfterSelectionSetting : (BOOL )flag
172179{
173180 size_t actualCursor = self.actualCandidateCursorIndex ;
174- Formosa::Gramambular2::ReadingGrid::Candidate candidate (reading.UTF8String , value.UTF8String );
175- if (!_grid->overrideCandidate (actualCursor, candidate)) {
181+ std::string readingStr (reading.UTF8String );
182+ std::string valueStr (value.UTF8String );
183+ Formosa::Gramambular2::ReadingGrid::Candidate candidate (readingStr, valueStr);
184+ if (!_grid->overrideCandidate (actualCursor, candidate,
185+ Formosa::Gramambular2::ReadingGrid::Node::OverrideType::kOverrideValueWithScoreFromTopUnigram )) {
176186 return ;
177187 }
178188
179- Formosa::Gramambular2::ReadingGrid::WalkResult prevWalk = _latestWalk;
189+ // Structurally fix the overridden node so the walk is constrained to it.
190+ auto nodeOpt = _grid->findInSpan (actualCursor, [&readingStr](const auto & node) {
191+ return node->reading () == readingStr && node->isOverridden ();
192+ });
193+ if (nodeOpt) {
194+ _grid->fixSpan (actualCursor, *nodeOpt);
195+ }
196+
180197 [self _walk ];
181198
182- // Update the user override model if warranted.
183199 size_t accumulatedCursor = 0 ;
184200 auto nodeIter = _latestWalk.findNodeAt (actualCursor, &accumulatedCursor);
185201 if (nodeIter == _latestWalk.nodes .cend ()) {
186202 return ;
187203 }
188204 Formosa::Gramambular2::ReadingGrid::NodePtr currentNode = *nodeIter;
189- if (currentNode != nullptr && currentNode->currentUnigram ().score () > -8 ) {
190- _userOverrideModel->observe (prevWalk, _latestWalk, self.actualCandidateCursorIndex , [NSDate date ].timeIntervalSince1970 );
205+ if (currentNode == nullptr ) {
206+ _grid->setCursor (originalCursorIndex);
207+ return ;
191208 }
192209
193- if (currentNode != nullptr && flag && Preferences.moveCursorAfterSelectingCandidate ) {
210+ std::string leftReading = Formosa::Gramambular2::ContextualUserModel::kStartSentinel ;
211+ std::string leftValue;
212+ if (nodeIter != _latestWalk.nodes .cbegin ()) {
213+ auto prevNode = *(nodeIter - 1 );
214+ leftReading = prevNode->reading ();
215+ leftValue = prevNode->value ();
216+ }
217+ _contextualUserModel->observe (leftReading, leftValue,
218+ currentNode->reading (), currentNode->value (),
219+ [NSDate date ].timeIntervalSince1970 );
220+ [LanguageModelManager saveContextualUserModel ];
221+
222+ if (flag && Preferences.moveCursorAfterSelectingCandidate ) {
194223 _grid->setCursor (accumulatedCursor);
195224 } else {
196225 _grid->setCursor (originalCursorIndex);
@@ -525,16 +554,6 @@ - (BOOL)handleInput:(KeyHandlerInput *)input state:(InputState *)inState stateCa
525554 _grid->insertReading (reading);
526555 [self _walk ];
527556
528- // get user override model suggestion
529- if (_inputMode != InputModePlainBopomofo) {
530- McBopomofo::UserOverrideModel::Suggestion suggestion = _userOverrideModel->suggest (_latestWalk, self.actualCandidateCursorIndex , [NSDate date ].timeIntervalSince1970 );
531- if (!suggestion.empty ()) {
532- Formosa::Gramambular2::ReadingGrid::Node::OverrideType type = suggestion.forceHighScoreOverride ? Formosa::Gramambular2::ReadingGrid::Node::OverrideType::kOverrideValueWithHighScore : Formosa::Gramambular2::ReadingGrid::Node::OverrideType::kOverrideValueWithScoreFromTopUnigram ;
533- _grid->overrideCandidate (self.actualCandidateCursorIndex , suggestion.candidate , type);
534- [self _walk ];
535- }
536- }
537-
538557 // then update the text
539558 _bpmfReadingBuffer->clear ();
540559
0 commit comments