MacVim のインライン変換で、変換対象の文節の範囲とそれ以外との表示に差がない問題を修正する
- 環境:MacVim release 174 + ATOK (2022-12-28の最新版)
- 問題点:インラインIM変換対象の文節のアンダーラインが、変換対象でない部分との表示の差がなく、文節長を調節する際などに不便。
- たとえば下の画像では「吾輩は」までが変換対象の文節になっているのだが、「猫である」と表示に差がない。
- 参考
- MacVim-KaoriYa 20180324(最新版) で Core Text レンダラを切れば、この問題は発生しない。
MacVimとMacVim-KaoriYaの差分 を参考に、問題に関与していると思われる部分を取り込んだパッチを作成した(ページの下部に diff を掲載する)。パッチを当てると、下の画像のように変換対象文字列が他の部分と区別できるようになった。
- 備考:ビルドは 以前の記事 の設定でおこなった。
パッチ:
diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 3c3dcaddd..d9d35d8a2 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -59,7 +59,7 @@ static id evalExprCocoa(NSString * expr, NSString ** errstr); void im_preedit_start_macvim(); void im_preedit_end_macvim(); void im_preedit_abandon_macvim(); -void im_preedit_changed_macvim(char *preedit_string, int cursor_index); +void im_preedit_changed_macvim(char *preedit_string, int start_index, int cursor_index); enum { MMBlinkStateNone = 0, @@ -3290,21 +3290,22 @@ extern GuiFont gui_mch_retain_font(GuiFont font); - (void)handleMarkedText:(NSData *)data { const void *bytes = [data bytes]; + unsigned textlen = *((unsigned*)bytes); bytes += sizeof(unsigned); int32_t pos = *((int32_t*)bytes); bytes += sizeof(int32_t); unsigned len = *((unsigned*)bytes); bytes += sizeof(unsigned); char *chars = (char *)bytes; - ASLogDebug(@"pos=%d len=%d chars=%s", pos, len, chars); + ASLogDebug(@"textlen=%d pos=%d len=%d chars=%s", textlen, pos, len, chars); if (pos < 0) { im_preedit_abandon_macvim(); - } else if (len == 0) { + } else if (textlen == 0) { im_preedit_end_macvim(); } else { if (!preedit_get_status()) im_preedit_start_macvim(); - im_preedit_changed_macvim(chars, pos); + im_preedit_changed_macvim(chars, pos, pos + len); } } diff --git a/src/MacVim/MMTextViewHelper.m b/src/MacVim/MMTextViewHelper.m index 43b718522..63ab3f594 100644 --- a/src/MacVim/MMTextViewHelper.m +++ b/src/MacVim/MMTextViewHelper.m @@ -42,7 +42,7 @@ static float MMDragAreaSize = 73.0f; - (void)setCursor; - (NSRect)trackingRect; - (BOOL)inputManagerHandleMouseEvent:(NSEvent *)event; -- (void)sendMarkedText:(NSString *)text position:(int32_t)pos; +- (void)sendMarkedText:(NSString *)text position:(int32_t)pos length:(unsigned)len; - (void)abandonMarkedText; - (void)sendGestureEvent:(int)gesture flags:(int)flags; @end @@ -224,7 +224,7 @@ KeyboardInputSourcesEqual(TISInputSourceRef a, TISInputSourceRef b) - (void)insertText:(id)string { if ([self hasMarkedText]) { - [self sendMarkedText:nil position:0]; + [self sendMarkedText:nil position:0 length:0]; // NOTE: If this call is left out then the marked text isn't properly // erased when Return is used to accept the text. @@ -335,7 +335,7 @@ KeyboardInputSourcesEqual(TISInputSourceRef a, TISInputSourceRef b) if ([self hasMarkedText]) { // We must clear the marked text since the cursor may move if the // marked text moves outside the view as a result of scrolling. - [self sendMarkedText:nil position:0]; + [self sendMarkedText:nil position:0 length:0]; [self unmarkText]; [[NSTextInputContext currentInputContext] discardMarkedText]; } @@ -659,7 +659,7 @@ KeyboardInputSourcesEqual(TISInputSourceRef a, TISInputSourceRef b) imRange = range; } - [self sendMarkedText:text position:range.location]; + [self sendMarkedText:text position:range.location length:range.length]; return; } @@ -1129,19 +1129,20 @@ KeyboardInputSourcesEqual(TISInputSourceRef a, TISInputSourceRef b) return NO; } -- (void)sendMarkedText:(NSString *)text position:(int32_t)pos +- (void)sendMarkedText:(NSString *)text position:(int32_t)pos length:(unsigned)len { if (![self useInlineIm]) return; NSMutableData *data = [NSMutableData data]; - unsigned len = text == nil ? 0 + unsigned textlen = text == nil ? 0 : [text lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + [data appendBytes:&textlen length:sizeof(unsigned)]; [data appendBytes:&pos length:sizeof(int32_t)]; [data appendBytes:&len length:sizeof(unsigned)]; - if (len > 0) { - [data appendBytes:[text UTF8String] length:len]; + if (textlen > 0) { + [data appendBytes:[text UTF8String] length:textlen]; [data appendBytes:"\x00" length:1]; } @@ -1155,7 +1156,7 @@ KeyboardInputSourcesEqual(TISInputSourceRef a, TISInputSourceRef b) // Send an empty marked text message with position set to -1 to indicate // that the marked text should be abandoned. (If pos is set to 0 Vim will // send backspace sequences to delete the old marked text.) - [self sendMarkedText:nil position:-1]; + [self sendMarkedText:nil position:-1 length:0]; [[NSTextInputContext currentInputContext] discardMarkedText]; } diff --git a/src/gui_xim.c b/src/gui_xim.c index ce2e79c95..fdcf29aaa 100644 --- a/src/gui_xim.c +++ b/src/gui_xim.c @@ -184,6 +184,7 @@ init_preedit_start_col(void) static int im_is_active = FALSE; // IM is enabled for current mode static int preedit_is_active = FALSE; +static int im_preedit_start = 0; /* start offset in characters */ static int im_preedit_cursor = 0; // cursor offset in characters static int im_preedit_trailing = 0; // number of characters after cursor @@ -715,7 +716,7 @@ im_preedit_abandon_macvim() im_preedit_changed_cb(GtkIMContext *context, gpointer data UNUSED) # else void -im_preedit_changed_macvim(char *preedit_string, int cursor_index) +im_preedit_changed_macvim(char *preedit_string, int start_index, int cursor_index) # endif { # ifndef FEAT_GUI_MACVIM @@ -736,6 +737,8 @@ im_preedit_changed_macvim(char *preedit_string, int cursor_index) gtk_im_context_get_preedit_string(context, &preedit_string, NULL, NULL); +# else + im_preedit_start = start_index; # endif #ifdef XIM_DEBUG @@ -924,7 +927,10 @@ im_get_feedback_attr(int col UNUSED) return char_attr; # else - return HL_UNDERLINE; + if (col >= im_preedit_start && col < im_preedit_cursor) + return HL_UNDERCURL; + else + return HL_UNDERLINE; # endif }