mirror of
https://github.com/qvacua/vimr.git
synced 2024-11-24 11:37:32 +03:00
GH-625 Pass env vars from vimr
to UI
- Use a temporary json file
This commit is contained in:
parent
8504bdbba8
commit
c50439d660
@ -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) {
|
||||
|
@ -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()
|
||||
|
@ -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="
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
138
VimR/VimR/vimr
138
VimR/VimR/vimr
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user