Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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.)
  • Loading branch information...
commit f99ce72a11a72738955fa9b4246ecc2ba964c105 1 parent 3a36672
@b4winckler authored
View
4 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
View
2  src/MacVim/Info.plist
@@ -531,7 +531,7 @@
<key>CFBundleIconFile</key>
<string>vim_gloss</string>
<key>CFBundleIdentifier</key>
- <string>org.vim.MacVim</string>
+ <string>org.vim.MacVim-odb</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
View
1  src/MacVim/MMAppController.h
@@ -20,6 +20,7 @@
NSString *openSelectionString;
ATSFontContainerRef fontContainerRef;
BOOL untitledWindowOpening;
+ NSMutableDictionary *pidArguments;
}
- (void)removeVimController:(id)controller;
View
158 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 @@ - (void)openFile:(NSPasteboard *)pboard userData:(NSString *)userData
@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 @@ - (id)init
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 @@ - (void)dealloc
{
//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 @@ - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
- (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 @@ - (NSApplicationTerminateReply)applicationShouldTerminate:
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 @@ - (IBAction)fontSizeDown:(id)sender
connectBackend:(byref in id <MMBackendProtocol>)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 @@ - (IBAction)fontSizeDown:(id)sender
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 @@ - (void)openFile:(NSPasteboard *)pboard userData:(NSString *)userData
vc = [self topmostVimController];
if (vc) {
- [vc dropFiles:filenames];
+ [vc dropFiles:filenames forceOpen:YES];
} else {
[self application:NSApp openFiles:filenames];
}
@@ -528,7 +615,7 @@ - (MMVimController *)topmostVimController
return nil;
}
-- (void)launchVimProcessWithArguments:(NSArray *)args
+- (int)launchVimProcessWithArguments:(NSArray *)args
{
NSString *taskPath = nil;
NSArray *taskArgs = nil;
@@ -536,7 +623,7 @@ - (void)launchVimProcessWithArguments:(NSArray *)args
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 @@ - (void)launchVimProcessWithArguments:(NSArray *)args
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 @@ - (NSArray *)filterFilesAndNotify:(NSArray *)filenames
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 @@ - (NSArray *)filterOpenFilesAndRaiseFirst:(NSArray *)filenames
[[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 @@ - (NSArray *)filterOpenFilesAndRaiseFirst:(NSArray *)filenames
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)
View
111 src/MacVim/MMBackend.m
@@ -85,6 +85,8 @@ - (void)handleScrollbarEvent:(NSData *)data;
- (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 @@ - (void)update
[[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 @@ - (BOOL)waitForInput:(int)milliseconds
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 @@ - (void)handleInputEvent:(int)msgid data:(NSData *)data
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 @@ - (void)handleDropFiles:(NSData *)data
#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 @@ - (void)handleDropFiles:(NSData *)data
// 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:
+ @"<C-\\><C-N>:tab drop"];
+#endif
int i;
for (i = 0; i < n && bytes < end; ++i) {
@@ -2003,6 +2020,7 @@ - (void)handleDropFiles:(NSData *)data
[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 @@ - (void)handleDropFiles:(NSData *)data
gui_update_cursor(FALSE, FALSE);
maketitle();
gui_mch_flush();
+#else
+ [cmd appendString:@"|redr|f<CR>"];
+
+ [self addInput:cmd];
+#endif
}
#endif // FEAT_DND
}
@@ -2064,6 +2087,90 @@ - (void)handleDropString:(NSData *)data
#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;
View
2  src/MacVim/MMTextView.m
@@ -699,7 +699,7 @@ - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
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;
}
View
4 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;
View
58 src/MacVim/MMVimController.m
@@ -182,11 +182,12 @@ - (int)pid
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 @@ - (void)dropString:(NSString *)string
}
}
+- (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)",
View
16 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'
+
View
2  src/MacVim/MacVim.m
@@ -70,6 +70,8 @@
"AddInputMsgID",
"SetPreEditPositionMsgID",
"TerminateNowMsgID",
+ "ODBEditMsgID",
+ "XcodeModMsgID",
};
View
90 src/MacVim/gui_macvim.m
@@ -1708,3 +1708,93 @@
}
#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
View
4 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
View
9 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",
View
7 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
View
4 src/fileio.c
@@ -4717,6 +4717,10 @@ buf_write(buf, fname, sfname, start, end, eap, append, forceit,
/* Update machine specific information. */
mch_post_buffer_write(buf);
#endif
+#ifdef FEAT_ODB_EDITOR
+ odb_post_buffer_write(buf);
+#endif
+
return retval;
}
View
3  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
View
5 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);
+
View
5 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
};
View
5 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",
Please sign in to comment.
Something went wrong with that request. Please try again.