diff --git a/NeoVimServer/NeoVimServer.m b/NeoVimServer/NeoVimServer.m index 943297e8..467ddf93 100644 --- a/NeoVimServer/NeoVimServer.m +++ b/NeoVimServer/NeoVimServer.m @@ -7,6 +7,15 @@ #import "server_globals.h" #import "Logging.h" #import "CocoaCategories.h" +#import "Wrapper.h" + +#define FileInfo CarbonFileInfo +#define Boolean CarbonBoolean + +#import +#import +#import +#import // When #define'd you can execute the NeoVimServer binary and neovim will be started: @@ -42,13 +51,46 @@ static inline NSData *data_without_response_id(NSData *data) { @interface NeoVimServer () +- (NSCondition *)outputCondition; - (NSData *)handleMessageWithId:(SInt32)msgid data:(NSData *)data; @end +static CFDataRef dataByWaiting(NSCondition *condition, CFDataRef data, argv_callback cb) { + Wrapper *wrapper = [[Wrapper alloc] init]; + NSDate *deadline = [[NSDate date] dateByAddingTimeInterval:qTimeout]; + + [condition lock]; + + loop_schedule(&main_loop, event_create(1, cb, 3, data, condition, wrapper)); + + while (wrapper.data == nil && [condition waitUntilDate:deadline]); + [condition unlock]; + + return (__bridge CFDataRef) wrapper.data; +} + static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info) { @autoreleasepool { NeoVimServer *neoVimServer = (__bridge NeoVimServer *) info; + CFRetain(data); // release in the loop callbacks! + + switch (msgid) { + + case NeoVimAgentMsgIdSelectWindow: { + loop_schedule(&main_loop, event_create(1, neovim_select_window, 1, data)); + return NULL; + } + + case NeoVimAgentMsgIdGetTabs: { + return dataByWaiting(neoVimServer.outputCondition, data, neovim_tabs); + } + + default: + break; + + } + NSData *responseData = [neoVimServer handleMessageWithId:msgid data:(__bridge NSData *) data]; if (responseData == nil) { @@ -69,6 +111,12 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD NSThread *_localServerThread; CFMessagePortRef _localServerPort; CFRunLoopRef _localServerRunLoop; + + NSCondition *_outputCondition; +} + +- (NSCondition *)outputCondition { + return _outputCondition; } - (instancetype)initWithLocalServerName:(NSString *)localServerName remoteServerName:(NSString *)remoteServerName { @@ -77,6 +125,8 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD return nil; } + _outputCondition = [[NSCondition alloc] init]; + _localServerName = localServerName; _remoteServerName = remoteServerName; @@ -223,12 +273,6 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD return nil; } - case NeoVimAgentMsgIdSelectWindow: { - int *values = data_to_int_array(data, 1); - server_select_win(values[0]); - return nil; - } - case NeoVimAgentMsgIdQuit: // exit() after returning the response such that the agent can get the response and so does not log a warning. [self performSelector:@selector(quit) onThread:_localServerThread withObject:nil waitUntilDone:NO]; @@ -254,10 +298,6 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD return [NSKeyedArchiver archivedDataWithRootObject:server_buffers()]; } - case NeoVimAgentMsgIdGetTabs: { - return [NSKeyedArchiver archivedDataWithRootObject:server_tabs()]; - } - case NeoVimAgentMsgIdGetBoolOption: { NSUInteger responseId = response_id_from_data(data); NSString *optionName = [[NSString alloc] initWithData:data_without_response_id(data) diff --git a/NeoVimServer/Wrapper.h b/NeoVimServer/Wrapper.h new file mode 100644 index 00000000..8ea92e7a --- /dev/null +++ b/NeoVimServer/Wrapper.h @@ -0,0 +1,13 @@ +// +// Created by Tae Won Ha on 1/5/17. +// Copyright (c) 2017 Tae Won Ha. All rights reserved. +// + +#import + + +@interface Wrapper : NSObject + +@property (nonatomic, nonnull) NSData *data; + +@end diff --git a/NeoVimServer/Wrapper.m b/NeoVimServer/Wrapper.m new file mode 100644 index 00000000..252b23d9 --- /dev/null +++ b/NeoVimServer/Wrapper.m @@ -0,0 +1,12 @@ +// +// Created by Tae Won Ha on 1/5/17. +// Copyright (c) 2017 Tae Won Ha. All rights reserved. +// + +#import "Wrapper.h" + + +@implementation Wrapper { + +} +@end diff --git a/NeoVimServer/server_globals.h b/NeoVimServer/server_globals.h index 9d5ad773..886aa665 100644 --- a/NeoVimServer/server_globals.h +++ b/NeoVimServer/server_globals.h @@ -23,5 +23,7 @@ extern NSArray *server_buffers(); extern NSArray *server_tabs(); extern void server_get_bool_option(NSUInteger responseId, NSString *option); extern void server_set_bool_option(NSUInteger responseId, NSString *option, bool value); -extern void server_select_win(int window_handle); extern void server_quit(); + +extern void neovim_select_window(void **argv); +extern void neovim_tabs(void **argv); diff --git a/NeoVimServer/server_ui.m b/NeoVimServer/server_ui.m index 51dcab9a..92b02a20 100644 --- a/NeoVimServer/server_ui.m +++ b/NeoVimServer/server_ui.m @@ -13,6 +13,7 @@ #import "NeoVimWindow.h" #import "NeoVimTab.h" #import "CocoaCategories.h" +#import "Wrapper.h" // FileInfo and Boolean are #defined by Carbon and NeoVim: Since we don't need the Carbon versions of them, we rename // them. @@ -479,18 +480,6 @@ static void neovim_input(void **argv) { } } -static void neovim_select_window(void **argv) { - win_T *window = (win_T *) argv[0]; - - Error err; - nvim_set_current_win(window->handle, &err); - // TODO: handle error - WLOG("Error selecting window with handle %d: %s", window->handle, err.msg); - - // nvim_set_current_win() does not seem to trigger a redraw. - ui_schedule_refresh(); -} - static void send_dirty_status() { bool new_dirty_status = server_has_dirty_docs(); DLOG("dirty status: %d vs. %d", _dirty, new_dirty_status); @@ -818,14 +807,6 @@ NSArray *server_tabs() { return tabs; } -void server_select_win(int window_handle) { - FOR_ALL_TAB_WINDOWS(tab, win) { - if (win->handle == window_handle) { - loop_schedule(&main_loop, event_create(1, neovim_select_window, 1, win)); - } - } -} - static void neovim_get_bool_option(void ** argv) { @autoreleasepool { NSUInteger *values = (NSUInteger *) argv[0]; @@ -912,3 +893,67 @@ void server_quit() { DLOG("NeoVimServer exiting..."); exit(0); } + +static inline NSUInteger response_id_from_data(NSData *data) { + NSUInteger *values = (NSUInteger *) data.bytes; + return values[0]; +} + +void neovim_select_window(void **argv) { + NSData *data = argv[0]; + int handle = ((int *) data.bytes)[0]; + [data release]; // retained in local_server_callback + + FOR_ALL_TAB_WINDOWS(tab, win) { + if (win->handle == handle) { + Error err; + nvim_set_current_win(win->handle, &err); + + if (err.set) { + WLOG("Error selecting window with handle %d: %s", win->handle, err.msg); + return; + } + + // nvim_set_current_win() does not seem to trigger a redraw. + ui_schedule_refresh(); + + return; + } + } +} + +void neovim_tabs(void **argv) { + NSData *data = argv[0]; + [data release]; // retained in local_server_callback + + NSCondition *outputCondition = argv[1]; + [outputCondition lock]; + + Wrapper *wrapper = argv[2]; + + NSMutableArray *tabs = [[NSMutableArray new] autorelease]; + FOR_ALL_TABS(t) { + NSMutableArray *windows = [NSMutableArray new]; + + FOR_ALL_WINDOWS_IN_TAB(win, t) { + NeoVimBuffer *buffer = buffer_for(win->w_buffer); + if (buffer == nil) { + continue; + } + + NeoVimWindow *window = [[NeoVimWindow alloc] initWithHandle:win->handle buffer:buffer]; + [windows addObject:window]; + [window release]; + } + + NeoVimTab *tab = [[NeoVimTab alloc] initWithHandle:t->handle windows:windows]; + [windows release]; + + [tabs addObject:tab]; + [tab release]; + } + + wrapper.data = [NSKeyedArchiver archivedDataWithRootObject:tabs]; + [outputCondition signal]; + [outputCondition unlock]; +} diff --git a/VimR.xcodeproj/project.pbxproj b/VimR.xcodeproj/project.pbxproj index 8580b00b..a0601318 100644 --- a/VimR.xcodeproj/project.pbxproj +++ b/VimR.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 1929B0E0C3BC59F52713D5A2 /* FoundationCommons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B9AF20D7BD6E5C975128 /* FoundationCommons.swift */; }; 1929B165820D7177743B537A /* Component.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B39DA7AC4A9B62D7CD39 /* Component.swift */; }; + 1929B18A0D7C7407C51DB642 /* Wrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB6CFF4CC0B5E8B00C62 /* Wrapper.m */; }; 1929B1E05C116514C1D3A384 /* CocoaCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929B5C3F2F1CA4113DABFFD /* CocoaCategories.m */; }; 1929B3CEE0C1A1850E9CCE2F /* BasicTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B2FBE11048569391E092 /* BasicTypes.swift */; }; 1929B3F5743967125F357C9F /* Matcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEEB33113B0E33C3830F /* Matcher.swift */; }; @@ -265,6 +266,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 1929B0EEBE4A765934AF8335 /* Wrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Wrapper.h; sourceTree = ""; }; 1929B15B7EDC9B0F40E5E95C /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = ""; }; 1929B1A51F076E088EF4CCA4 /* server_globals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = server_globals.h; sourceTree = ""; }; 1929B2FBE11048569391E092 /* BasicTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicTypes.swift; sourceTree = ""; }; @@ -282,6 +284,7 @@ 1929BA6128BFDD54CA92F46E /* ColorUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorUtils.swift; sourceTree = ""; }; 1929BA8AC40B901B20F20B71 /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = ""; }; 1929BB251F74BEFC82CEEF84 /* PrefPane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefPane.swift; sourceTree = ""; }; + 1929BB6CFF4CC0B5E8B00C62 /* Wrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Wrapper.m; sourceTree = ""; }; 1929BBC84557C8351EC6183E /* FileItemIgnorePatternTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItemIgnorePatternTest.swift; sourceTree = ""; }; 1929BC19C1BC19246AFF1621 /* MatcherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatcherTests.swift; sourceTree = ""; }; 1929BDF9EBAF1D9D44399045 /* ScoredFileItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScoredFileItem.swift; sourceTree = ""; }; @@ -643,6 +646,8 @@ 1929B5C3F2F1CA4113DABFFD /* CocoaCategories.m */, 1929BE69CF9AB1A10D0DD4F2 /* CocoaCategories.h */, 1929BFC86BF38D341F2DDCBD /* NeoVim Objects */, + 1929BB6CFF4CC0B5E8B00C62 /* Wrapper.m */, + 1929B0EEBE4A765934AF8335 /* Wrapper.h */, ); path = NeoVimServer; sourceTree = ""; @@ -1163,6 +1168,7 @@ 4BDD058D1DBBC50000D1B405 /* NeoVimWindow.m in Sources */, 4BDD05871DBBC50000D1B405 /* NeoVimBuffer.m in Sources */, 1929B1E05C116514C1D3A384 /* CocoaCategories.m in Sources */, + 1929B18A0D7C7407C51DB642 /* Wrapper.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };