diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 66ea170252..21ade18abb 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -1380,158 +1380,140 @@ - (NSString *)evaluateExpression:(in bycopy NSString *)expr return eval; } -/// Extracts the text currently selected in visual mode, and returns it. -/// -/// @return the string representing the selected text, or NULL if failure. -static char_u *extractSelectedText(void) -{ - // Note: Most of the functionality in Vim that allows for extracting useful - // text from a selection are in the register & clipboard utility functions. - // Unfortunately, most of those functions would actually send the text to - // the system clipboard, which we don't want (since we just want to extract - // the text instead of polluting the system clipboard). We don't want to - // refactor upstream Vim code too much to avoid merge pains later, so we - // duplicate a fair bit of the code from the functions below. - - if (!(VIsual_active && (State & MODE_NORMAL))) { - // This only works when we are in visual mode and have stuff to select. - return NULL; - } - - // Step 1: Find a register to yank the selection to. If we don't do this we - // have to duplicate a lot of code in op_yank(). We save it off to a backup - // first so we can restore it later to avoid polluting the registers. - - // Just use the yank / '0' register as it makes sense, but it doesn't - // really matter. - yankreg_T *target_reg = get_y_register(0); - - // Move the contents to the backup without doing memory allocs. - yankreg_T backup_reg = *target_reg; - target_reg->y_array = NULL; - target_reg->y_size = 0; - - // Step 2: Preserve the local states, and then invoke yank. - // Note: These were copied from clip_get_selection() in clipboard.c - yankreg_T *old_y_previous, *old_y_current; - pos_T old_cursor; - pos_T old_visual; - int old_visual_mode; - colnr_T old_curswant; - int old_set_curswant; - pos_T old_op_start, old_op_end; - oparg_T oa; - cmdarg_T ca; - - // Avoid triggering autocmds such as TextYankPost. - block_autocmds(); - - // Yank the selected text into the target register. - old_y_previous = get_y_previous(); - old_y_current = get_y_current(); - old_cursor = curwin->w_cursor; - old_curswant = curwin->w_curswant; - old_set_curswant = curwin->w_set_curswant; - old_op_start = curbuf->b_op_start; - old_op_end = curbuf->b_op_end; - old_visual = VIsual; - old_visual_mode = VIsual_mode; - clear_oparg(&oa); - oa.regname = '0'; // Use the '0' (yank) register. We will restore it later to avoid pollution. - oa.op_type = OP_YANK; - CLEAR_FIELD(ca); - ca.oap = &oa; - ca.cmdchar = 'y'; - ca.count1 = 1; - ca.retval = CA_NO_ADJ_OP_END; - do_pending_operator(&ca, 0, TRUE); - - // Step 3: Extract the text from the yank ('0') register. - char_u *str = get_reg_contents(0, 0); - - // Step 4: Clean up the yank register, and restore it back. - set_y_current(target_reg); // should not be necessary as it's done in do_pending_operator above (since regname was set to 0), but just to be safe and verbose in intention. - free_yank_all(); - *target_reg = backup_reg; - - // Step 5: Restore all the loose states that were modified during yank. - // Note: These were copied from clip_get_selection() in clipboard.c - set_y_previous(old_y_previous); - set_y_current(old_y_current); - curwin->w_cursor = old_cursor; - changed_cline_bef_curs(); // need to update w_virtcol et al - curwin->w_curswant = old_curswant; - curwin->w_set_curswant = old_set_curswant; - curbuf->b_op_start = old_op_start; - curbuf->b_op_end = old_op_end; - VIsual = old_visual; - VIsual_mode = old_visual_mode; - - unblock_autocmds(); - - return str; -} - -/// Extract the currently selected text (in visual mode) and send that to the -/// provided pasteboard. -- (BOOL)selectedTextToPasteboard:(byref NSPasteboard *)pboard +- (BOOL)hasSelectedText { - if (VIsual_active && (State & MODE_NORMAL)) { - // If there is no pasteboard, just return YES to indicate that there is - // text to copy. - if (!pboard) - return YES; + return (VIsual_active && (State & MODE_NORMAL)); +} - char_u *str = extractSelectedText(); - if (!str) - return NO; +/// Returns the currently selected text. +- (NSString *)selectedText +{ + if (VIsual_active && (State & MODE_NORMAL)) { + // This is basically doing the following: + // - join(getregion(getpos("."), getpos("v"), { type: visualmode() }),"\n") + // - Add extra "\n" if we have a linewise selection - if (output_conv.vc_type != CONV_NONE) { - char_u *conv_str = string_convert(&output_conv, str, NULL); - if (conv_str) { - vim_free(str); - str = conv_str; + // Call getpos() + typval_T pos1, pos2; + { + typval_T arg_posmark; + init_tv(&arg_posmark); + arg_posmark.v_type = VAR_STRING; + + arg_posmark.vval.v_string = (char_u*)"."; + typval_T args1[1] = { arg_posmark }; + f_getpos(args1, &pos1); + if (pos1.v_type != VAR_LIST) + return nil; + + arg_posmark.vval.v_string = (char_u*)"v"; + typval_T args2[1] = { arg_posmark }; + f_getpos(args2, &pos2); + if (pos2.v_type != VAR_LIST) { + list_unref(pos1.vval.v_list); + return nil; } } - NSString *string = [[NSString alloc] initWithUTF8String:(char*)str]; - - NSArray *types = [NSArray arrayWithObject:NSPasteboardTypeString]; - [pboard declareTypes:types owner:nil]; - BOOL ok = [pboard setString:string forType:NSPasteboardTypeString]; - - [string release]; - vim_free(str); + // Call getregion() + typval_T arg_opts; + init_tv(&arg_opts); + arg_opts.v_type = VAR_DICT; + arg_opts.vval.v_dict = dict_alloc(); + arg_opts.vval.v_dict->dv_refcount += 1; + + char_u visualmode[2] = { VIsual_mode, '\0' }; + dict_add_string(arg_opts.vval.v_dict, "type", visualmode); + + typval_T args[3] = { pos1, pos2, arg_opts }; + typval_T regionLines; + f_getregion(args, ®ionLines); + + // Join the results + NSMutableArray *returnLines = [NSMutableArray array]; + if (regionLines.v_type == VAR_LIST) { + list_T *lines = regionLines.vval.v_list; + for (listitem_T *item = lines->lv_first; item != NULL; item = item->li_next) { + if (item->li_tv.v_type == VAR_STRING) { + char_u *str = item->li_tv.vval.v_string; + if (output_conv.vc_type != CONV_NONE) { + char_u *conv_str = string_convert(&output_conv, str, NULL); + if (conv_str) { + [returnLines addObject:[NSString stringWithUTF8String:(char*)conv_str]]; + vim_free(conv_str); + } + } else { + [returnLines addObject:[NSString stringWithUTF8String:(char*)str]]; + } + } + } + list_unref(lines); + } + dict_unref(arg_opts.vval.v_dict); + list_unref(pos1.vval.v_list); + list_unref(pos2.vval.v_list); - return ok; + if (VIsual_mode == 'V') + [returnLines addObject:@""]; // need trailing endline for linewise + return [returnLines componentsJoinedByString:@"\n"]; } - - return NO; + return nil; } -/// Returns the currently selected text. We should consolidate this with -/// selectedTextToPasteboard: above when we have time. (That function has a -/// fast path just to query whether selected text exists) -- (NSString *)selectedText +/// Replace the selected text in visual mode with the new suppiled one. +- (oneway void)replaceSelectedText:(in bycopy NSString *)text { if (VIsual_active && (State & MODE_NORMAL)) { - char_u *str = extractSelectedText(); - if (!str) - return nil; - - if (output_conv.vc_type != CONV_NONE) { - char_u *conv_str = string_convert(&output_conv, str, NULL); - if (conv_str) { - vim_free(str); - str = conv_str; - } - } - - NSString *string = [[NSString alloc] initWithUTF8String:(char*)str]; - vim_free(str); - return [string autorelease]; + // The only real way Vim has in doing this consistently is to use the + // register put functionality as there is no generic API for this. + // We find an arbitrary register ('0'), back it up, replace it with our + // own content, paste it in, then restore the register to old value. + yankreg_T *target_reg = get_y_register(0); + yankreg_T backup_reg = *target_reg; + target_reg->y_array = NULL; + target_reg->y_size = 0; + + // If selection is blockwise, we try to match it. Only do it if input + // and selected text have same number of lines, as otherwise it could + // be awkward. + int yank_type = MAUTO; + char_u *vimtext = [text vimStringSave]; + if (VIsual_mode == Ctrl_V) { + long text_lines = string_count(vimtext, (char_u*)"\n", FALSE) + 1; + + linenr_T v1 = VIsual.lnum; + linenr_T v2 = curwin->w_cursor.lnum; + long num_lines = v1 > v2 ? v1 - v2 + 1 : v2 - v1 + 1; + + if (text_lines == num_lines) + yank_type = MBLOCK; + } + write_reg_contents_ex('0', vimtext, -1, FALSE, yank_type, -1); + vim_free(vimtext); + + oparg_T oap; + CLEAR_FIELD(oap); + oap.regname = '0'; + + cmdarg_T cap; + CLEAR_FIELD(cap); + cap.oap = &oap; + cap.cmdchar = 'P'; + cap.count1 = 1; + + nv_put(&cap); + + // Clean up the temporary register, and restore the old state. + yankreg_T *old_y_current = get_y_current(); + set_y_current(target_reg); + free_yank_all(); + set_y_current(old_y_current); + *target_reg = backup_reg; + + // nv_put does not trigger a redraw command as it's done on a higher + // level, so just do a manual one here to make sure it's done. + [self redrawScreen]; } - return nil; } /// Returns whether the provided mouse screen position is on a visually diff --git a/src/MacVim/MMCoreTextView.m b/src/MacVim/MMCoreTextView.m index 480de9674f..7a44da201c 100644 --- a/src/MacVim/MMCoreTextView.m +++ b/src/MacVim/MMCoreTextView.m @@ -648,8 +648,7 @@ - (void)keyDown:(NSEvent *)event - (void)insertText:(id)string replacementRange:(NSRange)replacementRange { - // We are not currently replacementRange right now. - [helper insertText:string]; + [helper insertText:string replacementRange:replacementRange]; } - (void)doCommandBySelector:(SEL)selector @@ -1992,7 +1991,7 @@ - (void)quickLookWithEvent:(NSEvent *)event // top of said selection and if so, show definition of that instead. MMVimController *vc = [self vimController]; id backendProxy = [vc backendProxy]; - if ([backendProxy selectedTextToPasteboard:nil]) { + if ([backendProxy hasSelectedText]) { int selRow = 0, selCol = 0; const BOOL isMouseInSelection = [backendProxy mouseScreenposIsSelection:row column:col selRow:&selRow selCol:&selCol]; diff --git a/src/MacVim/MMTextView.m b/src/MacVim/MMTextView.m index e7c4923cf2..6de0e56478 100644 --- a/src/MacVim/MMTextView.m +++ b/src/MacVim/MMTextView.m @@ -724,7 +724,7 @@ - (void)keyDown:(NSEvent *)event - (void)insertText:(id)string { - [helper insertText:string]; + [helper insertText:string replacementRange:NSMakeRange(0, 0)]; } - (void)doCommandBySelector:(SEL)selector diff --git a/src/MacVim/MMTextViewHelper.h b/src/MacVim/MMTextViewHelper.h index 0aca93ea78..71983661fb 100644 --- a/src/MacVim/MMTextViewHelper.h +++ b/src/MacVim/MMTextViewHelper.h @@ -65,7 +65,7 @@ - (NSColor *)insertionPointColor; - (void)keyDown:(NSEvent *)event; -- (void)insertText:(id)string; +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange; - (void)doCommandBySelector:(SEL)selector; - (void)scrollWheel:(NSEvent *)event; - (void)mouseDown:(NSEvent *)event; diff --git a/src/MacVim/MMTextViewHelper.m b/src/MacVim/MMTextViewHelper.m index b2834f7bd7..c854e95b87 100644 --- a/src/MacVim/MMTextViewHelper.m +++ b/src/MacVim/MMTextViewHelper.m @@ -221,7 +221,7 @@ - (void)keyDown:(NSEvent *)event currentEvent = nil; } -- (void)insertText:(id)string +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange { if ([self hasMarkedText]) { [self sendMarkedText:nil position:0]; @@ -241,6 +241,20 @@ - (void)insertText:(id)string if ([string isKindOfClass:[NSAttributedString class]]) string = [string string]; + if (replacementRange.length > 0) + { + // Replacement range is a concept we don't really have a way to fulfill + // as we don't have proper access to the underlying text storage. This + // should usually be triggered when we have selected text though, and + // so we simply ask Vim to replace the current selection with the new + // text, and it should hopefully work. + // Only known way of this being called is Apple Intelligence Writing + // Tools. + MMVimController *vc = [self vimController]; + [vc replaceSelectedText:string]; + return; + } + //int len = [string length]; //ASLogDebug(@"len=%d char[0]=%#x char[1]=%#x string='%@'", [string length], // [string characterAtIndex:0], diff --git a/src/MacVim/MMVimController.h b/src/MacVim/MMVimController.h index 3656ea22be..ec4bc9505c 100644 --- a/src/MacVim/MMVimController.h +++ b/src/MacVim/MMVimController.h @@ -92,6 +92,9 @@ - (NSString *)evaluateVimExpression:(NSString *)expr; - (id)evaluateVimExpressionCocoa:(NSString *)expr errorString:(NSString **)errstr; +- (BOOL)hasSelectedText; +- (NSString *)selectedText; +- (void)replaceSelectedText:(NSString *)text; - (void)processInputQueue:(NSArray *)queue; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12_2 - (NSTouchBar *)makeTouchBar; diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index de6b9d0964..4d0a295722 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -533,6 +533,49 @@ - (id)evaluateVimExpressionCocoa:(NSString *)expr return eval; } +- (BOOL)hasSelectedText +{ + BOOL hasSelectedText = NO; + if (backendProxy) { + @try { + hasSelectedText = [backendProxy hasSelectedText]; + } + @catch (NSException *ex) { + ASLogDebug(@"hasSelectedText: failed: pid=%d reason=%@", + pid, ex); + } + } + return hasSelectedText; +} + +- (NSString *)selectedText +{ + NSString *selectedText = nil; + if (backendProxy) { + @try { + selectedText = [backendProxy selectedText]; + } + @catch (NSException *ex) { + ASLogDebug(@"selectedText: failed: pid=%d reason=%@", + pid, ex); + } + } + return selectedText; +} + +- (void)replaceSelectedText:(NSString *)text +{ + if (backendProxy) { + @try { + [backendProxy replaceSelectedText:text]; + } + @catch (NSException *ex) { + ASLogDebug(@"replaceSelectedText: failed: pid=%d reason=%@", + pid, ex); + } + } +} + - (id)backendProxy { return backendProxy; diff --git a/src/MacVim/MMWindowController.h b/src/MacVim/MMWindowController.h index 0ba7d1cc2d..e3c9cdfb10 100644 --- a/src/MacVim/MMWindowController.h +++ b/src/MacVim/MMWindowController.h @@ -18,7 +18,7 @@ @class MMVimView; @interface MMWindowController : NSWindowController< - NSWindowDelegate + NSWindowDelegate, NSServicesMenuRequestor #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 , NSMenuItemValidation #endif diff --git a/src/MacVim/MMWindowController.m b/src/MacVim/MMWindowController.m index 45f2c8c86f..c6cb16605f 100644 --- a/src/MacVim/MMWindowController.m +++ b/src/MacVim/MMWindowController.m @@ -86,7 +86,6 @@ - (void)resizeWindowToFitContentSize:(NSSize)contentSize keepOnScreen:(BOOL)onScreen; - (NSSize)constrainContentSizeToScreenSize:(NSSize)contentSize; - (NSRect)constrainFrame:(NSRect)frame; -- (BOOL)askBackendForSelectedText:(NSPasteboard *)pb; - (void)updateTablineSeparator; - (void)hideTablineSeparator:(BOOL)hide; - (void)doFindNext:(BOOL)next; @@ -1642,7 +1641,7 @@ - (IBAction)unjoinAllStageManagerSets:(id)sender - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType { - const BOOL sendOk = (([sendType isEqual:NSPasteboardTypeString] && [self askBackendForSelectedText:nil]) + const BOOL sendOk = (([sendType isEqual:NSPasteboardTypeString] && [self.vimController hasSelectedText]) || [sendType length] == 0); const BOOL returnOk = ([returnType isEqual:NSPasteboardTypeString] || [returnType length] == 0); if (sendOk && returnOk) @@ -1662,7 +1661,14 @@ - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard // We should really be fine here since we already checked the types in // validRequestsForSendType: above. (void)types; - return [self askBackendForSelectedText:pboard]; + + NSString *string = [vimController selectedText]; + if (string != nil) { + NSArray *types = [NSArray arrayWithObject:NSPasteboardTypeString]; + [pboard declareTypes:types owner:nil]; + return [pboard setString:string forType:NSPasteboardTypeString]; + } + return NO; } /// Called by the OS when it tries to update the selection. This could happen @@ -1672,9 +1678,8 @@ - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard // Replace the current selection with the text on the pasteboard. NSArray *types = [pboard types]; if ([types containsObject:NSPasteboardTypeString]) { - NSString *input = [NSString stringWithFormat:@"s%@", - [pboard stringForType:NSPasteboardTypeString]]; - [vimController addVimInput:input]; + NSString *input = [pboard stringForType:NSPasteboardTypeString]; + [vimController replaceSelectedText:input]; return YES; } @@ -1986,30 +1991,6 @@ - (NSRect)constrainFrame:(NSRect)frame return [decoratedWindow frameRectForContentRect:contentRect]; } -/// Ask Vim to fill in the pasteboard with the currently selected text in visual mode. -- (BOOL)askBackendForSelectedText:(NSPasteboard *)pb -{ - // This could potentially be done via evaluateExpression by yanking the - // selection, then returning the results via getreg('@') and restoring the - // register. Using a dedicated API is probably a little safer (e.g. it - // prevents TextYankPost autocmd's from triggering) and efficient - // and hence this is what we use for now. - BOOL reply = NO; - id backendProxy = [vimController backendProxy]; - - if (backendProxy) { - @try { - reply = [backendProxy selectedTextToPasteboard:pb]; - } - @catch (NSException *ex) { - ASLogDebug(@"selectedTextToPasteboard: failed: pid=%d reason=%@", - [vimController pid], ex); - } - } - - return reply; -} - - (void)updateTablineSeparator { BOOL tablineVisible = ![[vimView tabline] isHidden]; diff --git a/src/MacVim/MacVim.h b/src/MacVim/MacVim.h index b863bad620..3d2b17425f 100644 --- a/src/MacVim/MacVim.h +++ b/src/MacVim/MacVim.h @@ -192,8 +192,9 @@ typedef NSString* NSAttributedStringKey; - (NSString *)evaluateExpression:(in bycopy NSString *)expr; - (id)evaluateExpressionCocoa:(in bycopy NSString *)expr errorString:(out bycopy NSString **)errstr; -- (BOOL)selectedTextToPasteboard:(byref NSPasteboard *)pboard; +- (BOOL)hasSelectedText; - (NSString *)selectedText; +- (oneway void)replaceSelectedText:(in bycopy NSString *)text; - (BOOL)mouseScreenposIsSelection:(int)row column:(int)column selRow:(byref int *)startRow selCol:(byref int *)startCol; - (oneway void)acknowledgeConnection; @end diff --git a/src/MacVim/MacVimTests/MacVimTests.m b/src/MacVim/MacVimTests/MacVimTests.m index 6cb8c5d294..463affa405 100644 --- a/src/MacVim/MacVimTests/MacVimTests.m +++ b/src/MacVim/MacVimTests/MacVimTests.m @@ -362,17 +362,22 @@ - (void)waitTimeout:(double)delaySecs { /// Send a single key to MacVim via event handling system. - (void)sendKeyToVim:(NSString*)chars withMods:(int)mods { NSApplication* app = [NSApplication sharedApplication]; + + NSString *modChars = chars; + if (mods & NSEventModifierFlagControl) { + unichar ch = [chars characterAtIndex:0] & ~0x60; + modChars = [NSString stringWithCharacters:&ch length:1]; + } NSEvent* keyEvent = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(50, 50) modifierFlags:mods timestamp:100 windowNumber:[[NSApp mainWindow] windowNumber] context:0 - characters:chars + characters:modChars charactersIgnoringModifiers:chars isARepeat:NO keyCode:0]; - [app postEvent:keyEvent atStart:NO]; } @@ -1384,4 +1389,74 @@ - (void) testFullScreenNonNativeMultiScreen { XCTAssertTrue(NSPointInRect(winController.window.frame.origin, NSScreen.screens[0].frame)); } +#pragma mark Vim IPC + +/// Test the selected text related IPC APIs +- (void)testIPCSelectedText { + [self createTestVimWindow]; + [self sendStringToVim:@":put =['abcd', 'efgh', 'ijkl']\nggdd" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + + MMAppController *app = MMAppController.sharedInstance; + MMVimController *vc = app.keyVimController; + + // Set up register + [self sendStringToVim:@"ggyy" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + NSString *regcontents = [vc evaluateVimExpression:@"getreg()"]; + XCTAssertEqualObjects(regcontents, @"abcd\n"); + + // Get selected texts in visual mode + XCTAssertFalse([vc hasSelectedText]); + XCTAssertNil([vc selectedText]); + [self sendStringToVim:@"lvjl" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertTrue([vc hasSelectedText]); + XCTAssertEqualObjects([vc selectedText], @"bcd\nefg"); + + // Get selected texts in visual line mode + [self sendStringToVim:@"V" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertTrue([vc hasSelectedText]); + XCTAssertEqualObjects([vc selectedText], @"abcd\nefgh\n"); + + // Get selected texts in visual block mode + [self sendKeyToVim:@"v" withMods:NSEventModifierFlagControl]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertTrue([vc hasSelectedText]); + XCTAssertEqualObjects([vc selectedText], @"bc\nfg"); + + // Set selected texts in visual block mode + NSString *changedtick = [vc evaluateVimExpression:@"b:changedtick"]; + [vc replaceSelectedText:@"xyz\n1234"]; + NSString *changedtick2 = [vc evaluateVimExpression:@"b:changedtick"]; + XCTAssertEqualObjects([vc evaluateVimExpression:@"getline(1)"], @"axyz d"); + XCTAssertEqualObjects([vc evaluateVimExpression:@"getline(2)"], @"e1234h"); + XCTAssertEqualObjects([vc evaluateVimExpression:@"getline(3)"], @"ijkl"); + XCTAssertNotEqualObjects(changedtick, changedtick2); + + // Make sure replacing texts when nothing is selected won't set anything + [vc replaceSelectedText:@"foobar"]; + NSString *changedtick3 = [vc evaluateVimExpression:@"b:changedtick"]; + XCTAssertEqualObjects(changedtick2, changedtick3); + + // Select in visual block again but send a different number of lines, make sure we intentionaly won't treat it as block text + [self sendStringToVim:@"ggjjvll" withMods:0]; + [self sendKeyToVim:@"v" withMods:NSEventModifierFlagControl]; + [self waitForEventHandlingAndVimProcess]; + [vc replaceSelectedText:@"xyz\n1234\n"]; // ending in newline means it gets interpreted as line-wise + XCTAssertEqualObjects([vc evaluateVimExpression:@"getline(1)"], @"axyz d"); + XCTAssertEqualObjects([vc evaluateVimExpression:@"getline(2)"], @"e1234h"); + XCTAssertEqualObjects([vc evaluateVimExpression:@"getline(3)"], @"xyz"); + XCTAssertEqualObjects([vc evaluateVimExpression:@"getline(4)"], @"1234"); + XCTAssertEqualObjects([vc evaluateVimExpression:@"getline(5)"], @"l"); + + // Make sure registers didn't get stomped (internally the implementation uses register and manually restores it) + regcontents = [[app keyVimController] evaluateVimExpression:@"getreg()"]; + XCTAssertEqualObjects(regcontents, @"abcd\n"); + + [self sendStringToVim:@":set nomodified\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; +} + @end diff --git a/src/evalfunc.c b/src/evalfunc.c index 60dd4ddbf1..dd1441fde1 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -72,10 +72,8 @@ static void f_getenv(typval_T *argvars, typval_T *rettv); static void f_getfontname(typval_T *argvars, typval_T *rettv); static void f_getjumplist(typval_T *argvars, typval_T *rettv); static void f_getpid(typval_T *argvars, typval_T *rettv); -static void f_getpos(typval_T *argvars, typval_T *rettv); static void f_getreg(typval_T *argvars, typval_T *rettv); static void f_getreginfo(typval_T *argvars, typval_T *rettv); -static void f_getregion(typval_T *argvars, typval_T *rettv); static void f_getregionpos(typval_T *argvars, typval_T *rettv); static void f_getregtype(typval_T *argvars, typval_T *rettv); static void f_gettagstack(typval_T *argvars, typval_T *rettv); @@ -5758,7 +5756,7 @@ f_getcursorcharpos(typval_T *argvars, typval_T *rettv) /* * "getpos(string)" function */ - static void + void f_getpos(typval_T *argvars, typval_T *rettv) { if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) @@ -5949,7 +5947,7 @@ getregionpos( /* * "getregion()" function */ - static void + void f_getregion(typval_T *argvars, typval_T *rettv) { pos_T p1, p2; diff --git a/src/normal.c b/src/normal.c index 1e02164ce2..fa27efceee 100644 --- a/src/normal.c +++ b/src/normal.c @@ -112,7 +112,6 @@ static void nv_record(cmdarg_T *cap); static void nv_at(cmdarg_T *cap); static void nv_halfpage(cmdarg_T *cap); static void nv_join(cmdarg_T *cap); -static void nv_put(cmdarg_T *cap); static void nv_put_opt(cmdarg_T *cap, int fix_indent); static void nv_open(cmdarg_T *cap); #ifdef FEAT_NETBEANS_INTG @@ -7339,7 +7338,7 @@ nv_join(cmdarg_T *cap) /* * "P", "gP", "p" and "gp" commands. */ - static void + void nv_put(cmdarg_T *cap) { nv_put_opt(cap, FALSE); diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro index 3f17bfdad9..a581bb8aa1 100644 --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -30,5 +30,7 @@ long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, typval_T * // MacVim only void f_getcurpos(typval_T *argvars, typval_T *rettv); +void f_getpos(typval_T *argvars, typval_T *rettv); +void f_getregion(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/proto/normal.pro b/src/proto/normal.pro index 36a26ec480..ea74d6d7d0 100644 --- a/src/proto/normal.pro +++ b/src/proto/normal.pro @@ -33,4 +33,8 @@ void may_start_select(int c); int unadjust_for_sel(void); int unadjust_for_sel_inner(pos_T *pp); void set_cursor_for_append_to_line(void); + +// MacVim only +void nv_put(cmdarg_T *cap); + /* vim: set ft=c : */