mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-02 11:19:22 +02:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 204e14b994 | |||
| 2e8bea32db | |||
| e70fb38670 | |||
| 3d56969f47 | |||
| 294d03baf6 | |||
| ac96dc67ba | |||
| 0413dcf3f1 | |||
| 40263195cb | |||
| dd76f85f0b | |||
| e93e9c4201 | |||
| a4a14b39ac | |||
| 49eeb133ac | |||
| 28de969ae3 | |||
| 6961a51e9a | |||
| f679af784b | |||
| fd8e8e0b4f | |||
| cd5ca1918d | |||
| 27d4ee55ae | |||
| 23e38c4298 | |||
| 0486075221 | |||
| 7a010c4a46 | |||
| 9aeade7147 | |||
| 5663536b1f |
@@ -1153,7 +1153,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>44</string>
|
||||
<string>45</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
int preloadPid;
|
||||
BOOL shouldActivateWhenNextWindowOpens;
|
||||
int numChildProcesses;
|
||||
NSMutableDictionary *inputQueues;
|
||||
int processingFlag;
|
||||
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4)
|
||||
FSEventStreamRef fsEventStream;
|
||||
|
||||
+175
-67
@@ -125,6 +125,8 @@ typedef struct
|
||||
- (void)loadDefaultFont;
|
||||
- (int)executeInLoginShell:(NSString *)path arguments:(NSArray *)args;
|
||||
- (void)reapChildProcesses:(id)sender;
|
||||
- (void)processInputQueues:(id)sender;
|
||||
- (void)addVimController:(MMVimController *)vc;
|
||||
|
||||
#ifdef MM_ENABLE_PLUGINS
|
||||
- (void)removePlugInMenu;
|
||||
@@ -208,6 +210,7 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
|
||||
cachedVimControllers = [NSMutableArray new];
|
||||
preloadPid = -1;
|
||||
pidArguments = [NSMutableDictionary new];
|
||||
inputQueues = [NSMutableDictionary new];
|
||||
|
||||
#ifdef MM_ENABLE_PLUGINS
|
||||
NSString *plugInTitle = NSLocalizedString(@"Plug-In",
|
||||
@@ -249,6 +252,7 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
|
||||
//NSLog(@"MMAppController dealloc");
|
||||
|
||||
[connection release]; connection = nil;
|
||||
[inputQueues release]; inputQueues = nil;
|
||||
[pidArguments release]; pidArguments = nil;
|
||||
[vimControllers release]; vimControllers = nil;
|
||||
[cachedVimControllers release]; cachedVimControllers = nil;
|
||||
@@ -994,7 +998,7 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
|
||||
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
[panel setAllowsMultipleSelection:YES];
|
||||
[panel setAccessoryView:openPanelAccessoryView()];
|
||||
[panel setAccessoryView:showHiddenFilesView()];
|
||||
|
||||
int result = [panel runModalForDirectory:dir file:nil types:nil];
|
||||
if (NSOKButton == result)
|
||||
@@ -1093,64 +1097,79 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
|
||||
}
|
||||
}
|
||||
|
||||
- (byref id <MMFrontendProtocol>)
|
||||
connectBackend:(byref in id <MMBackendProtocol>)backend
|
||||
pid:(int)pid
|
||||
- (MMVimController *)keyVimController
|
||||
{
|
||||
//NSLog(@"Connect backend (pid=%d)", pid);
|
||||
NSNumber *pidKey = [NSNumber numberWithInt:pid];
|
||||
MMVimController *vc = nil;
|
||||
|
||||
@try {
|
||||
[(NSDistantObject*)backend
|
||||
setProtocolForProxy:@protocol(MMBackendProtocol)];
|
||||
|
||||
vc = [[[MMVimController alloc] initWithBackend:backend pid:pid]
|
||||
autorelease];
|
||||
|
||||
if (preloadPid == pid) {
|
||||
// This backend was preloaded, so add it to the cache and schedule
|
||||
// another vim process to be preloaded.
|
||||
preloadPid = -1;
|
||||
[vc setIsPreloading:YES];
|
||||
[cachedVimControllers addObject:vc];
|
||||
[self scheduleVimControllerPreloadAfterDelay:1];
|
||||
|
||||
return vc;
|
||||
NSWindow *keyWindow = [NSApp keyWindow];
|
||||
if (keyWindow) {
|
||||
unsigned i, count = [vimControllers count];
|
||||
for (i = 0; i < count; ++i) {
|
||||
MMVimController *vc = [vimControllers objectAtIndex:i];
|
||||
if ([[[vc windowController] window] isEqual:keyWindow])
|
||||
return vc;
|
||||
}
|
||||
|
||||
[vimControllers addObject:vc];
|
||||
|
||||
id args = [pidArguments objectForKey:pidKey];
|
||||
if (args && [NSNull null] != args)
|
||||
[vc passArguments:args];
|
||||
|
||||
// HACK! MacVim does not get activated if it is launched from the
|
||||
// terminal, so we forcibly activate here unless it is an untitled
|
||||
// window opening. Untitled windows are treated differently, else
|
||||
// MacVim would steal the focus if another app was activated while the
|
||||
// untitled window was loading.
|
||||
if (!args || args != [NSNull null])
|
||||
[self activateWhenNextWindowOpens];
|
||||
|
||||
if (args)
|
||||
[pidArguments removeObjectForKey:pidKey];
|
||||
|
||||
return vc;
|
||||
}
|
||||
|
||||
@catch (NSException *e) {
|
||||
NSLog(@"Exception caught in %s: \"%@\"", _cmd, e);
|
||||
|
||||
if (vc)
|
||||
[vimControllers removeObject:vc];
|
||||
|
||||
[pidArguments removeObjectForKey:pidKey];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (unsigned)connectBackend:(byref in id <MMBackendProtocol>)proxy pid:(int)pid
|
||||
{
|
||||
//NSLog(@"[%s] pid=%d", _cmd, pid);
|
||||
|
||||
[(NSDistantObject*)proxy setProtocolForProxy:@protocol(MMBackendProtocol)];
|
||||
|
||||
// NOTE: Allocate the vim controller now but don't add it to the list of
|
||||
// controllers since this is a distributed object call and as such can
|
||||
// arrive at unpredictable times (e.g. while iterating the list of vim
|
||||
// controllers).
|
||||
// (What if input arrives before the vim controller is added to the list of
|
||||
// controllers? This should not be a problem since the input isn't
|
||||
// processed immediately (see processInput:forIdentifier:).)
|
||||
MMVimController *vc = [[MMVimController alloc] initWithBackend:proxy
|
||||
pid:pid];
|
||||
[self performSelector:@selector(addVimController:)
|
||||
withObject:vc
|
||||
afterDelay:0];
|
||||
|
||||
[vc release];
|
||||
|
||||
return [vc identifier];
|
||||
}
|
||||
|
||||
- (oneway void)processInput:(in bycopy NSArray *)queue
|
||||
forIdentifier:(unsigned)identifier
|
||||
{
|
||||
// NOTE: Input is not handled immediately since this is a distribued object
|
||||
// call and as such can arrive at unpredictable times. Instead, queue the
|
||||
// input and process it when the run loop is updated.
|
||||
|
||||
if (!(queue && identifier)) {
|
||||
NSLog(@"[%s] Bad input for identifier=%d", _cmd, identifier);
|
||||
return;
|
||||
}
|
||||
|
||||
//NSLog(@"[%s] QUEUE for identifier=%d: <<< %@>>>", _cmd, identifier,
|
||||
// debugStringForMessageQueue(queue));
|
||||
|
||||
NSNumber *key = [NSNumber numberWithUnsignedInt:identifier];
|
||||
NSArray *q = [inputQueues objectForKey:key];
|
||||
if (q) {
|
||||
q = [q arrayByAddingObjectsFromArray:queue];
|
||||
[inputQueues setObject:q forKey:key];
|
||||
} else {
|
||||
[inputQueues setObject:queue forKey:key];
|
||||
}
|
||||
|
||||
// NOTE: We must use "event tracking mode" as well as "default mode",
|
||||
// otherwise the input queue will not be processed e.g. during live
|
||||
// resizing.
|
||||
[self performSelector:@selector(processInputQueues:)
|
||||
withObject:nil
|
||||
afterDelay:0
|
||||
inModes:[NSArray arrayWithObjects:NSDefaultRunLoopMode,
|
||||
NSEventTrackingRunLoopMode, nil]];
|
||||
}
|
||||
|
||||
- (NSArray *)serverList
|
||||
{
|
||||
NSMutableArray *array = [NSMutableArray array];
|
||||
@@ -1165,21 +1184,6 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
|
||||
return array;
|
||||
}
|
||||
|
||||
- (MMVimController *)keyVimController
|
||||
{
|
||||
NSWindow *keyWindow = [NSApp keyWindow];
|
||||
if (keyWindow) {
|
||||
unsigned i, count = [vimControllers count];
|
||||
for (i = 0; i < count; ++i) {
|
||||
MMVimController *vc = [vimControllers objectAtIndex:i];
|
||||
if ([[[vc windowController] window] isEqual:keyWindow])
|
||||
return vc;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end // MMAppController
|
||||
|
||||
|
||||
@@ -2115,4 +2119,108 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processInputQueues:(id)sender
|
||||
{
|
||||
// NOTE: Because we use distributed objects it is quite possible for this
|
||||
// function to be re-entered. This can cause all sorts of unexpected
|
||||
// problems so we guard against it here so that the rest of the code does
|
||||
// not need to worry about it.
|
||||
|
||||
// The processing flag is > 0 if this function is already on the call
|
||||
// stack; < 0 if this function was also re-entered.
|
||||
if (processingFlag != 0) {
|
||||
NSLog(@"[%s] BUSY!", _cmd);
|
||||
processingFlag = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Be _very_ careful that no exceptions can be raised between here
|
||||
// and the point at which 'processingFlag' is reset. Otherwise the above
|
||||
// test could end up always failing and no input queues would ever be
|
||||
// processed!
|
||||
processingFlag = 1;
|
||||
|
||||
// NOTE: New input may arrive while we're busy processing; we deal with
|
||||
// this by putting the current queue aside and creating a new input queue
|
||||
// for future input.
|
||||
NSDictionary *queues = inputQueues;
|
||||
inputQueues = [NSMutableDictionary new];
|
||||
|
||||
// Pass each input queue on to the vim controller with matching
|
||||
// identifier (and note that it could be cached).
|
||||
NSEnumerator *e = [queues keyEnumerator];
|
||||
NSNumber *key;
|
||||
while ((key = [e nextObject])) {
|
||||
unsigned ukey = [key unsignedIntValue];
|
||||
int i = 0, count = [vimControllers count];
|
||||
for (i = 0; i < count; ++i) {
|
||||
MMVimController *vc = [vimControllers objectAtIndex:i];
|
||||
if (ukey == [vc identifier]) {
|
||||
[vc processInputQueue:[queues objectForKey:key]]; // !exceptions
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < count) continue;
|
||||
|
||||
count = [cachedVimControllers count];
|
||||
for (i = 0; i < count; ++i) {
|
||||
MMVimController *vc = [cachedVimControllers objectAtIndex:i];
|
||||
if (ukey == [vc identifier]) {
|
||||
[vc processInputQueue:[queues objectForKey:key]]; // !exceptions
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == count)
|
||||
NSLog(@"[%s] WARNING: No Vim controller for identifier=%d",
|
||||
_cmd, ukey);
|
||||
}
|
||||
|
||||
[queues release];
|
||||
|
||||
// If new input arrived while we were processing it would have been
|
||||
// blocked so we have to schedule it to be processed again.
|
||||
if (processingFlag < 0)
|
||||
[self performSelector:@selector(processInputQueues:)
|
||||
withObject:nil
|
||||
afterDelay:0
|
||||
inModes:[NSArray arrayWithObjects:NSDefaultRunLoopMode,
|
||||
NSEventTrackingRunLoopMode, nil]];
|
||||
|
||||
processingFlag = 0;
|
||||
}
|
||||
|
||||
- (void)addVimController:(MMVimController *)vc
|
||||
{
|
||||
int pid = [vc pid];
|
||||
NSNumber *pidKey = [NSNumber numberWithInt:pid];
|
||||
|
||||
if (preloadPid == pid) {
|
||||
// This controller was preloaded, so add it to the cache and
|
||||
// schedule another vim process to be preloaded.
|
||||
preloadPid = -1;
|
||||
[vc setIsPreloading:YES];
|
||||
[cachedVimControllers addObject:vc];
|
||||
[self scheduleVimControllerPreloadAfterDelay:1];
|
||||
} else {
|
||||
[vimControllers addObject:vc];
|
||||
|
||||
id args = [pidArguments objectForKey:pidKey];
|
||||
if (args && [NSNull null] != args)
|
||||
[vc passArguments:args];
|
||||
|
||||
// HACK! MacVim does not get activated if it is launched from the
|
||||
// terminal, so we forcibly activate here unless it is an untitled
|
||||
// window opening. Untitled windows are treated differently, else
|
||||
// MacVim would steal the focus if another app was activated while the
|
||||
// untitled window was loading.
|
||||
if (!args || args != [NSNull null])
|
||||
[self activateWhenNextWindowOpens];
|
||||
|
||||
if (args)
|
||||
[pidArguments removeObjectForKey:pidKey];
|
||||
}
|
||||
}
|
||||
|
||||
@end // MMAppController (Private)
|
||||
|
||||
@@ -1139,7 +1139,7 @@ defaultLineHeightForFont(NSFont *font)
|
||||
|
||||
if (flags & DRAW_UNDERL)
|
||||
{
|
||||
[fg set];
|
||||
[sp set];
|
||||
NSRectFill(NSMakeRect(rect.origin.x,
|
||||
(row + 1) * cellSize.height + kUnderlineOffset,
|
||||
rect.size.width, kUnderlineHeight));
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
NSMutableArray *inputQueue;
|
||||
NSMutableData *drawData;
|
||||
NSConnection *connection;
|
||||
id frontendProxy;
|
||||
id appProxy;
|
||||
unsigned identifier;
|
||||
NSDictionary *colorDict;
|
||||
NSDictionary *sysColorDict;
|
||||
NSDictionary *actionDict;
|
||||
|
||||
+27
-35
@@ -21,7 +21,7 @@
|
||||
*
|
||||
* It is very important to realize that all state is held by the backend, the
|
||||
* frontend must either ask for state [MMBackend evaluateExpression:] or wait
|
||||
* for the backend to update [MMVimController processCommandQueue:].
|
||||
* for the backend to update [MMAppController processInput:forIdentifier:].
|
||||
*
|
||||
* The client/server functionality of Vim is handled by the backend. It sets
|
||||
* up a named NSConnection to which other Vim processes can connect.
|
||||
@@ -178,7 +178,6 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
[inputQueue release]; inputQueue = nil;
|
||||
[outputQueue release]; outputQueue = nil;
|
||||
[drawData release]; drawData = nil;
|
||||
[frontendProxy release]; frontendProxy = nil;
|
||||
[connection release]; connection = nil;
|
||||
[actionDict release]; actionDict = nil;
|
||||
[sysColorDict release]; sysColorDict = nil;
|
||||
@@ -328,29 +327,29 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL ok = NO;
|
||||
@try {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(connectionDidDie:)
|
||||
name:NSConnectionDidDieNotification object:connection];
|
||||
|
||||
id proxy = [connection rootProxy];
|
||||
[proxy setProtocolForProxy:@protocol(MMAppProtocol)];
|
||||
appProxy = [[connection rootProxy] retain];
|
||||
[appProxy setProtocolForProxy:@protocol(MMAppProtocol)];
|
||||
|
||||
// NOTE: We do not set any new timeout values for the connection to the
|
||||
// frontend. This means that if the frontend is "stuck" (e.g. in a
|
||||
// modal loop) then any calls to the frontend will block indefinitely
|
||||
// (the default timeouts are huge).
|
||||
|
||||
int pid = [[NSProcessInfo processInfo] processIdentifier];
|
||||
|
||||
frontendProxy = [proxy connectBackend:self pid:pid];
|
||||
if (frontendProxy) {
|
||||
[frontendProxy retain];
|
||||
[frontendProxy setProtocolForProxy:@protocol(MMFrontendProtocol)];
|
||||
ok = YES;
|
||||
}
|
||||
identifier = [appProxy connectBackend:self pid:pid];
|
||||
return YES;
|
||||
}
|
||||
@catch (NSException *e) {
|
||||
NSLog(@"Exception caught when trying to connect backend: \"%@\"", e);
|
||||
}
|
||||
|
||||
return ok;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)openGUIWindow
|
||||
@@ -506,10 +505,11 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
[self insertVimStateMessage];
|
||||
|
||||
@try {
|
||||
[frontendProxy processCommandQueue:outputQueue];
|
||||
//NSLog(@"[%s] Flushing (count=%d)", _cmd, [outputQueue count]);
|
||||
[appProxy processInput:outputQueue forIdentifier:identifier];
|
||||
}
|
||||
@catch (NSException *e) {
|
||||
NSLog(@"Exception caught when processing command queue: \"%@\"", e);
|
||||
NSLog(@"[%s] Exception caught: \"%@\"", _cmd, e);
|
||||
NSLog(@"outputQueue(len:%d)=%@", [outputQueue count]/2,
|
||||
outputQueue);
|
||||
if (![connection isValid]) {
|
||||
@@ -575,7 +575,7 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
// Flush the entire queue in case a VimLeave autocommand added
|
||||
// something to the queue.
|
||||
[self queueMessage:CloseWindowMsgID data:nil];
|
||||
[frontendProxy processCommandQueue:outputQueue];
|
||||
[appProxy processInput:outputQueue forIdentifier:identifier];
|
||||
}
|
||||
@catch (NSException *e) {
|
||||
NSLog(@"Exception caught when sending CloseWindowMsgID: \"%@\"", e);
|
||||
@@ -695,9 +695,10 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
{
|
||||
char_u *s = NULL;
|
||||
|
||||
@try {
|
||||
[frontendProxy showSavePanelWithAttributes:attr];
|
||||
[self queueMessage:BrowseForFileMsgID properties:attr];
|
||||
[self flushQueue:YES];
|
||||
|
||||
@try {
|
||||
[self waitForDialogReturn];
|
||||
|
||||
if (dialogReturn && [dialogReturn isKindOfClass:[NSString class]])
|
||||
@@ -706,7 +707,7 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
[dialogReturn release]; dialogReturn = nil;
|
||||
}
|
||||
@catch (NSException *e) {
|
||||
NSLog(@"Exception caught when showing save panel: \"%@\"", e);
|
||||
NSLog(@"[%s] Exception caught: \"%@\"", _cmd, e);
|
||||
}
|
||||
|
||||
return (char *)s;
|
||||
@@ -734,9 +735,10 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
@try {
|
||||
[frontendProxy presentDialogWithAttributes:attr];
|
||||
[self queueMessage:ShowDialogMsgID properties:attr];
|
||||
[self flushQueue:YES];
|
||||
|
||||
@try {
|
||||
[self waitForDialogReturn];
|
||||
|
||||
if (dialogReturn && [dialogReturn isKindOfClass:[NSArray class]]
|
||||
@@ -758,7 +760,7 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
[dialogReturn release]; dialogReturn = nil;
|
||||
}
|
||||
@catch (NSException *e) {
|
||||
NSLog(@"Exception caught while showing alert dialog: \"%@\"", e);
|
||||
NSLog(@"[%s] Exception caught: \"%@\"", _cmd, e);
|
||||
}
|
||||
|
||||
return retval;
|
||||
@@ -1107,16 +1109,6 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
[inputQueue addObject:(data ? (id)data : [NSNull null])];
|
||||
}
|
||||
|
||||
- (oneway void)processInputAndData:(in bycopy NSArray *)messages
|
||||
{
|
||||
// This is just a convenience method that allows the frontend to delay
|
||||
// sending messages.
|
||||
int i, count = [messages count];
|
||||
for (i = 1; i < count; i+=2)
|
||||
[self processInput:[[messages objectAtIndex:i-1] intValue]
|
||||
data:[messages objectAtIndex:i]];
|
||||
}
|
||||
|
||||
- (id)evaluateExpressionCocoa:(in bycopy NSString *)expr
|
||||
errorString:(out bycopy NSString **)errstr
|
||||
{
|
||||
@@ -1453,7 +1445,7 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
if (waitForAck) {
|
||||
// Never received a connection acknowledgement, so die.
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[frontendProxy release]; frontendProxy = nil;
|
||||
[appProxy release]; appProxy = nil;
|
||||
|
||||
// NOTE: We intentionally do not call mch_exit() since this in turn
|
||||
// will lead to -[MMBackend exit] getting called which we want to
|
||||
@@ -2488,9 +2480,9 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
|
||||
if ([args objectForKey:@"remoteID"]) {
|
||||
// NOTE: We have to delay processing any ODB related arguments since
|
||||
// the file(s) may not be opened until the input buffer is processed.
|
||||
[self performSelectorOnMainThread:@selector(startOdbEditWithArguments:)
|
||||
withObject:args
|
||||
waitUntilDone:NO];
|
||||
[self performSelector:@selector(startOdbEditWithArguments:)
|
||||
withObject:args
|
||||
afterDelay:0];
|
||||
}
|
||||
|
||||
NSString *lineString = [args objectForKey:@"cursorLine"];
|
||||
|
||||
@@ -20,14 +20,11 @@
|
||||
|
||||
|
||||
|
||||
@interface MMVimController : NSObject <MMFrontendProtocol>
|
||||
{
|
||||
@interface MMVimController : NSObject {
|
||||
unsigned identifier;
|
||||
BOOL isInitialized;
|
||||
MMWindowController *windowController;
|
||||
id backendProxy;
|
||||
BOOL inProcessCommandQueue;
|
||||
NSMutableArray *sendQueue;
|
||||
NSMutableArray *receiveQueue;
|
||||
NSMenu *mainMenu;
|
||||
NSMutableArray *popupMenuItems;
|
||||
NSToolbar *toolbar;
|
||||
@@ -43,6 +40,7 @@
|
||||
}
|
||||
|
||||
- (id)initWithBackend:(id)backend pid:(int)processIdentifier;
|
||||
- (unsigned)identifier;
|
||||
- (id)backendProxy;
|
||||
- (int)pid;
|
||||
- (void)setServerName:(NSString *)name;
|
||||
@@ -65,7 +63,9 @@
|
||||
timeout:(NSTimeInterval)timeout;
|
||||
- (void)addVimInput:(NSString *)string;
|
||||
- (NSString *)evaluateVimExpression:(NSString *)expr;
|
||||
- (id)evaluateVimExpressionCocoa:(NSString *)expr errorString:(NSString **)errstr;
|
||||
- (id)evaluateVimExpressionCocoa:(NSString *)expr
|
||||
errorString:(NSString **)errstr;
|
||||
- (void)processInputQueue:(NSArray *)queue;
|
||||
#ifdef MM_ENABLE_PLUGINS
|
||||
- (MMPlugInInstanceMediator *)instanceMediator;
|
||||
#endif
|
||||
|
||||
+236
-286
@@ -10,17 +10,20 @@
|
||||
/*
|
||||
* MMVimController
|
||||
*
|
||||
* Coordinates input/output to/from backend. Each MMBackend communicates
|
||||
* directly with a MMVimController.
|
||||
* Coordinates input/output to/from backend. A MMVimController sends input
|
||||
* directly to a MMBackend, but communication from MMBackend to MMVimController
|
||||
* goes via MMAppController so that it can coordinate all incoming distributed
|
||||
* object messages.
|
||||
*
|
||||
* MMVimController does not deal with visual presentation. Essentially it
|
||||
* should be able to run with no window present.
|
||||
*
|
||||
* Output from the backend is received in processCommandQueue:. Input is sent
|
||||
* to the backend via sendMessage:data: or addVimInput:. The latter allows
|
||||
* execution of arbitrary strings in the Vim process, much like the Vim script
|
||||
* function remote_send() does. The messages that may be passed between
|
||||
* frontend and backend are defined in an enum in MacVim.h.
|
||||
* Output from the backend is received in processInputQueue: (this message is
|
||||
* called from MMAppController so it is not a DO call). Input is sent to the
|
||||
* backend via sendMessage:data: or addVimInput:. The latter allows execution
|
||||
* of arbitrary strings in the Vim process, much like the Vim script function
|
||||
* remote_send() does. The messages that may be passed between frontend and
|
||||
* backend are defined in an enum in MacVim.h.
|
||||
*/
|
||||
|
||||
#import "MMAppController.h"
|
||||
@@ -49,9 +52,7 @@ static NSTimeInterval MMBackendProxyRequestTimeout = 0;
|
||||
// Timeout used for setDialogReturn:.
|
||||
static NSTimeInterval MMSetDialogReturnTimeout = 1.0;
|
||||
|
||||
// Maximum number of items in the receiveQueue. (It is hard to predict what
|
||||
// consequences changing this number will have.)
|
||||
static int MMReceiveQueueCap = 100;
|
||||
static unsigned identifierCounter = 1;
|
||||
|
||||
static BOOL isUnsafeMessage(int msgid);
|
||||
|
||||
@@ -65,7 +66,7 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
|
||||
|
||||
@interface MMVimController (Private)
|
||||
- (void)doProcessCommandQueue:(NSArray *)queue;
|
||||
- (void)doProcessInputQueue:(NSArray *)queue;
|
||||
- (void)handleMessage:(int)msgid data:(NSData *)data;
|
||||
- (void)savePanelDidEnd:(NSSavePanel *)panel code:(int)code
|
||||
context:(void *)context;
|
||||
@@ -95,6 +96,8 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
- (void)popupMenuWithAttributes:(NSDictionary *)attrs;
|
||||
- (void)connectionDidDie:(NSNotification *)notification;
|
||||
- (void)scheduleClose;
|
||||
- (void)handleBrowseForFile:(NSDictionary *)attr;
|
||||
- (void)handleShowDialog:(NSDictionary *)attr;
|
||||
@end
|
||||
|
||||
|
||||
@@ -107,11 +110,12 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
// TODO: Come up with a better way of creating an identifier.
|
||||
identifier = identifierCounter++;
|
||||
|
||||
windowController =
|
||||
[[MMWindowController alloc] initWithVimController:self];
|
||||
backendProxy = [backend retain];
|
||||
sendQueue = [NSMutableArray new];
|
||||
receiveQueue = [NSMutableArray new];
|
||||
popupMenuItems = [[NSMutableArray alloc] init];
|
||||
toolbarItemDict = [[NSMutableDictionary alloc] init];
|
||||
pid = processIdentifier;
|
||||
@@ -168,8 +172,6 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
|
||||
[serverName release]; serverName = nil;
|
||||
[backendProxy release]; backendProxy = nil;
|
||||
[sendQueue release]; sendQueue = nil;
|
||||
[receiveQueue release]; receiveQueue = nil;
|
||||
|
||||
[toolbarItemDict release]; toolbarItemDict = nil;
|
||||
[toolbar release]; toolbar = nil;
|
||||
@@ -183,6 +185,11 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (unsigned)identifier
|
||||
{
|
||||
return identifier;
|
||||
}
|
||||
|
||||
- (MMWindowController *)windowController
|
||||
{
|
||||
return windowController;
|
||||
@@ -321,21 +328,11 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
|
||||
- (void)sendMessage:(int)msgid data:(NSData *)data
|
||||
{
|
||||
//NSLog(@"sendMessage:%s (isInitialized=%d inProcessCommandQueue=%d)",
|
||||
// MessageStrings[msgid], isInitialized, inProcessCommandQueue);
|
||||
//NSLog(@"sendMessage:%s (isInitialized=%d)",
|
||||
// MessageStrings[msgid], isInitialized);
|
||||
|
||||
if (!isInitialized) return;
|
||||
|
||||
if (inProcessCommandQueue) {
|
||||
//NSLog(@"In process command queue; delaying message send.");
|
||||
[sendQueue addObject:[NSNumber numberWithInt:msgid]];
|
||||
if (data)
|
||||
[sendQueue addObject:data];
|
||||
else
|
||||
[sendQueue addObject:[NSNull null]];
|
||||
return;
|
||||
}
|
||||
|
||||
@try {
|
||||
[backendProxy processInput:msgid data:data];
|
||||
}
|
||||
@@ -353,7 +350,7 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
// ball forever. In almost all circumstances sendMessage:data: should be
|
||||
// used instead.
|
||||
|
||||
if (!isInitialized || inProcessCommandQueue)
|
||||
if (!isInitialized)
|
||||
return NO;
|
||||
|
||||
if (timeout < 0) timeout = 0;
|
||||
@@ -400,7 +397,8 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
return eval;
|
||||
}
|
||||
|
||||
- (id)evaluateVimExpressionCocoa:(NSString *)expr errorString:(NSString **)errstr
|
||||
- (id)evaluateVimExpressionCocoa:(NSString *)expr
|
||||
errorString:(NSString **)errstr
|
||||
{
|
||||
id eval = nil;
|
||||
|
||||
@@ -423,6 +421,9 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
{
|
||||
if (!isInitialized) return;
|
||||
|
||||
// Remove any delayed calls made on this object.
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
|
||||
isInitialized = NO;
|
||||
[toolbar setDelegate:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
@@ -431,204 +432,19 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
[windowController cleanup];
|
||||
}
|
||||
|
||||
- (oneway void)showSavePanelWithAttributes:(in bycopy NSDictionary *)attr
|
||||
- (void)processInputQueue:(NSArray *)queue
|
||||
{
|
||||
if (!isInitialized) return;
|
||||
|
||||
BOOL inDefaultMode = [[[NSRunLoop currentRunLoop] currentMode]
|
||||
isEqual:NSDefaultRunLoopMode];
|
||||
if (!inDefaultMode) {
|
||||
// Delay call until run loop is in default mode.
|
||||
[self performSelectorOnMainThread:
|
||||
@selector(showSavePanelWithAttributes:)
|
||||
withObject:attr
|
||||
waitUntilDone:NO
|
||||
modes:[NSArray arrayWithObject:
|
||||
NSDefaultRunLoopMode]];
|
||||
return;
|
||||
// NOTE: This method must not raise any exceptions (see comment in the
|
||||
// calling method).
|
||||
@try {
|
||||
[self doProcessInputQueue:queue];
|
||||
[windowController processInputQueueDidFinish];
|
||||
}
|
||||
|
||||
NSString *dir = [attr objectForKey:@"dir"];
|
||||
BOOL saving = [[attr objectForKey:@"saving"] boolValue];
|
||||
|
||||
if (!dir) {
|
||||
// 'dir == nil' means: set dir to the pwd of the Vim process, or let
|
||||
// open dialog decide (depending on the below user default).
|
||||
BOOL trackPwd = [[NSUserDefaults standardUserDefaults]
|
||||
boolForKey:MMDialogsTrackPwdKey];
|
||||
if (trackPwd)
|
||||
dir = [vimState objectForKey:@"pwd"];
|
||||
@catch (NSException *ex) {
|
||||
NSLog(@"[%s] Caught exception (pid=%d): %@", _cmd, pid, ex);
|
||||
}
|
||||
|
||||
if (saving) {
|
||||
[[NSSavePanel savePanel] beginSheetForDirectory:dir file:nil
|
||||
modalForWindow:[windowController window]
|
||||
modalDelegate:self
|
||||
didEndSelector:@selector(savePanelDidEnd:code:context:)
|
||||
contextInfo:NULL];
|
||||
} else {
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
[panel setAllowsMultipleSelection:NO];
|
||||
[panel setAccessoryView:openPanelAccessoryView()];
|
||||
|
||||
[panel beginSheetForDirectory:dir file:nil types:nil
|
||||
modalForWindow:[windowController window]
|
||||
modalDelegate:self
|
||||
didEndSelector:@selector(savePanelDidEnd:code:context:)
|
||||
contextInfo:NULL];
|
||||
}
|
||||
}
|
||||
|
||||
- (oneway void)presentDialogWithAttributes:(in bycopy NSDictionary *)attr
|
||||
{
|
||||
if (!isInitialized) return;
|
||||
|
||||
BOOL inDefaultMode = [[[NSRunLoop currentRunLoop] currentMode]
|
||||
isEqual:NSDefaultRunLoopMode];
|
||||
if (!inDefaultMode) {
|
||||
// Delay call until run loop is in default mode.
|
||||
[self performSelectorOnMainThread:
|
||||
@selector(presentDialogWithAttributes:)
|
||||
withObject:attr
|
||||
waitUntilDone:NO
|
||||
modes:[NSArray arrayWithObject:
|
||||
NSDefaultRunLoopMode]];
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *buttonTitles = [attr objectForKey:@"buttonTitles"];
|
||||
if (!(buttonTitles && [buttonTitles count])) return;
|
||||
|
||||
int style = [[attr objectForKey:@"alertStyle"] intValue];
|
||||
NSString *message = [attr objectForKey:@"messageText"];
|
||||
NSString *text = [attr objectForKey:@"informativeText"];
|
||||
NSString *textFieldString = [attr objectForKey:@"textFieldString"];
|
||||
MMAlert *alert = [[MMAlert alloc] init];
|
||||
|
||||
// NOTE! This has to be done before setting the informative text.
|
||||
if (textFieldString)
|
||||
[alert setTextFieldString:textFieldString];
|
||||
|
||||
[alert setAlertStyle:style];
|
||||
|
||||
if (message) {
|
||||
[alert setMessageText:message];
|
||||
} else {
|
||||
// If no message text is specified 'Alert' is used, which we don't
|
||||
// want, so set an empty string as message text.
|
||||
[alert setMessageText:@""];
|
||||
}
|
||||
|
||||
if (text) {
|
||||
[alert setInformativeText:text];
|
||||
} else if (textFieldString) {
|
||||
// Make sure there is always room for the input text field.
|
||||
[alert setInformativeText:@""];
|
||||
}
|
||||
|
||||
unsigned i, count = [buttonTitles count];
|
||||
for (i = 0; i < count; ++i) {
|
||||
NSString *title = [buttonTitles objectAtIndex:i];
|
||||
// NOTE: The title of the button may contain the character '&' to
|
||||
// indicate that the following letter should be the key equivalent
|
||||
// associated with the button. Extract this letter and lowercase it.
|
||||
NSString *keyEquivalent = nil;
|
||||
NSRange hotkeyRange = [title rangeOfString:@"&"];
|
||||
if (NSNotFound != hotkeyRange.location) {
|
||||
if ([title length] > NSMaxRange(hotkeyRange)) {
|
||||
NSRange keyEquivRange = NSMakeRange(hotkeyRange.location+1, 1);
|
||||
keyEquivalent = [[title substringWithRange:keyEquivRange]
|
||||
lowercaseString];
|
||||
}
|
||||
|
||||
NSMutableString *string = [NSMutableString stringWithString:title];
|
||||
[string deleteCharactersInRange:hotkeyRange];
|
||||
title = string;
|
||||
}
|
||||
|
||||
[alert addButtonWithTitle:title];
|
||||
|
||||
// Set key equivalent for the button, but only if NSAlert hasn't
|
||||
// already done so. (Check the documentation for
|
||||
// - [NSAlert addButtonWithTitle:] to see what key equivalents are
|
||||
// automatically assigned.)
|
||||
NSButton *btn = [[alert buttons] lastObject];
|
||||
if ([[btn keyEquivalent] length] == 0 && keyEquivalent) {
|
||||
[btn setKeyEquivalent:keyEquivalent];
|
||||
}
|
||||
}
|
||||
|
||||
[alert beginSheetModalForWindow:[windowController window]
|
||||
modalDelegate:self
|
||||
didEndSelector:@selector(alertDidEnd:code:context:)
|
||||
contextInfo:NULL];
|
||||
|
||||
[alert release];
|
||||
}
|
||||
|
||||
- (oneway void)processCommandQueue:(in bycopy NSArray *)queue
|
||||
{
|
||||
if (!isInitialized) return;
|
||||
|
||||
if (inProcessCommandQueue) {
|
||||
// NOTE! If a synchronous DO call is made during
|
||||
// doProcessCommandQueue: below it may happen that this method is
|
||||
// called a second time while the synchronous message is waiting for a
|
||||
// reply (could also happen if doProcessCommandQueue: enters a modal
|
||||
// loop, see comment below). Since this method cannot be considered
|
||||
// reentrant, we queue the input and return immediately.
|
||||
//
|
||||
// If doProcessCommandQueue: enters a modal loop (happens e.g. on
|
||||
// ShowPopupMenuMsgID) then the receiveQueue could grow to become
|
||||
// arbitrarily large because DO calls still get processed. To avoid
|
||||
// this we set a cap on the size of the queue and simply clear it if it
|
||||
// becomes too large. (That is messages will be dropped and hence Vim
|
||||
// and MacVim will at least temporarily be out of sync.)
|
||||
if ([receiveQueue count] >= MMReceiveQueueCap)
|
||||
[receiveQueue removeAllObjects];
|
||||
|
||||
[receiveQueue addObject:queue];
|
||||
return;
|
||||
}
|
||||
|
||||
inProcessCommandQueue = YES;
|
||||
[self doProcessCommandQueue:queue];
|
||||
|
||||
int i;
|
||||
for (i = 0; i < [receiveQueue count]; ++i) {
|
||||
// Note that doProcessCommandQueue: may cause the receiveQueue to grow
|
||||
// or get cleared (due to cap being hit). Make sure to retain the item
|
||||
// to process or it may get released from under us.
|
||||
NSArray *q = [[receiveQueue objectAtIndex:i] retain];
|
||||
[self doProcessCommandQueue:q];
|
||||
[q release];
|
||||
}
|
||||
|
||||
// We assume that the remaining calls make no synchronous DO calls. If
|
||||
// that did happen anyway, the command queue could get processed out of
|
||||
// order.
|
||||
|
||||
// See comment below why this is called here and not later.
|
||||
[windowController processCommandQueueDidFinish];
|
||||
|
||||
// NOTE: Ensure that no calls are made after this "if" clause that may call
|
||||
// sendMessage::. If this happens anyway, such messages will be put on the
|
||||
// send queue and then the queue will not be flushed until the next time
|
||||
// this method is called.
|
||||
if ([sendQueue count] > 0) {
|
||||
@try {
|
||||
[backendProxy processInputAndData:sendQueue];
|
||||
}
|
||||
@catch (NSException *e) {
|
||||
// Connection timed out, just ignore this.
|
||||
//NSLog(@"WARNING! Connection timed out in %s", _cmd);
|
||||
}
|
||||
|
||||
[sendQueue removeAllObjects];
|
||||
}
|
||||
|
||||
[receiveQueue removeAllObjects];
|
||||
inProcessCommandQueue = NO;
|
||||
}
|
||||
|
||||
- (NSToolbarItem *)toolbar:(NSToolbar *)theToolbar
|
||||
@@ -659,67 +475,60 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
|
||||
@implementation MMVimController (Private)
|
||||
|
||||
- (void)doProcessCommandQueue:(NSArray *)queue
|
||||
- (void)doProcessInputQueue:(NSArray *)queue
|
||||
{
|
||||
NSMutableArray *delayQueue = nil;
|
||||
|
||||
@try {
|
||||
unsigned i, count = [queue count];
|
||||
if (count % 2) {
|
||||
NSLog(@"WARNING: Uneven number of components (%d) in command "
|
||||
"queue. Skipping...", count);
|
||||
return;
|
||||
}
|
||||
|
||||
//NSLog(@"======== %s BEGIN ========", _cmd);
|
||||
for (i = 0; i < count; i += 2) {
|
||||
NSData *value = [queue objectAtIndex:i];
|
||||
NSData *data = [queue objectAtIndex:i+1];
|
||||
|
||||
int msgid = *((int*)[value bytes]);
|
||||
//NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
|
||||
|
||||
BOOL inDefaultMode = [[[NSRunLoop currentRunLoop] currentMode]
|
||||
isEqual:NSDefaultRunLoopMode];
|
||||
if (!inDefaultMode && isUnsafeMessage(msgid)) {
|
||||
// NOTE: Because we may be listening to DO messages in "event
|
||||
// tracking mode" we have to take extra care when doing things
|
||||
// like releasing view items (and other Cocoa objects).
|
||||
// Messages that may be potentially "unsafe" are delayed until
|
||||
// the run loop is back to default mode at which time they are
|
||||
// safe to call again.
|
||||
// A problem with this approach is that it is hard to
|
||||
// classify which messages are unsafe. As a rule of thumb, if
|
||||
// a message may release an object used by the Cocoa framework
|
||||
// (e.g. views) then the message should be considered unsafe.
|
||||
// Delaying messages may have undesired side-effects since it
|
||||
// means that messages may not be processed in the order Vim
|
||||
// sent them, so beware.
|
||||
if (!delayQueue)
|
||||
delayQueue = [NSMutableArray array];
|
||||
|
||||
//NSLog(@"Adding unsafe message '%s' to delay queue (mode=%@)",
|
||||
// MessageStrings[msgid],
|
||||
// [[NSRunLoop currentRunLoop] currentMode]);
|
||||
[delayQueue addObject:value];
|
||||
[delayQueue addObject:data];
|
||||
} else {
|
||||
[self handleMessage:msgid data:data];
|
||||
}
|
||||
}
|
||||
//NSLog(@"======== %s END ========", _cmd);
|
||||
unsigned i, count = [queue count];
|
||||
if (count % 2) {
|
||||
NSLog(@"WARNING: Uneven number of components (%d) in command "
|
||||
"queue. Skipping...", count);
|
||||
return;
|
||||
}
|
||||
@catch (NSException *e) {
|
||||
NSLog(@"Exception caught whilst processing command queue: %@", e);
|
||||
|
||||
//NSLog(@"======== %s BEGIN ========", _cmd);
|
||||
for (i = 0; i < count; i += 2) {
|
||||
NSData *value = [queue objectAtIndex:i];
|
||||
NSData *data = [queue objectAtIndex:i+1];
|
||||
|
||||
int msgid = *((int*)[value bytes]);
|
||||
//NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
|
||||
|
||||
BOOL inDefaultMode = [[[NSRunLoop currentRunLoop] currentMode]
|
||||
isEqual:NSDefaultRunLoopMode];
|
||||
if (!inDefaultMode && isUnsafeMessage(msgid)) {
|
||||
// NOTE: Because we may be listening to DO messages in "event
|
||||
// tracking mode" we have to take extra care when doing things
|
||||
// like releasing view items (and other Cocoa objects).
|
||||
// Messages that may be potentially "unsafe" are delayed until
|
||||
// the run loop is back to default mode at which time they are
|
||||
// safe to call again.
|
||||
// A problem with this approach is that it is hard to
|
||||
// classify which messages are unsafe. As a rule of thumb, if
|
||||
// a message may release an object used by the Cocoa framework
|
||||
// (e.g. views) then the message should be considered unsafe.
|
||||
// Delaying messages may have undesired side-effects since it
|
||||
// means that messages may not be processed in the order Vim
|
||||
// sent them, so beware.
|
||||
if (!delayQueue)
|
||||
delayQueue = [NSMutableArray array];
|
||||
|
||||
//NSLog(@"Adding unsafe message '%s' to delay queue (mode=%@)",
|
||||
// MessageStrings[msgid],
|
||||
// [[NSRunLoop currentRunLoop] currentMode]);
|
||||
[delayQueue addObject:value];
|
||||
[delayQueue addObject:data];
|
||||
} else {
|
||||
[self handleMessage:msgid data:data];
|
||||
}
|
||||
}
|
||||
//NSLog(@"======== %s END ========", _cmd);
|
||||
|
||||
if (delayQueue) {
|
||||
//NSLog(@" Flushing delay queue (%d items)", [delayQueue count]/2);
|
||||
[self performSelectorOnMainThread:@selector(processCommandQueue:)
|
||||
withObject:delayQueue
|
||||
waitUntilDone:NO
|
||||
modes:[NSArray arrayWithObject:
|
||||
NSDefaultRunLoopMode]];
|
||||
[self performSelector:@selector(processInputQueue:)
|
||||
withObject:delayQueue
|
||||
afterDelay:0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,12 +724,10 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
NSDictionary *attrs = [NSDictionary dictionaryWithData:data];
|
||||
|
||||
// The popup menu enters a modal loop so delay this call so that we
|
||||
// don't block inside processCommandQueue:.
|
||||
[self performSelectorOnMainThread:@selector(popupMenuWithAttributes:)
|
||||
withObject:attrs
|
||||
waitUntilDone:NO
|
||||
modes:[NSArray arrayWithObject:
|
||||
NSDefaultRunLoopMode]];
|
||||
// don't block inside processInputQueue:.
|
||||
[self performSelector:@selector(popupMenuWithAttributes:)
|
||||
withObject:attrs
|
||||
afterDelay:0];
|
||||
} else if (SetMouseShapeMsgID == msgid) {
|
||||
const void *bytes = [data bytes];
|
||||
int shape = *((int*)bytes); bytes += sizeof(int);
|
||||
@@ -981,6 +788,18 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
showWithText:[dict objectForKey:@"text"]
|
||||
flags:[[dict objectForKey:@"flags"] intValue]];
|
||||
}
|
||||
} else if (ActivateKeyScriptID == msgid) {
|
||||
KeyScript(smKeySysScript);
|
||||
} else if (DeactivateKeyScriptID == msgid) {
|
||||
KeyScript(smKeyRoman);
|
||||
} else if (BrowseForFileMsgID == msgid) {
|
||||
NSDictionary *dict = [NSDictionary dictionaryWithData:data];
|
||||
if (dict)
|
||||
[self handleBrowseForFile:dict];
|
||||
} else if (ShowDialogMsgID == msgid) {
|
||||
NSDictionary *dict = [NSDictionary dictionaryWithData:data];
|
||||
if (dict)
|
||||
[self handleShowDialog:dict];
|
||||
// IMPORTANT: When adding a new message, make sure to update
|
||||
// isUnsafeMessage() if necessary!
|
||||
} else {
|
||||
@@ -1428,13 +1247,142 @@ static BOOL isUnsafeMessage(int msgid);
|
||||
// following call ensures that the vim controller is not released until the
|
||||
// run loop is back in the 'default' mode.
|
||||
[[MMAppController sharedInstance]
|
||||
performSelectorOnMainThread:@selector(removeVimController:)
|
||||
withObject:self
|
||||
waitUntilDone:NO
|
||||
modes:[NSArray arrayWithObject:
|
||||
NSDefaultRunLoopMode]];
|
||||
performSelector:@selector(removeVimController:)
|
||||
withObject:self
|
||||
afterDelay:0];
|
||||
}
|
||||
|
||||
// NSSavePanel delegate
|
||||
- (void)panel:(id)sender willExpand:(BOOL)expanding
|
||||
{
|
||||
// Show or hide the "show hidden files" button
|
||||
if (expanding) {
|
||||
[sender setAccessoryView:showHiddenFilesView()];
|
||||
} else {
|
||||
[sender setShowsHiddenFiles:NO];
|
||||
[sender setAccessoryView:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleBrowseForFile:(NSDictionary *)attr
|
||||
{
|
||||
if (!isInitialized) return;
|
||||
|
||||
NSString *dir = [attr objectForKey:@"dir"];
|
||||
BOOL saving = [[attr objectForKey:@"saving"] boolValue];
|
||||
|
||||
if (!dir) {
|
||||
// 'dir == nil' means: set dir to the pwd of the Vim process, or let
|
||||
// open dialog decide (depending on the below user default).
|
||||
BOOL trackPwd = [[NSUserDefaults standardUserDefaults]
|
||||
boolForKey:MMDialogsTrackPwdKey];
|
||||
if (trackPwd)
|
||||
dir = [vimState objectForKey:@"pwd"];
|
||||
}
|
||||
|
||||
if (saving) {
|
||||
NSSavePanel *panel = [NSSavePanel savePanel];
|
||||
|
||||
// The delegate will be notified when the panel is expanded at which
|
||||
// time we may hide/show the "show hidden files" button (this button is
|
||||
// always visible for the open panel since it is always expanded).
|
||||
[panel setDelegate:self];
|
||||
if ([panel isExpanded])
|
||||
[panel setAccessoryView:showHiddenFilesView()];
|
||||
|
||||
[panel beginSheetForDirectory:dir file:nil
|
||||
modalForWindow:[windowController window]
|
||||
modalDelegate:self
|
||||
didEndSelector:@selector(savePanelDidEnd:code:context:)
|
||||
contextInfo:NULL];
|
||||
} else {
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
[panel setAllowsMultipleSelection:NO];
|
||||
[panel setAccessoryView:showHiddenFilesView()];
|
||||
|
||||
[panel beginSheetForDirectory:dir file:nil types:nil
|
||||
modalForWindow:[windowController window]
|
||||
modalDelegate:self
|
||||
didEndSelector:@selector(savePanelDidEnd:code:context:)
|
||||
contextInfo:NULL];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleShowDialog:(NSDictionary *)attr
|
||||
{
|
||||
if (!isInitialized) return;
|
||||
|
||||
NSArray *buttonTitles = [attr objectForKey:@"buttonTitles"];
|
||||
if (!(buttonTitles && [buttonTitles count])) return;
|
||||
|
||||
int style = [[attr objectForKey:@"alertStyle"] intValue];
|
||||
NSString *message = [attr objectForKey:@"messageText"];
|
||||
NSString *text = [attr objectForKey:@"informativeText"];
|
||||
NSString *textFieldString = [attr objectForKey:@"textFieldString"];
|
||||
MMAlert *alert = [[MMAlert alloc] init];
|
||||
|
||||
// NOTE! This has to be done before setting the informative text.
|
||||
if (textFieldString)
|
||||
[alert setTextFieldString:textFieldString];
|
||||
|
||||
[alert setAlertStyle:style];
|
||||
|
||||
if (message) {
|
||||
[alert setMessageText:message];
|
||||
} else {
|
||||
// If no message text is specified 'Alert' is used, which we don't
|
||||
// want, so set an empty string as message text.
|
||||
[alert setMessageText:@""];
|
||||
}
|
||||
|
||||
if (text) {
|
||||
[alert setInformativeText:text];
|
||||
} else if (textFieldString) {
|
||||
// Make sure there is always room for the input text field.
|
||||
[alert setInformativeText:@""];
|
||||
}
|
||||
|
||||
unsigned i, count = [buttonTitles count];
|
||||
for (i = 0; i < count; ++i) {
|
||||
NSString *title = [buttonTitles objectAtIndex:i];
|
||||
// NOTE: The title of the button may contain the character '&' to
|
||||
// indicate that the following letter should be the key equivalent
|
||||
// associated with the button. Extract this letter and lowercase it.
|
||||
NSString *keyEquivalent = nil;
|
||||
NSRange hotkeyRange = [title rangeOfString:@"&"];
|
||||
if (NSNotFound != hotkeyRange.location) {
|
||||
if ([title length] > NSMaxRange(hotkeyRange)) {
|
||||
NSRange keyEquivRange = NSMakeRange(hotkeyRange.location+1, 1);
|
||||
keyEquivalent = [[title substringWithRange:keyEquivRange]
|
||||
lowercaseString];
|
||||
}
|
||||
|
||||
NSMutableString *string = [NSMutableString stringWithString:title];
|
||||
[string deleteCharactersInRange:hotkeyRange];
|
||||
title = string;
|
||||
}
|
||||
|
||||
[alert addButtonWithTitle:title];
|
||||
|
||||
// Set key equivalent for the button, but only if NSAlert hasn't
|
||||
// already done so. (Check the documentation for
|
||||
// - [NSAlert addButtonWithTitle:] to see what key equivalents are
|
||||
// automatically assigned.)
|
||||
NSButton *btn = [[alert buttons] lastObject];
|
||||
if ([[btn keyEquivalent] length] == 0 && keyEquivalent) {
|
||||
[btn setKeyEquivalent:keyEquivalent];
|
||||
}
|
||||
}
|
||||
|
||||
[alert beginSheetModalForWindow:[windowController window]
|
||||
modalDelegate:self
|
||||
didEndSelector:@selector(alertDidEnd:code:context:)
|
||||
contextInfo:NULL];
|
||||
|
||||
[alert release];
|
||||
}
|
||||
|
||||
|
||||
@end // MMVimController (Private)
|
||||
|
||||
|
||||
@@ -1525,6 +1473,8 @@ isUnsafeMessage(int msgid)
|
||||
EnterFullscreenMsgID, // Modifies delegate of window controller
|
||||
LeaveFullscreenMsgID, // Modifies delegate of window controller
|
||||
CloseWindowMsgID, // See note below
|
||||
BrowseForFileMsgID, // Enters modal loop
|
||||
ShowDialogMsgID, // Enters modal loop
|
||||
};
|
||||
|
||||
// NOTE about CloseWindowMsgID: If this arrives at the same time as say
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
MMVimView *vimView;
|
||||
BOOL setupDone;
|
||||
BOOL shouldResizeVimView;
|
||||
int shouldUpdateToolbar;
|
||||
int updateToolbarFlag;
|
||||
BOOL keepOnScreen;
|
||||
BOOL fullscreenEnabled;
|
||||
NSString *windowAutosaveKey;
|
||||
@@ -55,7 +55,7 @@
|
||||
- (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore;
|
||||
- (void)setFont:(NSFont *)font;
|
||||
- (void)setWideFont:(NSFont *)font;
|
||||
- (void)processCommandQueueDidFinish;
|
||||
- (void)processInputQueueDidFinish;
|
||||
- (void)showTabBar:(BOOL)on;
|
||||
- (void)showToolbar:(BOOL)on size:(int)size mode:(int)mode;
|
||||
- (void)setMouseShape:(int)shape;
|
||||
|
||||
@@ -308,7 +308,7 @@
|
||||
// NOTE: The only place where the (rows,columns) of the vim view are
|
||||
// modified is here and when entering/leaving full-screen. Setting these
|
||||
// values have no immediate effect, the actual resizing of the view is done
|
||||
// in processCommandQueueDidFinish.
|
||||
// in processInputQueueDidFinish.
|
||||
//
|
||||
// The 'live' flag indicates that this resize originated from a live
|
||||
// resize; it may very well happen that the view is no longer in live
|
||||
@@ -419,11 +419,8 @@
|
||||
[[vimView textView] setWideFont:font];
|
||||
}
|
||||
|
||||
- (void)processCommandQueueDidFinish
|
||||
- (void)processInputQueueDidFinish
|
||||
{
|
||||
// IMPORTANT! No synchronous DO calls are allowed in this method. They
|
||||
// may cause the command queue to get processed out of order.
|
||||
|
||||
// NOTE: Resizing is delayed until after all commands have been processed
|
||||
// since it often happens that more than one command will cause a resize.
|
||||
// If we were to immediately resize then the vim view size would jitter
|
||||
@@ -453,10 +450,8 @@
|
||||
keepOnScreen = NO;
|
||||
}
|
||||
|
||||
if (shouldUpdateToolbar != 0) {
|
||||
if (updateToolbarFlag != 0)
|
||||
[self updateToolbar];
|
||||
shouldUpdateToolbar = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showTabBar:(BOOL)on
|
||||
@@ -491,16 +486,16 @@
|
||||
[toolbar setSizeMode:size];
|
||||
[toolbar setDisplayMode:mode];
|
||||
|
||||
// Positive flag shows toolbar, negative hides it.
|
||||
updateToolbarFlag = on ? 1 : -1;
|
||||
|
||||
// NOTE: If the window is not visible we must toggle the toolbar
|
||||
// immediately, otherwise "set go-=T" in .gvimrc will lead to the toolbar
|
||||
// showing its hide animation every time a new window is opened. (See
|
||||
// processCommandQueueDidFinish for the reason why we need to delay
|
||||
// toggling the toolbar when the window is visible.)
|
||||
if ([decoratedWindow isVisible]) {
|
||||
shouldUpdateToolbar = on ? 1 : -1;
|
||||
} else {
|
||||
// processInputQueueDidFinish for the reason why we need to delay toggling
|
||||
// the toolbar when the window is visible.)
|
||||
if (![decoratedWindow isVisible])
|
||||
[self updateToolbar];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setMouseShape:(int)shape
|
||||
@@ -1061,9 +1056,10 @@
|
||||
- (void)updateToolbar
|
||||
{
|
||||
NSToolbar *toolbar = [decoratedWindow toolbar];
|
||||
if (!toolbar) return;
|
||||
if (nil == toolbar || 0 == updateToolbarFlag) return;
|
||||
|
||||
BOOL on = shouldUpdateToolbar > 0 ? YES : NO;
|
||||
// Positive flag shows toolbar, negative hides it.
|
||||
BOOL on = updateToolbarFlag > 0 ? YES : NO;
|
||||
[toolbar setVisible:on];
|
||||
|
||||
if (([decoratedWindow styleMask] & NSTexturedBackgroundWindowMask) == 0) {
|
||||
@@ -1079,6 +1075,8 @@
|
||||
// is visible (because it brings its own separator).
|
||||
[self hideTablineSeparator:![[vimView tabBarControl] isHidden]];
|
||||
}
|
||||
|
||||
updateToolbarFlag = 0;
|
||||
}
|
||||
|
||||
@end // MMWindowController (Private)
|
||||
|
||||
+24
-20
@@ -33,7 +33,6 @@
|
||||
//
|
||||
@protocol MMBackendProtocol
|
||||
- (oneway void)processInput:(int)msgid data:(in bycopy NSData *)data;
|
||||
- (oneway void)processInputAndData:(in bycopy NSArray *)messages;
|
||||
- (oneway void)setDialogReturn:(in bycopy id)obj;
|
||||
- (NSString *)evaluateExpression:(in bycopy NSString *)expr;
|
||||
- (id)evaluateExpressionCocoa:(in bycopy NSString *)expr
|
||||
@@ -42,30 +41,23 @@
|
||||
- (oneway void)acknowledgeConnection;
|
||||
@end
|
||||
|
||||
//
|
||||
// This is the protocol MMVimController implements.
|
||||
//
|
||||
// Be very careful if you want to add methods to this protocol. Since DO
|
||||
// messages may arrive while Cocoa is in the middle of processing some other
|
||||
// message be sure to consider reentrancy issues. Look at processCommandQueue:
|
||||
// to see an example of how to deal with this.
|
||||
//
|
||||
@protocol MMFrontendProtocol
|
||||
- (oneway void)processCommandQueue:(in bycopy NSArray *)queue;
|
||||
- (oneway void)showSavePanelWithAttributes:(in bycopy NSDictionary *)attr;
|
||||
- (oneway void)presentDialogWithAttributes:(in bycopy NSDictionary *)attr;
|
||||
@end
|
||||
|
||||
|
||||
//
|
||||
// This is the protocol MMAppController implements.
|
||||
//
|
||||
// It handles connections between MacVim and Vim.
|
||||
// It handles connections between MacVim and Vim and communication from Vim to
|
||||
// MacVim.
|
||||
//
|
||||
// Do not add methods to this interface without a _very_ good reason (if
|
||||
// possible, instead add a new message to the *MsgID enum below and pass it via
|
||||
// processInput:forIdentifier). Methods should not modify the state directly
|
||||
// but should instead delay any potential modifications (see
|
||||
// connectBackend:pid: and processInput:forIdentifier:).
|
||||
//
|
||||
@protocol MMAppProtocol
|
||||
- (byref id <MMFrontendProtocol>)
|
||||
connectBackend:(byref in id <MMBackendProtocol>)backend
|
||||
pid:(int)pid;
|
||||
- (unsigned)connectBackend:(byref in id <MMBackendProtocol>)proxy pid:(int)pid;
|
||||
- (oneway void)processInput:(in bycopy NSArray *)queue
|
||||
forIdentifier:(unsigned)identifier;
|
||||
- (NSArray *)serverList;
|
||||
@end
|
||||
|
||||
@@ -109,7 +101,7 @@
|
||||
extern char *MessageStrings[];
|
||||
|
||||
enum {
|
||||
OpenWindowMsgID = 1,
|
||||
OpenWindowMsgID = 1, // NOTE: FIRST IN ENUM MUST BE 1
|
||||
InsertTextMsgID,
|
||||
KeyDownMsgID,
|
||||
CmdKeyMsgID,
|
||||
@@ -177,6 +169,11 @@ enum {
|
||||
SetFullscreenColorMsgID,
|
||||
ShowFindReplaceDialogMsgID,
|
||||
FindReplaceMsgID,
|
||||
ActivateKeyScriptID,
|
||||
DeactivateKeyScriptID,
|
||||
BrowseForFileMsgID,
|
||||
ShowDialogMsgID,
|
||||
LastMsgID // NOTE: MUST BE LAST MESSAGE IN ENUM!
|
||||
};
|
||||
|
||||
|
||||
@@ -215,6 +212,13 @@ enum {
|
||||
MMTabInfoCount
|
||||
};
|
||||
|
||||
|
||||
// Create a string holding the labels of all messages in message queue for
|
||||
// debugging purposes (condense some messages since there may typically be LOTS
|
||||
// of them on a queue).
|
||||
NSString *debugStringForMessageQueue(NSArray *queue);
|
||||
|
||||
|
||||
// Argument used to stop MacVim from opening an empty window on startup
|
||||
// (techincally this is a user default but should not be used as such).
|
||||
extern NSString *MMNoWindowKey;
|
||||
|
||||
@@ -84,6 +84,11 @@ char *MessageStrings[] =
|
||||
"SetFullscreenColorMsgID",
|
||||
"ShowFindReplaceDialogMsgID",
|
||||
"FindReplaceMsgID",
|
||||
"ActivateKeyScriptID",
|
||||
"DeactivateKeyScriptID",
|
||||
"BrowseForFileMsgID",
|
||||
"ShowDialogMsgID",
|
||||
"END OF MESSAGE IDs" // NOTE: Must be last!
|
||||
};
|
||||
|
||||
|
||||
@@ -98,6 +103,34 @@ NSString *VimPBoardType = @"VimPBoardType";
|
||||
|
||||
|
||||
|
||||
// Create a string holding the labels of all messages in message queue for
|
||||
// debugging purposes (condense some messages since there may typically be LOTS
|
||||
// of them on a queue).
|
||||
NSString *
|
||||
debugStringForMessageQueue(NSArray *queue)
|
||||
{
|
||||
NSMutableString *s = [NSMutableString new];
|
||||
unsigned i, count = [queue count];
|
||||
int item = 0, menu = 0, enable = 0;
|
||||
for (i = 0; i < count; i += 2) {
|
||||
NSData *value = [queue objectAtIndex:i];
|
||||
int msgid = *((int*)[value bytes]);
|
||||
if (msgid < 1 || msgid >= LastMsgID)
|
||||
continue;
|
||||
if (msgid == AddMenuItemMsgID) ++item;
|
||||
else if (msgid == AddMenuMsgID) ++menu;
|
||||
else if (msgid == EnableMenuItemMsgID) ++enable;
|
||||
else [s appendFormat:@"%s ", MessageStrings[msgid]];
|
||||
}
|
||||
if (item > 0) [s appendFormat:@"AddMenuItemMsgID(%d) ", item];
|
||||
if (menu > 0) [s appendFormat:@"AddMenuMsgID(%d) ", menu];
|
||||
if (enable > 0) [s appendFormat:@"EnableMenuItemMsgID(%d) ", enable];
|
||||
|
||||
return [s autorelease];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@implementation NSString (MMExtras)
|
||||
|
||||
|
||||
@@ -737,7 +737,7 @@
|
||||
i386,
|
||||
);
|
||||
COPY_PHASE_STRIP = YES;
|
||||
CURRENT_PROJECT_VERSION = 44;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)",
|
||||
@@ -778,7 +778,7 @@
|
||||
buildSettings = {
|
||||
ARCHS = "$(NATIVE_ARCH)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 44;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)",
|
||||
@@ -810,7 +810,7 @@
|
||||
buildSettings = {
|
||||
ARCHS = "$(NATIVE_ARCH)";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
CURRENT_PROJECT_VERSION = 44;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)",
|
||||
|
||||
@@ -95,7 +95,7 @@ enum {
|
||||
@end
|
||||
|
||||
|
||||
@interface NSOpenPanel (MMExtras)
|
||||
@interface NSSavePanel (MMExtras)
|
||||
- (void)hiddenFilesButtonToggled:(id)sender;
|
||||
- (void)setShowsHiddenFiles:(BOOL)show;
|
||||
@end
|
||||
@@ -130,7 +130,8 @@ enum {
|
||||
|
||||
|
||||
|
||||
// Create a view to be used as accessory for open panel. This function assumes
|
||||
// ownership of the view so do not release it.
|
||||
NSView *openPanelAccessoryView();
|
||||
// Create a view with a "show hidden files" button to be used as accessory for
|
||||
// open/save panels. This function assumes ownership of the view so do not
|
||||
// release it.
|
||||
NSView *showHiddenFilesView();
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ NSString *MMLoadDefaultFontKey = @"MMLoadDefaultFont";
|
||||
|
||||
|
||||
|
||||
@implementation NSOpenPanel (MMExtras)
|
||||
@implementation NSSavePanel (MMExtras)
|
||||
|
||||
- (void)hiddenFilesButtonToggled:(id)sender
|
||||
{
|
||||
@@ -122,7 +122,7 @@ NSString *MMLoadDefaultFontKey = @"MMLoadDefaultFont";
|
||||
[invocation invoke];
|
||||
}
|
||||
|
||||
@end // NSOpenPanel (MMExtras)
|
||||
@end // NSSavePanel (MMExtras)
|
||||
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ NSString *MMLoadDefaultFontKey = @"MMLoadDefaultFont";
|
||||
|
||||
|
||||
NSView *
|
||||
openPanelAccessoryView()
|
||||
showHiddenFilesView()
|
||||
{
|
||||
// Return a new button object for each NSOpenPanel -- several of them
|
||||
// could be displayed at once.
|
||||
@@ -272,13 +272,13 @@ openPanelAccessoryView()
|
||||
NSButton *button = [[[NSButton alloc]
|
||||
initWithFrame:NSMakeRect(0, 0, 140, 18)] autorelease];
|
||||
[button setTitle:
|
||||
NSLocalizedString(@"Show Hidden Files", @"Open File Dialog")];
|
||||
NSLocalizedString(@"Show Hidden Files", @"Show Hidden Files Checkbox")];
|
||||
[button setButtonType:NSSwitchButton];
|
||||
|
||||
[button setTarget:nil];
|
||||
[button setAction:@selector(hiddenFilesButtonToggled:)];
|
||||
|
||||
// use the regular control size (checkbox is a bit smaller without this)
|
||||
// Use the regular control size (checkbox is a bit smaller without this)
|
||||
NSControlSize buttonSize = NSRegularControlSize;
|
||||
float fontSize = [NSFont systemFontSizeForControlSize:buttonSize];
|
||||
NSCell *theCell = [button cell];
|
||||
|
||||
+17
-18
@@ -20,16 +20,16 @@ is very easy to pick up if you know C and some object oriented programming.)
|
||||
Each editor window in MacVim runs its own Vim process (but there is always
|
||||
only one MacVim process). Communication between MacVim and a Vim process is
|
||||
done using Distributed Objects (DO). Each Vim process is represented by a
|
||||
backend object (MMBackend) and it communicates with a frontend object in the
|
||||
Vim process (MMVimController). The interface between the backend and frontend
|
||||
backend object (MMBackend) and it communicates with the frontend object in the
|
||||
Vim process (MMAppController). The interface between the backend and frontend
|
||||
is defined in MacVim.h.
|
||||
|
||||
The frontend sends input to the backend by calling
|
||||
-[MMBackend processInput:data:]. The backend queues output on a command queue
|
||||
and sends it to the frontend at opportune times by calling
|
||||
-[MMVimController processCommandQueue:]. These are both asynchronous calls so
|
||||
MacVim can keep drawing and receiving input while Vim is working away, thus
|
||||
always keeping the user interface responsive.
|
||||
-[MMAppController processInput:forIdentifier:]. These are both asynchronous
|
||||
calls so MacVim can keep drawing and receiving input while Vim is working away,
|
||||
thus always keeping the user interface responsive.
|
||||
|
||||
The state of each editor window is kept entirely in the Vim process. MacVim
|
||||
should remain "ignorant" in the sense that it knows nothing of the actual
|
||||
@@ -46,7 +46,7 @@ to MacVim inside -[MMBackend queueVimStateMessage].
|
||||
|
||||
Vim:
|
||||
|
||||
Hooks from within Vim are implmented in gui_macvim.m, the name of such
|
||||
Hooks from within Vim are implemented in gui_macvim.m, the name of such
|
||||
functions usually start with "gui_mch_" and they should simply put a message
|
||||
on the output queue, by calling queueMessage:properties: on the singleton
|
||||
MMBackend object [MMBackend sharedInstance] (see e.g. gui_mch_destroy_menu()).
|
||||
@@ -63,7 +63,7 @@ for some reason fails to update the run loop then incoming DO calls will not
|
||||
be processed and for this reason it is best to avoid making synchronous DO
|
||||
calls from MacVim. (If synchronous calls must be made then it is important to
|
||||
set proper timeouts so that MacVim doesn't "hang", see
|
||||
-[MMVimConroller sendMessageNow:::] to see how this can be done.)
|
||||
-[MMVimController sendMessageNow:::] to see how this can be done.)
|
||||
|
||||
|
||||
MacVim:
|
||||
@@ -71,16 +71,15 @@ MacVim:
|
||||
The main nib of MacVim.app is MainMenu.nib which contains the default menu and
|
||||
an instance of MMAppController, which is connected as the delegate of
|
||||
NSApplication. That means, when MacVim starts it will load this nib file and
|
||||
automatically create an instance of the MMAppController singleton.
|
||||
automatically create an instance of the MMAppController singleton. All
|
||||
incoming distributed object calls go via MMAppController.
|
||||
|
||||
A new editor window is opened by calling
|
||||
-[MMAppController launchVimProcessWithArguments:]. This functions starts a
|
||||
new Vim process (by executing the Vim binary). The Vim process lets MacVim
|
||||
know when it has launched by calling -[MMAppController connectBackend:pid:]
|
||||
and MacVim responds to this message by creating a new frontend object
|
||||
(MMVimController) and returns a proxy to this object back to the Vim process.
|
||||
From this point onward the Vim process communicates directly with the
|
||||
MMVimController.
|
||||
and MacVim responds to this message by creating a new vim controller and
|
||||
returns an identifier for this object back to the Vim process.
|
||||
|
||||
The MMVimController represents the frontend of a Vim process inside MacVim.
|
||||
It coordinates all communication with the Vim process and delegates output
|
||||
@@ -88,7 +87,7 @@ that affects visual presentation to a MMWindowController object. Read the
|
||||
Cocoa documentation on the responsibilities of a window controller.
|
||||
|
||||
Input (keyboard & mouse) handling and drawing is handled by a helper object
|
||||
(MMTextViewHelper) to the current text view (MMTextView, MMAtsuiTextView).
|
||||
(MMTextViewHelper) to the current text view (MMTextView, MMAtsuiTextView, ...).
|
||||
|
||||
|
||||
Distributed Object dangers:
|
||||
@@ -106,7 +105,7 @@ message may arrive.
|
||||
the menu flashes briefly. During this "flash" a modal loop is entered.
|
||||
|
||||
Item 1 can cause a problem if MacVim sends a synchronous message and before a
|
||||
reply reacheds MacVim another message is received. From the source code it
|
||||
reply reaches MacVim another message is received. From the source code it
|
||||
looks like the synchronous message blocks but in fact the other message is
|
||||
executed during this "block". If the other message changes state radically
|
||||
something may go wrong after the synchronous DO message returns.
|
||||
@@ -117,8 +116,8 @@ which may be even more puzzling.
|
||||
One way to alleviate these problems is to ensure a DO message isn't entered
|
||||
twice by setting a boolean at the beginning of the message and clearing it
|
||||
afterwards. If the boolean is already set when entering the call must somehow
|
||||
be delayed. See -[MMVimController processCommandQueue:] for a concrete
|
||||
example.
|
||||
be delayed. See processInput:forIdentifier: and processInputQueues: inside
|
||||
MMAppController for a concrete example.
|
||||
|
||||
Another danger is that we must take care when releasing objects that Cocoa may
|
||||
be using. See -[MMVimController connectionDidDie:] how MacVim releases
|
||||
@@ -133,7 +132,7 @@ what they contain:
|
||||
MMAppController.* Everything related to running the application
|
||||
MMBackend.* Object representing a Vim process in backend
|
||||
MMTextView.* Handles input and drawing
|
||||
MMVimController.* Object representing a Vim Process in frontend
|
||||
MMVimController.* Object representing a Vim process in frontend
|
||||
MMVimView.* Cocoa view object
|
||||
MMWindowController.* Coordinates visual presentation
|
||||
MacVim.* Code shared between MacVim and Vim
|
||||
@@ -161,4 +160,4 @@ The application bundle can be found inside "src/MacVim/build/Release".
|
||||
|
||||
|
||||
Bjorn Winckler <bjorn.winckler@gmail.com>
|
||||
November 22, 2008
|
||||
April 5, 2009
|
||||
|
||||
@@ -1265,8 +1265,10 @@ im_set_active(int active)
|
||||
// respectively.
|
||||
SInt32 systemScript = GetScriptManagerVariable(smSysScript);
|
||||
|
||||
if (!p_imdisable && smRoman != systemScript)
|
||||
KeyScript(active ? smKeySysScript : smKeyRoman);
|
||||
if (!p_imdisable && smRoman != systemScript) {
|
||||
int msgid = active ? ActivateKeyScriptID : DeactivateKeyScriptID;
|
||||
[[MMBackend sharedInstance] queueMessage:msgid properties:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,31 @@
|
||||
Sparkle supports updates in zip, tar, tbz, tgz, or dmg format.
|
||||
-->
|
||||
|
||||
<item>
|
||||
<title>Snapshot 45 released</title>
|
||||
<description><![CDATA[
|
||||
<h1>MacVim snapshot 45 released</h1>
|
||||
|
||||
<p> Changes since snapshot 44:
|
||||
<ul>
|
||||
<li> The toolbar is not hidden by default again (if you prefer having the toolbar hidden, then add the line "set go-=T" to your ~/.gvimrc file) </li>
|
||||
<li> The ATSUI renderer honors the 'guisp' highlighting color </li>
|
||||
<li> Fix the forever bouncing Dock icon bug (Kazuki Sakamoto) </li>
|
||||
<li> Add the "Show Hidden Files" checkbox button to the Save dialog whenever the file browser is expanded </li>
|
||||
<li> Frontend refactoring </li>
|
||||
</ul>
|
||||
</p>
|
||||
]]></description>
|
||||
<pubDate>Mon, 13 Apr 2009 19:19 CET</pubDate>
|
||||
<enclosure type="application/octet-stream"
|
||||
url="http://newmacvim.muskokamug.org/mirror/files/MacVim-snapshot-45.tbz"
|
||||
length="8135831"
|
||||
sparkle:version="45"
|
||||
sparkle:shortVersionString="7.2"
|
||||
/>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<title>Snapshot 44 released</title>
|
||||
<description><![CDATA[
|
||||
|
||||
Reference in New Issue
Block a user