From 9cd2306313db888b929c71d77175255e2a01d12e Mon Sep 17 00:00:00 2001 From: zonble Date: Thu, 13 Jan 2022 19:57:08 +0800 Subject: [PATCH] Adds emacs-style hotkeys. --- Source/InputMethodController.mm | 98 ++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index 26901fab..a501406e 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -104,6 +104,16 @@ enum { kDeleteKeyCode = 117 }; +typedef NS_ENUM(NSUInteger, McBpomofoEmacsKey) { + McBpomofoEmacsKeyNone, + McBpomofoEmacsKeyForward, + McBpomofoEmacsKeyBackward, + McBpomofoEmacsKeyHome, + McBpomofoEmacsKeyEnd, + McBpomofoEmacsKeyDelete, + McBpomofoEmacsKeyNextPage, +}; + VTCandidateController *gCurrentCandidateController = nil; // if DEBUG is defined, a DOT file (GraphViz format) will be written to the @@ -567,7 +577,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } NSBeep(); } -- (string)currentLayout +- (string)_currentLayout { string layout = string("Standard_");; NSInteger keyboardLayout = [[NSUserDefaults standardUserDefaults] integerForKey:kKeyboardLayoutPreferenceKey]; @@ -636,6 +646,33 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return [LanguageModelManager writeUserPhrase:currentMarkedPhrase]; } +- (McBpomofoEmacsKey)detectEmacsKeyFromCharCode:(UniChar)charCode modifiers:(NSUInteger)flags +{ + if (flags & NSControlKeyMask) { + char c = charCode + 'a' - 1; + if (c == 'a') { + return McBpomofoEmacsKeyHome; + } + else if (c == 'e') { + return McBpomofoEmacsKeyEnd; + } + else if (c == 'f') { + return McBpomofoEmacsKeyForward; + } + else if (c == 'b') { + return McBpomofoEmacsKeyBackward; + } + else if (c == 'd') { + return McBpomofoEmacsKeyDelete; + } + else if (c == 'v') { + return McBpomofoEmacsKeyNextPage; + } + } + return McBpomofoEmacsKeyNone; +} + + - (BOOL)handleInputText:(NSString*)inputText key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)client { NSRect textFrame = NSZeroRect; @@ -661,6 +698,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // get the unicode character code UniChar charCode = [inputText length] ? [inputText characterAtIndex:0] : 0; + McBpomofoEmacsKey emacsKey = [self detectEmacsKeyFromCharCode:charCode modifiers:flags]; + if ([[client bundleIdentifier] isEqualToString:@"com.apple.Terminal"] && [NSStringFromClass([client class]) isEqualToString:@"IPMDServerClientWrapper"]) { // special handling for com.apple.Terminal _currentDeferredClient = client; @@ -672,7 +711,9 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // if the composing buffer is empty and there's no reading, and there is some function key combination, we ignore it - if (![_composingBuffer length] && _bpmfReadingBuffer->isEmpty() && ((flags & NSCommandKeyMask) || (flags & NSControlKeyMask) || (flags & NSAlternateKeyMask) || (flags & NSNumericPadKeyMask))) { + if (![_composingBuffer length] && + _bpmfReadingBuffer->isEmpty() && + ((flags & NSCommandKeyMask) || (flags & NSControlKeyMask) || (flags & NSAlternateKeyMask) || (flags & NSNumericPadKeyMask))) { return NO; } @@ -716,7 +757,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // if we have candidate, it means we need to pass the event to the candidate handler if ([_candidates count]) { - return [self handleCandidateEventWithInputText:inputText charCode:charCode keyCode:keyCode]; + return [self _handleCandidateEventWithInputText:inputText charCode:charCode keyCode:keyCode emacsKey:(McBpomofoEmacsKey)emacsKey]; } // If we have marker index. @@ -739,7 +780,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } // Shift + left - if (keyCode == cursorBackwardKey && (flags & NSShiftKeyMask)) { + if ((keyCode == cursorBackwardKey || emacsKey == McBpomofoEmacsKeyBackward) + && (flags & NSShiftKeyMask)) { if (_builder->markerCursorIndex() > 0) { _builder->setMarkerCursorIndex(_builder->markerCursorIndex() - 1); } @@ -750,7 +792,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } // Shift + Right - if (keyCode == cursorForwardKey && (flags & NSShiftKeyMask)) { + if ((keyCode == cursorForwardKey || emacsKey == McBpomofoEmacsKeyForward) + && (flags & NSShiftKeyMask)) { if (_builder->markerCursorIndex() < _builder->length()) { _builder->setMarkerCursorIndex(_builder->markerCursorIndex() + 1); } @@ -883,7 +926,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // handle cursor backward - if (keyCode == cursorBackwardKey) { + if (keyCode == cursorBackwardKey || emacsKey == McBpomofoEmacsKeyBackward) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -915,7 +958,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // handle cursor forward - if (keyCode == cursorForwardKey) { + if (keyCode == cursorForwardKey || emacsKey == McBpomofoEmacsKeyForward) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -945,7 +988,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } - if (keyCode == kHomeKeyCode) { + if (keyCode == kHomeKeyCode || emacsKey == McBpomofoEmacsKeyHome) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -966,7 +1009,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } - if (keyCode == kEndKeyCode) { + if (keyCode == kEndKeyCode || emacsKey == McBpomofoEmacsKeyEnd) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -1019,7 +1062,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // Delete - if (keyCode == kDeleteKeyCode) { + if (keyCode == kDeleteKeyCode || emacsKey == McBpomofoEmacsKeyDelete) { if (_bpmfReadingBuffer->isEmpty()) { if (![_composingBuffer length]) { return NO; @@ -1041,7 +1084,6 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } - // Enter if (charCode == 13) { if (![_composingBuffer length]) { @@ -1069,16 +1111,16 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // if nothing is matched, see if it's a punctuation key for current layout. - string layout = [self currentLayout]; + string layout = [self _currentLayout]; string punctuationNamePrefix = (_halfSizePunctuationEnabled ? string("_half_punctuation_"): string("_punctuation_")); string customPunctuation = punctuationNamePrefix + layout + string(1, (char)charCode); - if ([self handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) { + if ([self _handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) { return YES; } // if nothing is matched, see if it's a punctuation key. string punctuation = punctuationNamePrefix + string(1, (char)charCode); - if ([self handlePunctuation:punctuation usingVerticalMode:useVerticalMode client:client]) { + if ([self _handlePunctuation:punctuation usingVerticalMode:useVerticalMode client:client]) { return YES; } @@ -1094,7 +1136,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return NO; } -- (BOOL)handlePunctuation:(string)customPunctuation usingVerticalMode:(BOOL)useVerticalMode client:(id)client +- (BOOL)_handlePunctuation:(string)customPunctuation usingVerticalMode:(BOOL)useVerticalMode client:(id)client { if (_languageModel->hasUnigramsForKey(customPunctuation)) { if (_bpmfReadingBuffer->isEmpty()) { @@ -1120,7 +1162,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return NO; } -- (BOOL)handleCandidateEventWithInputText:(NSString *)inputText charCode:(UniChar)charCode keyCode:(NSUInteger)keyCode +- (BOOL)_handleCandidateEventWithInputText:(NSString *)inputText charCode:(UniChar)charCode keyCode:(NSUInteger)keyCode emacsKey:(McBpomofoEmacsKey)emacsKey { BOOL cancelCandidateKey = (charCode == 27) || @@ -1143,7 +1185,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } [self candidateController:gCurrentCandidateController didSelectCandidateAtIndex:gCurrentCandidateController.selectedCandidateIndex]; return YES; } - else if (charCode == 32 || keyCode == kPageDownKeyCode) { + else if (charCode == 32 || keyCode == kPageDownKeyCode || emacsKey == McBpomofoEmacsKeyNextPage) { BOOL updated = [gCurrentCandidateController showNextPage]; if (!updated) { [self beep]; @@ -1177,6 +1219,14 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } } + else if (emacsKey == McBpomofoEmacsKeyBackward) { + BOOL updated = [gCurrentCandidateController highlightPreviousCandidate]; + if (!updated) { + [self beep]; + } + [self updateClientComposingBuffer:_currentCandidateClient]; + return YES; + } else if (keyCode == kRightKeyCode) { if ([gCurrentCandidateController isKindOfClass:[VTHorizontalCandidateController class]]) { BOOL updated = [gCurrentCandidateController highlightNextCandidate]; @@ -1195,6 +1245,14 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } } + else if (emacsKey == McBpomofoEmacsKeyForward) { + BOOL updated = [gCurrentCandidateController highlightNextCandidate]; + if (!updated) { + [self beep]; + } + [self updateClientComposingBuffer:_currentCandidateClient]; + return YES; + } else if (keyCode == kUpKeyCode) { if ([gCurrentCandidateController isKindOfClass:[VTHorizontalCandidateController class]]) { BOOL updated = [gCurrentCandidateController showPreviousPage]; @@ -1231,7 +1289,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } } - else if (keyCode == kHomeKeyCode) { + else if (keyCode == kHomeKeyCode || emacsKey == McBpomofoEmacsKeyHome) { if (gCurrentCandidateController.selectedCandidateIndex == 0) { [self beep]; @@ -1243,7 +1301,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } [self updateClientComposingBuffer:_currentCandidateClient]; return YES; } - else if (keyCode == kEndKeyCode && [_candidates count] > 0) { + else if ((keyCode == kEndKeyCode || emacsKey == McBpomofoEmacsKeyEnd) && [_candidates count] > 0) { if (gCurrentCandidateController.selectedCandidateIndex == [_candidates count] - 1) { [self beep]; } @@ -1273,7 +1331,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } if (_inputMode == kPlainBopomofoModeIdentifier) { - string layout = [self currentLayout]; + string layout = [self _currentLayout]; string customPunctuation = string("_punctuation_") + layout + string(1, (char)charCode); string punctuation = string("_punctuation_") + string(1, (char)charCode);