1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-12-26 07:13:24 +03:00

GH-302, GH-421 Add passthrough arguments to vimr script

This commit is contained in:
Tae Won Ha 2017-06-12 00:30:33 +02:00
parent 8241d596fd
commit 3216b08699
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
13 changed files with 173 additions and 64 deletions

View File

@ -10,7 +10,9 @@
@interface NeoVimServer : NSObject
- (instancetype)initWithLocalServerName:(NSString *)localServerName remoteServerName:(NSString *)remoteServerName;
- (instancetype)initWithLocalServerName:(NSString *)localServerName
remoteServerName:(NSString *)remoteServerName
nvimArgs:(NSArray<NSString *> *)nvimArgs;
- (void)sendMessageWithId:(NeoVimServerMsgId)msgid;
- (void)sendMessageWithId:(NeoVimServerMsgId)msgid data:(NSData *)data;

View File

@ -29,6 +29,7 @@ static const double qTimeout = 10;
@interface NeoVimServer ()
- (NSArray<NSString *> *)nvimArgs;
- (NSCondition *)outputCondition;
- (void)handleQuitMsg;
@ -61,7 +62,7 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
switch (msgid) {
case NeoVimAgentMsgIdAgentReady:
start_neovim();
start_neovim(neoVimServer.nvimArgs);
return NULL;
case NeoVimAgentMsgIdQuit:
@ -110,6 +111,7 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
@implementation NeoVimServer {
NSString *_localServerName;
NSString *_remoteServerName;
NSArray<NSString *> *_nvimArgs;
CFMessagePortRef _remoteServerPort;
@ -120,11 +122,18 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
NSCondition *_outputCondition;
}
- (NSArray<NSString *> *)nvimArgs {
return _nvimArgs;
}
- (NSCondition *)outputCondition {
return _outputCondition;
}
- (instancetype)initWithLocalServerName:(NSString *)localServerName remoteServerName:(NSString *)remoteServerName {
- (instancetype)initWithLocalServerName:(NSString *)localServerName
remoteServerName:(NSString *)remoteServerName
nvimArgs:(NSArray<NSString*> *)nvimArgs {
self = [super init];
if (self == nil) {
return nil;
@ -134,6 +143,7 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
_localServerName = localServerName;
_remoteServerName = remoteServerName;
_nvimArgs = nvimArgs;
_localServerThread = [[NSThread alloc] initWithTarget:self selector:@selector(runLocalServer) object:nil];
_localServerThread.name = localServerName;

View File

@ -19,7 +19,7 @@ void observe_parent_termination() {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t source = dispatch_source_create(
DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) parentPID, DISPATCH_PROC_EXIT, queue
DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) parentPID, DISPATCH_PROC_EXIT, queue
);
if (source == NULL) {
@ -44,10 +44,14 @@ int main(int argc, const char *argv[]) {
NSArray<NSString *> *arguments = [NSProcessInfo processInfo].arguments;
NSString *remoteServerName = arguments[1];
NSString *localServerName = arguments[2];
NSArray<NSString *> *nvimArgs = argc > 3 ? [arguments subarrayWithRange:NSMakeRange(3, (NSUInteger) (argc - 3))]
: nil;
_neovim_server = [[NeoVimServer alloc] initWithLocalServerName:localServerName remoteServerName:remoteServerName];
DLOG("Started neovim server '%s' and connected it with the remote agent '%s'.",
localServerName.cstr, remoteServerName.cstr);
_neovim_server = [[NeoVimServer alloc] initWithLocalServerName:localServerName
remoteServerName:remoteServerName
nvimArgs:nvimArgs];
DLOG("Started neovim server '%s' with args '%@' and connected it with the remote agent '%s'.",
localServerName.cstr, nvimArgs, remoteServerName.cstr);
[_neovim_server notifyReadiness];
}

View File

@ -11,7 +11,7 @@
extern NeoVimServer *_neovim_server;
extern CFRunLoopRef _mainRunLoop;
extern void start_neovim();
extern void start_neovim(NSArray<NSString *> *args);
extern void quit_neovim();
extern void neovim_select_window(void **argv);

View File

@ -166,10 +166,27 @@ static void delete_marked_text() {
}
static void run_neovim(void *arg __unused) {
char *argv[1];
argv[0] = "nvim";
int argc = 1;
char **argv;
nvim_main(1, argv);
@autoreleasepool {
NSArray<NSString *> *nvimArgs = (NSArray *) arg;
NSLog(@"%@", nvimArgs);
argc = (int) nvimArgs.count + 1;
argv = (char **) malloc((argc + 1) * sizeof(char *));
argv[0] = "nvim";
for (int i = 0; i < nvimArgs.count; i++) {
argv[i + 1] = (char *) nvimArgs[(NSUInteger) i].cstr;
}
[nvimArgs release]; // retained in start_neovim()
}
nvim_main(argc, argv);
free(argv);
}
static void set_ui_size(UIBridgeData *bridge, int width, int height) {
@ -543,7 +560,7 @@ void custom_ui_autocmds_groups(
}
#pragma mark Other help functions
void start_neovim() {
void start_neovim(NSArray<NSString *> *args) {
// set $VIMRUNTIME to ${RESOURCE_PATH_OF_XPC_BUNDLE}/runtime
NSString *bundlePath = [NSBundle bundleForClass:[NeoVimServer class]].bundlePath;
NSString *resourcesPath = [bundlePath.stringByDeletingLastPathComponent
@ -557,7 +574,7 @@ void start_neovim() {
uv_mutex_init(&_mutex);
uv_cond_init(&_condition);
uv_thread_create(&_nvim_thread, run_neovim, NULL);
uv_thread_create(&_nvim_thread, run_neovim, [args retain]); // released in run_neovim()
DLOG("NeoVim started");
// continue only after our UI main code for neovim has been fully initialized

View File

@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface NeoVimAgent : NSObject
@property (nonatomic) bool useInteractiveZsh;
@property (nonatomic) NSURL *cwd;
@property (nonatomic, nullable) NSArray<NSString *> *nvimArgs;
@property (readonly, atomic) bool neoVimIsQuitting;
@property (nonatomic, weak) id <NeoVimUiBridgeProtocol> bridge;

View File

@ -23,8 +23,11 @@ static type *data_to_ ## type ## _array(NSData *data, NSUInteger count) { \
}
data_to_array(NSUInteger)
data_to_array(NSInteger)
data_to_array(bool)
data_to_array(CellAttributes)
static void log_cfmachport_error(SInt32 err, NeoVimAgentMsgId msgid, NSData *inputData) {
@ -168,7 +171,7 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
#endif
_neoVimServerTask.standardInput = inputPipe;
_neoVimServerTask.currentDirectoryPath = NSHomeDirectory();
_neoVimServerTask.currentDirectoryPath = self.cwd == nil ? NSHomeDirectory() : self.cwd.path;
_neoVimServerTask.launchPath = shellPath;
_neoVimServerTask.arguments = shellArgs;
[_neoVimServerTask launch];
@ -177,6 +180,15 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
[self neoVimServerExecutablePath],
[self localServerName],
[self remoteServerName]];
if (self.nvimArgs != nil) {
NSMutableArray *args = [NSMutableArray new];
for (NSString *arg in self.nvimArgs) {
[args addObject:[NSString stringWithFormat:@"'%@'", arg]];
}
NSLog(@"mapped %@", args);
cmd = [cmd stringByAppendingFormat:@" %@", [args componentsJoinedByString:@" "]];
NSLog(@"%@", cmd);
}
NSFileHandle *writeHandle = inputPipe.fileHandleForWriting;
[writeHandle writeData:[cmd dataUsingEncoding:NSUTF8StringEncoding]];
@ -252,13 +264,13 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
}
- (void)resizeToWidth:(int)width height:(int)height {
int values[] = { width, height };
int values[] = {width, height};
NSData *data = [[NSData alloc] initWithBytes:values length:(2 * sizeof(int))];
[self sendMessageWithId:NeoVimAgentMsgIdResize data:data expectsReply:NO];
}
- (void)cursorGoToRow:(int)row column:(int)column {
int values[] = { row, column };
int values[] = {row, column};
NSData *data = [[NSData alloc] initWithBytes:values length:(2 * sizeof(int))];
[self sendMessageWithId:NeoVimAgentMsgIdCursorGoto data:data expectsReply:NO];
}
@ -275,7 +287,7 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
}
- (NSString *)escapedFileName:(NSString *)fileName {
NSArray<NSString *> *fileNames = [self escapedFileNames:@[ fileName ]];
NSArray<NSString *> *fileNames = [self escapedFileNames:@[fileName]];
if (fileNames.count == 0) {
return nil;
}
@ -296,7 +308,7 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
- (void)setBoolOption:(NSString *)option to:(bool)value {
NSMutableData *data = [NSMutableData new];
bool values[] = { value };
bool values[] = {value};
const char *cstr = [option cStringUsingEncoding:NSUTF8StringEncoding];
[data appendBytes:values length:sizeof(bool)];
@ -306,18 +318,18 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
}
- (void)scrollHorizontal:(NSInteger)horiz vertical:(NSInteger)vert {
NSInteger values[] = { horiz, vert };
NSInteger values[] = {horiz, vert};
NSData *data = [[NSData alloc] initWithBytes:values length:2 * sizeof(NSInteger)];
[self sendMessageWithId:NeoVimAgentMsgIdScroll data:data expectsReply:NO];
}
- (void)selectWindow:(NeoVimWindow *)window {
int values[] = { (int) window.handle };
int values[] = {(int) window.handle};
NSData *data = [[NSData alloc] initWithBytes:values length:sizeof(int)];
[self sendMessageWithId:NeoVimAgentMsgIdSelectWindow data:data expectsReply:NO];
}
- (NSArray <NSString *>*)escapedFileNames:(NSArray <NSString *>*)fileNames {
- (NSArray <NSString *> *)escapedFileNames:(NSArray <NSString *> *)fileNames {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:fileNames];
NSData *response = [self sendMessageWithId:NeoVimAgentMsgIdGetEscapeFileNames data:data expectsReply:YES];
if (response == nil) {
@ -416,9 +428,9 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
if (_neoVimIsQuitting == 0) {
[_bridge ipcBecameInvalid:
[NSString stringWithFormat:
@"Reason: sending msg to neovim failed for %lu with %d", (unsigned long) msgid, responseCode
]
[NSString stringWithFormat:
@"Reason: sending msg to neovim failed for %lu with %d", (unsigned long) msgid, responseCode
]
];
}
@ -483,8 +495,8 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
case NeoVimServerMsgIdSetPosition: {
NSInteger *values = data_to_NSInteger_array(data, 4);
[_bridge gotoPosition:(Position) { .row = values[0], .column = values[1] }
textPosition:(Position) { .row = values[2], .column = values[3] }];
[_bridge gotoPosition:(Position) {.row = values[0], .column = values[1]}
textPosition:(Position) {.row = values[2], .column = values[3]}];
return;
}
@ -617,7 +629,7 @@ static CFDataRef local_server_callback(CFMessagePortRef local, SInt32 msgid, CFD
if (data.length == sizeof(NSUInteger) + sizeof(NSInteger)) {
NSUInteger *values = (NSUInteger *) data.bytes;
NeoVimAutoCommandEvent event = (NeoVimAutoCommandEvent) values[0];
NSInteger bufferHandle = ((NSInteger *)(values + 1))[0];
NSInteger bufferHandle = ((NSInteger *) (values + 1))[0];
[_bridge autoCommandEvent:event bufferHandle:bufferHandle];
} else {
NSUInteger *values = data_to_NSUInteger_array(data, 1);

View File

@ -14,9 +14,16 @@ public class NeoVimView: NSView,
public struct Config {
var useInteractiveZsh: Bool
var cwd: URL
var nvimArgs: [String]?
public init(useInteractiveZsh: Bool,
cwd: URL = URL(fileURLWithPath: NSHomeDirectory()),
nvimArgs: [String]? = nil) {
public init(useInteractiveZsh: Bool) {
self.useInteractiveZsh = useInteractiveZsh
self.cwd = cwd
self.nvimArgs = nvimArgs
}
}
@ -115,6 +122,8 @@ public class NeoVimView: NSView,
// We cannot set bridge in init since self is not available before super.init()...
self.agent.bridge = self
self.agent.useInteractiveZsh = config.useInteractiveZsh
self.agent.cwd = config.cwd
self.agent.nvimArgs = config.nvimArgs
self.launchNeoVim()
}

View File

@ -8,23 +8,12 @@ import RxSwift
import PureLayout
import Sparkle
/// Keep the rawValues in sync with Action in the `vimr` Python script.
fileprivate enum VimRUrlAction: String {
case activate = "activate"
case open = "open"
case newWindow = "open-in-new-window"
case separateWindows = "open-in-separate-windows"
}
fileprivate let filePrefix = "file="
fileprivate let cwdPrefix = "cwd="
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
enum Action {
case newMainWindow(urls: [URL], cwd: URL)
case newMainWindow(urls: [URL], cwd: URL, nvimArgs: [String]?)
case openInKeyWindow(urls: [URL], cwd: URL)
case preferences
@ -47,7 +36,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
}
initialAppState.mainWindowTemplate.htmlPreview.server
= Marked(baseServerUrl.appendingPathComponent(HtmlPreviewToolReducer.selectFirstPath))
= Marked(baseServerUrl.appendingPathComponent(HtmlPreviewToolReducer.selectFirstPath))
self.stateContext = Context(baseServerUrl: baseServerUrl, state: initialAppState)
self.emit = self.stateContext.actionEmitter.typedEmit()
@ -172,7 +161,7 @@ extension AppDelegate {
// For drag & dropping files on the App icon.
func application(_ sender: NSApplication, openFiles filenames: [String]) {
let urls = filenames.map { URL(fileURLWithPath: $0) }
self.emit(.newMainWindow(urls: urls, cwd: FileUtils.userHomeUrl))
self.emit(.newMainWindow(urls: urls, cwd: FileUtils.userHomeUrl, nvimArgs: nil))
sender.reply(toOpenOrPrint: .success)
}
@ -216,13 +205,24 @@ extension AppDelegate {
switch action {
case .activate, .newWindow:
self.emit(.newMainWindow(urls: urls, cwd: cwd))
self.emit(.newMainWindow(urls: urls, cwd: cwd, nvimArgs: nil))
case .open:
self.emit(.openInKeyWindow(urls: urls, cwd: cwd))
case .separateWindows:
urls.forEach { self.emit(.newMainWindow(urls: [$0], cwd: cwd)) }
urls.forEach { self.emit(.newMainWindow(urls: [$0], cwd: cwd, nvimArgs: nil)) }
case .nvim:
guard let nvimArgs = queryParams?
.filter({ $0.hasPrefix(nvimArgsPrefix) })
.flatMap({ $0.without(prefix: nvimArgsPrefix).removingPercentEncoding }) else {
break
}
NSLog("app delegate: \(nvimArgs)")
self.emit(.newMainWindow(urls: [], cwd: cwd, nvimArgs: nvimArgs))
}
}
@ -232,7 +232,7 @@ extension AppDelegate {
extension AppDelegate {
@IBAction func newDocument(_ sender: Any?) {
self.emit(.newMainWindow(urls: [], cwd: FileUtils.userHomeUrl))
self.emit(.newMainWindow(urls: [], cwd: FileUtils.userHomeUrl, nvimArgs: nil))
}
@IBAction func openInNewWindow(_ sender: Any?) {
@ -256,7 +256,21 @@ extension AppDelegate {
let urls = panel.urls
let commonParentUrl = FileUtils.commonParent(of: urls)
self.emit(.newMainWindow(urls: urls, cwd: commonParentUrl))
self.emit(.newMainWindow(urls: urls, cwd: commonParentUrl, nvimArgs: nil))
}
}
}
/// Keep the rawValues in sync with Action in the `vimr` Python script.
fileprivate enum VimRUrlAction: String {
case activate = "activate"
case open = "open"
case newWindow = "open-in-new-window"
case separateWindows = "open-in-separate-windows"
case nvim = "nvim"
}
fileprivate let filePrefix = "file="
fileprivate let cwdPrefix = "cwd="
fileprivate let nvimArgsPrefix = "nvim-args="

View File

@ -18,8 +18,14 @@ class AppDelegateReducer {
switch pair.action {
case let .newMainWindow(urls, cwd):
let mainWindow = self.newMainWindow(with: state, urls: urls, cwd: cwd)
case let .newMainWindow(urls, cwd, nvimArgs):
let mainWindow: MainWindow.State
if let args = nvimArgs {
mainWindow = self.newMainWindow(with: state, urls: [], cwd: cwd, nvimArgs: args)
} else {
mainWindow = self.newMainWindow(with: state, urls: urls, cwd: cwd)
}
state.mainWindows[mainWindow.uuid] = mainWindow
case let .openInKeyWindow(urls, cwd):
@ -42,7 +48,11 @@ class AppDelegateReducer {
fileprivate let baseServerUrl: URL
fileprivate func newMainWindow(with state: AppState, urls: [URL], cwd: URL) -> MainWindow.State {
fileprivate func newMainWindow(with state: AppState,
urls: [URL],
cwd: URL,
nvimArgs: [String]? = nil) -> MainWindow.State {
var mainWindow = state.mainWindowTemplate
mainWindow.uuid = UUID().uuidString
mainWindow.isDirty = false
@ -50,6 +60,7 @@ class AppDelegateReducer {
htmlFile: nil,
server: Marked(self.baseServerUrl.appendingPathComponent(HtmlPreviewToolReducer.selectFirstPath))
)
mainWindow.nvimArgs = nvimArgs
mainWindow.urlsToOpen = urls.toDict { url in MainWindow.OpenMode.default }

View File

@ -79,8 +79,10 @@ class MainWindow: NSObject,
self.editorPosition = state.preview.editorPosition
self.previewPosition = state.preview.previewPosition
self.neoVimView = NeoVimView(frame: CGRect.zero,
config: NeoVimView.Config(useInteractiveZsh: state.useInteractiveZsh))
let neoVimViewConfig = NeoVimView.Config(useInteractiveZsh: state.useInteractiveZsh,
cwd: state.cwd,
nvimArgs: state.nvimArgs)
self.neoVimView = NeoVimView(frame: .zero, config: neoVimViewConfig)
self.neoVimView.configureForAutoLayout()
let workspace = Workspace(mainView: self.neoVimView)

View File

@ -216,6 +216,7 @@ extension MainWindow {
var appearance = AppearanceState.default
var useInteractiveZsh = false
var nvimArgs: [String]?
// to be cleaned
var urlsToOpen = [URL: OpenMode]()

View File

@ -4,6 +4,7 @@ import urllib
import subprocess
import argparse
import os
from pprint import pprint
def call_open(arg, dry_run=False):
@ -17,9 +18,21 @@ def abspath(path):
return os.path.abspath(os.path.expanduser(path))
def vimr(action, args=None, dry_run=False):
files = args.file
def vimr_nvim(args=None, dry_run=False):
cwd = os.getcwd()
query_params = {
"cwd": cwd
}
if args:
query_params["nvim-args"] = args
url = "vimr://{0}?{1}".format(action, urllib.urlencode(query_params, True).replace('+', '%20'))
call_open(url, dry_run)
def vimr(action, args=None, dry_run=False):
cwd = os.getcwd()
if args.cwd is not None:
cwd = abspath(args.cwd)
@ -28,6 +41,7 @@ def vimr(action, args=None, dry_run=False):
"cwd": cwd
}
files = args.file
if files:
query_params["file"] = [abspath(f) for f in files]
@ -40,6 +54,7 @@ class Action:
OPEN = "open"
NEW_WINDOW = "open-in-new-window"
SEPARATE_WINDOWS = "open-in-separate-windows"
NVIM = "nvim"
description = """
Open files in VimR: By default all files are open in tabs in the front most window or in a new window if there is none.
@ -50,21 +65,31 @@ parser = argparse.ArgumentParser(description=description)
parser.add_argument("--dry-run", action="store_true", dest="dry_run", help="Just print the 'open' command.")
parser.add_argument("--cwd", action="store", help="Set the working directory.")
parser.add_argument("--nvim", action="store_true", help="All other arguments will be passthrough to nvim.")
group = parser.add_mutually_exclusive_group()
# no option => Open files in tabs in the front most window.
group.add_argument("-n", action="store_true", dest="new_window", help="Open files in tabs in a new window.")
group.add_argument("-s", action="store_true", dest="separate_windows", help="Open files in separate windows.")
parser.add_argument("file", nargs="*")
args = parser.parse_args()
args, _ = parser.parse_known_args()
dry_run = args.dry_run
if not args.file:
action = Action.ACTIVATE
elif args.new_window:
action = Action.NEW_WINDOW
elif args.separate_windows:
action = Action.SEPARATE_WINDOWS
else:
action = Action.OPEN
if args.nvim is not None:
action = Action.NVIM
nvim_parser = argparse.ArgumentParser()
nvim_parser.add_argument("--nvim", action="store_true")
nvim_parser.add_argument("--dry-run", action="store_true")
_, nvim_args = nvim_parser.parse_known_args()
vimr_nvim(nvim_args, dry_run)
vimr(action, args, dry_run)
else:
if not args.file:
action = Action.ACTIVATE
elif args.new_window:
action = Action.NEW_WINDOW
elif args.separate_windows:
action = Action.SEPARATE_WINDOWS
else:
action = Action.OPEN
vimr(action, args, dry_run)