1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-11-24 11:37:32 +03:00

Use one queue from NvimView in bridge and api

This commit is contained in:
Tae Won Ha 2018-05-05 14:32:44 +02:00
parent 8737766db3
commit 11bc536367
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
11 changed files with 108 additions and 106 deletions

View File

@ -77,7 +77,11 @@ public class NvimApi {
return self.msgpackRpc.stream
}
public var queue = DispatchQueue(label: "com.qvacua.NvimMsgpack.NvimApi", qos: .userInitiated)
public var queue = DispatchQueue(label: "com.qvacua.NvimMsgpack.NvimApi", qos: .userInitiated) {
didSet {
self.msgpackRpc.queue = self.queue
}
}
public init() {
self.msgpackRpc.queue = self.queue

View File

@ -22,44 +22,44 @@ extension NvimView {
}
public func currentBuffer() -> Single<NvimView.Buffer> {
return self.nvim
return self.api
.getCurrentBuf()
.flatMap { self.neoVimBuffer(for: $0, currentBuffer: $0) }
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func allBuffers() -> Single<[NvimView.Buffer]> {
return Single
.zip(self.nvim.getCurrentBuf(), self.nvim.listBufs()) { (curBuf: $0, bufs: $1) }
.zip(self.api.getCurrentBuf(), self.api.listBufs()) { (curBuf: $0, bufs: $1) }
.map { tuple in tuple.bufs.map { buf in self.neoVimBuffer(for: buf, currentBuffer: tuple.curBuf) } }
.flatMap(Single.fromSinglesToSingleOfArray)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func isCurrentBufferDirty() -> Single<Bool> {
return self
.currentBuffer()
.map { $0.isDirty }
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func allTabs() -> Single<[NvimView.Tabpage]> {
return Single.zip(self.nvim.getCurrentBuf(),
self.nvim.getCurrentTabpage(),
self.nvim.listTabpages()) { (curBuf: $0, curTab: $1, tabs: $2) }
return Single.zip(self.api.getCurrentBuf(),
self.api.getCurrentTabpage(),
self.api.listTabpages()) { (curBuf: $0, curTab: $1, tabs: $2) }
.map { tuple in
return tuple.tabs.map { tab in
return self.neoVimTab(for: tab, currentTabpage: tuple.curTab, currentBuffer: tuple.curBuf)
}
}
.flatMap(Single.fromSinglesToSingleOfArray)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func newTab() -> Completable {
return self.nvim
return self.api
.command(command: "tabe", expectsReturnValue: false)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func `open`(urls: [URL]) -> Completable {
@ -74,20 +74,20 @@ extension NvimView {
let bufExists = buffers.contains { $0.url == url }
let wins = tabs.map({ $0.windows }).flatMap({ $0 })
if let win = bufExists ? wins.first(where: { win in win.buffer.url == url }) : nil {
return self.nvim.setCurrentWin(window: NvimApi.Window(win.handle), expectsReturnValue: false)
return self.api.setCurrentWin(window: NvimApi.Window(win.handle), expectsReturnValue: false)
}
return currentBufferIsTransient ? self.open(url, cmd: "e") : self.open(url, cmd: "tabe")
}
)
}
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func openInNewTab(urls: [URL]) -> Completable {
return Completable
.concat(urls.map { url in self.open(url, cmd: "tabe") })
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func openInCurrentTab(url: URL) -> Completable {
@ -97,13 +97,13 @@ extension NvimView {
public func openInHorizontalSplit(urls: [URL]) -> Completable {
return Completable
.concat(urls.map { url in self.open(url, cmd: "sp") })
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func openInVerticalSplit(urls: [URL]) -> Completable {
return Completable
.concat(urls.map { url in self.open(url, cmd: "vsp") })
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func select(buffer: NvimView.Buffer) -> Completable {
@ -112,69 +112,69 @@ extension NvimView {
.map { tabs in tabs.map { $0.windows }.flatMap { $0 } }
.flatMapCompletable { wins -> Completable in
if let win = wins.first(where: { $0.buffer == buffer }) {
return self.nvim.setCurrentWin(window: NvimApi.Window(win.handle), expectsReturnValue: false)
return self.api.setCurrentWin(window: NvimApi.Window(win.handle), expectsReturnValue: false)
}
return self.nvim.command(command: "tab sb \(buffer.handle)", expectsReturnValue: false)
return self.api.command(command: "tab sb \(buffer.handle)", expectsReturnValue: false)
}
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
/// Closes the current window.
public func closeCurrentTab() -> Completable {
return self.nvim
return self.api
.command(command: "q", expectsReturnValue: true)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func saveCurrentTab() -> Completable {
return self.nvim
return self.api
.command(command: "w", expectsReturnValue: true)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func saveCurrentTab(url: URL) -> Completable {
return self.nvim
return self.api
.command(command: "w \(url.path)", expectsReturnValue: true)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func closeCurrentTabWithoutSaving() -> Completable {
return self.nvim
return self.api
.command(command: "q!", expectsReturnValue: true)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func quitNeoVimWithoutSaving() -> Completable {
self.bridgeLogger.mark()
return self.nvim
return self.api
.command(command: "qa!", expectsReturnValue: true)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func vimOutput(of command: String) -> Single<String> {
return self.nvim
return self.api
.commandOutput(str: command)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
public func cursorGo(to position: Position) -> Completable {
return self.nvim
return self.api
.getCurrentWin()
.flatMapCompletable { curWin in self.nvim.winSetCursor(window: curWin, pos: [position.row, position.column]) }
.subscribeOn(self.nvimApiScheduler)
.flatMapCompletable { curWin in self.api.winSetCursor(window: curWin, pos: [position.row, position.column]) }
.subscribeOn(self.scheduler)
}
public func didBecomeMain() -> Completable {
return self.uiBridge.focusGained(true)
return self.bridge.focusGained(true)
}
public func didResignMain() -> Completable {
return self.uiBridge.focusGained(false)
return self.bridge.focusGained(false)
}
func neoVimBuffer(for buf: NvimApi.Buffer, currentBuffer: NvimApi.Buffer?) -> Single<NvimView.Buffer> {
return self.nvim
return self.api
.getBufGetInfo(buffer: buf)
.map { info -> NvimView.Buffer in
let current = buf == currentBuffer
@ -195,27 +195,27 @@ extension NvimView {
isCurrent: current,
isListed: listed)
}
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
func waitForNeoVimToQuit() {
self.uiBridge.nvimQuitCondition.lock()
defer { self.uiBridge.nvimQuitCondition.unlock() }
while self.uiBridge.isNvimQuit == false
&& self.uiBridge.nvimQuitCondition.wait(until: Date(timeIntervalSinceNow: neoVimQuitTimeout)) {}
self.bridge.nvimQuitCondition.lock()
defer { self.bridge.nvimQuitCondition.unlock() }
while self.bridge.isNvimQuit == false
&& self.bridge.nvimQuitCondition.wait(until: Date(timeIntervalSinceNow: neoVimQuitTimeout)) {}
}
private func `open`(_ url: URL, cmd: String) -> Completable {
return self.nvim
return self.api
.command(command: "\(cmd) \(url.path)", expectsReturnValue: false)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
}
private func neoVimWindow(for window: NvimApi.Window,
currentWindow: NvimApi.Window?,
currentBuffer: NvimApi.Buffer?) -> Single<NvimView.Window> {
return self.nvim
return self.api
.winGetBuf(window: window)
.flatMap { buf in self.neoVimBuffer(for: buf, currentBuffer: currentBuffer) }
.map { buffer in NvimView.Window(apiWindow: window, buffer: buffer, isCurrentInTab: window == currentWindow) }
@ -226,8 +226,8 @@ extension NvimView {
currentBuffer: NvimApi.Buffer?) -> Single<NvimView.Tabpage> {
return Single.zip(
self.nvim.tabpageGetWin(tabpage: tabpage),
self.nvim.tabpageListWins(tabpage: tabpage)) { (curWin: $0, wins: $1) }
self.api.tabpageGetWin(tabpage: tabpage),
self.api.tabpageListWins(tabpage: tabpage)) { (curWin: $0, wins: $1) }
.map { tuple in
tuple.wins.map { win in
return self.neoVimWindow(for: win, currentWindow: tuple.curWin, currentBuffer: currentBuffer)

View File

@ -27,7 +27,7 @@ extension NvimView {
}
self.open(urls: paths.map { URL(fileURLWithPath: $0) })
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "\(paths) could not be opened.", cause: error))
})

View File

@ -40,7 +40,7 @@ extension NvimView {
let namedChars = KeyUtils.namedKey(from: charsIgnoringModifiers)
let finalInput = isWrapNeeded ? self.wrapNamedKeys(flags + namedChars) : self.vimPlainString(chars)
self.uiBridge
self.bridge
.vimInput(finalInput)
.subscribe()
@ -53,12 +53,12 @@ extension NvimView {
switch aString {
case let string as String:
self.uiBridge
self.bridge
.vimInput(self.vimPlainString(string))
.subscribe()
case let attributedString as NSAttributedString:
self.uiBridge
self.bridge
.vimInput(self.vimPlainString(attributedString.string))
.subscribe()
@ -112,7 +112,7 @@ extension NvimView {
// Control code \0 causes rpc parsing problems.
// So we escape as early as possible
if chars == "\0" {
self.uiBridge
self.bridge
.vimInput(self.wrapNamedKeys("Nul"))
.subscribe()
return true
@ -122,14 +122,14 @@ extension NvimView {
// See special cases in vim/os_win32.c from vim sources
// Also mentioned in MacVim's KeyBindings.plist
if .control == flags && chars == "6" {
self.uiBridge
self.bridge
.vimInput("\u{1e}") // AKA ^^
.subscribe()
return true
}
if .control == flags && chars == "2" {
// <C-2> should generate \0, escaping as above
self.uiBridge
self.bridge
.vimInput(self.wrapNamedKeys("Nul"))
.subscribe()
return true
@ -157,7 +157,7 @@ extension NvimView {
.flatMap { length -> Observable<Never> in
// eg -> hanja popup, cf comment for self.lastMarkedText
if length > 0 {
return self.uiBridge.deleteCharacters(length).asObservable()
return self.bridge.deleteCharacters(length).asObservable()
}
return Completable.empty().asObservable()
@ -180,7 +180,7 @@ extension NvimView {
return Disposables.create()
}
)
.andThen(self.uiBridge.vimInputMarkedText(self.markedText!))
.andThen(self.bridge.vimInputMarkedText(self.markedText!))
.subscribe()
self.keyDownDone = true

View File

@ -49,13 +49,13 @@ extension NvimView {
@IBAction func undo(_ sender: AnyObject?) {
switch self.mode {
case .insert, .replace:
self.uiBridge
self.bridge
.vimInput("<Esc>ui")
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not undo", cause: error))
})
case .normal, .visual:
self.uiBridge
self.bridge
.vimInput("u")
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not undo", cause: error))
@ -68,13 +68,13 @@ extension NvimView {
@IBAction func redo(_ sender: AnyObject?) {
switch self.mode {
case .insert, .replace:
self.uiBridge
self.bridge
.vimInput("<Esc><C-r>i")
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not redo", cause: error))
})
case .normal, .visual:
self.uiBridge
self.bridge
.vimInput("<C-r>")
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not redo", cause: error))
@ -87,7 +87,7 @@ extension NvimView {
@IBAction func cut(_ sender: AnyObject?) {
switch self.mode {
case .visual, .normal:
self.uiBridge
self.bridge
.vimInput("\"+d")
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not cut", cause: error))
@ -100,7 +100,7 @@ extension NvimView {
@IBAction func copy(_ sender: AnyObject?) {
switch self.mode {
case .visual, .normal:
self.uiBridge
self.bridge
.vimInput("\"+y")
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not copy", cause: error))
@ -118,7 +118,7 @@ extension NvimView {
if self.mode == .cmdline || self.mode == .cmdlineInsert || self.mode == .cmdlineReplace
|| self.mode == .replace
|| self.mode == .termFocus {
self.uiBridge
self.bridge
.vimInput(self.vimPlainString(content))
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not paste \(content)", cause: error))
@ -126,11 +126,11 @@ extension NvimView {
return
}
self.nvim
self.api
.getOption(name: "paste")
.flatMap { curPasteMode -> Single<Bool> in
if curPasteMode == false {
return self.nvim
return self.api
.setOption(name: "paste", value: .bool(true))
.andThen(Single.just(true))
} else {
@ -141,12 +141,12 @@ extension NvimView {
switch self.mode {
case .insert:
return self.uiBridge
return self.bridge
.vimInput("<ESC>\"+pa")
.andThen(Single.just(pasteModeSet))
case .normal, .visual:
return self.uiBridge
return self.bridge
.vimInput("\"+p")
.andThen(Single.just(pasteModeSet))
@ -157,7 +157,7 @@ extension NvimView {
}
.flatMapCompletable { pasteModeSet -> Completable in
if pasteModeSet {
return self.nvim.setOption(name: "paste", value: .bool(false))
return self.api.setOption(name: "paste", value: .bool(false))
}
return Completable.empty()
@ -170,7 +170,7 @@ extension NvimView {
@IBAction func delete(_ sender: AnyObject?) {
switch self.mode {
case .normal, .visual:
self.uiBridge
self.bridge
.vimInput("x")
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not delete", cause: error))
@ -183,13 +183,13 @@ extension NvimView {
@IBAction public override func selectAll(_ sender: Any?) {
switch self.mode {
case .insert, .visual:
self.uiBridge
self.bridge
.vimInput("<Esc>ggVG")
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not select all", cause: error))
})
default:
self.uiBridge
self.bridge
.vimInput("ggVG")
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not select all", cause: error))

View File

@ -33,9 +33,9 @@ extension NvimView {
let (vimInputX, vimInputY) = self.vimScrollInputFor(deltaX: deltaX, deltaY: deltaY,
modifierFlags: event.modifierFlags,
cellPosition: cellPosition)
self.uiBridge
self.bridge
.vimInput(vimInputX)
.andThen(self.uiBridge.vimInput(vimInputY))
.andThen(self.bridge.vimInput(vimInputY))
.subscribe()
return
@ -46,7 +46,7 @@ extension NvimView {
min(Int(ceil(abs(deltaY) / self.trackpadScrollResistance)), maxScrollDeltaY)
)
let (horizSign, vertSign) = (deltaX > 0 ? 1 : -1, deltaY > 0 ? 1 : -1)
self.uiBridge
self.bridge
.scroll(horizontal: horizSign * absDeltaX, vertical: vertSign * absDeltaY, at: cellPosition)
.subscribe()
}
@ -105,7 +105,7 @@ extension NvimView {
}
// self.logger.debug("\(#function): \(result)")
self.uiBridge
self.bridge
.vimInput(result)
.subscribe()
}

View File

@ -55,7 +55,7 @@ extension NvimView {
self.xOffset = floor((size.width - self.cellSize.width * CGFloat(discreteSize.width)) / 2)
self.yOffset = floor((size.height - self.cellSize.height * CGFloat(discreteSize.height)) / 2)
try? self.uiBridge
try? self.bridge
.resize(width: discreteSize.width, height: discreteSize.height)
.wait()
}
@ -64,9 +64,9 @@ extension NvimView {
self.logger.info("=== Starting neovim...")
let sockPath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("vimr_\(self.uuid).sock").path
self.uiBridge
self.bridge
.runLocalServerAndNvim(width: size.width, height: size.height)
.andThen(self.nvim.run(at: sockPath))
.andThen(self.api.run(at: sockPath))
.subscribe(onError: { error in
self.eventsSubject.onError(Error.nvimLaunch(msg: "Could not launch nvim", cause: error))
})

View File

@ -126,9 +126,9 @@ extension NvimView: NSTouchBarDelegate, NSScrubberDataSource, NSScrubberDelegate
}
let window = tab.currentWindow ?? tab.windows[0]
self.nvim
self.api
.setCurrentWin(window: NvimApi.Window(window.handle), expectsReturnValue: false)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
.subscribe(onError: { error in
self.eventsSubject.onNext(.apiError(msg: "Could not set current window to \(window.handle).", cause: error))
})

View File

@ -156,9 +156,9 @@ extension NvimView {
func stop() {
self.bridgeLogger.hr()
try? self.nvim
try? self.api
.stop()
.andThen(self.uiBridge.quit())
.andThen(self.bridge.quit())
.wait()
gui.async {
@ -192,7 +192,7 @@ extension NvimView {
self.bridgeLogger.debug(reason)
gui.async {
if self.uiBridge.isNvimQuitting || self.uiBridge.isNvimQuit {
if self.bridge.isNvimQuitting || self.bridge.isNvimQuit {
return
}
@ -200,9 +200,9 @@ extension NvimView {
self.eventsSubject.onCompleted()
self.bridgeLogger.error("Force-closing due to IPC error.")
try? self.nvim
try? self.api
.stop()
.andThen(self.uiBridge.forceQuit())
.andThen(self.bridge.forceQuit())
.wait()
}
}

View File

@ -170,9 +170,9 @@ public class NvimView: NSView,
}
set {
self.nvim
self.api
.setCurrentDir(dir: newValue.path, expectsReturnValue: false)
.subscribeOn(self.nvimApiScheduler)
.subscribeOn(self.scheduler)
.subscribe(onError: { error in
self.eventsSubject.onError(Error.ipc(msg: "Could not set cwd to \(newValue)", cause: error))
})
@ -183,7 +183,8 @@ public class NvimView: NSView,
return true
}
public let nvimApiScheduler = SerialDispatchQueueScheduler(qos: .userInitiated)
public let queue = DispatchQueue(label: "com.qvacua.NvimView.NvimView", qos: .userInitiated)
public let scheduler: SerialDispatchQueueScheduler
public internal(set) var currentPosition = Position.beginning
@ -193,7 +194,9 @@ public class NvimView: NSView,
public init(frame rect: NSRect, config: Config) {
self.drawer = TextDrawer(font: self._font)
self.uiBridge = UiBridge(uuid: self.uuid, config: config)
self.bridge = UiBridge(uuid: self.uuid, queue: self.queue, config: config)
self.scheduler = SerialDispatchQueueScheduler(queue: self.queue,
internalSerialQueueName: "com.qvacua.NvimView.NvimView")
super.init(frame: .zero)
self.registerForDraggedTypes([NSPasteboard.PasteboardType(String(kUTTypeFileURL))])
@ -203,7 +206,8 @@ public class NvimView: NSView,
self.descent = self.drawer.descent
self.leading = self.drawer.leading
self.uiBridge.stream
self.api.queue = self.queue
self.bridge.stream
.subscribe(onNext: { [unowned self] msg in
switch msg {
@ -309,7 +313,7 @@ public class NvimView: NSView,
@IBAction public func debug1(_ sender: Any?) {
self.logger.debug("DEBUG 1 - Start")
self.uiBridge
self.bridge
.debug()
.subscribe()
self.logger.debug("DEBUG 1 - End")
@ -332,8 +336,8 @@ public class NvimView: NSView,
let bridgeLogger = LogContext.fileLogger(as: NvimView.self,
with: URL(fileURLWithPath: "/tmp/nvv-bridge.log"),
shouldLogDebug: nil)
let uiBridge: UiBridge
let nvim = NvimApi()
let bridge: UiBridge
let api = NvimApi()
let grid = Grid()
let drawer: TextDrawer

View File

@ -54,29 +54,20 @@ class UiBridge {
return self.streamSubject.asObservable()
}
var scheduler: SerialDispatchQueueScheduler
var queue = DispatchQueue(label: "com.qvacua.NvimView.UiBridge", qos: .userInitiated) {
didSet {
self.scheduler = SerialDispatchQueueScheduler(queue: self.queue,
internalSerialQueueName: "com.qvacua.NvimView.UiBridge")
}
}
let nvimQuitCondition = NSCondition()
private(set) var isNvimQuitting = false
private(set) var isNvimQuit = false
init(uuid: String, config: NvimView.Config) {
init(uuid: String, queue: DispatchQueue, config: NvimView.Config) {
self.uuid = uuid
self.useInteractiveZsh = config.useInteractiveZsh
self.nvimArgs = config.nvimArgs ?? []
self.cwd = config.cwd
self.scheduler = SerialDispatchQueueScheduler(queue: self.queue,
internalSerialQueueName: "com.qvacua.NvimView.UiBridge")
self.queue = queue
self.scheduler = SerialDispatchQueueScheduler(queue: queue, internalSerialQueueName: "com.qvacua.NvimView.UiBridge")
self.client.queue = self.queue
self.server.queue = self.queue
@ -454,6 +445,9 @@ class UiBridge {
private var runLocalServerAndNvimCompletable: Completable.CompletableObserver?
private let scheduler: SerialDispatchQueueScheduler
private let queue: DispatchQueue
private let streamSubject = PublishSubject<Message>()
private let disposeBag = DisposeBag()