From f99ce72a11a72738955fa9b4246ecc2ba964c105 Mon Sep 17 00:00:00 2001 From: Bjorn Winckler Date: Tue, 27 Nov 2007 21:07:08 +0100 Subject: [PATCH] ODB Editor protocol (aka 'external editor') support Programs that support ODB asks MacVim to open files. When a file is written or closed, MacVim notifies the program of these events. (At the moment the 'Burl' parameter is parsed but ignored. The 'RdEV' parameter is ignored.) --- runtime/doc/gui_mac.txt | 4 + src/MacVim/Info.plist | 2 +- src/MacVim/MMAppController.h | 1 + src/MacVim/MMAppController.m | 158 +++++++++++++++++++++++++++++++---- src/MacVim/MMBackend.m | 111 +++++++++++++++++++++++- src/MacVim/MMTextView.m | 2 +- src/MacVim/MMVimController.h | 4 +- src/MacVim/MMVimController.m | 58 ++++++++++++- src/MacVim/MacVim.h | 16 ++++ src/MacVim/MacVim.m | 2 + src/MacVim/gui_macvim.m | 90 ++++++++++++++++++++ src/buffer.c | 4 + src/eval.c | 9 ++ src/feature.h | 7 ++ src/fileio.c | 4 + src/main.c | 3 + src/proto/gui_macvim.pro | 5 ++ src/structs.h | 5 ++ src/version.c | 5 ++ 19 files changed, 466 insertions(+), 24 deletions(-) diff --git a/runtime/doc/gui_mac.txt b/runtime/doc/gui_mac.txt index 98b703426e..59c62be5a5 100644 --- a/runtime/doc/gui_mac.txt +++ b/runtime/doc/gui_mac.txt @@ -102,6 +102,10 @@ window, unless Vim is in command-line mode. In command-line mode the names of the files are added to the command line. Holding down modifier keys whilst dragging is not supported. +If a file is dropped on the Dock icon, it is always opened in a new tab +regardless of the mode Vim is currently in. The same holds if you +double-click on a file in the Finder. + *macvim-default-menu* The default menu in MacVim has been changed to conform better with the Apple Human Interface Guidelines (HIG). At the moment this breaks the localized diff --git a/src/MacVim/Info.plist b/src/MacVim/Info.plist index 1d24daeb97..99cfcf0822 100644 --- a/src/MacVim/Info.plist +++ b/src/MacVim/Info.plist @@ -531,7 +531,7 @@ CFBundleIconFile vim_gloss CFBundleIdentifier - org.vim.MacVim + org.vim.MacVim-odb CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/src/MacVim/MMAppController.h b/src/MacVim/MMAppController.h index 5033d7e719..a9fbe44511 100644 --- a/src/MacVim/MMAppController.h +++ b/src/MacVim/MMAppController.h @@ -20,6 +20,7 @@ NSString *openSelectionString; ATSFontContainerRef fontContainerRef; BOOL untitledWindowOpening; + NSMutableDictionary *pidArguments; } - (void)removeVimController:(id)controller; diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index 6a2c2bea2a..a488250901 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -32,6 +32,7 @@ + // Default timeout intervals on all connections. static NSTimeInterval MMRequestTimeout = 5; static NSTimeInterval MMReplyTimeout = 5; @@ -49,9 +50,13 @@ static NSTimeInterval MMReplyTimeout = 5; @interface MMAppController (Private) - (MMVimController *)keyVimController; - (MMVimController *)topmostVimController; -- (void)launchVimProcessWithArguments:(NSArray *)args; +- (int)launchVimProcessWithArguments:(NSArray *)args; - (NSArray *)filterFilesAndNotify:(NSArray *)files; -- (NSArray *)filterOpenFilesAndRaiseFirst:(NSArray *)filenames; +- (NSArray *)filterOpenFiles:(NSArray *)filenames remote:(OSType)theID + path:(NSString *)path + token:(NSAppleEventDescriptor *)token; +- (void)handleXcodeModEvent:(NSAppleEventDescriptor *)event + replyEvent:(NSAppleEventDescriptor *)reply; @end @interface NSMenu (MMExtras) @@ -99,6 +104,7 @@ static NSTimeInterval MMReplyTimeout = 5; fontContainerRef = loadFonts(); vimControllers = [NSMutableArray new]; + pidArguments = [NSMutableDictionary new]; // NOTE! If the name of the connection changes here it must also be // updated in MMBackend.m. @@ -128,12 +134,22 @@ static NSTimeInterval MMReplyTimeout = 5; { //NSLog(@"MMAppController dealloc"); + [pidArguments release]; [vimControllers release]; [openSelectionString release]; [super dealloc]; } +- (void)applicationWillFinishLaunching:(NSNotification *)notification +{ + [[NSAppleEventManager sharedAppleEventManager] + setEventHandler:self + andSelector:@selector(handleXcodeModEvent:replyEvent:) + forEventClass:'KAHL' + andEventID:'MOD ']; +} + - (void)applicationDidFinishLaunching:(NSNotification *)notification { [NSApp setServicesProvider:self]; @@ -150,26 +166,79 @@ static NSTimeInterval MMReplyTimeout = 5; - (BOOL)applicationOpenUntitledFile:(NSApplication *)sender { - //NSLog(@"%s NSapp=%@ theApp=%@", _cmd, NSApp, sender); - [self newWindow:self]; return YES; } - (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames { - filenames = [self filterOpenFilesAndRaiseFirst:filenames]; + OSType remoteID; + NSString *remotePath; + NSAppleEventDescriptor *remoteToken; + NSAppleEventDescriptor *odbdesc = + [[NSAppleEventManager sharedAppleEventManager] currentAppleEvent]; + + if (![odbdesc paramDescriptorForKeyword:keyFileSender]) { + // The ODB paramaters may hide inside the 'keyAEPropData' descriptor. + odbdesc = [odbdesc paramDescriptorForKeyword:keyAEPropData]; + if (![odbdesc paramDescriptorForKeyword:keyFileSender]) + odbdesc = nil; + } + + if (odbdesc) { + remoteID = [[odbdesc paramDescriptorForKeyword:keyFileSender] + typeCodeValue]; + remotePath = [[odbdesc paramDescriptorForKeyword:keyFileCustomPath] + stringValue]; + remoteToken = [[odbdesc paramDescriptorForKeyword:keyFileSenderToken] + copy]; + + //NSLog(@"ODB parameters: ID=0x%x path=%@ token=%@", + // remoteID, remotePath, remoteToken); + } + + filenames = [self filterOpenFiles:filenames remote:remoteID path:remotePath + token:remoteToken]; if ([filenames count]) { MMVimController *vc; BOOL openInTabs = [[NSUserDefaults standardUserDefaults] boolForKey:MMOpenFilesInTabsKey]; if (openInTabs && (vc = [self topmostVimController])) { - [vc dropFiles:filenames]; + // Open files in tabs in the topmost window. + [vc dropFiles:filenames forceOpen:YES]; + if (odbdesc) + [vc odbEdit:filenames server:remoteID path:remotePath + token:remoteToken]; } else { + // Open files in tabs in a new window. NSMutableArray *args = [NSMutableArray arrayWithObject:@"-p"]; [args addObjectsFromArray:filenames]; - [self launchVimProcessWithArguments:args]; + int pid = [self launchVimProcessWithArguments:args]; + + // The Vim process starts asynchronously. Some arguments cannot be + // on the command line, so store them in a dictionary and pass them + // to the process once it has started. + // + // TODO: If the Vim process fails to start, or if it changes PID, + // then the memory allocated for these parameters will leak. + // Ensure that this cannot happen or somehow detect it. + if (odbdesc) { + // The remote token can be arbitrary data so it is cannot + // (without encoding it as text) be passed on the command line. + NSMutableDictionary *args = + [NSMutableDictionary dictionaryWithObjectsAndKeys: + filenames, @"filenames", + [NSNumber numberWithUnsignedInt:remoteID], @"remoteID", + nil]; + if (remotePath) + [args setObject:remotePath forKey:@"remotePath"]; + if (remoteToken) + [args setObject:remoteToken forKey:@"remoteToken"]; + + [pidArguments setObject:args + forKey:[NSNumber numberWithInt:pid]]; + } } } @@ -232,8 +301,12 @@ static NSTimeInterval MMReplyTimeout = 5; return reply; } -- (void)applicationWillTerminate:(NSNotification *)aNotification +- (void)applicationWillTerminate:(NSNotification *)notification { + [[NSAppleEventManager sharedAppleEventManager] + removeEventHandlerForEventClass:'KAHL' + andEventID:'MOD ']; + // This will invalidate all connections (since they were spawned from the // default connection). [[NSConnection defaultConnection] invalidate]; @@ -382,8 +455,7 @@ static NSTimeInterval MMReplyTimeout = 5; connectBackend:(byref in id )backend pid:(int)pid { - //NSLog(@"Frontend got connection request from backend...adding new " - // "MMVimController"); + //NSLog(@"Connect backend (pid=%d)", pid); [(NSDistantObject*)backend setProtocolForProxy:@protocol(MMBackendProtocol)]; @@ -410,6 +482,21 @@ static NSTimeInterval MMReplyTimeout = 5; untitledWindowOpening = NO; + // Arguments to a new Vim process that cannot be passed on the command line + // are stored in a dictionary and passed to the Vim process here. + NSNumber *key = [NSNumber numberWithInt:pid]; + NSDictionary *args = [pidArguments objectForKey:key]; + if (args) { + if ([args objectForKey:@"remoteID"]) { + [vc odbEdit:[args objectForKey:@"filenames"] + server:[[args objectForKey:@"remoteID"] unsignedIntValue] + path:[args objectForKey:@"remotePath"] + token:[args objectForKey:@"remoteToken"]]; + } + + [pidArguments removeObjectForKey:key]; + } + return vc; } @@ -483,7 +570,7 @@ static NSTimeInterval MMReplyTimeout = 5; vc = [self topmostVimController]; if (vc) { - [vc dropFiles:filenames]; + [vc dropFiles:filenames forceOpen:YES]; } else { [self application:NSApp openFiles:filenames]; } @@ -528,7 +615,7 @@ static NSTimeInterval MMReplyTimeout = 5; return nil; } -- (void)launchVimProcessWithArguments:(NSArray *)args +- (int)launchVimProcessWithArguments:(NSArray *)args { NSString *taskPath = nil; NSArray *taskArgs = nil; @@ -536,7 +623,7 @@ static NSTimeInterval MMReplyTimeout = 5; if (!path) { NSLog(@"ERROR: Vim executable could not be found inside app bundle!"); - return; + return 0; } if ([[NSUserDefaults standardUserDefaults] boolForKey:MMLoginShellKey]) { @@ -577,8 +664,12 @@ static NSTimeInterval MMReplyTimeout = 5; taskArgs = [taskArgs arrayByAddingObjectsFromArray:args]; } - //NSLog(@"Launching: %@ args: %@", taskPath, taskArgs); - [NSTask launchedTaskWithLaunchPath:taskPath arguments:taskArgs]; + NSTask *task =[NSTask launchedTaskWithLaunchPath:taskPath + arguments:taskArgs]; + //NSLog(@"launch %@ with args=%@ (pid=%d)", [task processIdentifier], + // taskPath, taskArgs); + + return [task processIdentifier]; } - (NSArray *)filterFilesAndNotify:(NSArray *)filenames @@ -627,12 +718,15 @@ static NSTimeInterval MMReplyTimeout = 5; return files; } -- (NSArray *)filterOpenFilesAndRaiseFirst:(NSArray *)filenames +- (NSArray *)filterOpenFiles:(NSArray *)filenames remote:(OSType)theID + path:(NSString *)path + token:(NSAppleEventDescriptor *)token { // Check if any of the files in the 'filenames' array are open in any Vim // process. Remove the files that are open from the 'filenames' array and // return it. If all files were filtered out, then raise the first file in - // the Vim process it is open. + // the Vim process it is open. Files that are filtered are sent an odb + // open event in case theID is not zero. MMVimController *raiseController = nil; NSString *raiseFile = nil; @@ -658,6 +752,11 @@ static NSTimeInterval MMReplyTimeout = 5; [[raiseFile retain] autorelease]; } + // Send an odb open event to the Vim process. + if (theID != 0) + [controller odbEdit:[files objectsAtIndexes:idxSet] + server:theID path:path token:token]; + // Remove all the files that were open in this Vim process and // create a new expression to evaluate. [files removeObjectsAtIndexes:idxSet]; @@ -688,6 +787,31 @@ static NSTimeInterval MMReplyTimeout = 5; return files; } +- (void)handleXcodeModEvent:(NSAppleEventDescriptor *)event + replyEvent:(NSAppleEventDescriptor *)reply +{ +#if 0 + // Xcode sends this event to query MacVim which open files have been + // modified. + NSLog(@"reply:%@", reply); + NSLog(@"event:%@", event); + + NSEnumerator *e = [vimControllers objectEnumerator]; + id vc; + while ((vc = [e nextObject])) { + DescType type = [reply descriptorType]; + unsigned len = [[type data] length]; + NSMutableData *data = [NSMutableData data]; + + [data appendBytes:&type length:sizeof(DescType)]; + [data appendBytes:&len length:sizeof(unsigned)]; + [data appendBytes:[reply data] length:len]; + + [vc sendMessage:XcodeModMsgID data:data]; + } +#endif +} + @end // MMAppController (Private) diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 184d980ff6..0343771d01 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -85,6 +85,8 @@ enum { - (void)handleSetFont:(NSData *)data; - (void)handleDropFiles:(NSData *)data; - (void)handleDropString:(NSData *)data; +- (void)handleOdbEdit:(NSData *)data; +- (void)handleXcodeMod:(NSData *)data; - (BOOL)checkForModifiedBuffers; - (void)addInput:(NSString *)input; @end @@ -392,10 +394,12 @@ enum { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]; +#if 0 // Keyboard and mouse input is handled directly, other input is queued and // processed here. This call may enter a blocking loop. if ([inputQueue count] > 0) [self processInputQueue]; +#endif } - (void)flushQueue:(BOOL)force @@ -451,6 +455,8 @@ enum { BOOL yn = inputReceived; inputReceived = NO; + // Keyboard and mouse input is handled directly, other input is queued and + // processed here. This call may enter a blocking loop. if ([inputQueue count] > 0) [self processInputQueue]; @@ -1586,6 +1592,10 @@ enum { const void *bytes = [data bytes]; int shape = *((int*)bytes); bytes += sizeof(int); update_mouseshape(shape); + } else if (ODBEditMsgID == msgid) { + [self handleOdbEdit:data]; + } else if (XcodeModMsgID == msgid) { + [self handleXcodeMod:data]; } else { NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid); } @@ -1959,12 +1969,14 @@ enum { #ifdef FEAT_DND const void *bytes = [data bytes]; const void *end = [data bytes] + [data length]; + BOOL forceOpen = *((BOOL*)bytes); bytes += sizeof(BOOL); int n = *((int*)bytes); bytes += sizeof(int); - if (State & CMDLINE) { + if (!forceOpen && (State & CMDLINE)) { // HACK! If Vim is in command line mode then the files names // should be added to the command line, instead of opening the - // files in tabs. This is taken care of by gui_handle_drop(). + // files in tabs (unless forceOpen is set). This is taken care of by + // gui_handle_drop(). char_u **fnames = (char_u **)alloc(n * sizeof(char_u *)); if (fnames) { int i = 0; @@ -1990,7 +2002,12 @@ enum { // HACK! I'm not sure how to get Vim to open a list of files in // tabs, so instead I create a ':tab drop' command with all the // files to open and execute it. +#if 1 NSMutableString *cmd = [NSMutableString stringWithString:@":tab drop"]; +#else + NSMutableString *cmd = [NSMutableString stringWithString: + @":tab drop"]; +#endif int i; for (i = 0; i < n && bytes < end; ++i) { @@ -2003,6 +2020,7 @@ enum { [cmd appendString:file]; } +#if 1 // By going to the last tabpage we ensure that the new tabs will // appear last (if this call is left out, the taborder becomes // messy). @@ -2025,6 +2043,11 @@ enum { gui_update_cursor(FALSE, FALSE); maketitle(); gui_mch_flush(); +#else + [cmd appendString:@"|redr|f"]; + + [self addInput:cmd]; +#endif } #endif // FEAT_DND } @@ -2064,6 +2087,90 @@ enum { #endif // FEAT_DND } +- (void)handleOdbEdit:(NSData *)data +{ +#ifdef FEAT_ODB_EDITOR + const void *bytes = [data bytes]; + + OSType serverID = *((OSType*)bytes); bytes += sizeof(OSType); + + char_u *path = NULL; + int pathLen = *((int*)bytes); bytes += sizeof(int); + if (pathLen > 0) { + path = (char_u*)bytes; + bytes += pathLen; +#ifdef FEAT_MBYTE + path = CONVERT_FROM_UTF8(path); +#endif + } + + NSAppleEventDescriptor *token = nil; + DescType tokenType = *((DescType*)bytes); bytes += sizeof(DescType); + int descLen = *((int*)bytes); bytes += sizeof(int); + if (descLen > 0) { + token = [NSAppleEventDescriptor descriptorWithDescriptorType:tokenType + bytes:bytes + length:descLen]; + bytes += descLen; + } + + unsigned i, numFiles = *((unsigned*)bytes); bytes += sizeof(unsigned); + for (i = 0; i < numFiles; ++i) { + int len = *((int*)bytes); bytes += sizeof(int); + char_u *filename = (char_u*)bytes; +#ifdef FEAT_MBYTE + filename = CONVERT_FROM_UTF8(filename); +#endif + buf_T *buf = buflist_findname(filename); + if (buf) { + if (buf->b_odb_token) { + [(NSAppleEventDescriptor*)(buf->b_odb_token) release]; + buf->b_odb_token = NULL; + } + + if (buf->b_odb_fname) { + vim_free(buf->b_odb_fname); + buf->b_odb_fname = NULL; + } + + buf->b_odb_server_id = serverID; + + if (token) + buf->b_odb_token = [token retain]; + if (path) + buf->b_odb_fname = vim_strsave(path); + } else { + NSLog(@"WARNING: Could not find buffer '%s' for ODB editing.", + filename); + } + +#ifdef FEAT_MBYTE + CONVERT_FROM_UTF8_FREE(filename); +#endif + bytes += len; + } +#ifdef FEAT_MBYTE + CONVERT_FROM_UTF8_FREE(path); +#endif +#endif // FEAT_ODB_EDITOR +} + +- (void)handleXcodeMod:(NSData *)data +{ +#if 0 + const void *bytes = [data bytes]; + DescType type = *((DescType*)bytes); bytes += sizeof(DescType); + unsigned len = *((unsigned*)bytes); bytes += sizeof(unsigned); + if (0 == len) + return; + + NSAppleEventDescriptor *replyEvent = [NSAppleEventDescriptor + descriptorWithDescriptorType:type + bytes:bytes + length:len]; +#endif +} + - (BOOL)checkForModifiedBuffers { buf_T *buf; diff --git a/src/MacVim/MMTextView.m b/src/MacVim/MMTextView.m index 36e623da07..21ab4537f0 100644 --- a/src/MacVim/MMTextView.m +++ b/src/MacVim/MMTextView.m @@ -699,7 +699,7 @@ static NSString *MMKeypadEnterString = @"KA"; return YES; } else if ([[pboard types] containsObject:NSFilenamesPboardType]) { NSArray *files = [pboard propertyListForType:NSFilenamesPboardType]; - [[self vimController] dropFiles:files]; + [[self vimController] dropFiles:files forceOpen:NO]; return YES; } diff --git a/src/MacVim/MMVimController.h b/src/MacVim/MMVimController.h index 78ca65843f..14fa504dc5 100644 --- a/src/MacVim/MMVimController.h +++ b/src/MacVim/MMVimController.h @@ -49,8 +49,10 @@ - (NSString *)serverName; - (MMWindowController *)windowController; - (void)cleanup; -- (void)dropFiles:(NSArray *)filenames; +- (void)dropFiles:(NSArray *)filenames forceOpen:(BOOL)force; - (void)dropString:(NSString *)string; +- (void)odbEdit:(NSArray *)filenames server:(OSType)theID path:(NSString *)path + token:(NSAppleEventDescriptor *)token; - (void)sendMessage:(int)msgid data:(NSData *)data; - (BOOL)sendMessageNow:(int)msgid data:(NSData *)data timeout:(NSTimeInterval)timeout; diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index d436af5f68..8f5a027c07 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -182,11 +182,12 @@ static NSTimeInterval MMResendInterval = 0.5; return pid; } -- (void)dropFiles:(NSArray *)filenames +- (void)dropFiles:(NSArray *)filenames forceOpen:(BOOL)force { - int i, numberOfFiles = [filenames count]; + unsigned i, numberOfFiles = [filenames count]; NSMutableData *data = [NSMutableData data]; + [data appendBytes:&force length:sizeof(BOOL)]; [data appendBytes:&numberOfFiles length:sizeof(int)]; for (i = 0; i < numberOfFiles; ++i) { @@ -216,6 +217,59 @@ static NSTimeInterval MMResendInterval = 0.5; } } +- (void)odbEdit:(NSArray *)filenames server:(OSType)theID path:(NSString *)path + token:(NSAppleEventDescriptor *)token +{ + int len; + unsigned i, numberOfFiles = [filenames count]; + NSMutableData *data = [NSMutableData data]; + + if (0 == numberOfFiles || 0 == theID) + return; + + [data appendBytes:&theID length:sizeof(theID)]; + + if (path && [path length] > 0) { + len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + [data appendBytes:&len length:sizeof(int)]; + [data appendBytes:[path UTF8String] length:len]; + } else { + len = 0; + [data appendBytes:&len length:sizeof(int)]; + } + + if (token) { + DescType tokenType = [token descriptorType]; + NSData *tokenData = [token data]; + len = [tokenData length]; + + [data appendBytes:&tokenType length:sizeof(tokenType)]; + [data appendBytes:&len length:sizeof(int)]; + if (len > 0) + [data appendBytes:[tokenData bytes] length:len]; + } else { + DescType tokenType = 0; + len = 0; + [data appendBytes:&tokenType length:sizeof(tokenType)]; + [data appendBytes:&len length:sizeof(int)]; + } + + [data appendBytes:&numberOfFiles length:sizeof(int)]; + + for (i = 0; i < numberOfFiles; ++i) { + NSString *file = [filenames objectAtIndex:i]; + len = [file lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + + if (len > 0) { + ++len; // include NUL as well + [data appendBytes:&len length:sizeof(unsigned)]; + [data appendBytes:[file UTF8String] length:len]; + } + } + + [self sendMessage:ODBEditMsgID data:data]; +} + - (void)sendMessage:(int)msgid data:(NSData *)data { //NSLog(@"sendMessage:%s (isInitialized=%d inProcessCommandQueue=%d)", diff --git a/src/MacVim/MacVim.h b/src/MacVim/MacVim.h index 515f887775..f2d351ba1a 100644 --- a/src/MacVim/MacVim.h +++ b/src/MacVim/MacVim.h @@ -157,6 +157,8 @@ enum { AddInputMsgID, SetPreEditPositionMsgID, TerminateNowMsgID, + ODBEditMsgID, + XcodeModMsgID, }; @@ -234,3 +236,17 @@ ATSFontContainerRef loadFonts(); @interface NSIndexSet (MMExtras) + (id)indexSetWithVimList:(NSString *)list; @end + + + + +// ODB Editor Suite Constants (taken from ODBEditorSuite.h) +#define keyFileSender 'FSnd' +#define keyFileSenderToken 'FTok' +#define keyFileCustomPath 'Burl' +#define kODBEditorSuite 'R*ch' +#define kAEModifiedFile 'FMod' +#define keyNewLocation 'New?' +#define kAEClosedFile 'FCls' +#define keySenderToken 'Tokn' + diff --git a/src/MacVim/MacVim.m b/src/MacVim/MacVim.m index 5e718fd6d0..041ffee58d 100644 --- a/src/MacVim/MacVim.m +++ b/src/MacVim/MacVim.m @@ -70,6 +70,8 @@ char *MessageStrings[] = "AddInputMsgID", "SetPreEditPositionMsgID", "TerminateNowMsgID", + "ODBEditMsgID", + "XcodeModMsgID", }; diff --git a/src/MacVim/gui_macvim.m b/src/MacVim/gui_macvim.m index 839b8383e6..0f20fe999c 100644 --- a/src/MacVim/gui_macvim.m +++ b/src/MacVim/gui_macvim.m @@ -1708,3 +1708,93 @@ serverSendReply(char_u *serverid, char_u *reply) } #endif // MAC_CLIENTSERVER + + + + +// -- ODB Editor Support ---------------------------------------------------- + +#ifdef FEAT_ODB_EDITOR +/* + * The ODB Editor protocol works like this: + * - An external program (the server) asks MacVim to open a file and associates + * three things with this file: (1) a server id (a four character code that + * identifies the server), (2) a path that can be used as window title for + * the file (optional), (3) an arbitrary token (optional) + * - When a file is saved or closed, MacVim should tell the server about which + * file was modified and also pass back the token + * + * All communication between MacVim and the server goes via Apple Events. + */ + + static OSErr +odb_event(buf_T *buf, const AEEventID action) +{ + if (!(buf->b_odb_server_id && buf->b_ffname)) + return noErr; + + NSAppleEventDescriptor *targetDesc = [NSAppleEventDescriptor + descriptorWithDescriptorType:typeApplSignature + bytes:&buf->b_odb_server_id + length:sizeof(OSType)]; + + NSString *path = [NSString stringWithUTF8String:(char*)buf->b_ffname]; + NSData *pathData = [[[NSURL fileURLWithPath:path] absoluteString] + dataUsingEncoding:NSUTF8StringEncoding]; + NSAppleEventDescriptor *pathDesc = [NSAppleEventDescriptor + descriptorWithDescriptorType:typeFileURL data:pathData]; + + NSAppleEventDescriptor *event = [NSAppleEventDescriptor + appleEventWithEventClass:kODBEditorSuite + eventID:action + targetDescriptor:targetDesc + returnID:kAutoGenerateReturnID + transactionID:kAnyTransactionID]; + + [event setParamDescriptor:pathDesc forKeyword:keyDirectObject]; + + if (buf->b_odb_token) + [event setParamDescriptor:buf->b_odb_token forKeyword:keySenderToken]; + + return AESendMessage([event aeDesc], NULL, kAENoReply | kAENeverInteract, + kAEDefaultTimeout); +} + + OSErr +odb_buffer_close(buf_T *buf) +{ + OSErr err = noErr; + if (buf) { + err = odb_event(buf, kAEClosedFile); + + buf->b_odb_server_id = 0; + + if (buf->b_odb_token) { + [(NSAppleEventDescriptor *)(buf->b_odb_token) release]; + buf->b_odb_token = NULL; + } + + if (buf->b_odb_fname) { + vim_free(buf->b_odb_fname); + buf->b_odb_fname = NULL; + } + } + + return err; +} + + OSErr +odb_post_buffer_write(buf_T *buf) +{ + return buf ? odb_event(buf, kAEModifiedFile) : noErr; +} + + void +odb_end(void) +{ + buf_T *buf; + for (buf = firstbuf; buf != NULL; buf = buf->b_next) + odb_buffer_close(buf); +} + +#endif // FEAT_ODB_EDITOR diff --git a/src/buffer.c b/src/buffer.c index ab5a25a682..7cddc11f93 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -441,6 +441,10 @@ close_buffer(win, buf, action) if (usingNetbeans) netbeans_file_closed(buf); #endif +#ifdef FEAT_ODB_EDITOR + odb_buffer_close(buf); +#endif + /* Change directories when the 'acd' option is set. */ DO_AUTOCHDIR diff --git a/src/eval.c b/src/eval.c index fe81d74349..21a97c7741 100644 --- a/src/eval.c +++ b/src/eval.c @@ -10865,6 +10865,9 @@ f_has(argvars, rettv) #if !defined(USE_SYSTEM) && defined(UNIX) "fork", #endif +#ifdef FEAT_FULLSCREEN + "fullscreen", +#endif #ifdef FEAT_GETTEXT "gettext", #endif @@ -11052,6 +11055,9 @@ f_has(argvars, rettv) #ifdef FEAT_NETBEANS_INTG "netbeans_intg", #endif +#ifdef FEAT_ODB_EDITOR + "odbeditor", +#endif #ifdef FEAT_SPELL "spell", #endif @@ -11093,6 +11099,9 @@ f_has(argvars, rettv) #ifdef FEAT_TOOLBAR "toolbar", #endif +#ifdef FEAT_TRANSPARENCY + "transparency", +#endif #ifdef FEAT_USR_CMDS "user-commands", /* was accidentally included in 5.4 */ "user_commands", diff --git a/src/feature.h b/src/feature.h index a139b0db2f..f263cf091f 100644 --- a/src/feature.h +++ b/src/feature.h @@ -1282,3 +1282,10 @@ #ifdef FEAT_GUI_MACVIM # define FEAT_FULLSCREEN #endif + +/* + * +odbeditor ODBEditor protocol support (aka. external editor) + */ +#ifdef FEAT_GUI_MACVIM +# define FEAT_ODB_EDITOR +#endif diff --git a/src/fileio.c b/src/fileio.c index 9911abfca0..3d2ea6be7e 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4717,6 +4717,10 @@ nofail: /* Update machine specific information. */ mch_post_buffer_write(buf); #endif +#ifdef FEAT_ODB_EDITOR + odb_post_buffer_write(buf); +#endif + return retval; } diff --git a/src/main.c b/src/main.c index 4b46964889..17132624b7 100644 --- a/src/main.c +++ b/src/main.c @@ -1375,6 +1375,9 @@ getout(exitval) #ifdef FEAT_NETBEANS_INTG netbeans_end(); #endif +#ifdef FEAT_ODB_EDITOR + odb_end(); +#endif #ifdef FEAT_CSCOPE cs_end(); #endif diff --git a/src/proto/gui_macvim.pro b/src/proto/gui_macvim.pro index b821f5b2e3..0b9437e412 100644 --- a/src/proto/gui_macvim.pro +++ b/src/proto/gui_macvim.pro @@ -194,3 +194,8 @@ void gui_mch_enter_fullscreen(void); void gui_mch_leave_fullscreen(void); void gui_macvim_update_modified_flag(); + +OSErr odb_buffer_close(buf_T *buf); +OSErr odb_post_buffer_write(buf_T *buf); +void odb_end(void); + diff --git a/src/structs.h b/src/structs.h index f7ce6fbfd6..118035be95 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1583,6 +1583,11 @@ struct file_buffer int b_was_netbeans_file;/* TRUE if b_netbeans_file was once set */ #endif +#ifdef FEAT_ODB_EDITOR + OSType b_odb_server_id; /* FourCC of the ODB server (0 if none) */ + void *b_odb_token; /* NSAppleEventDescriptor (optional) */ + char_u *b_odb_fname; /* Custom file name (optional) */ +#endif }; diff --git a/src/version.c b/src/version.c index be38a78fa0..a7abb05b0f 100644 --- a/src/version.c +++ b/src/version.c @@ -395,6 +395,11 @@ static char *(features[]) = #else "-netbeans_intg", #endif +#ifdef FEAT_ODB_EDITOR + "+odbeditor", +#else + "-odbeditor", +#endif #ifdef FEAT_GUI_W32 # ifdef FEAT_OLE "+ole",