1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-11-28 02:54:31 +03:00
vimr/NeoVimServer/NeoVimServer.m

236 lines
6.5 KiB
Mathematica
Raw Normal View History

/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
#import "NeoVimServer.h"
#import "server_globals.h"
2016-07-10 15:43:26 +03:00
#import "Logging.h"
#import "CocoaCategories.h"
2017-01-06 12:31:45 +03:00
#import "DataWrapper.h"
2017-01-06 12:16:55 +03:00
// FileInfo and Boolean are #defined by Carbon and NeoVim: Since we don't need the Carbon versions of them, we rename
// them.
#define FileInfo CarbonFileInfo
#define Boolean CarbonBoolean
#import <nvim/vim.h>
#import <nvim/main.h>
// When #define'd you can execute the NeoVimServer binary and neovim will be started:
// $ ./NeoVimServer local remote
#undef DEBUG_NEOVIM_SERVER_STANDALONE
//#define DEBUG_NEOVIM_SERVER_STANDALONE
static const double qTimeout = 10;
2016-07-09 23:52:59 +03:00
@interface NeoVimServer ()
- (NSCondition *)outputCondition;
2017-01-06 12:16:55 +03:00
- (void)handleQuitMsg;
2016-07-09 23:52:59 +03:00
@end
2017-01-06 09:12:05 +03:00
static CFDataRef null_data_async(CFDataRef data, argv_callback cb) {
2017-01-06 08:42:23 +03:00
loop_schedule(&main_loop, event_create(1, cb, 1, data));
return NULL;
}
2017-01-06 09:12:05 +03:00
static CFDataRef data_sync(CFDataRef data, NSCondition *condition, argv_callback cb) {
2017-01-06 12:31:45 +03:00
DataWrapper *wrapper = [[DataWrapper alloc] init];
NSDate *deadline = [[NSDate date] dateByAddingTimeInterval:qTimeout];
[condition lock];
loop_schedule(&main_loop, event_create(1, cb, 3, data, condition, wrapper));
2017-01-06 10:28:04 +03:00
while (wrapper.isDataReady == false && [condition waitUntilDate:deadline]);
[condition unlock];
2017-01-06 11:25:11 +03:00
if (wrapper.data == nil) {
return NULL;
}
return CFDataCreateCopy(kCFAllocatorDefault, (__bridge CFDataRef) wrapper.data);
}
static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info) {
@autoreleasepool {
2016-07-10 15:24:45 +03:00
NeoVimServer *neoVimServer = (__bridge NeoVimServer *) info;
2017-01-06 08:42:23 +03:00
NSCondition *outputCondition = neoVimServer.outputCondition;
CFRetain(data); // release in the loop callbacks!
switch (msgid) {
2017-01-06 11:36:46 +03:00
case NeoVimAgentMsgIdAgentReady:
2017-01-06 12:16:55 +03:00
start_neovim();
return NULL;
case NeoVimAgentMsgIdQuit: [neoVimServer handleQuitMsg];
2017-01-06 11:36:46 +03:00
2017-01-06 10:13:47 +03:00
case NeoVimAgentMsgIdCommandOutput: return data_sync(data, outputCondition, neovim_vim_command_output);
2017-01-06 09:12:05 +03:00
case NeoVimAgentMsgIdSelectWindow: return null_data_async(data, neovim_select_window);
2017-01-06 09:12:05 +03:00
case NeoVimAgentMsgIdGetTabs: return data_sync(data, outputCondition, neovim_tabs);
2017-01-06 08:42:23 +03:00
2017-01-06 09:12:05 +03:00
case NeoVimAgentMsgIdGetBuffers: return data_sync(data, outputCondition, neovim_buffers);
2017-01-06 10:33:16 +03:00
case NeoVimAgentMsgIdGetBoolOption: return data_sync(data, outputCondition, neovim_get_bool_option);
2017-01-06 10:28:04 +03:00
case NeoVimAgentMsgIdSetBoolOption: return data_sync(data, outputCondition, neovim_set_bool_option);
2017-01-06 10:38:13 +03:00
case NeoVimAgentMsgIdGetEscapeFileNames: return data_sync(data, outputCondition, neovim_escaped_filenames);
2017-01-06 10:51:01 +03:00
case NeoVimAgentMsgIdGetDirtyDocs: return data_sync(data, outputCondition, neovim_has_dirty_docs);
2017-01-06 10:57:26 +03:00
case NeoVimAgentMsgIdResize: return null_data_async(data, neovim_resize);
2017-01-06 11:22:16 +03:00
case NeoVimAgentMsgIdCommand: return null_data_async(data, neovim_vim_command);
2017-01-06 12:06:47 +03:00
case NeoVimAgentMsgIdInput: return null_data_async(data, neovim_vim_input);
case NeoVimAgentMsgIdInputMarked: return null_data_async(data, neovim_vim_input_marked_text);
case NeoVimAgentMsgIdDelete: return null_data_async(data, neovim_delete);
2017-01-06 12:16:55 +03:00
default: return NULL;
}
}
}
@implementation NeoVimServer {
NSString *_localServerName;
NSString *_remoteServerName;
CFMessagePortRef _remoteServerPort;
NSThread *_localServerThread;
CFMessagePortRef _localServerPort;
CFRunLoopRef _localServerRunLoop;
NSCondition *_outputCondition;
}
- (NSCondition *)outputCondition {
return _outputCondition;
}
2016-07-10 15:43:26 +03:00
- (instancetype)initWithLocalServerName:(NSString *)localServerName remoteServerName:(NSString *)remoteServerName {
self = [super init];
if (self == nil) {
return nil;
}
_outputCondition = [[NSCondition alloc] init];
2016-07-10 15:24:45 +03:00
_localServerName = localServerName;
_remoteServerName = remoteServerName;
_localServerThread = [[NSThread alloc] initWithTarget:self selector:@selector(runLocalServer) object:nil];
_localServerThread.name = localServerName;
[_localServerThread start];
#ifndef DEBUG_NEOVIM_SERVER_STANDALONE
2016-07-10 15:24:45 +03:00
_remoteServerPort = CFMessagePortCreateRemote(kCFAllocatorDefault, (__bridge CFStringRef) _remoteServerName);
#endif
return self;
}
- (void)dealloc {
if (CFMessagePortIsValid(_remoteServerPort)) {
CFMessagePortInvalidate(_remoteServerPort);
}
CFRelease(_remoteServerPort);
if (CFMessagePortIsValid(_localServerPort)) {
CFMessagePortInvalidate(_localServerPort);
}
CFRelease(_localServerPort);
CFRunLoopStop(_localServerRunLoop);
[_localServerThread cancel];
}
- (void)runLocalServer {
@autoreleasepool {
unsigned char shouldFree = false;
CFMessagePortContext localContext = {
.version = 0,
2016-07-10 15:24:45 +03:00
.info = (__bridge void *) self,
.retain = NULL,
.release = NULL,
.copyDescription = NULL
};
_localServerPort = CFMessagePortCreateLocal(
kCFAllocatorDefault,
2016-07-10 15:24:45 +03:00
(__bridge CFStringRef) _localServerName,
local_server_callback,
&localContext,
&shouldFree
);
// FIXME: handle shouldFree == true
}
2016-07-10 15:10:09 +03:00
_localServerRunLoop = CFRunLoopGetCurrent();
2016-07-10 15:10:09 +03:00
CFRunLoopSourceRef runLoopSrc = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, _localServerPort, 0);
CFRunLoopAddSource(_localServerRunLoop, runLoopSrc, kCFRunLoopCommonModes);
2016-07-10 15:10:09 +03:00
CFRelease(runLoopSrc);
#ifdef DEBUG_NEOVIM_SERVER_STANDALONE
server_start_neovim();
#endif
2016-07-10 15:10:09 +03:00
CFRunLoopRun();
}
- (void)sendMessageWithId:(NeoVimServerMsgId)msgid {
[self sendMessageWithId:msgid data:nil];
}
- (void)sendMessageWithId:(NeoVimServerMsgId)msgid data:(NSData *)data {
#ifdef DEBUG_NEOVIM_SERVER_STANDALONE
return;
#endif
2016-07-09 23:52:59 +03:00
if (_remoteServerPort == NULL) {
WLOG("Remote server is null: The msg (%lu:%s) could not be sent.", (unsigned long) msgid, data.cdesc);
2016-07-09 23:52:59 +03:00
return;
}
SInt32 responseCode = CFMessagePortSendRequest(
2016-07-10 15:24:45 +03:00
_remoteServerPort, msgid, (__bridge CFDataRef) data, qTimeout, qTimeout, NULL, NULL
);
if (responseCode == kCFMessagePortSuccess) {
return;
}
WLOG("The msg (%lu:%s) could not be sent: %d", (unsigned long) msgid, data.cdesc, responseCode);
}
- (void)notifyReadiness {
#ifndef DEBUG_NEOVIM_SERVER_STANDALONE
[self sendMessageWithId:NeoVimServerMsgIdServerReady data:nil];
#endif
}
- (void)quit {
2017-01-06 12:16:55 +03:00
quit_neovim();
}
2017-01-06 12:16:55 +03:00
- (void)handleQuitMsg {
// 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];
2016-07-09 23:52:59 +03:00
}
@end