Fix Writing Tools integration and fix up Services integration with texts

Apple "Intelligence" Writing Tools was previously not working correctly
with MacVim. When the user chooses to replace the original selection
with the updated texts, MacVim mistreats the input and treat them as
commands instead of raw texts. The reason was that even though this
service uses the NSServicesMenuRequestor API to obtain the selected
texts, it does not use it to send over the replacement. Instead, it uses
NSTextInput's `insertText:replacementRange` to do so instead, which we
never implemented properly. The reason behind this choice was probably
because Writing Tools first shows a UI with user interaction and has a
delay between obtaining the texts and replacing them, like a regular
Services menu. This means the selection may already be invalid by the
time it requests a replacement.

To fix this, add a new IPC API `replaceSelectedText` to replace the
selected texts and redirect `insertText:replacementRange` to use it if
the replacement range is non-empty. This isn't the most correct
implementation of the protocol but should work in most cases. We don't
have a way to implement it "correctly" as MacVim does not have easy
access to Vim's internal text storage. Also make sure the Service
menu uses this API (for things like "convert to full width" and
Traditional/Simplified Chinese conversions). The old method of simple
injecting a normal mode command `s` before the text was horribly buggy.
It also works with visual block selection properly now.

The implementation uses Vim's register put functionality because Vim
doesn't have an API to simply replace a block of text, and everything
has to go through registers. At the same time, replace the
implementation for the old `selectedText` IPC API to not do this,
because Vim *did* end an API to do so for obtaining texts (via
`getregion()`) and it's more stable to use this than to manually
cache/restore registers.

Related: vim/vim#16596 (fixes `setreg()` which this uses)

Fix #1512
This commit is contained in:
Yee Cheng Chin
2025-02-11 11:25:33 -08:00
parent 252a1330da
commit db26a3b1ac
15 changed files with 287 additions and 186 deletions
+122 -140
View File
@@ -1380,160 +1380,142 @@ static struct specialkey
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)
- (BOOL)hasSelectedText
{
// 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;
return (VIsual_active && (State & MODE_NORMAL));
}
/// Extract the currently selected text (in visual mode) and send that to the
/// provided pasteboard.
- (BOOL)selectedTextToPasteboard:(byref NSPasteboard *)pboard
{
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;
char_u *str = extractSelectedText();
if (!str)
return NO;
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];
NSArray *types = [NSArray arrayWithObject:NSPasteboardTypeString];
[pboard declareTypes:types owner:nil];
BOOL ok = [pboard setString:string forType:NSPasteboardTypeString];
[string release];
vim_free(str);
return ok;
}
return NO;
}
/// 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)
/// Returns the currently selected text.
- (NSString *)selectedText
{
if (VIsual_active && (State & MODE_NORMAL)) {
char_u *str = extractSelectedText();
if (!str)
return nil;
// 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];
vim_free(str);
return [string autorelease];
// 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, &regionLines);
// 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);
if (VIsual_mode == 'V')
[returnLines addObject:@""]; // need trailing endline for linewise
return [returnLines componentsJoinedByString:@"\n"];
}
return nil;
}
/// 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)) {
// 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];
}
}
/// Returns whether the provided mouse screen position is on a visually
/// selected range of text.
///
+2 -3
View File
@@ -648,8 +648,7 @@ static void grid_free(Grid *grid) {
- (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 @@ static void rowColFromUtfRange(const Grid* grid, NSRange range,
// top of said selection and if so, show definition of that instead.
MMVimController *vc = [self vimController];
id<MMBackendProtocol> 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];
+1 -1
View File
@@ -724,7 +724,7 @@
- (void)insertText:(id)string
{
[helper insertText:string];
[helper insertText:string replacementRange:NSMakeRange(0, 0)];
}
- (void)doCommandBySelector:(SEL)selector
+1 -1
View File
@@ -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;
+15 -1
View File
@@ -221,7 +221,7 @@ KeyboardInputSourcesEqual(TISInputSourceRef a, TISInputSourceRef b)
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 @@ KeyboardInputSourcesEqual(TISInputSourceRef a, TISInputSourceRef b)
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],
+3
View File
@@ -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;
+43
View File
@@ -533,6 +533,49 @@ static BOOL isUnsafeMessage(int msgid);
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;
+1 -1
View File
@@ -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
+11 -30
View File
@@ -86,7 +86,6 @@
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 @@
- (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 @@
// 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 @@
// 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 @@
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];
+2 -1
View File
@@ -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
+77 -2
View File
@@ -362,17 +362,22 @@ static BOOL vimProcessInputBlocked = NO;
/// 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 @@ do { \
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
+2 -4
View File
@@ -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;
+1 -2
View File
@@ -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);
+2
View File
@@ -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 : */
+4
View File
@@ -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 : */