2016-07-09 15:04:09 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
#import "NeoVimAgent.h"
|
2016-07-09 23:52:59 +03:00
|
|
|
#import "NeoVimMsgIds.h"
|
|
|
|
#import "NeoVimUiBridgeProtocol.h"
|
2016-08-26 18:27:42 +03:00
|
|
|
#import "Logger.h"
|
2016-08-12 16:19:06 +03:00
|
|
|
#import "NeoVimBuffer.h"
|
2016-10-22 23:20:22 +03:00
|
|
|
#import "NeoVimWindow.h"
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
|
2016-12-07 22:53:12 +03:00
|
|
|
static const double qTimeout = 10;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
#define data_to_array(type) \
|
|
|
|
static type *data_to_ ## type ## _array(NSData *data, NSUInteger count) { \
|
|
|
|
NSUInteger length = count * sizeof( type ); \
|
|
|
|
if (data.length != length) { \
|
|
|
|
return NULL; \
|
|
|
|
} \
|
|
|
|
return ( type *) data.bytes; \
|
|
|
|
}
|
|
|
|
|
|
|
|
data_to_array(int)
|
2016-12-19 23:31:16 +03:00
|
|
|
data_to_array(NSUInteger)
|
2016-07-18 21:16:56 +03:00
|
|
|
data_to_array(bool)
|
2016-07-09 23:52:59 +03:00
|
|
|
data_to_array(CellAttributes)
|
|
|
|
|
2016-11-24 21:46:41 +03:00
|
|
|
static void log_cfmachport_error(SInt32 err, NeoVimAgentMsgId msgid, NSData *inputData) {
|
|
|
|
switch (err) {
|
|
|
|
case kCFMessagePortSendTimeout:
|
|
|
|
log4Warn("Got response kCFMessagePortSendTimeout = %d for the msg %lu with data %@.",
|
|
|
|
err, (unsigned long) msgid, inputData);
|
|
|
|
case kCFMessagePortReceiveTimeout:
|
|
|
|
log4Warn("Got response kCFMessagePortReceiveTimeout = %d for the msg %lu with data %@.",
|
|
|
|
err, (unsigned long) msgid, inputData);
|
|
|
|
case kCFMessagePortIsInvalid:
|
|
|
|
log4Warn("Got response kCFMessagePortIsInvalid = %d for the msg %lu with data %@.",
|
|
|
|
err, (unsigned long) msgid, inputData);
|
|
|
|
case kCFMessagePortTransportError:
|
|
|
|
log4Warn("Got response kCFMessagePortTransportError = %d for the msg %lu with data %@.",
|
|
|
|
err, (unsigned long) msgid, inputData);
|
|
|
|
case kCFMessagePortBecameInvalidError:
|
|
|
|
log4Warn("Got response kCFMessagePortBecameInvalidError = %d for the msg %lu with data %@.",
|
|
|
|
err, (unsigned long) msgid, inputData);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-09 15:04:09 +03:00
|
|
|
@interface NeoVimAgent ()
|
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
- (void)handleMessageWithId:(SInt32)msgid data:(NSData *)data;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info) {
|
|
|
|
@autoreleasepool {
|
|
|
|
NeoVimAgent *agent = (__bridge NeoVimAgent *) info;
|
2016-07-09 23:52:59 +03:00
|
|
|
[agent handleMessageWithId:msgid data:(__bridge NSData *) (data)];
|
2016-07-09 15:04:09 +03:00
|
|
|
}
|
2016-07-10 15:24:45 +03:00
|
|
|
|
|
|
|
return NULL;
|
2016-07-09 15:04:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NeoVimAgent {
|
|
|
|
NSString *_uuid;
|
|
|
|
|
2016-07-18 21:16:56 +03:00
|
|
|
CFMessagePortRef _remoteServerPort;
|
|
|
|
|
2016-07-09 15:04:09 +03:00
|
|
|
CFMessagePortRef _localServerPort;
|
|
|
|
NSThread *_localServerThread;
|
2016-07-18 21:16:56 +03:00
|
|
|
CFRunLoopRef _localServerRunLoop;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
NSTask *_neoVimServerTask;
|
2016-08-25 23:52:31 +03:00
|
|
|
|
2016-08-21 18:32:35 +03:00
|
|
|
bool _neoVimIsReady;
|
2016-12-14 21:33:15 +03:00
|
|
|
NSCondition *_neoVimReadyCondition;
|
2016-08-25 23:52:31 +03:00
|
|
|
bool _isInitErrorPresent;
|
2016-09-02 19:22:40 +03:00
|
|
|
|
2017-01-07 15:55:53 +03:00
|
|
|
volatile uint32_t _neoVimIsQuitting;
|
2016-07-09 15:04:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (instancetype)initWithUuid:(NSString *)uuid {
|
|
|
|
self = [super init];
|
|
|
|
if (self == nil) {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
_uuid = uuid;
|
2016-09-24 17:31:14 +03:00
|
|
|
_useInteractiveZsh = NO;
|
2016-08-21 18:32:35 +03:00
|
|
|
_neoVimIsReady = NO;
|
2016-12-14 21:33:15 +03:00
|
|
|
_neoVimReadyCondition = [NSCondition new];
|
2016-08-25 23:52:31 +03:00
|
|
|
_isInitErrorPresent = NO;
|
2016-07-09 23:52:59 +03:00
|
|
|
|
2016-12-14 21:33:15 +03:00
|
|
|
_neoVimIsQuitting = 0;
|
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2016-12-14 22:49:56 +03:00
|
|
|
- (bool)neoVimIsQuitting {
|
|
|
|
return _neoVimIsQuitting == 1;
|
|
|
|
}
|
|
|
|
|
2017-01-07 15:55:53 +03:00
|
|
|
- (void)debug {
|
2017-01-07 23:04:27 +03:00
|
|
|
#ifdef DEBUG
|
2017-01-07 15:55:53 +03:00
|
|
|
[self sendMessageWithId:NeoVimAgentDebug1 data:nil expectsReply:NO];
|
2017-01-07 23:04:27 +03:00
|
|
|
#endif
|
2017-01-07 15:55:53 +03:00
|
|
|
}
|
|
|
|
|
2016-07-17 16:56:20 +03:00
|
|
|
// We cannot use -dealloc for this since -dealloc is not called until the run loop in the thread stops.
|
2016-08-12 19:00:05 +03:00
|
|
|
- (void)quit {
|
2016-12-14 21:33:15 +03:00
|
|
|
OSAtomicOr32Barrier(1, &_neoVimIsQuitting);
|
|
|
|
|
2016-08-26 18:27:42 +03:00
|
|
|
// Wait till we get the response from the server.
|
|
|
|
// If we don't wait here, then the NSTask.terminate msg below could get caught by neovim which causes a warning log.
|
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdQuit data:nil expectsReply:YES];
|
2016-08-12 19:00:05 +03:00
|
|
|
|
2016-07-18 21:16:56 +03:00
|
|
|
if (CFMessagePortIsValid(_remoteServerPort)) {
|
|
|
|
CFMessagePortInvalidate(_remoteServerPort);
|
|
|
|
}
|
2016-07-09 23:52:59 +03:00
|
|
|
CFRelease(_remoteServerPort);
|
2017-01-03 18:21:04 +03:00
|
|
|
_remoteServerPort = NULL;
|
2016-07-09 23:52:59 +03:00
|
|
|
|
2016-07-18 21:16:56 +03:00
|
|
|
if (CFMessagePortIsValid(_localServerPort)) {
|
|
|
|
CFMessagePortInvalidate(_localServerPort);
|
|
|
|
}
|
2016-07-09 23:52:59 +03:00
|
|
|
CFRelease(_localServerPort);
|
2017-01-03 18:21:04 +03:00
|
|
|
_localServerPort = NULL;
|
2016-07-09 23:52:59 +03:00
|
|
|
|
2016-07-18 21:16:56 +03:00
|
|
|
CFRunLoopStop(_localServerRunLoop);
|
2016-07-09 23:52:59 +03:00
|
|
|
[_localServerThread cancel];
|
|
|
|
|
2016-08-26 18:27:42 +03:00
|
|
|
// Just to be sure...
|
|
|
|
[_neoVimServerTask interrupt];
|
|
|
|
[_neoVimServerTask terminate];
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-08-26 18:27:42 +03:00
|
|
|
- (void)launchNeoVimUsingLoginShell {
|
|
|
|
NSString *shellPath = [NSProcessInfo processInfo].environment[@"SHELL"];
|
|
|
|
if (shellPath == nil) {
|
|
|
|
shellPath = @"/bin/bash";
|
|
|
|
}
|
2016-08-18 18:40:30 +03:00
|
|
|
|
2016-09-10 09:56:29 +03:00
|
|
|
NSString *shellName = shellPath.lastPathComponent;
|
2016-08-26 18:27:42 +03:00
|
|
|
NSMutableArray *shellArgs = [NSMutableArray new];
|
2016-09-10 09:56:29 +03:00
|
|
|
if (![shellName isEqualToString:@"tcsh"]) {
|
2016-09-09 20:23:06 +03:00
|
|
|
// tcsh does not like the -l option
|
2016-08-26 18:27:42 +03:00
|
|
|
[shellArgs addObject:@"-l"];
|
|
|
|
}
|
2016-09-24 17:31:14 +03:00
|
|
|
if (_useInteractiveZsh && [shellName isEqualToString:@"zsh"]) {
|
2016-09-10 09:56:29 +03:00
|
|
|
[shellArgs addObject:@"-i"];
|
|
|
|
}
|
2016-08-18 18:40:30 +03:00
|
|
|
|
2016-09-09 20:23:06 +03:00
|
|
|
NSPipe *inputPipe = [NSPipe pipe];
|
2016-08-26 18:27:42 +03:00
|
|
|
_neoVimServerTask = [[NSTask alloc] init];
|
2016-09-11 15:28:56 +03:00
|
|
|
|
|
|
|
#ifndef DEBUG
|
|
|
|
NSFileHandle *nullFileHandle = [NSFileHandle fileHandleWithNullDevice];
|
|
|
|
_neoVimServerTask.standardOutput = nullFileHandle;
|
|
|
|
_neoVimServerTask.standardError = nullFileHandle;
|
|
|
|
#endif
|
|
|
|
|
2016-09-09 20:23:06 +03:00
|
|
|
_neoVimServerTask.standardInput = inputPipe;
|
2016-07-10 15:05:10 +03:00
|
|
|
_neoVimServerTask.currentDirectoryPath = NSHomeDirectory();
|
2016-08-26 18:27:42 +03:00
|
|
|
_neoVimServerTask.launchPath = shellPath;
|
|
|
|
_neoVimServerTask.arguments = shellArgs;
|
2016-07-09 15:04:09 +03:00
|
|
|
[_neoVimServerTask launch];
|
2016-09-09 20:23:06 +03:00
|
|
|
|
|
|
|
NSString *cmd = [NSString stringWithFormat:@"exec \"%@\" '%@' '%@'",
|
|
|
|
[self neoVimServerExecutablePath],
|
|
|
|
[self localServerName],
|
|
|
|
[self remoteServerName]];
|
|
|
|
|
|
|
|
NSFileHandle *writeHandle = inputPipe.fileHandleForWriting;
|
|
|
|
[writeHandle writeData:[cmd dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
[writeHandle closeFile];
|
2016-08-26 18:27:42 +03:00
|
|
|
}
|
|
|
|
|
2016-08-26 18:45:58 +03:00
|
|
|
- (bool)runLocalServerAndNeoVim {
|
2016-08-26 18:27:42 +03:00
|
|
|
_localServerThread = [[NSThread alloc] initWithTarget:self selector:@selector(runLocalServer) object:nil];
|
|
|
|
[_localServerThread start];
|
|
|
|
|
|
|
|
[self launchNeoVimUsingLoginShell];
|
2016-08-21 18:32:35 +03:00
|
|
|
|
2016-12-14 21:33:15 +03:00
|
|
|
// Wait until neovim is ready.
|
2016-08-26 18:27:42 +03:00
|
|
|
NSDate *deadline = [[NSDate date] dateByAddingTimeInterval:qTimeout];
|
2016-12-14 21:33:15 +03:00
|
|
|
[_neoVimReadyCondition lock];
|
|
|
|
while (!_neoVimIsReady && [_neoVimReadyCondition waitUntilDate:deadline]);
|
|
|
|
[_neoVimReadyCondition unlock];
|
|
|
|
_neoVimReadyCondition = nil;
|
2016-08-25 23:52:31 +03:00
|
|
|
|
|
|
|
return !_isInitErrorPresent;
|
2016-07-09 23:52:59 +03:00
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-08-04 21:01:03 +03:00
|
|
|
- (void)vimCommand:(NSString *)string {
|
|
|
|
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
|
2016-08-12 19:00:05 +03:00
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdCommand data:data expectsReply:NO];
|
2016-08-04 21:01:03 +03:00
|
|
|
}
|
|
|
|
|
2016-11-10 21:28:09 +03:00
|
|
|
- (NSString *)vimCommandOutput:(NSString *)string {
|
2017-01-06 10:13:47 +03:00
|
|
|
NSData *data = [self sendMessageWithId:NeoVimAgentMsgIdCommandOutput
|
|
|
|
data:[string dataUsingEncoding:NSUTF8StringEncoding]
|
|
|
|
expectsReply:YES];
|
2016-11-10 21:28:09 +03:00
|
|
|
|
2017-01-06 13:17:53 +03:00
|
|
|
if (data == nil) {
|
2016-12-05 23:30:25 +03:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2017-01-06 10:13:47 +03:00
|
|
|
NSString *result = [NSKeyedUnarchiver unarchiveObjectWithData:data];
|
2016-09-02 19:22:40 +03:00
|
|
|
return [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
|
|
}
|
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
- (void)vimInput:(NSString *)string {
|
|
|
|
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
|
2016-08-12 19:00:05 +03:00
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdInput data:data expectsReply:NO];
|
2016-07-09 15:04:09 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
- (void)vimInputMarkedText:(NSString *_Nonnull)markedText {
|
|
|
|
NSData *data = [markedText dataUsingEncoding:NSUTF8StringEncoding];
|
2016-08-12 19:00:05 +03:00
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdInputMarked data:data expectsReply:NO];
|
2016-07-09 15:04:09 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
- (void)deleteCharacters:(NSInteger)count {
|
|
|
|
NSData *data = [[NSData alloc] initWithBytes:&count length:sizeof(NSInteger)];
|
2016-08-12 19:00:05 +03:00
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdDelete data:data expectsReply:NO];
|
2016-07-09 23:52:59 +03:00
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
- (void)resizeToWidth:(int)width height:(int)height {
|
|
|
|
int values[] = { width, height };
|
|
|
|
NSData *data = [[NSData alloc] initWithBytes:values length:(2 * sizeof(int))];
|
2016-08-12 19:00:05 +03:00
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdResize data:data expectsReply:NO];
|
2016-07-09 15:04:09 +03:00
|
|
|
}
|
|
|
|
|
2017-01-07 17:12:07 +03:00
|
|
|
- (void)cursorGoToRow:(int)row column:(int)column {
|
|
|
|
int values[] = { row, column };
|
|
|
|
NSData *data = [[NSData alloc] initWithBytes:values length:(2 * sizeof(int))];
|
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdCursorGoto data:data expectsReply:NO];
|
|
|
|
}
|
|
|
|
|
2016-07-18 21:16:56 +03:00
|
|
|
- (bool)hasDirtyDocs {
|
2016-08-25 00:06:39 +03:00
|
|
|
NSData *response = [self sendMessageWithId:NeoVimAgentMsgIdGetDirtyDocs data:nil expectsReply:YES];
|
2016-08-13 12:20:03 +03:00
|
|
|
if (response == nil) {
|
2016-08-25 00:06:39 +03:00
|
|
|
log4Warn("The response for the msg %lu was nil.", NeoVimAgentMsgIdGetDirtyDocs);
|
2017-01-07 15:55:53 +03:00
|
|
|
return NO;
|
2016-07-18 21:16:56 +03:00
|
|
|
}
|
2016-11-20 14:05:14 +03:00
|
|
|
|
2017-01-06 10:51:01 +03:00
|
|
|
NSNumber *value = [NSKeyedUnarchiver unarchiveObjectWithData:response];
|
|
|
|
return (bool) value.boolValue;
|
2016-07-18 21:16:56 +03:00
|
|
|
}
|
|
|
|
|
2016-08-25 00:06:39 +03:00
|
|
|
- (NSString *)escapedFileName:(NSString *)fileName {
|
2017-03-13 20:44:57 +03:00
|
|
|
NSArray<NSString *> *fileNames = [self escapedFileNames:@[ fileName ]];
|
|
|
|
if (fileNames.count == 0) {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fileNames[0];
|
2016-08-25 00:06:39 +03:00
|
|
|
}
|
|
|
|
|
2016-12-05 23:30:25 +03:00
|
|
|
- (NSNumber *)boolOption:(NSString *)option {
|
2017-01-06 10:33:16 +03:00
|
|
|
NSData *data = [option dataUsingEncoding:NSUTF8StringEncoding];
|
|
|
|
NSData *resultData = [self sendMessageWithId:NeoVimAgentMsgIdGetBoolOption data:data expectsReply:YES];
|
2016-12-05 23:30:25 +03:00
|
|
|
if (resultData == nil) {
|
|
|
|
return nil;
|
|
|
|
}
|
2016-11-10 22:42:42 +03:00
|
|
|
|
2016-12-07 22:24:25 +03:00
|
|
|
return [NSKeyedUnarchiver unarchiveObjectWithData:resultData];
|
2016-11-10 19:33:14 +03:00
|
|
|
}
|
|
|
|
|
2016-12-07 22:24:25 +03:00
|
|
|
- (void)setBoolOption:(NSString *)option to:(bool)value {
|
2016-11-10 19:33:14 +03:00
|
|
|
NSMutableData *data = [NSMutableData new];
|
|
|
|
|
|
|
|
bool values[] = { value };
|
|
|
|
const char *cstr = [option cStringUsingEncoding:NSUTF8StringEncoding];
|
|
|
|
|
|
|
|
[data appendBytes:values length:sizeof(bool)];
|
2016-11-10 22:42:42 +03:00
|
|
|
[data appendBytes:cstr length:strlen(cstr)];
|
|
|
|
|
2017-01-06 10:28:04 +03:00
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdSetBoolOption data:data expectsReply:YES];
|
2016-11-10 19:33:14 +03:00
|
|
|
}
|
|
|
|
|
2016-10-23 20:42:51 +03:00
|
|
|
- (void)selectWindow:(NeoVimWindow *)window {
|
|
|
|
int values[] = { (int) window.handle };
|
|
|
|
NSData *data = [[NSData alloc] initWithBytes:values length:sizeof(int)];
|
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdSelectWindow data:data expectsReply:NO];
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:51:16 +03:00
|
|
|
- (NSArray <NSString *>*)escapedFileNames:(NSArray <NSString *>*)fileNames {
|
|
|
|
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:fileNames];
|
2016-08-25 00:06:39 +03:00
|
|
|
NSData *response = [self sendMessageWithId:NeoVimAgentMsgIdGetEscapeFileNames data:data expectsReply:YES];
|
2016-08-13 12:20:03 +03:00
|
|
|
if (response == nil) {
|
2016-08-25 00:06:39 +03:00
|
|
|
log4Warn("The response for the msg %lu was nil.", NeoVimAgentMsgIdGetEscapeFileNames);
|
2016-08-13 12:20:03 +03:00
|
|
|
return @[];
|
|
|
|
}
|
2016-08-12 11:51:16 +03:00
|
|
|
|
2016-08-13 12:20:03 +03:00
|
|
|
return [NSKeyedUnarchiver unarchiveObjectWithData:response];
|
2016-08-12 11:51:16 +03:00
|
|
|
}
|
|
|
|
|
2016-08-12 15:32:42 +03:00
|
|
|
- (NSArray <NeoVimBuffer *> *)buffers {
|
2017-01-04 19:38:18 +03:00
|
|
|
NSData *response = [self sendMessageWithId:NeoVimAgentMsgIdGetBuffers data:nil expectsReply:YES];
|
|
|
|
if (response == nil) {
|
2016-08-13 12:20:03 +03:00
|
|
|
log4Warn("The response for the msg %lu was nil.", NeoVimAgentMsgIdGetBuffers);
|
|
|
|
return @[];
|
|
|
|
}
|
|
|
|
|
2017-01-04 19:38:18 +03:00
|
|
|
return [NSKeyedUnarchiver unarchiveObjectWithData:response];
|
2016-08-12 15:32:42 +03:00
|
|
|
}
|
|
|
|
|
2016-10-22 23:20:22 +03:00
|
|
|
- (NSArray<NeoVimWindow *> *)tabs {
|
2017-01-04 19:38:28 +03:00
|
|
|
NSData *response = [self sendMessageWithId:NeoVimAgentMsgIdGetTabs data:nil expectsReply:YES];
|
|
|
|
if (response == nil) {
|
2016-10-22 23:20:22 +03:00
|
|
|
log4Warn("The response for the msg %lu was nil.", NeoVimAgentMsgIdGetTabs);
|
|
|
|
return @[];
|
|
|
|
}
|
|
|
|
|
2017-01-04 19:38:28 +03:00
|
|
|
return [NSKeyedUnarchiver unarchiveObjectWithData:response];
|
2016-10-22 23:20:22 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 15:04:09 +03:00
|
|
|
- (void)runLocalServer {
|
|
|
|
@autoreleasepool {
|
|
|
|
CFMessagePortContext localContext = {
|
|
|
|
.version = 0,
|
|
|
|
.info = (__bridge void *) self,
|
|
|
|
.retain = NULL,
|
|
|
|
.release = NULL,
|
|
|
|
.copyDescription = NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
unsigned char shouldFreeLocalServer = false;
|
|
|
|
_localServerPort = CFMessagePortCreateLocal(
|
|
|
|
kCFAllocatorDefault,
|
|
|
|
(__bridge CFStringRef) [self localServerName],
|
|
|
|
local_server_callback,
|
|
|
|
&localContext,
|
|
|
|
&shouldFreeLocalServer
|
|
|
|
);
|
|
|
|
|
|
|
|
// FIXME: handle shouldFreeLocalServer = true
|
|
|
|
}
|
2016-07-10 15:10:09 +03:00
|
|
|
|
2016-07-18 21:16:56 +03:00
|
|
|
_localServerRunLoop = CFRunLoopGetCurrent();
|
2016-07-10 15:10:09 +03:00
|
|
|
CFRunLoopSourceRef runLoopSrc = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, _localServerPort, 0);
|
2016-07-18 21:16:56 +03:00
|
|
|
CFRunLoopAddSource(_localServerRunLoop, runLoopSrc, kCFRunLoopCommonModes);
|
2016-07-10 15:10:09 +03:00
|
|
|
CFRelease(runLoopSrc);
|
|
|
|
CFRunLoopRun();
|
2016-07-09 15:04:09 +03:00
|
|
|
}
|
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
- (void)establishNeoVimConnection {
|
|
|
|
_remoteServerPort = CFMessagePortCreateRemote(
|
|
|
|
kCFAllocatorDefault,
|
|
|
|
(__bridge CFStringRef) [self remoteServerName]
|
|
|
|
);
|
|
|
|
|
2016-08-13 12:20:03 +03:00
|
|
|
[self sendMessageWithId:NeoVimAgentMsgIdAgentReady data:nil expectsReply:NO];
|
2016-07-09 23:52:59 +03:00
|
|
|
}
|
|
|
|
|
2016-08-12 11:51:16 +03:00
|
|
|
- (NSData *)sendMessageWithId:(NeoVimAgentMsgId)msgid data:(NSData *)data expectsReply:(bool)expectsReply {
|
2017-01-03 18:21:04 +03:00
|
|
|
if (_neoVimIsQuitting == 1 && msgid != NeoVimAgentMsgIdQuit) {
|
|
|
|
// This happens often, e.g. when exiting full screen by closing all buffers. We try to resize the window after
|
|
|
|
// the message port has been closed. This is a quick-and-dirty fix.
|
|
|
|
// TODO: Fix for real...
|
2017-01-06 12:27:08 +03:00
|
|
|
log4Warn("Neovim is quitting, but trying to send message: %lu", (unsigned long) msgid);
|
2017-01-03 18:21:04 +03:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
if (_remoteServerPort == NULL) {
|
2016-08-13 12:20:03 +03:00
|
|
|
log4Warn("Remote server is null: The msg %lu with data %@ could not be sent.", (unsigned long) msgid, data);
|
2016-07-18 21:16:56 +03:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
CFDataRef responseData = NULL;
|
2016-08-12 16:16:43 +03:00
|
|
|
CFStringRef replyMode = expectsReply ? kCFRunLoopDefaultMode : NULL;
|
2016-07-09 23:52:59 +03:00
|
|
|
|
|
|
|
SInt32 responseCode = CFMessagePortSendRequest(
|
2016-07-18 21:16:56 +03:00
|
|
|
_remoteServerPort, msgid, (__bridge CFDataRef) data, qTimeout, qTimeout, replyMode, &responseData
|
2016-07-09 23:52:59 +03:00
|
|
|
);
|
|
|
|
|
2016-12-14 21:33:15 +03:00
|
|
|
if (msgid == NeoVimAgentMsgIdQuit || _neoVimIsQuitting == 1) {
|
2016-12-07 22:24:25 +03:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2016-07-18 21:16:56 +03:00
|
|
|
if (responseCode != kCFMessagePortSuccess) {
|
2016-11-24 21:46:41 +03:00
|
|
|
log_cfmachport_error(responseCode, msgid, data);
|
2016-12-07 22:24:25 +03:00
|
|
|
|
2016-12-14 22:49:56 +03:00
|
|
|
if (_neoVimIsQuitting == 0) {
|
|
|
|
[_bridge ipcBecameInvalid:
|
2017-01-06 12:27:08 +03:00
|
|
|
[NSString stringWithFormat:
|
|
|
|
@"Reason: sending msg to neovim failed for %lu with %d", (unsigned long) msgid, responseCode
|
|
|
|
]
|
2016-12-14 22:49:56 +03:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2016-07-18 21:16:56 +03:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (responseData == NULL) {
|
|
|
|
return nil;
|
2016-07-09 23:52:59 +03:00
|
|
|
}
|
|
|
|
|
2016-09-04 00:13:52 +03:00
|
|
|
NSData *result = (__bridge_transfer NSData *) responseData;
|
|
|
|
return result;
|
2016-07-09 23:52:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)neoVimServerExecutablePath {
|
|
|
|
return [[[NSBundle bundleForClass:[self class]] builtInPlugInsPath] stringByAppendingPathComponent:@"NeoVimServer"];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)localServerName {
|
2016-07-12 09:13:40 +03:00
|
|
|
return [NSString stringWithFormat:@"com.qvacua.vimr.%@", _uuid];
|
2016-07-09 23:52:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)remoteServerName {
|
2016-07-12 09:13:40 +03:00
|
|
|
return [NSString stringWithFormat:@"com.qvacua.vimr.neovim-server.%@", _uuid];
|
2016-07-09 23:52:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handleMessageWithId:(SInt32)msgid data:(NSData *)data {
|
2016-07-09 15:04:09 +03:00
|
|
|
switch (msgid) {
|
|
|
|
|
|
|
|
case NeoVimServerMsgIdServerReady:
|
2016-07-09 23:52:59 +03:00
|
|
|
[self establishNeoVimConnection];
|
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-08-15 11:56:19 +03:00
|
|
|
case NeoVimServerMsgIdNeoVimReady: {
|
2016-08-25 23:52:31 +03:00
|
|
|
bool *value = data_to_bool_array(data, 1);
|
|
|
|
_isInitErrorPresent = value[0];
|
2016-08-21 18:32:35 +03:00
|
|
|
|
2016-12-14 21:33:15 +03:00
|
|
|
[_neoVimReadyCondition lock];
|
2016-08-25 23:52:31 +03:00
|
|
|
_neoVimIsReady = YES;
|
2016-12-14 21:33:15 +03:00
|
|
|
[_neoVimReadyCondition signal];
|
|
|
|
[_neoVimReadyCondition unlock];
|
2016-08-21 18:32:35 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
return;
|
2016-08-15 11:56:19 +03:00
|
|
|
}
|
2016-07-09 23:52:59 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdResize: {
|
|
|
|
int *values = data_to_int_array(data, 2);
|
|
|
|
if (values == nil) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
[_bridge resizeToWidth:values[0] height:values[1]];
|
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdClear:
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge clear];
|
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdEolClear:
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge eolClear];
|
|
|
|
return;
|
|
|
|
|
|
|
|
case NeoVimServerMsgIdSetPosition: {
|
2017-01-07 17:53:32 +03:00
|
|
|
int *values = data_to_int_array(data, 6);
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge gotoPosition:(Position) { .row = values[0], .column = values[1] }
|
2017-01-07 17:53:32 +03:00
|
|
|
screenCursor:(Position) { .row = values[2], .column = values[3] }
|
|
|
|
currentPosition:(Position) { .row = values[4], .column = values[5] }];
|
2016-07-09 23:52:59 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdSetMenu:
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge updateMenu];
|
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdBusyStart:
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge busyStart];
|
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdBusyStop:
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge busyStop];
|
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdMouseOn:
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge mouseOn];
|
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdMouseOff:
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge mouseOff];
|
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdModeChange: {
|
|
|
|
int *values = data_to_int_array(data, 1);
|
2016-08-03 22:30:41 +03:00
|
|
|
[_bridge modeChange:(Mode) values[0]];
|
2016-07-09 23:52:59 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdSetScrollRegion: {
|
|
|
|
int *values = data_to_int_array(data, 4);
|
|
|
|
[_bridge setScrollRegionToTop:values[0] bottom:values[1] left:values[2] right:values[3]];
|
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdScroll: {
|
|
|
|
int *values = data_to_int_array(data, 1);
|
|
|
|
[_bridge scroll:values[0]];
|
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdSetHighlightAttributes: {
|
|
|
|
CellAttributes *values = data_to_CellAttributes_array(data, 1);
|
|
|
|
[_bridge highlightSet:values[0]];
|
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-08-15 21:30:44 +03:00
|
|
|
case NeoVimServerMsgIdPut:
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdPutMarked: {
|
2016-08-15 21:30:44 +03:00
|
|
|
int *values = (int *) data.bytes;
|
|
|
|
int row = values[0];
|
|
|
|
int column = values[1];
|
|
|
|
|
|
|
|
NSString *string = [[NSString alloc] initWithBytes:(values + 2)
|
|
|
|
length:data.length - 2 * sizeof(int)
|
|
|
|
encoding:NSUTF8StringEncoding];
|
|
|
|
|
|
|
|
if (msgid == NeoVimServerMsgIdPut) {
|
|
|
|
[_bridge put:string screenCursor:(Position) { .row=row, .column=column }];
|
|
|
|
} else {
|
|
|
|
[_bridge putMarkedText:string screenCursor:(Position) { .row=row, .column=column }];
|
|
|
|
}
|
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdUnmark: {
|
|
|
|
int *values = data_to_int_array(data, 2);
|
|
|
|
[_bridge unmarkRow:values[0] column:values[1]];
|
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdBell:
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge bell];
|
|
|
|
return;
|
|
|
|
|
|
|
|
case NeoVimServerMsgIdVisualBell:
|
|
|
|
[_bridge visualBell];
|
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
|
|
|
case NeoVimServerMsgIdFlush:
|
2016-07-09 23:52:59 +03:00
|
|
|
[_bridge flush];
|
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdSetForeground: {
|
2016-10-04 20:26:15 +03:00
|
|
|
int *values = data_to_int_array(data, 1);
|
|
|
|
[_bridge updateForeground:values[0]];
|
2016-07-09 23:52:59 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdSetBackground: {
|
2016-10-04 20:26:15 +03:00
|
|
|
int *values = data_to_int_array(data, 1);
|
|
|
|
[_bridge updateBackground:values[0]];
|
2016-07-09 23:52:59 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdSetSpecial: {
|
2016-10-04 20:26:15 +03:00
|
|
|
int *values = data_to_int_array(data, 1);
|
|
|
|
[_bridge updateSpecial:values[0]];
|
2016-07-09 23:52:59 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdSetTitle: {
|
|
|
|
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
|
|
[_bridge setTitle:string];
|
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-07-09 23:52:59 +03:00
|
|
|
case NeoVimServerMsgIdSetIcon: {
|
|
|
|
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
|
|
[_bridge setIcon:string];
|
|
|
|
return;
|
|
|
|
}
|
2016-07-09 15:04:09 +03:00
|
|
|
|
2016-11-20 23:53:22 +03:00
|
|
|
case NeoVimServerMsgIdStop:
|
|
|
|
[_bridge stop];
|
|
|
|
return;
|
|
|
|
|
2017-01-07 18:06:39 +03:00
|
|
|
case NeoVimServerMsgIdDirtyStatusChanged: {
|
|
|
|
bool *values = data_to_bool_array(data, 1);
|
|
|
|
[_bridge setDirtyStatus:values[0]];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-19 23:31:16 +03:00
|
|
|
case NeoVimServerMsgIdAutoCommandEvent: {
|
2016-12-20 01:57:42 +03:00
|
|
|
if (data.length == sizeof(NSUInteger) + sizeof(NSInteger)) {
|
|
|
|
NSUInteger *values = (NSUInteger *) data.bytes;
|
|
|
|
NeoVimAutoCommandEvent event = (NeoVimAutoCommandEvent) values[0];
|
|
|
|
NSInteger bufferHandle = ((NSInteger *)(values + 1))[0];
|
|
|
|
[_bridge autoCommandEvent:event bufferHandle:bufferHandle];
|
|
|
|
} else {
|
|
|
|
NSUInteger *values = data_to_NSUInteger_array(data, 1);
|
|
|
|
[_bridge autoCommandEvent:(NeoVimAutoCommandEvent) values[0] bufferHandle:-1];
|
|
|
|
}
|
2016-08-19 00:11:33 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-09 15:04:09 +03:00
|
|
|
default:
|
2016-07-09 23:52:59 +03:00
|
|
|
return;
|
2016-07-09 15:04:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|