       .subscribeOn(self.scheduler)
   }
 
-  public func openInNewTab(urls: [URL]) -> Completable {
-    return Completable
+  func openInNewTab(urls: [URL]) -> Completable {
+    Completable
       .concat(urls.map { url in self.open(url, cmd: "tabe") })
       .subscribeOn(self.scheduler)
   }
 
-  public func openInCurrentTab(url: URL) -> Completable {
-    return self.open(url, cmd: "e")
-  }
-
-  public func openInHorizontalSplit(urls: [URL]) -> Completable {
-    return Completable
+  func openInCurrentTab(url: URL) -> Completable {
+    self.open(url, cmd: "e")
+  }
+
+  func openInHorizontalSplit(urls: [URL]) -> Completable {
+    Completable
       .concat(urls.map { url in self.open(url, cmd: "sp") })
       .subscribeOn(self.scheduler)
   }
 
-  public func openInVerticalSplit(urls: [URL]) -> Completable {
-    return Completable
+  func openInVerticalSplit(urls: [URL]) -> Completable {
+    Completable
       .concat(urls.map { url in self.open(url, cmd: "vsp") })
       .subscribeOn(self.scheduler)
   }
 
-  public func select(buffer: NvimView.Buffer) -> Completable {
-    return self
+  func select(buffer: NvimView.Buffer) -> Completable {
+    self
       .allTabs()
-      .map { tabs in tabs.map { $0.windows }.flatMap { $0 } }
+      .map { tabs in tabs.map(\.windows).flatMap { $0 } }
       .flatMapCompletable { wins -> Completable in
         if let win = wins.first(where: { $0.buffer == buffer }) {
           return self.api.setCurrentWin(window: RxNeovimApi.Window(win.handle))

      .subscribeOn(self.scheduler)
  }

  public func goTo(line: Int) -> Completable {
    return self.api.command(command: "\(line)")
  }

/// Closes the current window.
  public func closeCurrentTab() -> Completable {
    return self.api
      .command(command: "q")
      .subscribeOn(self.scheduler)
  }

  public func saveCurrentTab() -> Completable {
    return self.api
      .command(command: "w")
      .subscribeOn(self.scheduler)
  }

  public func saveCurrentTab(url: URL) -> Completable {
    return self.api
      .command(command: "w \(url.path)")
      .subscribeOn(self.scheduler)
  }

  public func closeCurrentTabWithoutSaving() -> Completable {
    return self.api
      .command(command: "q!")
      .subscribeOn(self.scheduler)
  }

  public func quitNeoVimWithoutSaving() -> Completable {
    return self.api
      .command(command: "qa!")
      .subscribeOn(self.scheduler)
  }

  public func vimOutput(of command: String) -> Single<String> {
    return self.api
      .exec(src: command, output: true)
      .subscribeOn(self.scheduler)
  }

  public func cursorGo(to position: Position) -> Completable {
    return self.api
      .getCurrentWin()
      .flatMapCompletable { curWin in self.api.winSetCursor(window: curWin, pos: [position.row, position.column]) }
      .subscribeOn(self.scheduler)
  }

  public func didBecomeMain() -> Completable {
    return self.bridge.focusGained(true)
  }

  public func didResignMain() -> Completable {
    return self.bridge.focusGained(false)
  }

  func neoVimBuffer(for buf: RxNeovimApi.Buffer, currentBuffer: RxNeovimApi.Buffer?) -> Single<NvimView.Buffer> {
    return self.api
      .bufGetInfo(buffer: buf)
      .map { info -> NvimView.Buffer in
        let current = buf == currentBuffer

 */

import Cocoa
import MessagePack
import RxPack
import RxSwift

public extension NvimView {
  func isBlocked() -> Single<Bool> {
    self.api.getMode().map { dict in dict["blocking"]?.boolValue ?? false }
  }

  func hasDirtyBuffers() -> Single<Bool> {
    self.api.getDirtyStatus()
  }

  func waitTillNvimExits() {
    self.nvimExitedCondition.wait(for: 5)
  }

  func enterResizeMode() {
    self.currentlyResizing = true
    self.markForRenderWholeView()
  }

  func exitResizeMode() {
    self.currentlyResizing = false
    self.markForRenderWholeView()
    self.resizeNeoVimUi(to: self.bounds.size)
  }

  func currentBuffer() -> Single<NvimView.Buffer> {
    self.api
      .getCurrentBuf()
      .flatMap { self.neoVimBuffer(for: $0, currentBuffer: $0) }
      .subscribeOn(self.scheduler)
  }

  func allBuffers() -> Single<[NvimView.Buffer]> {
    Single
      .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.scheduler)
  }

  func isCurrentBufferDirty() -> Single<Bool> {
    self
      .currentBuffer()
      .map(\.isDirty)
      .subscribeOn(self.scheduler)
  }

  func allTabs() -> Single<[NvimView.Tabpage]> {
    Single.zip(
      self.api.getCurrentBuf(),
      self.api.getCurrentTabpage(),
      self.api.listTabpages()
    ) { (curBuf: $0, curTab: $1, tabs: $2) }
      .map { tuple in
        tuple.tabs.map { tab in
          self.neoVimTab(for: tab, currentTabpage: tuple.curTab, currentBuffer: tuple.curBuf)
        }
      }
      .flatMap(Single.fromSinglesToSingleOfArray)
      .subscribeOn(self.scheduler)
  }

  func newTab() -> Completable {
    self.api
      .command(command: "tabe")
      .subscribeOn(self.scheduler)
  }

  func open(urls: [URL]) -> Completable {
    self
      .allTabs()
      .flatMapCompletable { tabs -> Completable in
        let buffers = tabs.map(\.windows).flatMap { $0 }.map(\.buffer)
        let currentBufferIsTransient = buffers.first { $0.isCurrent }?.isTransient ?? false

        return Completable.concat(
          urls.map { url -> Completable in
            let bufExists = buffers.contains { $0.url == url }
            let wins = tabs.map(\.windows).flatMap { $0 }
            if let win = bufExists ? wins.first(where: { win in win.buffer.url == url }) : nil {
              return self.api.setCurrentWin(window: RxNeovimApi.Window(win.handle))
            }
