1010@interface SquirrelInputController (Private)
1111-(void )createSession ;
1212-(void )destroySession ;
13- -(void )rimeConsumeCommittedText ;
13+ -(BOOL )rimeConsumeCommittedText ;
1414-(void )rimeUpdate ;
1515-(void )updateAppOptions ;
1616@end
1717
1818const int N_KEY_ROLL_OVER = 50 ;
1919
2020@implementation SquirrelInputController {
21- id _currentClient;
22- NSString *_preeditString;
21+ NSMutableAttributedString *_preeditString;
22+ NSAttributedString *_originalString;
23+ NSString *_composedString;
2324 NSRange _selRange;
2425 NSUInteger _caretPos;
2526 NSArray *_candidates;
@@ -29,6 +30,10 @@ @implementation SquirrelInputController {
2930 NSString *_schemaId;
3031 BOOL _inlinePreedit;
3132 BOOL _inlineCandidate;
33+ BOOL _isComposing;
34+ // app-specific bug fix
35+ BOOL _inlinePlaceHolder;
36+ BOOL _panellessCommitFix;
3237 // for chord-typing
3338 int _chordKeyCodes[N_KEY_ROLL_OVER];
3439 int _chordModifiers[N_KEY_ROLL_OVER];
@@ -49,9 +54,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender
4954 // Key processing will not continue in that case. In other words the
5055 // system will not deliver a key down event to the application.
5156 // Returning NO means the original key down will be passed on to the client.
52-
53- _currentClient = sender;
54-
5557 NSUInteger modifiers = event.modifierFlags ;
5658
5759 BOOL handled = NO ;
@@ -64,7 +66,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender
6466 }
6567 }
6668
67- NSString * app = [_currentClient bundleIdentifier ];
69+ NSString * app = [sender bundleIdentifier ];
6870
6971 if (![_currentApp isEqualToString: app]) {
7072 _currentApp = [app copy ];
@@ -319,14 +321,15 @@ -(void)activateServer:(id)sender
319321 if (keyboardLayout) {
320322 [sender overrideKeyboardWithKeyboardNamed: keyboardLayout];
321323 }
322- _preeditString = @" " ;
324+ _preeditString = nil ;
325+ _originalString = nil ;
326+ _composedString = nil ;
323327}
324328
325329-(instancetype )initWithServer : (IMKServer*)server delegate : (id )delegate client : (id )inputClient
326330{
327331 // NSLog(@"initWithServer:delegate:client:");
328332 if (self = [super initWithServer: server delegate: delegate client: inputClient]) {
329- _currentClient = inputClient;
330333 [self createSession ];
331334 }
332335 return self;
@@ -335,7 +338,6 @@ -(instancetype)initWithServer:(IMKServer*)server delegate:(id)delegate client:(i
335338-(void )deactivateServer : (id )sender
336339{
337340 // NSLog(@"deactivateServer:");
338- [NSApp .squirrelAppDelegate.panel hide ];
339341 [self commitComposition: sender];
340342}
341343
@@ -353,13 +355,9 @@ -(void)deactivateServer:(id)sender
353355-(void )commitComposition : (id )sender
354356{
355357 // NSLog(@"commitComposition:");
356- // commit raw input
357358 if (_session) {
358- const char * raw_input = rime_get_api ()->get_input (_session);
359- if (raw_input) {
360- [self commitString: @(raw_input)];
361- rime_get_api ()->clear_composition (_session);
362- }
359+ [self commitString: [self composedString: sender]];
360+ rime_get_api ()->clear_composition (_session);
363361 }
364362}
365363
@@ -402,54 +400,85 @@ -(NSArray*)candidates:(id)sender
402400 return _candidates;
403401}
404402
405- -(void )dealloc
403+ - (void )hidePalettes
404+ {
405+ [NSApp .squirrelAppDelegate.panel hide ];
406+ }
407+
408+ - (void )dealloc
406409{
407410 [self destroySession ];
408411}
409412
410- -(void )commitString : (NSString *)string
413+ - (NSRange )selectionRange
414+ {
415+ return NSMakeRange (_caretPos, 0 );
416+ }
417+
418+ - (NSRange )replacementRange
419+ {
420+ return _isComposing ? self.client .selectedRange : NSMakeRange (NSNotFound , NSNotFound );
421+ }
422+
423+ - (void )commitString : (id )string
411424{
412425 // NSLog(@"commitString:");
413- [_currentClient insertText: string
414- replacementRange: NSMakeRange (NSNotFound , 0 )];
426+ [self .client insertText: string
427+ replacementRange: self .replacementRange];
428+ [self hidePalettes ];
415429
416- _preeditString = @" " ;
430+ _composedString = nil ;
431+ _originalString = nil ;
432+ _preeditString = nil ;
433+ }
417434
418- [NSApp .squirrelAppDelegate.panel hide ];
435+ - (void )cancelComposition
436+ {
437+ [self commitString: [self originalString: self .client]];
438+ rime_get_api ()->clear_composition (_session);
439+ }
440+
441+ - (void )updateComposition
442+ {
443+ [self .client setMarkedText: _preeditString
444+ selectionRange: self .selectionRange
445+ replacementRange: self .replacementRange];
419446}
420447
421448-(void )showPreeditString : (NSString *)preedit
422449 selRange : (NSRange )range
423450 caretPos : (NSUInteger )pos
424451{
425452 // NSLog(@"showPreeditString: '%@'", preedit);
426-
427- if ([_preeditString isEqualToString: preedit] &&
428- _caretPos == pos && _selRange.location == range.location && _selRange.length == range.length )
453+ if ([preedit isEqualToString: _preeditString.string] &&
454+ NSEqualRanges (range, _selRange) && pos == _caretPos) {
455+ if (_inlinePlaceHolder) {
456+ [self updateComposition ];
457+ }
429458 return ;
430-
431- _preeditString = preedit;
459+ }
432460 _selRange = range;
433461 _caretPos = pos;
434462
435463 // NSLog(@"selRange.location = %ld, selRange.length = %ld; caretPos = %ld",
436464 // range.location, range.length, pos);
437- NSDictionary * attrs;
438- NSMutableAttributedString * attrString = [[NSMutableAttributedString alloc ] initWithString: preedit];
465+ NSDictionary * attrs;
466+ _preeditString = [[NSMutableAttributedString alloc ] initWithString: preedit];
439467 if (range.location > 0 ) {
440468 NSRange convertedRange = NSMakeRange (0 , range.location );
441469 attrs = [self markForStyle: kTSMHiliteConvertedText atRange: convertedRange];
442- [attrString setAttributes : attrs range: convertedRange];
470+ [_preeditString addAttributes : attrs range: convertedRange];
443471 }
444- {
445- NSRange remainingRange = NSMakeRange (range.location , preedit.length - range.location );
446- attrs = [self markForStyle: kTSMHiliteSelectedRawText atRange: remainingRange];
447- [attrString setAttributes: attrs range: remainingRange];
472+ if (range.location < pos) {
473+ attrs = [self markForStyle: kTSMHiliteSelectedConvertedText atRange: range];
474+ [_preeditString addAttributes: attrs range: range];
448475 }
449- [_currentClient setMarkedText: attrString
450- selectionRange: NSMakeRange (pos, 0 )
451- replacementRange: NSMakeRange (NSNotFound , 0 )];
452-
476+ if (MIN (NSMaxRange (range), pos) < preedit.length ) {
477+ NSRange rawRange = NSMakeRange (MIN (NSMaxRange (range), pos), preedit.length - MIN (NSMaxRange (range), pos));
478+ attrs = [self markForStyle: kTSMHiliteSelectedRawText atRange: rawRange];
479+ [_preeditString addAttributes: attrs range: rawRange];
480+ }
481+ [self updateComposition ];
453482}
454483
455484-(void )showPanelWithPreedit : (NSString *)preedit
@@ -463,7 +492,7 @@ -(void)showPanelWithPreedit:(NSString*)preedit
463492 // NSLog(@"showPanelWithPreedit:...:");
464493 _candidates = candidates;
465494 NSRect inputPos;
466- [_currentClient attributesForCharacterIndex: 0 lineHeightRectangle: &inputPos];
495+ [self .client attributesForCharacterIndex: 0 lineHeightRectangle: &inputPos];
467496 SquirrelPanel* panel = NSApp .squirrelAppDelegate .panel ;
468497 panel.position = inputPos;
469498 panel.inputController = self;
@@ -485,7 +514,7 @@ @implementation SquirrelInputController(Private)
485514
486515-(void )createSession
487516{
488- NSString * app = [_currentClient bundleIdentifier ];
517+ NSString * app = [self .client bundleIdentifier ];
489518 NSLog (@" createSession: %@ " , app);
490519 _currentApp = [app copy ];
491520 _session = rime_get_api ()->create_session ();
@@ -508,6 +537,8 @@ -(void)updateAppOptions
508537 NSLog (@" set app option: %@ = %d " , key, value);
509538 rime_get_api ()->set_option (_session, key.UTF8String , value);
510539 }
540+ _panellessCommitFix = (appOptions[@" panelless_commit_fix" ] ? : @(NO )).boolValue ;
541+ _inlinePlaceHolder = (appOptions[@" inline_placeholder" ] ? : @(NO )).boolValue ;
511542 }
512543}
513544
@@ -521,15 +552,19 @@ -(void)destroySession
521552 [self clearChord ];
522553}
523554
524- -(void )rimeConsumeCommittedText
555+ -(BOOL )rimeConsumeCommittedText
525556{
526557 RIME_STRUCT (RimeCommit, commit);
527558 if (rime_get_api ()->get_commit (_session, &commit)) {
528559 NSString *commitText = @(commit.text );
529- [self showPreeditString: @" " selRange: NSMakeRange (0 , 0 ) caretPos: 0 ];
560+ if (_preeditString.length == 0 && _panellessCommitFix) {
561+ [self showPreeditString: @" " selRange: NSMakeRange (0 , 0 ) caretPos: 0 ];
562+ }
530563 [self commitString: commitText];
531564 rime_get_api ()->free_commit (&commit);
565+ return YES ;
532566 }
567+ return NO ;
533568}
534569
535570NSString *substr (const char *str, int length) {
@@ -542,7 +577,9 @@ -(void)rimeConsumeCommittedText
542577-(void )rimeUpdate
543578{
544579 // NSLog(@"rimeUpdate");
545- [self rimeConsumeCommittedText ];
580+ if ([self rimeConsumeCommittedText ]) {
581+ return ;
582+ }
546583
547584 RIME_STRUCT (RimeStatus, status);
548585 if (rime_get_api ()->get_status (_session, &status)) {
@@ -558,6 +595,7 @@ -(void)rimeUpdate
558595 !rime_get_api ()->get_option (_session, " no_inline" ));
559596 // if not inline, embed soft cursor in preedit string
560597 rime_get_api ()->set_option (_session, " soft_cursor" , !_inlinePreedit);
598+ _isComposing = status.is_composing ;
561599 }
562600 rime_get_api ()->free_status (&status);
563601 }
@@ -592,11 +630,11 @@ -(void)rimeUpdate
592630 if (_inlinePreedit) {
593631 [self showPreeditString: preeditText selRange: selRange caretPos: caretPos];
594632 } else {
595- NSRange empty = {0 , 0 };
596633 // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit.
597634 // note this is a full-shape space U+3000; using half shape characters like "..." will result in
598635 // an unstable baseline when composing Chinese characters.
599- [self showPreeditString: (preedit ? @" " : @" " ) selRange: empty caretPos: 0 ];
636+ [self showPreeditString: (preedit && _inlinePlaceHolder ? @" " : @" " )
637+ selRange: NSMakeRange (0 , 0 ) caretPos: 0 ];
600638 }
601639 }
602640 // update candidates
0 commit comments