From ffa192240ac64dfb7d5b26d22b4c7bf5c8691b5b Mon Sep 17 00:00:00 2001 From: Bjorn Winckler Date: Sat, 1 Sep 2007 19:35:31 +0000 Subject: [PATCH] Added partial client/server support (scripting not yet supported) git-svn-id: http://macvim.googlecode.com/svn/trunk@225 96c4425d-ca35-0410-94e5-3396d5c13a8f --- MMAppController.m | 37 ++++++++ MMBackend.h | 2 + MMBackend.m | 43 +++++++++- MMVimController.m | 3 + MacVim.h | 4 + MacVim.m | 2 + gui_macvim.m | 209 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 296 insertions(+), 4 deletions(-) diff --git a/MMAppController.m b/MMAppController.m index 25b07ef075..48a54eb056 100644 --- a/MMAppController.m +++ b/MMAppController.m @@ -433,6 +433,43 @@ static NSTimeInterval MMTerminateTimeout = 3; return vc; } +- (NSArray *)serverList +{ + NSMutableArray *array = [NSMutableArray array]; + + unsigned i, count = [vimControllers count]; + for (i = 0; i < count; ++i) { + MMVimController *controller = [vimControllers objectAtIndex:i]; + id proxy = [controller backendProxy]; + NSConnection *connection = [proxy connectionForProxy]; + if (!connection) + continue; + + // Set low timeouts since we don't want this call to potentially lock + // up MacVim for a while. + NSTimeInterval req = [connection requestTimeout]; + NSTimeInterval rep = [connection replyTimeout]; + [connection setRequestTimeout:0]; + [connection setReplyTimeout:.1]; + + @try { + NSString *eval = [proxy evaluateExpression:@"v:servername"]; + if (eval) { + [array addObject:eval]; + } + } + @catch (NSException *e) { + NSLog(@"WARNING: Got exception while listing servers: \"%@\"", e); + } + @finally { + [connection setRequestTimeout:req]; + [connection setReplyTimeout:rep]; + } + } + + return array; +} + @end // MMAppController diff --git a/MMBackend.h b/MMBackend.h index 68cbb6f521..7cee92b12b 100644 --- a/MMBackend.h +++ b/MMBackend.h @@ -54,6 +54,7 @@ - (void)setSpecialColor:(int)color; - (void)setDefaultColorsBackground:(int)bg foreground:(int)fg; +- (NSString *)macVimConnectionName; - (BOOL)checkin; - (BOOL)openVimWindow; - (void)clearAll; @@ -103,6 +104,7 @@ - (void)startBlink; - (void)stopBlink; - (void)adjustLinespace:(int)linespace; +- (void)activate; - (int)lookupColorWithKey:(NSString *)key; - (BOOL)hasSpecialKeyWithValue:(NSString *)value; diff --git a/MMBackend.m b/MMBackend.m index 08f7285181..6425344864 100644 --- a/MMBackend.m +++ b/MMBackend.m @@ -135,14 +135,19 @@ enum { [self queueMessage:SetDefaultColorsMsgID data:data]; } +- (NSString *)macVimConnectionName +{ + // NOTE! If the name of the connection changes here it must also be + // updated in MMAppController.m. + return [NSString stringWithFormat:@"%@-connection", + [[NSBundle mainBundle] bundleIdentifier]]; +} + - (BOOL)checkin { NSBundle *mainBundle = [NSBundle mainBundle]; - // NOTE! If the name of the connection changes here it must also be - // updated in MMAppController.m. - NSString *name = [NSString stringWithFormat:@"%@-connection", - [mainBundle bundleIdentifier]]; + NSString *name = [self macVimConnectionName]; connection = [NSConnection connectionWithRegisteredName:name host:nil]; if (!connection) { #if 0 @@ -365,6 +370,12 @@ enum { - (void)exit { +#ifdef MAC_CLIENTSERVER + // The default connection is used for the client/server code. + [[NSConnection defaultConnection] setRootObject:nil]; + [[NSConnection defaultConnection] invalidate]; +#endif + // By invalidating the NSConnection the MMWindowController immediately // finds out that the connection is down and as a result // [MMWindowController connectionDidDie:] is invoked. @@ -843,6 +854,11 @@ enum { [self queueMessage:AdjustLinespaceMsgID data:data]; } +- (void)activate +{ + [self queueMessage:ActivateMsgID data:nil]; +} + - (int)lookupColorWithKey:(NSString *)key { if (!(key && [key length] > 0)) @@ -1003,6 +1019,19 @@ enum { return NO; } +- (NSString *)evaluateExpression:(in bycopy NSString *)expr +{ + NSString *eval = nil; + char_u *res = eval_client_expr_to_string((char_u*)[expr UTF8String]); + + if (res != NULL) { + eval = [NSString stringWithUTF8String:(char*)res]; + vim_free(res); + } + + return eval; +} + @end // MMBackend @@ -1363,6 +1392,12 @@ enum { const void *bytes = [data bytes]; int shape = *((int*)bytes); bytes += sizeof(int); update_mouseshape(shape); + } else if (ServerAddInputMsgID == msgid) { + const void *bytes = [data bytes]; + /*int len = *((int*)bytes);*/ bytes += sizeof(int); + char_u *cmd = (char_u*)bytes; + + server_to_input_buf(cmd); } else { NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid); } diff --git a/MMVimController.m b/MMVimController.m index febdce2a42..39f968b92e 100644 --- a/MMVimController.m +++ b/MMVimController.m @@ -749,6 +749,9 @@ static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag) int linespace = *((int*)bytes); bytes += sizeof(int); [windowController adjustLinespace:linespace]; + } else if (ActivateMsgID == msgid) { + [NSApp activateIgnoringOtherApps:YES]; + [[windowController window] makeKeyAndOrderFront:self]; } else { NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid); } diff --git a/MacVim.h b/MacVim.h index 74d24d079e..7d3f0a528e 100644 --- a/MacVim.h +++ b/MacVim.h @@ -35,6 +35,7 @@ - (BOOL)checkForModifiedBuffers; - (oneway void)setDialogReturn:(in bycopy id)obj; - (BOOL)starRegisterToPasteboard:(byref NSPasteboard *)pboard; +- (NSString *)evaluateExpression:(in bycopy NSString *)expr; @end @@ -62,6 +63,7 @@ @protocol MMAppProtocol - (byref id )connectBackend: (byref in id )backend pid:(int)pid; +- (NSArray *)serverList; @end @@ -119,6 +121,8 @@ enum { MouseMovedMsgID, SetMouseShapeMsgID, AdjustLinespaceMsgID, + ActivateMsgID, + ServerAddInputMsgID, }; diff --git a/MacVim.m b/MacVim.m index f8772c3f65..69e810b365 100644 --- a/MacVim.m +++ b/MacVim.m @@ -57,6 +57,8 @@ char *MessageStrings[] = "MouseMovedMsgID", "SetMouseShapeMsgID", "AdjustLinespaceMsgID", + "ActivateMsgID", + "ServerAddInputMsgID", }; diff --git a/gui_macvim.m b/gui_macvim.m index 66365f487f..3c4084fd9f 100644 --- a/gui_macvim.m +++ b/gui_macvim.m @@ -1247,6 +1247,7 @@ gui_mch_invert_rectangle(int r, int c, int nr, int nc) void gui_mch_set_foreground(void) { + [[MMBackend sharedInstance] activate]; } #endif @@ -1326,3 +1327,211 @@ gui_macvim_is_valid_action(NSString *action) return [actionDict objectForKey:action] != nil; } + + + +// -- Client/Server --------------------------------------------------------- + +#ifdef MAC_CLIENTSERVER + +// +// NOTE: Client/Server stuff only works with the GUI. Theoretically it would +// be possible to make this code work with terminal Vim, but it would require +// that a run-loop is set up and checked, etc. +// + +static unsigned MMServerMax = 1000; +static NSTimeInterval MMEvaluateExpressionTimeout = 3; +static NSTimeInterval MMServerListTimeout = 3; + + + static NSString * +shortToLongName(NSString *shortName) +{ + NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + + return [NSString stringWithFormat:@"%@.%@", bundleIdentifier, shortName]; +} + + + static NSString * +longToShortName(NSString *longName) +{ + NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + NSRange range = [longName rangeOfString:bundleIdentifier]; + + return NSNotFound != range.location + ? [longName substringFromIndex:1+NSMaxRange(range)] + : longName; +} + + +/* + * Register connection with 'name'. The actual connection is named something + * like 'org.vim.MacVim.VIM3', whereas the server is called 'VIM3'. + */ + void +serverSetName(char_u *name) +{ + NSString *baseName = + shortToLongName([NSString stringWithUTF8String:(char*)name]); + NSString *longName = baseName; + NSConnection *connection = [NSConnection defaultConnection]; + unsigned i; + + for (i = 1; i < MMServerMax; ++i) { + //NSLog(@"Client-Server: Try to register connection '%@'", longName); + if ([connection registerName:longName]) { + NSString *shortName = longToShortName(longName); + + //NSLog(@"Client-Server: Did register server '%@'", shortName); + serverName = vim_strsave((char_u*)[shortName UTF8String]); +#ifdef FEAT_EVAL + set_vim_var_string(VV_SEND_SERVER, serverName, -1); +#endif +#ifdef FEAT_TITLE + need_maketitle = TRUE; +#endif + + // Don't wait for requests (time-out means that the message is + // dropped). + [connection setRequestTimeout:0]; + //[connection setReplyTimeout:MMReplyTimeout]; + [connection setRootObject:[MMBackend sharedInstance]]; + break; + } + + longName = [NSString stringWithFormat:@"%@%d", baseName, i]; + } +} + + +/* + * Send to an instance of Vim. + * Returns 0 for OK, negative for an error. + */ + int +serverSendToVim(char_u *name, char_u *cmd, char_u **result, + int *server, int asExpr, int silent) +{ + //NSLog(@"serverSendToVim(name=%s, cmd=%s, asExpr=%d, silent=%d)", + // name, cmd, asExpr, silent); + + NSString *longName = + shortToLongName([NSString stringWithUTF8String:(char*)name]); + NSConnection *connection = [NSConnection + connectionWithRegisteredName:longName host:nil]; + + if (!connection) { + if (!silent) + EMSG2(_(e_noserver), name); + return -1; + } + + [connection setRequestTimeout:0]; + + id proxy = [connection rootProxy]; + [proxy setProtocolForProxy:@protocol(MMBackendProtocol)]; + + // Expressions need to wait for a reply; otherwise just send off the input + // asynchronously. + if (asExpr) { + [connection setReplyTimeout:MMEvaluateExpressionTimeout]; + @try { + NSString *expr = [NSString stringWithUTF8String:(char*)cmd]; + NSString *eval = [proxy evaluateExpression:expr]; + if (result) { + *result = (eval ? vim_strsave((char_u*)[eval UTF8String]) + : vim_strsave((char_u*)_(e_invexprmsg))); + } + } + @catch (NSException *e) { + NSLog(@"WARNING: Caught exception during expression evaluation " + "\"%@\"", e); + return -1; + } + } else { + int len = 1 + STRLEN(cmd); + NSMutableData *data = [NSMutableData data]; + + [data appendBytes:&len length:sizeof(int)]; + [data appendBytes:cmd length:len]; + + [proxy processInput:ServerAddInputMsgID data:data]; + } + + return 0; +} + + +/* + * Ask MacVim for the names of all Vim servers. + */ + char_u * +serverGetVimNames(void) +{ + char_u *names = NULL; + NSString *name = [[MMBackend sharedInstance] macVimConnectionName]; + NSConnection *connection = [NSConnection connectionWithRegisteredName:name + host:nil]; + if (connection) { + NSArray *serverNames = nil; + id proxy = [connection rootProxy]; + + [proxy setProtocolForProxy:@protocol(MMAppProtocol)]; + [connection setRequestTimeout:0]; + [connection setReplyTimeout:MMServerListTimeout]; + + @try { + serverNames = [proxy serverList]; + } + @catch (NSException *e) { + NSLog(@"Exception caught when listing servers: \"%@\"", e); + } + + if (serverNames) { + NSString *string = [serverNames componentsJoinedByString:@"\n"]; + names = vim_strsave((char_u*)[string UTF8String]); + } + } + + return names; +} + + +/* + * Check for replies from 'serverid'. + * Return TRUE and a non-malloc'ed string if there is. Else return FALSE. + */ + int +serverPeekReply(char_u *serverid, char_u **str) +{ + return FALSE; +} + + +/* + * Wait for replies from server 'name'. + * Return 0 and the malloc'ed string when a reply is available. + * Return -1 on error. + */ + int +serverReadReply(char_u *name, char_u **str) +{ + //NSLog(@"serverReadReply(name=%s)", name); + return -1; +} + + +/* + * Send a reply string (notification) to client with id "name". + * Return -1 if the window is invalid. + */ + int +serverSendReply(char_u *name, char_u *reply) +{ + //NSLog(@"serverSendReply(name=%s, reply=%s)", name, reply); + return -1; +} + +#endif // MAC_CLIENTSERVER