From 800749d7a68b66b556e5ca5df87b305ffc3825c2 Mon Sep 17 00:00:00 2001 From: "bjorn.winckler" Date: Sun, 9 Sep 2007 17:20:03 +0000 Subject: [PATCH] First revision with full client/server support git-svn-id: http://macvim.googlecode.com/svn/trunk@230 96c4425d-ca35-0410-94e5-3396d5c13a8f --- MMAppController.m | 10 +- MMBackend.h | 19 ++- MMBackend.m | 462 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- MMVimController.h | 3 + MMVimController.m | 19 +++ MacVim.h | 37 ++++- MacVim.m | 1 + gui_macvim.m | 207 ++++++++---------------- 8 files changed, 598 insertions(+), 160 deletions(-) diff --git a/MMAppController.m b/MMAppController.m index 48a54eb0..906c5d41 100644 --- a/MMAppController.m +++ b/MMAppController.m @@ -405,8 +405,9 @@ static NSTimeInterval MMTerminateTimeout = 3; } } -- (byref id )connectBackend: - (byref in id )backend pid:(int)pid +- (byref id ) + connectBackend:(byref in id )backend + pid:(int)pid { //NSLog(@"Frontend got connection request from backend...adding new " // "MMVimController"); @@ -440,6 +441,7 @@ static NSTimeInterval MMTerminateTimeout = 3; unsigned i, count = [vimControllers count]; for (i = 0; i < count; ++i) { MMVimController *controller = [vimControllers objectAtIndex:i]; +#if 0 id proxy = [controller backendProxy]; NSConnection *connection = [proxy connectionForProxy]; if (!connection) @@ -465,6 +467,10 @@ static NSTimeInterval MMTerminateTimeout = 3; [connection setRequestTimeout:req]; [connection setReplyTimeout:rep]; } +#else + if ([controller serverName]) + [array addObject:[controller serverName]]; +#endif } return array; diff --git a/MMBackend.h b/MMBackend.h index 7cee92b1..cca74e36 100644 --- a/MMBackend.h +++ b/MMBackend.h @@ -10,6 +10,7 @@ #import #import "MacVim.h" +#import "vim.h" // If disabled, all input is dropped if input is already being processed. (If @@ -20,7 +21,8 @@ -@interface MMBackend : NSObject { +@interface MMBackend : NSObject { NSMutableArray *queue; NSMutableData *drawData; NSConnection *connection; @@ -45,6 +47,10 @@ #if MM_USE_INPUT_QUEUE NSMutableArray *inputQueue; #endif + NSMutableDictionary *connectionNameDict; + NSMutableDictionary *clientProxyDict; + NSMutableDictionary *serverReplyDict; + NSString *alternateServerName; } + (MMBackend *)sharedInstance; @@ -53,8 +59,8 @@ - (void)setForegroundColor:(int)color; - (void)setSpecialColor:(int)color; - (void)setDefaultColorsBackground:(int)bg foreground:(int)fg; +- (NSConnection *)connection; -- (NSString *)macVimConnectionName; - (BOOL)checkin; - (BOOL)openVimWindow; - (void)clearAll; @@ -109,4 +115,13 @@ - (int)lookupColorWithKey:(NSString *)key; - (BOOL)hasSpecialKeyWithValue:(NSString *)value; +- (void)registerServerWithName:(NSString *)name; +- (BOOL)sendToServer:(NSString *)name string:(NSString *)string + reply:(char_u **)reply port:(int *)port expression:(BOOL)expr + silent:(BOOL)silent; +- (NSArray *)serverList; +- (NSString *)peekForReplyOnPort:(int)port; +- (NSString *)waitForReplyOnPort:(int)port; +- (BOOL)sendReply:(NSString *)reply toPort:(int)port; + @end diff --git a/MMBackend.m b/MMBackend.m index 4175603a..a19b2699 100644 --- a/MMBackend.m +++ b/MMBackend.m @@ -9,7 +9,6 @@ */ #import "MMBackend.h" -#import "vim.h" @@ -19,6 +18,9 @@ // file). (The unit is seconds.) static float MMFlushTimeoutInterval = 0.1f; +static unsigned MMServerMax = 1000; +//static NSTimeInterval MMEvaluateExpressionTimeout = 3; + // TODO: Move to separate file. static int eventModifierFlagsToVimModMask(int modifierFlags); @@ -34,6 +36,13 @@ enum { }; + +@interface NSString (MMServerNameCompare) +- (NSComparisonResult)serverNameCompare:(NSString *)string; +@end + + + @interface MMBackend (Private) - (void)handleMessage:(int)msgid data:(NSData *)data; + (NSDictionary *)specialKeys; @@ -44,6 +53,12 @@ enum { - (void)focusChange:(BOOL)on; - (void)processInputBegin; - (void)processInputEnd; +- (NSString *)connectionNameFromServerName:(NSString *)name; +- (NSConnection *)connectionForServerName:(NSString *)name; +- (NSConnection *)connectionForServerPort:(int)port; +- (void)serverConnectionDidDie:(NSNotification *)notification; +- (void)addClient:(NSDistantObject *)client; +- (NSString *)alternateServerNameForName:(NSString *)name; @end @@ -64,6 +79,9 @@ enum { inputQueue = [[NSMutableArray alloc] init]; #endif drawData = [[NSMutableData alloc] initWithCapacity:1024]; + connectionNameDict = [[NSMutableDictionary alloc] init]; + clientProxyDict = [[NSMutableDictionary alloc] init]; + serverReplyDict = [[NSMutableDictionary alloc] init]; NSString *path = [[NSBundle mainBundle] pathForResource:@"Colors" ofType:@"plist"]; @@ -97,6 +115,10 @@ enum { #if MM_USE_INPUT_QUEUE [inputQueue release]; inputQueue = nil; #endif + [alternateServerName release]; alternateServerName = nil; + [serverReplyDict release]; serverReplyDict = nil; + [clientProxyDict release]; clientProxyDict = nil; + [connectionNameDict release]; connectionNameDict = nil; [queue release]; queue = nil; [drawData release]; drawData = nil; [frontendProxy release]; frontendProxy = nil; @@ -135,21 +157,26 @@ enum { [self queueMessage:SetDefaultColorsMsgID data:data]; } -- (NSString *)macVimConnectionName +- (NSConnection *)connection { - // NOTE! If the name of the connection changes here it must also be - // updated in MMAppController.m. - return [NSString stringWithFormat:@"%@-connection", - [[NSBundle mainBundle] bundleIdentifier]]; + if (!connection) { + // NOTE! If the name of the connection changes here it must also be + // updated in MMAppController.m. + NSString *name = [NSString stringWithFormat:@"%@-connection", + [[NSBundle mainBundle] bundleIdentifier]]; + + connection = [NSConnection connectionWithRegisteredName:name host:nil]; + [connection retain]; + } + + // NOTE: 'connection' may be nil here. + return connection; } - (BOOL)checkin { - NSBundle *mainBundle = [NSBundle mainBundle]; - - NSString *name = [self macVimConnectionName]; - connection = [NSConnection connectionWithRegisteredName:name host:nil]; - if (!connection) { + if (![self connection]) { + NSBundle *mainBundle = [NSBundle mainBundle]; #if 0 NSString *path = [mainBundle bundlePath]; if (![[NSWorkspace sharedWorkspace] launchApplication:path]) { @@ -190,8 +217,8 @@ enum { runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]; - connection = [NSConnection connectionWithRegisteredName:name - host:nil]; + // NOTE: This call will set 'connection' as a side-effect. + [self connection]; } if (!connection) { @@ -210,14 +237,14 @@ enum { int pid = [[NSProcessInfo processInfo] processIdentifier]; @try { - frontendProxy = [(NSDistantObject*)[proxy connectBackend:self - pid:pid] retain]; + frontendProxy = [proxy connectBackend:self pid:pid]; } @catch (NSException *e) { NSLog(@"Exception caught when trying to connect backend: \"%@\"", e); } if (frontendProxy) { + [frontendProxy retain]; [frontendProxy setProtocolForProxy:@protocol(MMAppProtocol)]; } @@ -859,6 +886,12 @@ enum { [self queueMessage:ActivateMsgID data:nil]; } +- (void)setServerName:(NSString *)name +{ + NSData *data = [name dataUsingEncoding:NSUTF8StringEncoding]; + [self queueMessage:SetServerNameMsgID data:data]; +} + - (int)lookupColorWithKey:(NSString *)key { if (!(key && [key length] > 0)) @@ -1020,6 +1053,7 @@ enum { return NO; } +#if 0 - (NSString *)evaluateExpression:(in bycopy NSString *)expr { NSString *eval = nil; @@ -1032,6 +1066,230 @@ enum { return eval; } +#endif + +- (oneway void)addReply:(in bycopy NSString *)reply + server:(in byref id )server +{ + //NSLog(@"addReply:%@ server:%@", reply, (id)server); + + // Replies might come at any time and in any order so we keep them in an + // array inside a dictionary with the send port used as key. + + NSConnection *conn = [(NSDistantObject*)server connectionForProxy]; + // HACK! Assume connection uses mach ports. + int port = [(NSMachPort*)[conn sendPort] machPort]; + NSNumber *key = [NSNumber numberWithInt:port]; + + NSMutableArray *replies = [serverReplyDict objectForKey:key]; + if (!replies) { + replies = [NSMutableArray array]; + [serverReplyDict setObject:replies forKey:key]; + } + + [replies addObject:reply]; +} + +- (void)addInput:(in bycopy NSString *)input + client:(in byref id )client +{ + //NSLog(@"addInput:%@ client:%@", input, (id)client); + + server_to_input_buf((char_u*)[input UTF8String]); + + [self addClient:(id)client]; + + inputReceived = YES; +} + +- (NSString *)evaluateExpression:(in bycopy NSString *)expr + client:(in byref id )client +{ + //NSLog(@"evaluateExpression:%@ client:%@", expr, (id)client); + + 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); + } + + [self addClient:(id)client]; + + return eval; +} + +- (void)registerServerWithName:(NSString *)name +{ + NSString *svrName = name; + NSConnection *svrConn = [NSConnection defaultConnection]; + unsigned i; + + for (i = 0; i < MMServerMax; ++i) { + NSString *connName = [self connectionNameFromServerName:svrName]; + + if ([svrConn registerName:connName]) { + //NSLog(@"Registered server with name: %@", svrName); + + // Don't wait for requests (time-out means that the message is + // dropped). + [svrConn setRequestTimeout:0]; + //[svrConn setReplyTimeout:MMReplyTimeout]; + [svrConn setRootObject:self]; + + // NOTE: 'serverName' is a global variable + serverName = vim_strsave((char_u*)[svrName UTF8String]); +#ifdef FEAT_EVAL + set_vim_var_string(VV_SEND_SERVER, serverName, -1); +#endif +#ifdef FEAT_TITLE + need_maketitle = TRUE; +#endif + [self queueMessage:SetServerNameMsgID data: + [svrName dataUsingEncoding:NSUTF8StringEncoding]]; + break; + } + + svrName = [NSString stringWithFormat:@"%@%d", name, i+1]; + } +} + +- (BOOL)sendToServer:(NSString *)name string:(NSString *)string + reply:(char_u **)reply port:(int *)port expression:(BOOL)expr + silent:(BOOL)silent +{ + // NOTE: If 'name' equals 'serverName' then the request is local (client + // and server are the same). This case is not handled separately, so a + // connection will be set up anyway (this simplifies the code). + + NSConnection *conn = [self connectionForServerName:name]; + if (!conn) { + if (!silent) + EMSG2(_(e_noserver), [name UTF8String]); + return NO; + } + + if (port) { + // HACK! Assume connection uses mach ports. + *port = [(NSMachPort*)[conn sendPort] machPort]; + } + + id proxy = [conn rootProxy]; + [proxy setProtocolForProxy:@protocol(MMVimServerProtocol)]; + + @try { + if (expr) { + NSString *eval = [proxy evaluateExpression:string client:self]; + if (reply) { + *reply = (eval ? vim_strsave((char_u*)[eval UTF8String]) + : vim_strsave((char_u*)_(e_invexprmsg))); + } + + if (!eval) + return NO; + } else { + [proxy addInput:string client:self]; + } + } + @catch (NSException *e) { + NSLog(@"WARNING: Caught exception in %s: \"%@\"", _cmd, e); + return NO; + } + + return YES; +} + +- (NSArray *)serverList +{ + NSArray *list = nil; + + if ([self connection]) { + id proxy = [connection rootProxy]; + [proxy setProtocolForProxy:@protocol(MMAppProtocol)]; + + @try { + list = [proxy serverList]; + } + @catch (NSException *e) { + NSLog(@"Exception caught when listing servers: \"%@\"", e); + } + } else { + EMSG(_("E???: No connection to MacVim, server listing not possible.")); + } + + return list; +} + +- (NSString *)peekForReplyOnPort:(int)port +{ + //NSLog(@"%s%d", _cmd, port); + + NSNumber *key = [NSNumber numberWithInt:port]; + NSMutableArray *replies = [serverReplyDict objectForKey:key]; + if (replies && [replies count]) { + //NSLog(@" %d replies, topmost is: %@", [replies count], + // [replies objectAtIndex:0]); + return [replies objectAtIndex:0]; + } + + //NSLog(@" No replies"); + return nil; +} + +- (NSString *)waitForReplyOnPort:(int)port +{ + //NSLog(@"%s%d", _cmd, port); + + NSConnection *conn = [self connectionForServerPort:port]; + if (!conn) + return nil; + + NSNumber *key = [NSNumber numberWithInt:port]; + NSMutableArray *replies = nil; + NSString *reply = nil; + + // Wait for reply as long as the connection to the server is valid (unless + // user interrupts wait with Ctrl-C). + while (!got_int && [conn isValid] && + !(replies = [serverReplyDict objectForKey:key])) { + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]; + } + + if (replies) { + if ([replies count] > 0) { + reply = [[replies objectAtIndex:0] retain]; + //NSLog(@" Got reply: %@", reply); + [replies removeObjectAtIndex:0]; + [reply autorelease]; + } + + if ([replies count] == 0) + [serverReplyDict removeObjectForKey:key]; + } + + return reply; +} + +- (BOOL)sendReply:(NSString *)reply toPort:(int)port +{ + id client = [clientProxyDict objectForKey:[NSNumber numberWithInt:port]]; + if (client) { + @try { + //NSLog(@"sendReply:%@ toPort:%d", reply, port); + [client addReply:reply server:self]; + return YES; + } + @catch (NSException *e) { + NSLog(@"WARNING: Exception caught in %s: \"%@\"", _cmd, e); + } + } else { + EMSG2(_("E???: server2client failed; no client with id 0x%x"), port); + } + + return NO; +} @end // MMBackend @@ -1586,11 +1844,185 @@ enum { inProcessInput = NO; } +- (NSString *)connectionNameFromServerName:(NSString *)name +{ + NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + + return [[NSString stringWithFormat:@"%@.%@", bundleIdentifier, name] + lowercaseString]; +} + +- (NSConnection *)connectionForServerName:(NSString *)name +{ + // TODO: Try 'name%d' if 'name' fails. + NSString *connName = [self connectionNameFromServerName:name]; + NSConnection *svrConn = [connectionNameDict objectForKey:connName]; + + if (!svrConn) { + svrConn = [NSConnection connectionWithRegisteredName:connName + host:nil]; +#if 0 + if (!svrConn && [name length] > 0) { + unichar lastChar = [name characterAtIndex:[name length]-1]; + if (lastChar < '0' && lastChar > '9') { + // No connection for 'name' exists, and 'name' does not end + // with a digit, so try to find connection with name 'name%d'. + int i; + for (i = 1; i <= 10; ++i) { + NSString *altName = [NSString stringWithFormat:@"%@%d", + connName, i]; + svrConn = [NSConnection + connectionWithRegisteredName:altName host:nil]; + if (svrConn) { + connName = altName; + break; + } + } + } + } +#else + // Try alternate server... + if (!svrConn && alternateServerName) { + //NSLog(@" trying to connect to alternate server: %@", + // alternateServerName); + connName = [self connectionNameFromServerName:alternateServerName]; + svrConn = [NSConnection connectionWithRegisteredName:connName + host:nil]; + } + + // Try looking for alternate servers... + if (!svrConn) { + NSLog(@" looking for alternate servers..."); + NSString *alt = [self alternateServerNameForName:name]; + if (alt != alternateServerName) { + //NSLog(@" found alternate server: %@", string); + [alternateServerName release]; + alternateServerName = [alt copy]; + } + } + + // Try alternate server again... + if (!svrConn && alternateServerName) { + //NSLog(@" trying to connect to alternate server: %@", + // alternateServerName); + connName = [self connectionNameFromServerName:alternateServerName]; + svrConn = [NSConnection connectionWithRegisteredName:connName + host:nil]; + } + +#endif + + if (svrConn) { + [connectionNameDict setObject:svrConn forKey:connName]; + + //NSLog(@"Adding %@ as connection observer for %@", self, svrConn); + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(serverConnectionDidDie:) + name:NSConnectionDidDieNotification object:svrConn]; + } + } + + return svrConn; +} + +- (NSConnection *)connectionForServerPort:(int)port +{ + NSConnection *conn; + NSEnumerator *e = [connectionNameDict objectEnumerator]; + + while ((conn = [e nextObject])) { + // HACK! Assume connection uses mach ports. + if (port == [(NSMachPort*)[conn sendPort] machPort]) + return conn; + } + + return nil; +} + +- (void)serverConnectionDidDie:(NSNotification *)notification +{ + //NSLog(@"%s%@", _cmd, notification); + + NSConnection *svrConn = [notification object]; + + //NSLog(@"Removing %@ as connection observer from %@", self, svrConn); + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:NSConnectionDidDieNotification + object:svrConn]; + + [connectionNameDict removeObjectsForKeys: + [connectionNameDict allKeysForObject:svrConn]]; + + // HACK! Assume connection uses mach ports. + int port = [(NSMachPort*)[svrConn sendPort] machPort]; + NSNumber *key = [NSNumber numberWithInt:port]; + + [clientProxyDict removeObjectForKey:key]; + [serverReplyDict removeObjectForKey:key]; +} + +- (void)addClient:(NSDistantObject *)client +{ + NSConnection *conn = [client connectionForProxy]; + // HACK! Assume connection uses mach ports. + int port = [(NSMachPort*)[conn sendPort] machPort]; + NSNumber *key = [NSNumber numberWithInt:port]; + + if (![clientProxyDict objectForKey:key]) { + [client setProtocolForProxy:@protocol(MMVimClientProtocol)]; + [clientProxyDict setObject:client forKey:key]; + } + + // NOTE: 'clientWindow' is a global variable which is used by + clientWindow = port; +} + +- (NSString *)alternateServerNameForName:(NSString *)name +{ + if (!(name && [name length] > 0)) + return nil; + + // Only look for alternates if 'name' doesn't end in a digit. + unichar lastChar = [name characterAtIndex:[name length]-1]; + if (lastChar >= '0' && lastChar <= '9') + return nil; + + // Look for alternates among all current servers. + NSArray *list = [self serverList]; + if (!(list && [list count] > 0)) + return nil; + + // Filter out servers starting with 'name' and ending with a number. The + // (?i) pattern ensures that the match case insensitive. + NSString *pat = [NSString stringWithFormat:@"(?i)%@[0-9]+\\z", name]; + NSPredicate *pred = [NSPredicate predicateWithFormat: + @"SELF MATCHES %@", pat]; + list = [list filteredArrayUsingPredicate:pred]; + if ([list count] > 0) { + list = [list sortedArrayUsingSelector:@selector(serverNameCompare:)]; + return [list objectAtIndex:0]; + } + + return nil; +} + @end // MMBackend (Private) +@implementation NSString (MMServerNameCompare) +- (NSComparisonResult)serverNameCompare:(NSString *)string +{ + return [self compare:string + options:NSCaseInsensitiveSearch|NSNumericSearch]; +} +@end + + + + static int eventModifierFlagsToVimModMask(int modifierFlags) { int modMask = 0; diff --git a/MMVimController.h b/MMVimController.h index 8c7f863c..c0ca0a4f 100644 --- a/MMVimController.h +++ b/MMVimController.h @@ -29,11 +29,14 @@ NSToolbar *toolbar; NSMutableDictionary *toolbarItemDict; int pid; + NSString *serverName; } - (id)initWithBackend:(id)backend pid:(int)processIdentifier; - (id)backendProxy; - (int)pid; +- (void)setServerName:(NSString *)name; +- (NSString *)serverName; - (MMWindowController *)windowController; - (void)cleanup; - (void)dropFiles:(NSArray *)filenames; diff --git a/MMVimController.m b/MMVimController.m index 39f968b9..e0d7d77b 100644 --- a/MMVimController.m +++ b/MMVimController.m @@ -143,6 +143,7 @@ static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag) //NSLog(@"%@ %s", [self className], _cmd); isInitialized = NO; + [serverName release]; serverName = nil; [backendProxy release]; backendProxy = nil; [sendQueue release]; sendQueue = nil; @@ -160,6 +161,19 @@ static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag) return windowController; } +- (void)setServerName:(NSString *)name +{ + if (name != serverName) { + [serverName release]; + serverName = [name copy]; + } +} + +- (NSString *)serverName +{ + return serverName; +} + - (int)pid { return pid; @@ -752,6 +766,11 @@ static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag) } else if (ActivateMsgID == msgid) { [NSApp activateIgnoringOtherApps:YES]; [[windowController window] makeKeyAndOrderFront:self]; + } else if (SetServerNameMsgID == msgid) { + NSString *name = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + [self setServerName:name]; + [name release]; } else { NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid); } diff --git a/MacVim.h b/MacVim.h index 7d3f0a52..f9628783 100644 --- a/MacVim.h +++ b/MacVim.h @@ -35,7 +35,9 @@ - (BOOL)checkForModifiedBuffers; - (oneway void)setDialogReturn:(in bycopy id)obj; - (BOOL)starRegisterToPasteboard:(byref NSPasteboard *)pboard; +#if 0 - (NSString *)evaluateExpression:(in bycopy NSString *)expr; +#endif @end @@ -61,12 +63,42 @@ // It handles connections between MacVim and Vim. // @protocol MMAppProtocol -- (byref id )connectBackend: - (byref in id )backend pid:(int)pid; +- (byref id ) + connectBackend:(byref in id )backend + pid:(int)pid; - (NSArray *)serverList; @end +@protocol MMVimServerProtocol; + +// +// The Vim client protocol (implemented by MMBackend). +// +// The client needs to keep track of server replies. Take a look at MMBackend +// if you want to implement this protocol in another program. +// +@protocol MMVimClientProtocol +- (oneway void)addReply:(in bycopy NSString *)reply + server:(in byref id )server; +@end + + +// +// The Vim server protocol (implemented by MMBackend). +// +// Note that addInput:client: is not asynchronous, because otherwise Vim might +// quit before the message has been passed (e.g. if --remote was used on the +// command line). +// +@protocol MMVimServerProtocol +- (void)addInput:(in bycopy NSString *)input + client:(in byref id )client; +- (NSString *)evaluateExpression:(in bycopy NSString *)expr + client:(in byref id )client; +@end + + // // The following enum lists all messages that are passed between MacVim and @@ -123,6 +155,7 @@ enum { AdjustLinespaceMsgID, ActivateMsgID, ServerAddInputMsgID, + SetServerNameMsgID, }; diff --git a/MacVim.m b/MacVim.m index 69e810b3..7cf09ea3 100644 --- a/MacVim.m +++ b/MacVim.m @@ -59,6 +59,7 @@ char *MessageStrings[] = "AdjustLinespaceMsgID", "ActivateMsgID", "ServerAddInputMsgID", + "SetServerNameMsgID", }; diff --git a/gui_macvim.m b/gui_macvim.m index 3c4084fd..da212fc9 100644 --- a/gui_macvim.m +++ b/gui_macvim.m @@ -1335,74 +1335,25 @@ gui_macvim_is_valid_action(NSString *action) #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. +// NOTE: Client/Server is only fully supported with a GUI. Theoretically it +// would be possible to make the server code work with terminal Vim, but it +// would require that a run-loop is set up and checked. This should not be +// difficult to implement, simply call gui_mch_update() at opportune moments +// and it will take care of the run-loop. Another (bigger) problem with +// supporting servers in terminal mode is that the server listing code talks to +// MacVim (the GUI) to figure out which servers are running. // -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]; - } +serverRegisterName(char_u *name) +{ + NSString *svrName = [NSString stringWithUTF8String:(char*)name]; + [[MMBackend sharedInstance] registerServerWithName:svrName]; } @@ -1412,55 +1363,17 @@ serverSetName(char_u *name) */ int serverSendToVim(char_u *name, char_u *cmd, char_u **result, - int *server, int asExpr, int silent) + int *port, 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]; + BOOL ok = [[MMBackend sharedInstance] + sendToServer:[NSString stringWithUTF8String:(char*)name] + string:[NSString stringWithUTF8String:(char*)cmd] + reply:result + port:port + expression:asExpr + silent:silent]; - [data appendBytes:&len length:sizeof(int)]; - [data appendBytes:cmd length:len]; - - [proxy processInput:ServerAddInputMsgID data:data]; - } - - return 0; + return ok ? 0 : -1; } @@ -1471,28 +1384,11 @@ serverSendToVim(char_u *name, char_u *cmd, char_u **result, 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); - } + NSArray *list = [[MMBackend sharedInstance] serverList]; - if (serverNames) { - NSString *string = [serverNames componentsJoinedByString:@"\n"]; - names = vim_strsave((char_u*)[string UTF8String]); - } + if (list) { + NSString *string = [list componentsJoinedByString:@"\n"]; + names = vim_strsave((char_u*)[string UTF8String]); } return names; @@ -1500,38 +1396,71 @@ serverGetVimNames(void) /* - * Check for replies from 'serverid'. + * 'str' is a hex int representing the send port of the connection. + */ + int +serverStrToPort(char_u *str) +{ + int port = 0; + + sscanf((char *)str, "0x%x", &port); + if (!port) + EMSG2(_("E573: Invalid server id used: %s"), str); + + return port; +} + + +/* + * Check for replies from server with send port 'port'. * Return TRUE and a non-malloc'ed string if there is. Else return FALSE. */ int -serverPeekReply(char_u *serverid, char_u **str) +serverPeekReply(int port, char_u **str) { - return FALSE; + NSString *reply = [[MMBackend sharedInstance] peekForReplyOnPort:port]; + if (str) + *str = (char_u*)[reply UTF8String]; + + return reply != nil; } /* - * Wait for replies from server 'name'. + * Wait for replies from server with send port 'port'. * Return 0 and the malloc'ed string when a reply is available. * Return -1 on error. */ int -serverReadReply(char_u *name, char_u **str) +serverReadReply(int port, char_u **str) { - //NSLog(@"serverReadReply(name=%s)", name); + NSString *reply = [[MMBackend sharedInstance] waitForReplyOnPort:port]; + if (reply && str) { + *str = vim_strsave((char_u*)[reply UTF8String]); + return 0; + } + return -1; } /* - * Send a reply string (notification) to client with id "name". + * Send a reply string (notification) to client with port given by "serverid". * 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; +serverSendReply(char_u *serverid, char_u *reply) +{ + int retval = -1; + int port = serverStrToPort(serverid); + if (port > 0 && reply) { + BOOL ok = [[MMBackend sharedInstance] + sendReply:[NSString stringWithUTF8String:(char*)reply] + toPort:port]; + retval = ok ? 0 : -1; + } + + return retval; } #endif // MAC_CLIENTSERVER -- 2.11.4.GIT