Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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
Björn Winckler authored
4 runtime/doc/gui_mac.txt
View
@@ -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
2  src/MacVim/Info.plist
View
@@ -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>
1  src/MacVim/MMAppController.h
View
@@ -20,6 +20,7 @@
NSString *openSelectionString;
ATSFontContainerRef fontContainerRef;
BOOL untitledWindowOpening;
+ NSMutableDictionary *pidArguments;
}
- (void)removeVimController:(id)controller;
158 src/MacVim/MMAppController.m
View
@@ -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)
111 src/MacVim/MMBackend.m
View
@@ -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;
2  src/MacVim/MMTextView.m
View
@@ -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;
}
4 src/MacVim/MMVimController.h
View
@@ -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;
58 src/MacVim/MMVimController.m
View
@@ -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)",
16 src/MacVim/MacVim.h
View
@@ -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'
+
2  src/MacVim/MacVim.m
View
@@ -70,6 +70,8 @@
"AddInputMsgID",
"SetPreEditPositionMsgID",
"TerminateNowMsgID",
+ "ODBEditMsgID",
+ "XcodeModMsgID",
};
90 src/MacVim/gui_macvim.m
View
@@ -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
4 src/buffer.c
View
@@ -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
9 src/eval.c
View
@@ -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",
7 src/feature.h
View
@@ -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
4 src/fileio.c
View
@@ -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;
}
3  src/main.c
View
@@ -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
5 src/proto/gui_macvim.pro
View
@@ -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);
+
5 src/structs.h
View
@@ -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
};
5 src/version.c
View
@@ -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.