mirror of
https://github.com/textmate/textmate.git
synced 2026-04-28 03:00:34 -04:00
Skeleton commit window server
This allows the commit command line tool to open a window as “native”. We use distributed objects for talking to TextMate and getting a response. For the response, we release the connection in the next iteration of the event loop and then gracefully exit the program. Though it’s not clear if this is enough time for distributed objects to reply the client (if not, an exception is thrown in the client about “connection disappeared while waiting for a reply”).
This commit is contained in:
@@ -11,11 +11,12 @@ windowTitleSCM = '${TM_SCM_BRANCH:+ ($TM_SCM_NAME: $TM_SCM_BRANCH)}'
|
||||
windowTitleProject = '${projectDirectory:+ — ${projectDirectory/^.*\///}}'
|
||||
windowTitle = '$TM_DISPLAYNAME$windowTitleProject$windowTitleSCM'
|
||||
|
||||
LANG = "en_US.UTF-8"
|
||||
LC_CTYPE = "en_US.UTF-8"
|
||||
TM_APP_PATH = "${CWD/\/Contents\/Resources$//}"
|
||||
TM_MATE = "$CWD/mate"
|
||||
TM_QUERY = "$CWD/tm_query"
|
||||
LANG = "en_US.UTF-8"
|
||||
LC_CTYPE = "en_US.UTF-8"
|
||||
TM_APP_PATH = "${CWD/\/Contents\/Resources$//}"
|
||||
TM_MATE = "$CWD/mate"
|
||||
TM_QUERY = "$CWD/tm_query"
|
||||
TM_SCM_COMMIT_WINDOW = "$CWD/commit"
|
||||
|
||||
[ attr.untitled ]
|
||||
fileType = text.plain
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#import <CrashReporter/CrashReporter.h>
|
||||
#import <DocumentWindow/DocumentController.h>
|
||||
#import <Find/Find.h>
|
||||
#import <CommitWindow/CommitWindow.h>
|
||||
#import <OakAppKit/NSMenuItem Additions.h>
|
||||
#import <OakAppKit/OakAppKit.h>
|
||||
#import <OakAppKit/OakPasteboard.h>
|
||||
@@ -297,6 +298,8 @@ BOOL HasDocumentWindow (NSArray* windows)
|
||||
[[CrashReporter sharedInstance] applicationDidFinishLaunching:aNotification];
|
||||
[[CrashReporter sharedInstance] postNewCrashReportsToURLString:REST_API @"/crashes"];
|
||||
|
||||
[OakCommitWindowServer sharedInstance]; // Setup server
|
||||
|
||||
self.didFinishLaunching = YES;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
SOURCES = src/*.{cc,mm}
|
||||
CP_Resources = resources/* icons/*.icns about/* @PrivilegedTool @mate @tm_query
|
||||
CP_Resources = resources/* icons/*.icns about/* @PrivilegedTool @mate @tm_query @commit
|
||||
CP_SharedSupport = support/*
|
||||
CP_PlugIns = @Dialog @Dialog2
|
||||
CP_Library/QuickLook = @TextMateQL
|
||||
FLAGS += -DREST_API='@"$rest_api"'
|
||||
LINK += bundles cf command document editor io network ns plist settings text kvdb
|
||||
LINK += BundleMenu BundleEditor BundlesManager CrashReporter DocumentWindow Find HTMLOutputWindow OakAppKit OakFilterList OakFoundation OakSystem OakTextView Preferences SoftwareUpdate updater license
|
||||
LINK += CommitWindow
|
||||
FRAMEWORKS = Cocoa
|
||||
HTML_HEADER = templates/header.html
|
||||
HTML_FOOTER = templates/footer.html
|
||||
|
||||
85
Applications/commit/src/commit.mm
Normal file
85
Applications/commit/src/commit.mm
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <CommitWindow/CommitWindow.h>
|
||||
#include <oak/oak.h>
|
||||
|
||||
static double const AppVersion = 1.0;
|
||||
static size_t const AppRevision = APP_REVISION;
|
||||
|
||||
@interface OakCommitWindowClient : NSObject <OakCommitWindowClientProtocol>
|
||||
@property (nonatomic) NSString* portName;
|
||||
@property (nonatomic) NSConnection* connection;
|
||||
@property (nonatomic) NSInteger returnCode;
|
||||
@end
|
||||
|
||||
@implementation OakCommitWindowClient
|
||||
- (id)init
|
||||
{
|
||||
if(self = [super init])
|
||||
{
|
||||
_portName = [NSString stringWithFormat:@"com.macromates.commit-window-client.%d", getpid()];
|
||||
_connection = [NSConnection new];
|
||||
|
||||
[_connection setRootObject:self];
|
||||
if([_connection registerName:_portName] == NO)
|
||||
{
|
||||
fprintf(stderr, "%s: failed vending object as ‘%s’\n", getprogname(), [_portName UTF8String]);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)connectFromServerWithOptions:(NSDictionary*)someOptions
|
||||
{
|
||||
if(NSString* err = someOptions[kOakCommitWindowStandardError])
|
||||
fprintf(stderr, "%s", [err UTF8String]);
|
||||
|
||||
if(NSString* out = someOptions[kOakCommitWindowStandardOutput])
|
||||
fprintf(stdout, "%s", [out UTF8String]);
|
||||
|
||||
_returnCode = [someOptions[kOakCommitWindowReturnCode] intValue];
|
||||
|
||||
// Tear down the connection in next event loop iteration.
|
||||
// This should allow the sender to get a reply.
|
||||
[self performSelector:@selector(setConnection:) withObject:nil afterDelay:0];
|
||||
}
|
||||
@end
|
||||
|
||||
int main (int argc, char* argv[])
|
||||
{
|
||||
if(argc == 2 && (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0))
|
||||
{
|
||||
fprintf(stderr, "%1$s %2$.1f (" COMPILE_DATE " revision %3$zu)\n", getprogname(), AppVersion, AppRevision);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
if(OakCommitWindowClient* client = [[OakCommitWindowClient alloc] init])
|
||||
{
|
||||
NSMutableArray* arg = [NSMutableArray array];
|
||||
for(size_t i = 0; i < argc; ++i)
|
||||
[arg addObject:[NSString stringWithUTF8String:argv[i]]];
|
||||
|
||||
NSDictionary* plist = @{
|
||||
kOakCommitWindowClientPortName : client.portName,
|
||||
kOakCommitWindowArguments : arg,
|
||||
kOakCommitWindowEnvironment : [[NSProcessInfo processInfo] environment],
|
||||
};
|
||||
|
||||
if(id proxy = [NSConnection rootProxyForConnectionWithRegisteredName:kOakCommitWindowServerConnectionName host:nil])
|
||||
{
|
||||
[proxy setProtocolForProxy:@protocol(OakCommitWindowServerProtocol)];
|
||||
[proxy connectFromClientWithOptions:plist];
|
||||
|
||||
while(client.connection)
|
||||
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
|
||||
|
||||
return client.returnCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: failed connecting to ‘%s’\n", getprogname(), [kOakCommitWindowServerConnectionName UTF8String]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
1
Applications/commit/target
Normal file
1
Applications/commit/target
Normal file
@@ -0,0 +1 @@
|
||||
SOURCES = src/*.mm
|
||||
21
Frameworks/CommitWindow/src/CommitWindow.h
Normal file
21
Frameworks/CommitWindow/src/CommitWindow.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#import <oak/misc.h>
|
||||
|
||||
static NSString* const kOakCommitWindowServerConnectionName = @"com.macromates.textmate.commit-window-server";
|
||||
static NSString* const kOakCommitWindowClientPortName = @"clientPortName";
|
||||
static NSString* const kOakCommitWindowArguments = @"arguments";
|
||||
static NSString* const kOakCommitWindowEnvironment = @"environment";
|
||||
static NSString* const kOakCommitWindowStandardOutput = @"stdout";
|
||||
static NSString* const kOakCommitWindowStandardError = @"stderr";
|
||||
static NSString* const kOakCommitWindowReturnCode = @"returnCode";
|
||||
|
||||
@protocol OakCommitWindowClientProtocol <NSObject>
|
||||
- (void)connectFromServerWithOptions:(NSDictionary*)someOptions;
|
||||
@end
|
||||
|
||||
@protocol OakCommitWindowServerProtocol <NSObject>
|
||||
- (void)connectFromClientWithOptions:(NSDictionary*)someOptions;
|
||||
@end
|
||||
|
||||
PUBLIC @interface OakCommitWindowServer : NSObject <OakCommitWindowServerProtocol>
|
||||
+ (instancetype)sharedInstance;
|
||||
@end
|
||||
192
Frameworks/CommitWindow/src/CommitWindow.mm
Normal file
192
Frameworks/CommitWindow/src/CommitWindow.mm
Normal file
@@ -0,0 +1,192 @@
|
||||
#import "CommitWindow.h"
|
||||
#import <OakAppKit/OakUIConstructionFunctions.h>
|
||||
|
||||
@interface OakCommitWindow : NSWindowController <NSWindowDelegate, NSTableViewDataSource>
|
||||
@property (nonatomic) NSArray* paths;
|
||||
@property (nonatomic) NSString* clientPortName;
|
||||
@property (nonatomic) NSScrollView* scrollView;
|
||||
@property (nonatomic) NSTableView* tableView;
|
||||
@property (nonatomic) OakCommitWindow* retainedSelf;
|
||||
@end
|
||||
|
||||
@implementation OakCommitWindow
|
||||
- (id)init
|
||||
{
|
||||
if((self = [super init]))
|
||||
{
|
||||
_paths = @[
|
||||
@{ @"path" : @"/path/to/foo" },
|
||||
@{ @"path" : @"/path/to/bar" },
|
||||
];
|
||||
|
||||
NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:@"path"];
|
||||
tableColumn.editable = NO;
|
||||
tableColumn.dataCell = [[NSTextFieldCell alloc] initTextCell:@""];
|
||||
[tableColumn.dataCell setLineBreakMode:NSLineBreakByTruncatingMiddle];
|
||||
|
||||
NSTableView* tableView = [[NSTableView alloc] initWithFrame:NSZeroRect];
|
||||
[tableView addTableColumn:tableColumn];
|
||||
tableView.headerView = nil;
|
||||
tableView.focusRingType = NSFocusRingTypeNone;
|
||||
tableView.usesAlternatingRowBackgroundColors = YES;
|
||||
tableView.doubleAction = @selector(didDoubleClickTableView:);
|
||||
tableView.target = self;
|
||||
tableView.dataSource = self;
|
||||
_tableView = tableView;
|
||||
|
||||
_scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect];
|
||||
_scrollView.hasVerticalScroller = YES;
|
||||
_scrollView.hasHorizontalScroller = NO;
|
||||
_scrollView.autohidesScrollers = YES;
|
||||
_scrollView.borderType = NSNoBorder;
|
||||
_scrollView.documentView = _tableView;
|
||||
|
||||
self.window = [[NSWindow alloc] initWithContentRect:NSMakeRect(600, 700, 400, 500) styleMask:(NSTitledWindowMask|NSClosableWindowMask|NSResizableWindowMask|NSMiniaturizableWindowMask|NSTexturedBackgroundWindowMask) backing:NSBackingStoreBuffered defer:NO];
|
||||
self.window.delegate = self;
|
||||
self.window.level = NSFloatingWindowLevel;
|
||||
self.window.releasedWhenClosed = NO;
|
||||
self.window.title = @"Commit";
|
||||
|
||||
NSButton* commitButton = OakCreateButton(@"Commit", NSTexturedRoundedBezelStyle);
|
||||
NSButton* cancelButton = OakCreateButton(@"Cancel", NSTexturedRoundedBezelStyle);
|
||||
|
||||
commitButton.action = @selector(performCommit:);
|
||||
cancelButton.action = @selector(cancel:);
|
||||
|
||||
NSDictionary* views = @{
|
||||
@"scrollView" : self.scrollView,
|
||||
@"bottomDivider" : OakCreateHorizontalLine([NSColor grayColor], [NSColor lightGrayColor]),
|
||||
@"cancel" : cancelButton,
|
||||
@"commit" : commitButton,
|
||||
};
|
||||
|
||||
NSView* contentView = self.window.contentView;
|
||||
for(NSView* view in [views allValues])
|
||||
{
|
||||
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[contentView addSubview:view];
|
||||
}
|
||||
|
||||
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView(==bottomDivider)]|" options:0 metrics:nil views:views]];
|
||||
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[cancel]-[commit]-(8)-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:views]];
|
||||
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[scrollView(>=50)][bottomDivider]-(5)-[commit]-(6)-|" options:0 metrics:nil views:views]];
|
||||
|
||||
self.window.defaultButtonCell = commitButton.cell;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)showWindow:(id)sender
|
||||
{
|
||||
[self.window recalculateKeyViewLoop];
|
||||
[super showWindow:sender];
|
||||
|
||||
self.retainedSelf = self;
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification*)aNotification
|
||||
{
|
||||
[self sendCommitMessageToClient:YES];
|
||||
}
|
||||
|
||||
- (void)sendCommitMessageToClient:(BOOL)success
|
||||
{
|
||||
if(!self.clientPortName) // Reply already sent
|
||||
return;
|
||||
|
||||
if(id proxy = [NSConnection rootProxyForConnectionWithRegisteredName:self.clientPortName host:nil])
|
||||
{
|
||||
[proxy setProtocolForProxy:@protocol(OakCommitWindowClientProtocol)];
|
||||
|
||||
if(success)
|
||||
{
|
||||
[proxy connectFromServerWithOptions:@{
|
||||
kOakCommitWindowStandardOutput : @"Hello world",
|
||||
kOakCommitWindowStandardError : @"",
|
||||
kOakCommitWindowReturnCode : @0,
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[proxy connectFromServerWithOptions:@{
|
||||
kOakCommitWindowReturnCode : @1,
|
||||
}];
|
||||
}
|
||||
|
||||
self.clientPortName = nil;
|
||||
}
|
||||
|
||||
[self performSelector:@selector(setRetainedSelf:) withObject:nil afterDelay:0];
|
||||
}
|
||||
|
||||
// ==================
|
||||
// = Action Methods =
|
||||
// ==================
|
||||
|
||||
- (void)didDoubleClickTableView:(id)sender
|
||||
{
|
||||
if(_tableView.clickedRow == -1)
|
||||
return;
|
||||
|
||||
NSDictionary* row = _paths[_tableView.clickedRow];
|
||||
NSLog(@"%s show diff for %@", sel_getName(_cmd), row);
|
||||
}
|
||||
|
||||
- (void)performCommit:(id)sender
|
||||
{
|
||||
[self sendCommitMessageToClient:YES];
|
||||
[self close];
|
||||
}
|
||||
|
||||
- (void)cancel:(id)sender
|
||||
{
|
||||
[self sendCommitMessageToClient:NO];
|
||||
[self close];
|
||||
}
|
||||
|
||||
// =========================
|
||||
// = NSTableViewDataSource =
|
||||
// =========================
|
||||
|
||||
- (NSInteger)numberOfRowsInTableView:(NSTableView*)aTableView
|
||||
{
|
||||
return [_paths count];
|
||||
}
|
||||
|
||||
- (id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(NSInteger)rowIndex
|
||||
{
|
||||
NSDictionary* row = _paths[rowIndex];
|
||||
return row[aTableColumn.identifier];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface OakCommitWindowServer ()
|
||||
@property (nonatomic) NSConnection* connection;
|
||||
@end
|
||||
|
||||
@implementation OakCommitWindowServer
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
static OakCommitWindowServer* sharedInstance = [self new];
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if(self = [super init])
|
||||
{
|
||||
_connection = [NSConnection new];
|
||||
[_connection setRootObject:self];
|
||||
if([_connection registerName:kOakCommitWindowServerConnectionName] == NO)
|
||||
NSLog(@"failed to setup connection ‘%@’", kOakCommitWindowServerConnectionName);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)connectFromClientWithOptions:(NSDictionary*)someOptions
|
||||
{
|
||||
OakCommitWindow* commitWindow = [[OakCommitWindow alloc] init];
|
||||
commitWindow.clientPortName = someOptions[kOakCommitWindowClientPortName];
|
||||
[commitWindow showWindow:self];
|
||||
}
|
||||
@end
|
||||
4
Frameworks/CommitWindow/target
Normal file
4
Frameworks/CommitWindow/target
Normal file
@@ -0,0 +1,4 @@
|
||||
SOURCES = src/*.mm
|
||||
EXPORT = src/CommitWindow.h
|
||||
LINK += OakAppKit OakFoundation
|
||||
FRAMEWORKS = Cocoa
|
||||
Reference in New Issue
Block a user