From b357d5d2c98369159074f41580af8635d99ead37 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 8 Feb 2024 18:19:10 +0100 Subject: [PATCH] prevent commit keystroke being hijacked & inline placeholder &fix terminal non-inline --- SquirrelConfig.h | 2 +- SquirrelConfig.m | 18 ++++--- SquirrelInputController.m | 111 +++++++++++++++++++++++++++----------- data/squirrel.yaml | 8 +++ 4 files changed, 100 insertions(+), 39 deletions(-) diff --git a/SquirrelConfig.h b/SquirrelConfig.h index c4bd50145..9b2e32d73 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -17,7 +17,7 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; - (BOOL)hasSection:(NSString *)section; - (BOOL)getBool:(NSString *)option; -- (NSInteger)getInt:(NSString *)option; +- (int)getInt:(NSString *)option; - (double)getDouble:(NSString *)option; - (NSNumber *)getOptionalBool:(NSString *)option; - (NSNumber *)getOptionalInt:(NSString *)option; diff --git a/SquirrelConfig.m b/SquirrelConfig.m index d816f1b40..0e4ca191e 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -67,8 +67,8 @@ - (BOOL)getBool:(NSString *)option { return [self getOptionalBool:option].boolValue; } -- (NSInteger)getInt:(NSString *)option { - return [self getOptionalInt:option].integerValue; +- (int)getInt:(NSString *)option { + return [self getOptionalInt:option].intValue; } - (double)getDouble:(NSString *)option { @@ -82,7 +82,7 @@ - (NSNumber *)getOptionalBool:(NSString *)option { } Bool value; if (_isOpen && rime_get_api()->config_get_bool(&_config, option.UTF8String, &value)) { - return _cache[option] = @(!!value); + return _cache[option] = [NSNumber numberWithBool:(BOOL)value]; } return [_baseConfig getOptionalBool:option]; } @@ -94,7 +94,7 @@ - (NSNumber *)getOptionalInt:(NSString *)option { } int value; if (_isOpen && rime_get_api()->config_get_int(&_config, option.UTF8String, &value)) { - return _cache[option] = @(value); + return _cache[option] = [NSNumber numberWithInt:value]; } return [_baseConfig getOptionalInt:option]; @@ -107,7 +107,7 @@ - (NSNumber *)getOptionalDouble:(NSString *)option { } double value; if (_isOpen && rime_get_api()->config_get_double(&_config, option.UTF8String, &value)) { - return _cache[option] = @(value); + return _cache[option] = [NSNumber numberWithDouble:value]; } return [_baseConfig getOptionalDouble:option]; } @@ -145,8 +145,12 @@ - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { rime_get_api()->config_begin_map(&iterator, &_config, rootKey.UTF8String); while (rime_get_api()->config_next(&iterator)) { //NSLog(@"DEBUG option[%d]: %s (%s)", iterator.index, iterator.key, iterator.path); - BOOL value = [self getBool:@(iterator.path)]; - appOptions[@(iterator.key)] = @(value); + NSNumber *value = [self getOptionalBool:@(iterator.path)] ? : + [self getOptionalInt:@(iterator.path)] ? : + [self getOptionalDouble:@(iterator.path)]; + if (value) { + appOptions[@(iterator.key)] = value; + } } rime_get_api()->config_end(&iterator); return [appOptions copy]; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 02f0ef8a4..7f9a4365a 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -10,7 +10,7 @@ @interface SquirrelInputController(Private) -(void)createSession; -(void)destroySession; --(void)rimeConsumeCommittedText; +-(BOOL)rimeConsumeCommittedText; -(void)rimeUpdate; -(void)updateAppOptions; @end @@ -29,6 +29,10 @@ @implementation SquirrelInputController { NSString *_schemaId; BOOL _inlinePreedit; BOOL _inlineCandidate; + // app-specific bug fix + BOOL _inlinePlaceholder; + BOOL _panellessCommitFix; + int _inlineOffset; // for chord-typing int _chordKeyCodes[N_KEY_ROLL_OVER]; int _chordModifiers[N_KEY_ROLL_OVER]; @@ -150,8 +154,20 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender modifiers & OSX_CAPITAL_MASK); if (rime_keycode) { int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); - handled = [self processKey:rime_keycode modifiers:rime_modifiers]; - [self rimeUpdate]; + if ((handled = [self processKey:rime_keycode modifiers:rime_modifiers])) { + [self rimeUpdate]; + } else if (_panellessCommitFix && [_currentClient markedRange].length > 0) { + if (rime_keycode == XK_Delete || (rime_keycode >= XK_Home && rime_keycode <= XK_KP_Delete) || + (rime_keycode >= XK_BackSpace && rime_keycode <= XK_Escape)) { + [self showPlaceholder:@""]; + } else if (!(modifiers & (NSEventModifierFlagControl | NSEventModifierFlagCommand)) && + event.characters.length > 0) { + [self showPlaceholder:nil]; + [_currentClient insertText:event.characters + replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; + return YES; + } + } } } break; default: @@ -418,6 +434,17 @@ -(void)commitString:(NSString*)string [NSApp.squirrelAppDelegate.panel hide]; } +-(void)showPlaceholder:(NSString*)placeholder +{ + NSDictionary* attrs = [self markForStyle:kTSMHiliteSelectedRawText + atRange:NSMakeRange(0, placeholder ? placeholder.length : 1)]; + NSAttributedString* attrString = [[NSAttributedString alloc] initWithString:placeholder ? : @"█" + attributes:attrs]; + [_currentClient setMarkedText:attrString + selectionRange:NSMakeRange(0, 0) + replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; +} + -(void)showPreeditString:(NSString*)preedit selRange:(NSRange)range caretPos:(NSUInteger)pos @@ -464,6 +491,8 @@ -(void)showPanelWithPreedit:(NSString*)preedit _candidates = candidates; NSRect inputPos; [_currentClient attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; + NSWidth(inputPos) > NSHeight(inputPos) ? (inputPos.origin.x += _inlineOffset) + : (inputPos.origin.y += _inlineOffset); SquirrelPanel* panel = NSApp.squirrelAppDelegate.panel; panel.position = inputPos; panel.inputController = self; @@ -504,10 +533,16 @@ -(void)updateAppOptions SquirrelAppOptions* appOptions = [NSApp.squirrelAppDelegate.config getAppOptions:_currentApp]; if (appOptions) { for (NSString* key in appOptions) { - BOOL value = appOptions[key].boolValue; - NSLog(@"set app option: %@ = %d", key, value); - rime_get_api()->set_option(_session, key.UTF8String, value); + NSNumber *number = appOptions[key]; + if (!strcmp(number.objCType, @encode(BOOL))) { + Bool value = number.intValue; + //NSLog(@"set app option: %@ = %d", key, value); + rime_get_api()->set_option(_session, key.UTF8String, value); + } } + _panellessCommitFix = appOptions[@"panelless_commit_fix"].boolValue; + _inlinePlaceholder = appOptions[@"inline_placeholder"].boolValue; + _inlineOffset = appOptions[@"inline_offset"].intValue; } } @@ -521,14 +556,22 @@ -(void)destroySession [self clearChord]; } --(void)rimeConsumeCommittedText +-(BOOL)rimeConsumeCommittedText { RIME_STRUCT(RimeCommit, commit); if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); - [self commitString: commitText]; + if (_panellessCommitFix) { + [self showPlaceholder:commitText]; + [self commitString:commitText]; + [self showPlaceholder:sizeof(commit.text) == 1 ? @"" : nil]; + } else { + [self commitString:commitText]; + } rime_get_api()->free_commit(&commit); + return YES; } + return NO; } NSString *substr(const char *str, int length) { @@ -541,7 +584,7 @@ -(void)rimeConsumeCommittedText -(void)rimeUpdate { //NSLog(@"rimeUpdate"); - [self rimeConsumeCommittedText]; + BOOL committedText = [self rimeConsumeCommittedText]; RIME_STRUCT(RimeStatus, status); if (rime_get_api()->get_status(_session, &status)) { @@ -570,32 +613,38 @@ -(void)rimeUpdate NSUInteger start = substr(preedit, ctx.composition.sel_start).length; NSUInteger end = substr(preedit, ctx.composition.sel_end).length; NSUInteger caretPos = substr(preedit, ctx.composition.cursor_pos).length; + NSUInteger numCandidate = ctx.menu.num_candidates; NSRange selRange = NSMakeRange(start, end - start); - if (_inlineCandidate) { - const char *candidatePreview = ctx.commit_text_preview; - NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; - if (_inlinePreedit) { - if ((caretPos >= NSMaxRange(selRange)) && (caretPos < preeditText.length)) { - candidatePreviewText = [candidatePreviewText stringByAppendingString:[preeditText substringWithRange:NSMakeRange(caretPos, preeditText.length-caretPos)]]; + if (!(_panellessCommitFix && committedText)) { + if (_inlineCandidate) { + const char *candidatePreview = ctx.commit_text_preview; + NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; + if (_inlinePreedit) { + if ((caretPos >= NSMaxRange(selRange)) && (caretPos < preeditText.length)) { + candidatePreviewText = [candidatePreviewText stringByAppendingString:[preeditText substringWithRange:NSMakeRange(caretPos, preeditText.length-caretPos)]]; + } + [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length-(preeditText.length-caretPos)]; + } else { + if ((NSMaxRange(selRange) < caretPos) && (caretPos > selRange.location)) { + candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(caretPos-NSMaxRange(selRange)))]; + } else if ((NSMaxRange(selRange) < preeditText.length) && (caretPos <= selRange.location)) { + candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(preeditText.length-NSMaxRange(selRange)))]; + } + [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length]; } - [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length-(preeditText.length-caretPos)]; } else { - if ((NSMaxRange(selRange) < caretPos) && (caretPos > selRange.location)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(caretPos-NSMaxRange(selRange)))]; - } else if ((NSMaxRange(selRange) < preeditText.length) && (caretPos <= selRange.location)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(preeditText.length-NSMaxRange(selRange)))]; + if (_inlinePreedit) { + _inlinePlaceholder && preeditText.length == 0 && numCandidate > 0 + ? [self showPlaceholder:@" "] + : [self showPreeditString:preeditText selRange:selRange caretPos:caretPos]; + } else { + NSRange empty = {0, 0}; + // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. + // note this is a full-shape space U+3000; using half shape characters like "..." will result in + // an unstable baseline when composing Chinese characters. + _inlinePlaceholder && preedit ? [self showPlaceholder:@" "] + : [self showPreeditString:@"" selRange:empty caretPos:0]; } - [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length]; - } - } else { - if (_inlinePreedit) { - [self showPreeditString:preeditText selRange:selRange caretPos:caretPos]; - } else { - NSRange empty = {0, 0}; - // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. - // note this is a full-shape space U+3000; using half shape characters like "..." will result in - // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit ? @" " : @"") selRange:empty caretPos:0]; } } // update candidates diff --git a/data/squirrel.yaml b/data/squirrel.yaml index a231691b4..2692803cc 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -341,6 +341,7 @@ app_options: com.apple.Terminal: ascii_mode: true no_inline: true + inline_placeholder: true com.googlecode.iterm2: ascii_mode: true no_inline: true @@ -350,6 +351,7 @@ app_options: vim_mode: true # 退出VIM插入模式自動切換輸入法狀態 com.apple.dt.Xcode: ascii_mode: true + no_inline: true com.barebones.textwrangler: ascii_mode: true com.macromates.TextMate.preview: @@ -367,9 +369,15 @@ app_options: no_inline: true co.zeit.hyper: ascii_mode: true + org.alacritty: + ascii_mode: true + vim_mode: true + panelless_commit_fix: true + inline_offset: -10 com.google.Chrome: # 規避 https://github.com/rime/squirrel/issues/435 inline: true + inline_placeholder: true ru.keepcoder.Telegram: # 規避 https://github.com/rime/squirrel/issues/475 inline: true