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

GH-625 Pass env vars from vimr to UI

- Use a temporary json file
This commit is contained in:
Tae Won Ha 2018-07-03 21:17:14 +02:00
parent 8504bdbba8
commit c50439d660
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
7 changed files with 166 additions and 88 deletions

View File

@ -17,14 +17,17 @@ public class NvimView: NSView,
var useInteractiveZsh: Bool
var cwd: URL
var nvimArgs: [String]?
var envDict: [String: String]?
public init(useInteractiveZsh: Bool,
cwd: URL = URL(fileURLWithPath: NSHomeDirectory()),
nvimArgs: [String]? = nil) {
cwd: URL,
nvimArgs: [String]?,
envDict: [String: String]?) {
self.useInteractiveZsh = useInteractiveZsh
self.cwd = cwd
self.nvimArgs = nvimArgs
self.envDict = envDict
}
}
@ -310,7 +313,10 @@ public class NvimView: NSView,
}
convenience override public init(frame rect: NSRect) {
self.init(frame: rect, config: Config(useInteractiveZsh: false))
self.init(frame: rect, config: Config(useInteractiveZsh: false,
cwd: URL(fileURLWithPath: NSHomeDirectory()),
nvimArgs: nil,
envDict: nil))
}
required public init?(coder: NSCoder) {

View File

@ -64,6 +64,17 @@ class UiBridge {
self.nvimArgs = config.nvimArgs ?? []
self.cwd = config.cwd
if let envDict = config.envDict {
self.envDict = envDict
logger.debug("using envs from vimr: \(envDict)")
} else {
let selfEnv = ProcessInfo.processInfo.environment
let shellUrl = URL(fileURLWithPath: selfEnv["SHELL"] ?? "/bin/bash")
let interactiveMode = shellUrl.lastPathComponent == "zsh" && !config.useInteractiveZsh ? false : true
self.envDict = ProcessUtils.envVars(of: shellUrl, usingInteractiveMode: interactiveMode)
logger.debug("using envs from login shell: \(self.envDict)")
}
self.queue = queue
self.scheduler = SerialDispatchQueueScheduler(queue: queue,
internalSerialQueueName: String(reflecting: UiBridge.self))
@ -364,13 +375,8 @@ class UiBridge {
}
private func launchNvimUsingLoginShellEnv() {
let selfEnv = ProcessInfo.processInfo.environment
let shellUrl = URL(fileURLWithPath: selfEnv["SHELL"] ?? "/bin/bash")
let interactiveMode = shellUrl.lastPathComponent == "zsh" && !self.useInteractiveZsh ? false : true
var env = ProcessUtils.envVars(of: shellUrl, usingInteractiveMode: interactiveMode)
let listenAddress = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("vimr_\(self.uuid).sock")
var env = self.envDict
env["NVIM_LISTEN_ADDRESS"] = listenAddress.path
let outPipe = Pipe()
@ -401,7 +407,8 @@ class UiBridge {
private let useInteractiveZsh: Bool
private let cwd: URL
private var nvimArgs: [String]
private let nvimArgs: [String]
private let envDict: [String: String]
private let server = RxMessagePortServer()
private let client = RxMessagePortClient()

View File

@ -14,7 +14,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
enum Action {
case newMainWindow(urls: [URL], cwd: URL, nvimArgs: [String]?, cliPipePath: String?)
case newMainWindow(urls: [URL], cwd: URL, nvimArgs: [String]?, cliPipePath: String?, envDict: [String: String]?)
case openInKeyWindow(urls: [URL], cwd: URL, cliPipePath: String?)
case preferences
@ -188,7 +188,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, nvimArgs: nil, cliPipePath: nil))
self.emit(.newMainWindow(urls: urls, cwd: FileUtils.userHomeUrl, nvimArgs: nil, cliPipePath: nil, envDict: nil))
sender.reply(toOpenOrPrint: .success)
}
@ -242,6 +242,20 @@ extension AppDelegate {
return
}
let envDict: [String: String]?
if let envPath = queryParam(envPathPrefix, from: rawParams, transforming: identity).first {
envDict = stringDict(from: URL(fileURLWithPath: envPath))
if FileManager.default.fileExists(atPath: envPath) {
do {
try FileManager.default.removeItem(atPath: envPath)
} catch {
fileLog.error(error.localizedDescription)
}
}
} else {
envDict = nil
}
let urls = queryParam(filePrefix, from: rawParams, transforming: { URL(fileURLWithPath: $0) })
let cwd = queryParam(cwdPrefix,
from: rawParams,
@ -258,26 +272,43 @@ extension AppDelegate {
switch action {
case .activate, .newWindow:
self.emit(.newMainWindow(urls: urls, cwd: cwd, nvimArgs: nil, cliPipePath: pipePath))
self.emit(.newMainWindow(urls: urls, cwd: cwd, nvimArgs: nil, cliPipePath: pipePath, envDict: envDict))
case .open:
self.emit(.openInKeyWindow(urls: urls, cwd: cwd, cliPipePath: pipePath))
case .separateWindows:
urls.forEach { self.emit(.newMainWindow(urls: [$0], cwd: cwd, nvimArgs: nil, cliPipePath: pipePath)) }
urls.forEach {
self.emit(.newMainWindow(urls: [$0], cwd: cwd, nvimArgs: nil, cliPipePath: pipePath, envDict: nil))
}
case .nvim:
self.emit(.newMainWindow(urls: [],
cwd: cwd,
nvimArgs: queryParam(nvimArgsPrefix, from: rawParams, transforming: identity),
cliPipePath: pipePath))
cliPipePath: pipePath,
envDict: envDict))
}
}
private func stringDict(from jsonUrl: URL) -> [String: String]? {
guard let data = try? Data(contentsOf: jsonUrl) else {
return nil
}
do {
return try JSONSerialization.jsonObject(with: data) as? [String: String]
} catch {
fileLog.error(error.localizedDescription)
}
return nil
}
private func queryParam<T>(_ prefix: String,
from rawParams: [String],
transforming transform: (String) -> T) -> [T] {
from rawParams: [String],
transforming transform: (String) -> T) -> [T] {
return rawParams
.filter { $0.hasPrefix(prefix) }
@ -294,7 +325,7 @@ extension AppDelegate {
}
@IBAction func newDocument(_ sender: Any?) {
self.emit(.newMainWindow(urls: [], cwd: FileUtils.userHomeUrl, nvimArgs: nil, cliPipePath: nil))
self.emit(.newMainWindow(urls: [], cwd: FileUtils.userHomeUrl, nvimArgs: nil, cliPipePath: nil, envDict: nil))
}
@IBAction func openInNewWindow(_ sender: Any?) {
@ -318,7 +349,7 @@ extension AppDelegate {
let urls = panel.urls
let commonParentUrl = FileUtils.commonParent(of: urls)
self.emit(.newMainWindow(urls: urls, cwd: commonParentUrl, nvimArgs: nil, cliPipePath: nil))
self.emit(.newMainWindow(urls: urls, cwd: commonParentUrl, nvimArgs: nil, cliPipePath: nil, envDict: nil))
}
}
}
@ -331,7 +362,7 @@ extension AppDelegate {
}
}
/// Keep the rawValues in sync with Action in the `vimr` Python script.
// Keep the rawValues in sync with Action in the `vimr` Python script.
private enum VimRUrlAction: String {
case activate = "activate"
case open = "open"
@ -344,8 +375,10 @@ private let updater = SUUpdater()
private let debugMenuItemIdentifier = NSUserInterfaceItemIdentifier("debug-menu-item")
// Keep in sync with QueryParamKey in the `vimr` Python script.
private let filePrefix = "file="
private let cwdPrefix = "cwd="
private let nvimArgsPrefix = "nvim-args="
private let pipePathPrefix = "pipe-path="
private let waitPrefix = "wait="
private let envPathPrefix = "env-path="

View File

@ -18,19 +18,25 @@ class AppDelegateReducer {
switch pair.action {
case let .newMainWindow(urls, cwd, nvimArgs, cliPipePath):
case let .newMainWindow(urls, cwd, nvimArgs, cliPipePath, envDict):
let mainWindow: MainWindow.State
if let args = nvimArgs {
mainWindow = self.newMainWindow(with: state, urls: [], cwd: cwd, nvimArgs: args, cliPipePath: cliPipePath)
mainWindow = self.newMainWindow(
with: state, urls: [], cwd: cwd, nvimArgs: args, cliPipePath: cliPipePath, envDict: envDict
)
} else {
mainWindow = self.newMainWindow(with: state, urls: urls, cwd: cwd, cliPipePath: cliPipePath)
mainWindow = self.newMainWindow(
with: state, urls: urls, cwd: cwd, nvimArgs: nil, cliPipePath: cliPipePath, envDict: envDict
)
}
state.mainWindows[mainWindow.uuid] = mainWindow
case let .openInKeyWindow(urls, cwd, cliPipePath):
guard let uuid = state.currentMainWindowUuid, state.mainWindows[uuid] != nil else {
let mainWindow = self.newMainWindow(with: state, urls: urls, cwd: cwd, cliPipePath: cliPipePath)
let mainWindow = self.newMainWindow(
with: state, urls: urls, cwd: cwd, nvimArgs: nil, cliPipePath: cliPipePath, envDict: nil
)
state.mainWindows[mainWindow.uuid] = mainWindow
break
}
@ -49,10 +55,11 @@ class AppDelegateReducer {
private let baseServerUrl: URL
private func newMainWindow(with state: AppState,
urls: [URL],
cwd: URL,
nvimArgs: [String]? = nil,
cliPipePath: String? = nil) -> MainWindow.State {
urls: [URL],
cwd: URL,
nvimArgs: [String]?,
cliPipePath: String?,
envDict: [String: String]?) -> MainWindow.State {
var mainWindow = state.mainWindowTemplate
@ -68,9 +75,10 @@ class AppDelegateReducer {
mainWindow.nvimArgs = nvimArgs
mainWindow.cliPipePath = cliPipePath
mainWindow.envDict = envDict
mainWindow.urlsToOpen = urls.toDict { _ in MainWindow.OpenMode.default }
mainWindow.frame = state.mainWindows.isEmpty ? state.mainWindowTemplate.frame
: self.frame(relativeTo: state.mainWindowTemplate.frame)
: self.frame(relativeTo: state.mainWindowTemplate.frame)
return mainWindow
}

View File

@ -112,7 +112,8 @@ class MainWindow: NSObject,
let neoVimViewConfig = NvimView.Config(useInteractiveZsh: state.useInteractiveZsh,
cwd: state.cwd,
nvimArgs: state.nvimArgs)
nvimArgs: state.nvimArgs,
envDict: state.envDict)
self.neoVimView = NvimView(frame: .zero, config: neoVimViewConfig)
self.neoVimView.configureForAutoLayout()

View File

@ -260,6 +260,7 @@ extension MainWindow {
var useInteractiveZsh = false
var nvimArgs: [String]?
var cliPipePath: String?
var envDict: [String: String]?
var isLeftOptionMeta = false
var isRightOptionMeta = false

View File

@ -5,6 +5,7 @@ import subprocess
import argparse
import os
import uuid
import json
class Action:
@ -17,13 +18,14 @@ class Action:
class QueryParamKey:
PIPE_PATH = "pipe-path"
ENV_PATH = "env-path"
CWD = "cwd"
FILE = "file"
NVIM_ARGS = "nvim-args"
WAIT = "wait"
def wait_for_ui_to_close():
def wait_for_ui_to_close(pipe_path):
with open(pipe_path, 'r') as fifo:
while True:
if len(fifo.read()) == 0:
@ -31,7 +33,6 @@ def wait_for_ui_to_close():
def call_open(action, query_params, args):
query_params[QueryParamKey.PIPE_PATH] = pipe_path
if args.wait:
query_params[QueryParamKey.WAIT] = "true"
@ -47,10 +48,8 @@ def abspath(path):
return os.path.abspath(os.path.expanduser(path))
def vimr_nvim(other_args, nvim_args):
query_params = {
QueryParamKey.CWD: os.getcwd()
}
def vimr_nvim(other_args, nvim_args, query_params):
query_params[QueryParamKey.CWD] = os.getcwd()
if nvim_args:
query_params[QueryParamKey.NVIM_ARGS] = nvim_args
@ -58,14 +57,12 @@ def vimr_nvim(other_args, nvim_args):
call_open(Action.NVIM, query_params, other_args)
def vimr(action, args):
def vimr(action, args, query_params):
cwd = os.getcwd()
if args.cwd is not None:
cwd = abspath(args.cwd)
query_params = {
QueryParamKey.CWD: cwd
}
query_params[QueryParamKey.CWD] = cwd
files = args.file
if files:
@ -74,67 +71,92 @@ def vimr(action, args):
call_open(action, query_params, args)
pipe_path = "/tmp/com_qvacua_vimr_cli_pipe_{0}".format(str(uuid.uuid4()))
def main(args):
uuid_str = str(uuid.uuid4())
pipe_path = "/tmp/com_qvacua_vimr_cli_pipe_{0}".format(uuid_str)
if os.path.exists(pipe_path):
os.remove(pipe_path)
try:
os.mkfifo(pipe_path, 0600)
except OSError as error:
print("ERROR: {0}\n"
"{1} could not be mkfifo'ed.\n"
"Please go to https://github.com/qvacua/vimr and create an issue.".format(error, pipe_path))
raise
query_params = {
QueryParamKey.PIPE_PATH: pipe_path
}
if args.cur_env:
env_file = "/tmp/com_qvacua_vimr_env_{0}".format(uuid_str)
with open(env_file, "w") as f:
f.write(json.dumps({k: v for (k, v) in os.environ.items()}))
os.chmod(env_file, 0600)
query_params[QueryParamKey.ENV_PATH] = env_file
if args.nvim:
nvim_parser = argparse.ArgumentParser()
nvim_parser.add_argument("--nvim", action="store_true")
nvim_parser.add_argument("--wait", action="store_true")
nvim_parser.add_argument("--dry-run", action="store_true")
other_args, nvim_args = nvim_parser.parse_known_args()
vimr_nvim(other_args, nvim_args, query_params)
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, query_params)
if args.dry_run:
exit(0)
wait_for_ui_to_close(pipe_path)
if os.path.exists(pipe_path):
os.remove(pipe_path)
try:
os.mkfifo(pipe_path, 0600)
except OSError as error:
print("ERROR: {0}\n"
"{1} could not be mkfifo'ed.\n"
"Please go to https://github.com/qvacua/vimr and create an issue.".format(error, pipe_path))
raise
description = """
def parse_args():
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.
The working directory will be set to the current directory.
"""
parser = argparse.ArgumentParser(description=description)
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("--wait",
action="store_true",
help="This command line tool will exit when the corresponding UI window is closed.")
parser.add_argument("--nvim",
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("--wait",
action="store_true",
help="This command line tool will exit when the corresponding UI window is closed.")
parser.add_argument("--nvim",
action="store_true",
help="All other arguments (except --dry-run and --wait) will be passed over 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.")
group = parser.add_mutually_exclusive_group()
# no option => Open files in tabs in the front most window.
group.add_argument("--cur-env",
action="store_true",
dest="cur_env",
help="Use the current environment variables when launching the background neovim process. "
"All files will be opened in a new 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="*")
parser.add_argument("file", nargs="*")
args, _ = parser.parse_known_args()
args, _ = parser.parse_known_args()
return args
if args.nvim:
nvim_parser = argparse.ArgumentParser()
nvim_parser.add_argument("--nvim", action="store_true")
nvim_parser.add_argument("--wait", action="store_true")
nvim_parser.add_argument("--dry-run", action="store_true")
other_args, nvim_args = nvim_parser.parse_known_args()
vimr_nvim(other_args, nvim_args)
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)
if args.dry_run:
exit(0)
wait_for_ui_to_close()
os.remove(pipe_path)
if __name__ == "__main__":
args = parse_args()
main(args)