Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prevent commit keystroke being hijacked #792

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SquirrelConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ typedef NSMutableDictionary<NSString *, NSNumber *> 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;
Expand Down
18 changes: 11 additions & 7 deletions SquirrelConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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];
}
Expand All @@ -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];

Expand All @@ -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];
}
Expand Down Expand Up @@ -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];
Expand Down
111 changes: 80 additions & 31 deletions SquirrelInputController.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
@interface SquirrelInputController(Private)
-(void)createSession;
-(void)destroySession;
-(void)rimeConsumeCommittedText;
-(BOOL)rimeConsumeCommittedText;
-(void)rimeUpdate;
-(void)updateAppOptions;
@end
Expand All @@ -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];
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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) {
Expand All @@ -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)) {
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions data/squirrel.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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
Loading