Index: /Users/chris/Library/Application Support/TextMate/Tools/Dialog PlugIn/Dialog.h
===================================================================
--- /Users/chris/Library/Application Support/TextMate/Tools/Dialog PlugIn/Dialog.h	(revision 6253)
+++ /Users/chris/Library/Application Support/TextMate/Tools/Dialog PlugIn/Dialog.h	(working copy)
@@ -8,10 +8,13 @@
 - (float)version;
 @end
 
-#define TextMateDialogServerProtocolVersion 5
+#define TextMateDialogServerProtocolVersion 6
 
 @protocol TextMateDialogServerProtocol
 - (int)textMateDialogServerProtocolVersion;
-- (id)showNib:(NSString*)aNibPath withParameters:(id)someParameters andInitialValues:(NSDictionary*)initialValues modal:(BOOL)flag center:(BOOL)shouldCenter;
+- (id)showNib:(NSString*)aNibPath withParameters:(id)someParameters andInitialValues:(NSDictionary*)initialValues modal:(BOOL)flag center:(BOOL)shouldCenter async:(BOOL)async;
 - (id)showMenuWithOptions:(NSDictionary*)someOptions;
+
+- (id) updateNib:(id)token withParameters:(id)someParameters;
+- (id) closeNib:(id)token;
 @end
Index: /Users/chris/Library/Application Support/TextMate/Tools/Dialog PlugIn/Dialog.mm
===================================================================
--- /Users/chris/Library/Application Support/TextMate/Tools/Dialog PlugIn/Dialog.mm	(revision 6253)
+++ /Users/chris/Library/Application Support/TextMate/Tools/Dialog PlugIn/Dialog.mm	(working copy)
@@ -2,8 +2,6 @@
 #import <string>
 #import "Dialog.h"
 
-NSLock* Lock = [NSLock new];
-
 // Apple ought to document this <rdar://4821265>
 @interface NSMethodSignature (Undocumented)
 +(NSMethodSignature*)signatureWithObjCTypes:(const char*)types;
@@ -22,50 +20,123 @@
 	NSWindow* window;
 	BOOL isModal;
 	BOOL center;
+	BOOL async;
 	BOOL didLock;
 	BOOL didCleanup;
+	NSLock* lock;
+	int		token;
 }
+
 - (NSDictionary*)instantiateNib:(NSNib*)aNib;
+- (void) updateParameters:(NSMutableDictionary *)params;
+- (void) waitForWindowToClose;
+
+// return unique ID for this NibLoader instance
+- (int)token;
++ (NibLoader *)nibLoaderForToken:(int)token;
+
 @end
 
 @implementation NibLoader
-- (id)initWithParameters:(NSMutableDictionary*)someParameters modal:(BOOL)flag center:(BOOL)shouldCenter
+
+static NSMutableArray *	sNibLoaders			= nil;
+static int				sNextNibLoaderToken	= 1;
+
+- (id)initWithParameters:(NSMutableDictionary*)someParameters modal:(BOOL)flag center:(BOOL)shouldCenter aysnc:(BOOL)inAsync
 {
 	if(self = [super init])
 	{
+		if(sNibLoaders == nil)
+		{
+			sNibLoaders = [[NSMutableArray alloc] init];
+		}
+		
 		parameters = [someParameters retain];
 		[parameters setObject:self forKey:@"controller"];
 		isModal = flag;
 		center = shouldCenter;
+		async = inAsync;
+		if( inAsync )
+		{
+			lock = nil;
+		}
+		else
+		{
+			lock = [NSLock new];
+		}
+		
+		token = sNextNibLoaderToken;
+		sNextNibLoaderToken += 1;
+		[sNibLoaders addObject:self];
 	}
 	return self;
 }
 
+// Async param updates
+- (void)updateParameters:(NSMutableDictionary *)updatedParams
+{
+	NSArray *	keys = [updatedParams allKeys];
+
+	enumerate(keys, id key)
+	{
+		[parameters setValue:[updatedParams valueForKey:key] forKey:key];
+	}
+}
+
 - (void)dealloc
 {
 	[[NSNotificationCenter defaultCenter] removeObserver:self];
-
+	
 	enumerate(topLevelObjects, id object)
 		[object release];
 	[topLevelObjects release];
 	[parameters release];
-
+	[lock release];
 	[super dealloc];
 }
 
+- (int)token
+{
+	return token;
+}
+
++ (NibLoader *)nibLoaderForToken:(int)token
+{
+	NibLoader *	outLoader = nil;
+	
+	enumerate(sNibLoaders, NibLoader * loader)
+	{
+		if([loader token] == token)
+		{
+			outLoader = loader;
+			break;
+		}
+	}
+
+	return outLoader;
+}
+
 - (void)lock
 {
-	[Lock lock];
+	[lock lock];
 	didLock = YES;
 }
 
 - (void)unlock
 {
 	if(didLock)
-		[Lock unlock];
+		[lock unlock];
 	didLock = NO;
 }
 
+// On the main thread, [NibLoader instantiateNib:] acquires the lock, and [NibLoader cleanupAndRelease] releases it.
+// Call waitForWindowToClose from the DO connection thread to block until the window is closed.
+- (void) waitForWindowToClose
+{
+	[lock lock];
+	[lock unlock];
+}
+
 - (void)setWindow:(NSWindow*)aWindow
 {
 	if(window != aWindow)
@@ -79,6 +150,8 @@
 
 - (void)cleanupAndRelease:(id)sender
 {
+	[sNibLoaders removeObject:self];
+	
 	if(didCleanup)
 		return;
 	didCleanup = YES;
@@ -181,7 +254,10 @@
 
 - (NSDictionary*)instantiateNib:(NSNib*)aNib
 {
-	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionDidDie:) name:NSPortDidBecomeInvalidNotification object:nil];
+	if(not async)
+	{
+		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionDidDie:) name:NSPortDidBecomeInvalidNotification object:nil];
+	}
 
 	BOOL didInstantiate = NO;
 	@try {
@@ -248,8 +324,10 @@
 	return TextMateDialogServerProtocolVersion;
 }
 
-- (id)showNib:(NSString*)aNibPath withParameters:(id)someParameters andInitialValues:(NSDictionary*)initialValues modal:(BOOL)flag center:(BOOL)shouldCenter
+- (id)showNib:(NSString*)aNibPath withParameters:(id)someParameters andInitialValues:(NSDictionary*)initialValues modal:(BOOL)flag center:(BOOL)shouldCenter async:(BOOL)async
 {
+	id output;
+	
 	if(![[NSFileManager defaultManager] fileExistsAtPath:aNibPath])
 	{
 		NSLog(@"%s nib file not found: %@", _cmd, aNibPath);
@@ -266,16 +344,57 @@
 		return nil;
 	}
 
-	NibLoader* nibOwner = [[NibLoader alloc] initWithParameters:someParameters modal:flag center:shouldCenter];
+	NibLoader* nibOwner = [[NibLoader alloc] initWithParameters:someParameters modal:flag center:shouldCenter aysnc:async];
 	if(!nibOwner)
 		NSLog(@"%s couldn't create nib loader", _cmd);
 	[nibOwner performSelectorOnMainThread:@selector(instantiateNib:) withObject:nib waitUntilDone:YES];
-	[Lock lock];
-	[Lock unlock];
+	
+	if(async)
+	{
+		output = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+										[NSNumber numberWithUnsignedInt:[nibOwner token]], @"token",
+										[NSNumber numberWithUnsignedInt:0], @"returnCode",
+										nil];
+	}
+	else
+	{
+		[nibOwner waitForWindowToClose];
+		output = someParameters;
+	}
 
-	return someParameters;
+	return output;
 }
 
+// Async updates of parameters
+- (id)updateNib:(id)token withParameters:(id)someParameters
+{
+	NibLoader *	nibLoader	= [NibLoader nibLoaderForToken:[token intValue]];
+	int			resultCode	= -43;
+	
+	if(nibLoader != nil)
+	{
+		[nibLoader updateParameters:someParameters];
+		resultCode = 0;
+	}
+	
+	return [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:resultCode] forKey:@"returnCode"];
+}
+
+// Async close
+- (id) closeNib:(id)token
+{
+	NibLoader *	nibLoader	= [NibLoader nibLoaderForToken:[token intValue]];
+	int			resultCode	= -43;
+	
+	if(nibLoader != nil)
+	{
+		[nibLoader connectionDidDie:nil];
+		resultCode = 0;
+	}
+	
+	return [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:resultCode] forKey:@"returnCode"];
+}
+
 - (id)showMenuWithOptions:(NSDictionary*)someOptions
 {
 	NSMutableDictionary* res = [[someOptions mutableCopy] autorelease];
Index: /Users/chris/Library/Application Support/TextMate/Tools/Dialog PlugIn/tm_dialog.mm
===================================================================
--- /Users/chris/Library/Application Support/TextMate/Tools/Dialog PlugIn/tm_dialog.mm	(revision 6253)
+++ /Users/chris/Library/Application Support/TextMate/Tools/Dialog PlugIn/tm_dialog.mm	(working copy)
@@ -43,21 +43,16 @@
 	return res;
 }
 
-int contact_server (std::string nibName, NSMutableDictionary* someParameters, NSDictionary* initialValues, bool center, bool modal, bool quiet)
+bool validate_proxy( id & outProxy )
 {
-	int res = -1;
+	bool	proxyValid = false;
 
 	id proxy = [NSConnection rootProxyForConnectionWithRegisteredName:@"TextMate dialog server" host:nil];
 	[proxy setProtocolForProxy:@protocol(TextMateDialogServerProtocol)];
 
 	if([proxy textMateDialogServerProtocolVersion] == TextMateDialogServerProtocolVersion)
 	{
-		NSString* aNibPath = [NSString stringWithUTF8String:nibName.c_str()];
-
-		NSDictionary* parameters = (NSDictionary*)[proxy showNib:aNibPath withParameters:someParameters andInitialValues:initialValues modal:modal center:center];
-		if(!quiet)
-			output_property_list(parameters);
-		res = [[parameters objectForKey:@"returnCode"] intValue];
+		proxyValid = true;
 	}
 	else
 	{
@@ -81,6 +76,60 @@
 			fprintf(stderr, "%s: failed to establish connection with TextMate.\n", AppName);
 		}
 	}
+	
+	outProxy = proxy;
+	return proxyValid;
+}
+
+int contact_server_async_update( const char * token, NSMutableDictionary* someParameters )
+{
+	id	proxy;
+	int	returnCode = -1;
+	if(validate_proxy(proxy))
+	{
+		id result = [proxy updateNib:[NSString stringWithUTF8String:token] withParameters:someParameters];
+		returnCode = [[result objectForKey:@"returnCode"] intValue];
+		
+		if(returnCode == -43)
+		{
+			fprintf(stderr, "%s: Window '%s' doesn't exist\n", AppName, token);
+		}
+	}
+	return returnCode;
+}
+
+int contact_server_async_close( const char * token )
+{
+	id	proxy;
+	int	returnCode = -1;
+	
+	if(validate_proxy(proxy))
+	{
+		id result = [proxy closeNib:[NSString stringWithUTF8String:token]];
+		returnCode = [[result objectForKey:@"returnCode"] intValue];
+		
+		if(returnCode == -43)
+		{
+			fprintf(stderr, "%s: Window '%s' doesn't exist\n", AppName, token);
+		}
+	}
+
+	return returnCode;
+}
+
+int contact_server_show_nib (std::string nibName, NSMutableDictionary* someParameters, NSDictionary* initialValues, bool center, bool modal, bool quiet, bool async)
+{
+	int res = -1;
+	id proxy;
+	
+	if(validate_proxy(proxy))
+	{
+		NSString* aNibPath = [NSString stringWithUTF8String:nibName.c_str()];
+		NSDictionary* parameters = (NSDictionary*)[proxy showNib:aNibPath withParameters:someParameters andInitialValues:initialValues modal:modal center:center async:async];
+		if(!quiet)
+			output_property_list(parameters);
+		res = [[parameters objectForKey:@"returnCode"] intValue];
+	}
 	return res;
 }
 
@@ -88,15 +137,22 @@
 {
 	fprintf(stderr, 
 		"%1$s r%2$s (" DATE ")\n"
-		"Usage: %1$s [-cmqp] nib_file\n"
+		"Usage: %1$s [-cmqpaxt] nib_file\n"
 		"Usage: %1$s [-p] -u\n"
 		"Options:\n"
-      " -c, --center               Center the window on screen.\n"
-      " -d, --defaults <plist>     Register initial values for user defaults.\n"
-      " -m, --modal                Show window as modal.\n"
-      " -q, --quiet                Do not write result to stdout.\n"
-      " -p, --parameters <plist>   Provide parameters as a plist.\n"
-      " -u, --menu                 Treat parameters as a menu structure.\n"
+      " -c, --center                 Center the window on screen.\n"
+      " -d, --defaults <plist>       Register initial values for user defaults.\n"
+      " -m, --modal                  Show window as modal.\n"
+      " -q, --quiet                  Do not write result to stdout.\n"
+      " -p, --parameters <plist>     Provide parameters as a plist.\n"
+      " -u, --menu                   Treat parameters as a menu structure.\n"
+      "\n Async Window Commands\n"
+      " -a, --async-window           Displays the window and returns a reference token for it\n"
+      "                              in the output property list.\n"
+      " -x, --close-winow <token>    Close and release an async window.\n"
+      " -t, --update-window <token>  Update an async window with new parameter values.\n"
+	  "                              Use the --parameters argument (or stdin) to specify the\n"
+	  "                              updated parameters.\n"
 		"", AppName, current_version());
 }
 
@@ -149,14 +205,18 @@
 		{ "parameters",		required_argument,	0,		'p'	},
 		{ "quiet",				no_argument,			0,		'q'	},
 		{ "menu",				no_argument,			0,		'u'	},
+		{ "async-window",				no_argument,			0,		'a'	},
+		{ "close-window",				required_argument,			0,		'x'	},
+		{ "update-window",				required_argument,			0,		't'	},
 		{ 0,						0,							0,		0		}
 	};
 
-	bool center = false, modal = false, quiet = false, menu = false;
+	bool center = false, modal = false, quiet = false, menu = false, async = false, close = false, update = false;
 	char const* parameters = NULL;
 	char const* defaults = NULL;
+	char const* token = NULL;
 	char ch;
-	while((ch = getopt_long(argc, argv, "cd:mp:qu", longopts, NULL)) != -1)
+	while((ch = getopt_long(argc, argv, "cd:mp:quax:t:", longopts, NULL)) != -1)
 	{
 		switch(ch)
 		{
@@ -166,6 +226,11 @@
 			case 'p':	parameters = optarg;		break;
 			case 'q':	quiet = true;				break;
 			case 'u':	menu = true;				break;
+			
+			case 'a':	async = true;				break;
+			case 'x':	async = true; close = true;		token = optarg;			break;
+			case 't':	async = true; update = true;	token = optarg;			break;
+
 			default:		usage();						break;
 		}
 	}
@@ -195,27 +260,35 @@
 	id plist = [data length] ? [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListMutableContainersAndLeaves format:nil errorDescription:NULL] : [NSMutableDictionary dictionary];
 
 	int res = -1;
-	if(argc == 1)
+	if(not menu)
 	{
 		id initialValues = defaults ? [NSPropertyListSerialization propertyListFromData:[NSData dataWithBytes:defaults length:strlen(defaults)] mutabilityOption:NSPropertyListImmutable format:nil errorDescription:NULL] : nil;
-		res = contact_server(find_nib(argv[0]), plist, initialValues, center, modal, quiet);
-	}
-	else if(menu)
-	{
-		id proxy = [NSConnection rootProxyForConnectionWithRegisteredName:@"TextMate dialog server" host:nil];
-		[proxy setProtocolForProxy:@protocol(TextMateDialogServerProtocol)];
-
-		if([proxy textMateDialogServerProtocolVersion] == TextMateDialogServerProtocolVersion)
+		
+		if( argc == 1 && (not async || not (close | update)) )
 		{
-			output_property_list([proxy showMenuWithOptions:plist]);
+			res = contact_server_show_nib(find_nib(argv[0]), plist, initialValues, center, modal, quiet, async);
 		}
+		else if( update )
+		{
+			res = contact_server_async_update(token, plist);
+		}
+		else if( close )
+		{
+			res = contact_server_async_close(token);
+		}
 		else
 		{
-			if(proxy)
-					fprintf(stderr, "%s: server version at v%d, this tool at v%d (they need to match)\n", AppName, [proxy textMateDialogServerProtocolVersion], TextMateDialogServerProtocolVersion);
-			else	fprintf(stderr, "%s: failed to establish connection with TextMate.\n", AppName);
+			usage();
 		}
 	}
+	else if(argc == 0)
+	{
+		id proxy;
+		if( validate_proxy(proxy) )
+		{
+			output_property_list([proxy showMenuWithOptions:plist]);
+		}
+	}
 	else
 	{
 		usage();
