mirror of
https://github.com/qvacua/vimr.git
synced 2024-12-27 07:42:48 +03:00
Fix memory leak
This commit is contained in:
parent
ad17863496
commit
87017f8df0
@ -43,10 +43,8 @@ public extension NvimView {
|
||||
return vim.fn.getbufinfo({"bufmodified": v:true})
|
||||
""", args: [])
|
||||
.map { result -> Bool in
|
||||
guard let info_array = result.arrayValue
|
||||
else {
|
||||
throw RxNeovimApi.Error
|
||||
.exception(message: "Could not convert values into info array.")
|
||||
guard let info_array = result.arrayValue else {
|
||||
throw RxNeovimApi.Error.exception(message: "Could not convert values into info array.")
|
||||
}
|
||||
return info_array.count > 0
|
||||
}
|
||||
@ -70,16 +68,23 @@ public extension NvimView {
|
||||
func currentBuffer() -> Single<NvimView.Buffer> {
|
||||
self.api
|
||||
.getCurrentBuf()
|
||||
.flatMap { self.neoVimBuffer(for: $0, currentBuffer: $0) }
|
||||
.flatMap { [weak self] in
|
||||
guard let single = self?.neoVimBuffer(for: $0, currentBuffer: $0) else {
|
||||
throw RxNeovimApi.Error.exception(message: "Could not get buffer")
|
||||
}
|
||||
return single
|
||||
}
|
||||
.subscribe(on: 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)
|
||||
} }
|
||||
.map { [weak self] tuple in
|
||||
tuple.bufs.compactMap { buf in
|
||||
self?.neoVimBuffer(for: buf, currentBuffer: tuple.curBuf)
|
||||
}
|
||||
}
|
||||
.flatMap(Single.fromSinglesToSingleOfArray)
|
||||
.subscribe(on: self.scheduler)
|
||||
}
|
||||
@ -97,9 +102,9 @@ public extension NvimView {
|
||||
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)
|
||||
.map { [weak self] tuple in
|
||||
tuple.tabs.compactMap { tab in
|
||||
self?.neoVimTab(for: tab, currentTabpage: tuple.curTab, currentBuffer: tuple.curBuf)
|
||||
}
|
||||
}
|
||||
.flatMap(Single.fromSinglesToSingleOfArray)
|
||||
@ -115,19 +120,19 @@ public extension NvimView {
|
||||
func open(urls: [URL]) -> Completable {
|
||||
self
|
||||
.allTabs()
|
||||
.flatMapCompletable { tabs -> Completable in
|
||||
.flatMapCompletable { [weak self] 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
|
||||
urls.compactMap { 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))
|
||||
return self?.api.setCurrentWin(window: RxNeovimApi.Window(win.handle))
|
||||
}
|
||||
|
||||
return currentBufferIsTransient ? self.open(url, cmd: "e") : self.open(url, cmd: "tabe")
|
||||
return currentBufferIsTransient ? self?.open(url, cmd: "e") : self?.open(url, cmd: "tabe")
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -136,7 +141,7 @@ public extension NvimView {
|
||||
|
||||
func openInNewTab(urls: [URL]) -> Completable {
|
||||
Completable
|
||||
.concat(urls.map { url in self.open(url, cmd: "tabe") })
|
||||
.concat(urls.compactMap { [weak self] url in self?.open(url, cmd: "tabe") })
|
||||
.subscribe(on: self.scheduler)
|
||||
}
|
||||
|
||||
@ -146,13 +151,13 @@ public extension NvimView {
|
||||
|
||||
func openInHorizontalSplit(urls: [URL]) -> Completable {
|
||||
Completable
|
||||
.concat(urls.map { url in self.open(url, cmd: "sp") })
|
||||
.concat(urls.compactMap { [weak self] url in self?.open(url, cmd: "sp") })
|
||||
.subscribe(on: self.scheduler)
|
||||
}
|
||||
|
||||
func openInVerticalSplit(urls: [URL]) -> Completable {
|
||||
Completable
|
||||
.concat(urls.map { url in self.open(url, cmd: "vsp") })
|
||||
.concat(urls.compactMap {[weak self] url in self?.open(url, cmd: "vsp") })
|
||||
.subscribe(on: self.scheduler)
|
||||
}
|
||||
|
||||
@ -160,18 +165,27 @@ public extension NvimView {
|
||||
self
|
||||
.allTabs()
|
||||
.map { tabs in tabs.map(\.windows).flatMap { $0 } }
|
||||
.flatMapCompletable { wins -> Completable in
|
||||
.flatMapCompletable { [weak self] wins -> Completable in
|
||||
if let win = wins.first(where: { $0.buffer == buffer }) {
|
||||
return self.api.setCurrentWin(window: RxNeovimApi.Window(win.handle))
|
||||
guard let completable = self?.api.setCurrentWin(window: RxNeovimApi.Window(win.handle)) else {
|
||||
throw RxNeovimApi.Error.exception(message: "Could not set current win")
|
||||
}
|
||||
|
||||
return completable
|
||||
}
|
||||
|
||||
return self.api.command(command: "tab sb \(buffer.handle)")
|
||||
guard let completable = self?.api.command(command: "tab sb \(buffer.handle)") else {
|
||||
throw RxNeovimApi.Error.exception(message: "Could tab sb")
|
||||
}
|
||||
return completable
|
||||
}
|
||||
.subscribe(on: self.scheduler)
|
||||
}
|
||||
|
||||
func goTo(line: Int) -> Completable {
|
||||
self.api.command(command: "\(line)")
|
||||
self.api
|
||||
.command(command: "\(line)")
|
||||
.subscribe(on: self.scheduler)
|
||||
}
|
||||
|
||||
/// Closes the current window.
|
||||
@ -212,10 +226,7 @@ public extension NvimView {
|
||||
retval in
|
||||
guard let output_value = retval["output"] ?? retval["output"],
|
||||
let output = output_value.stringValue
|
||||
else {
|
||||
throw RxNeovimApi.Error
|
||||
.exception(message: "Could not convert values to output.")
|
||||
}
|
||||
else { throw RxNeovimApi.Error.exception(message: "Could not convert values to output.") }
|
||||
return output
|
||||
}
|
||||
.subscribe(on: self.scheduler)
|
||||
@ -224,8 +235,12 @@ public extension NvimView {
|
||||
func cursorGo(to position: Position) -> Completable {
|
||||
self.api
|
||||
.getCurrentWin()
|
||||
.flatMapCompletable { curWin in
|
||||
self.api.winSetCursor(window: curWin, pos: [position.row, position.column])
|
||||
.flatMapCompletable { [weak self] curWin in
|
||||
guard let completable = self?.api.winSetCursor(window: curWin, pos: [position.row, position.column]) else {
|
||||
throw RxNeovimApi.Error.exception(message: "Could not set cursor")
|
||||
}
|
||||
|
||||
return completable
|
||||
}
|
||||
.subscribe(on: self.scheduler)
|
||||
}
|
||||
@ -242,8 +257,7 @@ public extension NvimView {
|
||||
for buf: RxNeovimApi.Buffer,
|
||||
currentBuffer: RxNeovimApi.Buffer?
|
||||
) -> Single<NvimView.Buffer> {
|
||||
self.api
|
||||
.execLua(code: """
|
||||
self.api.execLua(code: """
|
||||
local function map(tbl, f)
|
||||
local t = {}
|
||||
for k,v in pairs(tbl) do
|
||||
@ -262,8 +276,7 @@ public extension NvimView {
|
||||
info_array.count == 1,
|
||||
let raw_info = info_array[0].dictionaryValue
|
||||
else {
|
||||
throw RxNeovimApi.Error
|
||||
.exception(message: "Could not convert values into info array.")
|
||||
throw RxNeovimApi.Error.exception(message: "Could not convert values into info array.")
|
||||
}
|
||||
let info: [String: MessagePackValue] = .init(
|
||||
uniqueKeysWithValues: raw_info.map {
|
||||
@ -309,12 +322,19 @@ public extension NvimView {
|
||||
) -> Single<NvimView.Window> {
|
||||
self.api
|
||||
.winGetBuf(window: window)
|
||||
.flatMap { buf in self.neoVimBuffer(for: buf, currentBuffer: currentBuffer) }
|
||||
.flatMap { [weak self] buf in
|
||||
guard let single = self?.neoVimBuffer(for: buf, currentBuffer: currentBuffer) else {
|
||||
throw RxNeovimApi.Error.exception(message: "Could not get buffer")
|
||||
}
|
||||
|
||||
return single
|
||||
}
|
||||
.map { buffer in NvimView.Window(
|
||||
apiWindow: window,
|
||||
buffer: buffer,
|
||||
isCurrentInTab: window == currentWindow
|
||||
) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func neoVimTab(
|
||||
@ -326,9 +346,9 @@ public extension NvimView {
|
||||
self.api.tabpageGetWin(tabpage: tabpage),
|
||||
self.api.tabpageListWins(tabpage: tabpage)
|
||||
) { (curWin: $0, wins: $1) }
|
||||
.map { tuple in
|
||||
tuple.wins.map { win in
|
||||
self.neoVimWindow(for: win, currentWindow: tuple.curWin, currentBuffer: currentBuffer)
|
||||
.map { [weak self] tuple in
|
||||
tuple.wins.compactMap { win in
|
||||
self?.neoVimWindow(for: win, currentWindow: tuple.curWin, currentBuffer: currentBuffer)
|
||||
}
|
||||
}
|
||||
.flatMap(Single.fromSinglesToSingleOfArray)
|
||||
|
@ -22,14 +22,14 @@ extension NvimView {
|
||||
self.bridgeLogger.debug(region)
|
||||
self.setNeedsDisplay(self.rect(for: region))
|
||||
}
|
||||
|
||||
|
||||
final func stop() {
|
||||
self.bridgeLogger.debug()
|
||||
self.quit()
|
||||
.andThen(self.api.stop())
|
||||
.andThen(Completable.create { completable in
|
||||
self.eventsSubject.onNext(.neoVimStopped)
|
||||
self.eventsSubject.onCompleted()
|
||||
.andThen(Completable.create { [weak self] completable in
|
||||
self?.eventsSubject.onNext(.neoVimStopped)
|
||||
self?.eventsSubject.onCompleted()
|
||||
|
||||
completable(.completed)
|
||||
return Disposables.create()
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22155" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22155"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -79,8 +79,8 @@ public final class RxMsgpackRpc {
|
||||
self.errorPipe = errorPipe
|
||||
|
||||
return Completable.create { completable in
|
||||
self.queue.async {
|
||||
self.startReading()
|
||||
self.queue.async { [weak self] in
|
||||
self?.startReading()
|
||||
completable(.completed)
|
||||
}
|
||||
|
||||
@ -90,8 +90,8 @@ public final class RxMsgpackRpc {
|
||||
|
||||
public func stop() -> Completable {
|
||||
Completable.create { completable in
|
||||
self.queue.async {
|
||||
self.cleanUp()
|
||||
self.queue.async { [weak self] in
|
||||
self?.cleanUp()
|
||||
completable(.completed)
|
||||
}
|
||||
|
||||
@ -100,8 +100,8 @@ public final class RxMsgpackRpc {
|
||||
}
|
||||
|
||||
public func response(msgid: UInt32, error: Value, result: Value) -> Completable {
|
||||
Completable.create { completable in
|
||||
self.queue.async {
|
||||
Completable.create { [weak self] completable in
|
||||
self?.queue.async {
|
||||
let packed = pack(
|
||||
[
|
||||
.uint(MessageType.response.rawValue),
|
||||
@ -112,10 +112,10 @@ public final class RxMsgpackRpc {
|
||||
)
|
||||
|
||||
do {
|
||||
try self.inPipe?.fileHandleForWriting.write(contentsOf: packed)
|
||||
try self?.inPipe?.fileHandleForWriting.write(contentsOf: packed)
|
||||
completable(.completed)
|
||||
} catch {
|
||||
self.streamSubject.onError(Error(
|
||||
self?.streamSubject.onError(Error(
|
||||
msg: "Could not write to socket for msg id: \(msgid)", cause: error
|
||||
))
|
||||
|
||||
@ -136,11 +136,11 @@ public final class RxMsgpackRpc {
|
||||
params: [Value],
|
||||
expectsReturnValue: Bool
|
||||
) -> Single<Response> {
|
||||
Single.create { single in
|
||||
self.queue.async {
|
||||
let msgid = self.nextMsgid
|
||||
self.nextMsgid += 1
|
||||
return Single.create { [weak self] single in
|
||||
guard let msgid = self?.nextMsgid else { return Disposables.create() }
|
||||
self?.nextMsgid += 1
|
||||
|
||||
self?.queue.async {
|
||||
let packed = pack(
|
||||
[
|
||||
.uint(MessageType.request.rawValue),
|
||||
@ -151,13 +151,13 @@ public final class RxMsgpackRpc {
|
||||
)
|
||||
|
||||
if expectsReturnValue {
|
||||
self.streamQueue.async { self.singles[msgid] = single }
|
||||
self?.streamQueue.async { self?.singles[msgid] = single }
|
||||
}
|
||||
|
||||
do {
|
||||
try self.inPipe?.fileHandleForWriting.write(contentsOf: packed)
|
||||
try self?.inPipe?.fileHandleForWriting.write(contentsOf: packed)
|
||||
} catch {
|
||||
self.streamSubject.onError(Error(
|
||||
self?.streamSubject.onError(Error(
|
||||
msg: "Could not write to socket for msg id: \(msgid)", cause: error
|
||||
))
|
||||
|
||||
@ -168,7 +168,9 @@ public final class RxMsgpackRpc {
|
||||
return
|
||||
}
|
||||
|
||||
if !expectsReturnValue { single(.success(self.nilResponse(with: msgid))) }
|
||||
if !expectsReturnValue {
|
||||
single(.success(Response(msgid: msgid, error: .nil, result: .nil)))
|
||||
}
|
||||
}
|
||||
|
||||
return Disposables.create()
|
||||
@ -200,39 +202,39 @@ public final class RxMsgpackRpc {
|
||||
self.outPipe = nil
|
||||
self.errorPipe = nil
|
||||
|
||||
self.streamQueue.async {
|
||||
self.streamSubject.onCompleted()
|
||||
self.singles.forEach { _, single in single(.failure(Error(msg: "Pipe closed"))) }
|
||||
self.streamQueue.async { [weak self] in
|
||||
self?.streamSubject.onCompleted()
|
||||
self?.singles.forEach { _, single in single(.failure(Error(msg: "Pipe closed"))) }
|
||||
}
|
||||
}
|
||||
|
||||
private func startReading() {
|
||||
self.pipeReadQueue.async { [unowned self] in
|
||||
self.pipeReadQueue.async { [weak self] in
|
||||
var readData: Data
|
||||
var dataToUnmarshall = Data(capacity: Self.defaultReadBufferSize)
|
||||
repeat {
|
||||
do {
|
||||
guard let buffer = self.outPipe?.fileHandleForReading.availableData else { break }
|
||||
guard let buffer = self?.outPipe?.fileHandleForReading.availableData else { break }
|
||||
readData = buffer
|
||||
|
||||
if readData.count > 0 {
|
||||
dataToUnmarshall.append(readData)
|
||||
let (values, remainderData) = try self.unpackAllWithReminder(dataToUnmarshall)
|
||||
guard let (values, remainderData) = try self?.unpackAllWithReminder(dataToUnmarshall) else { throw Error(msg: "Nil when unpacking") }
|
||||
if let remainderData { dataToUnmarshall = remainderData }
|
||||
else { dataToUnmarshall.count = 0 }
|
||||
|
||||
self.streamQueue.async {
|
||||
values.forEach(self.processMessage)
|
||||
self?.streamQueue.async {
|
||||
values.forEach { value in self?.processMessage(value) }
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
self.streamQueue.async {
|
||||
self.streamSubject.onError(Error(msg: "Could not read from pipe", cause: error))
|
||||
self?.streamQueue.async {
|
||||
self?.streamSubject.onError(Error(msg: "Could not read from pipe", cause: error))
|
||||
}
|
||||
}
|
||||
} while readData.count > 0
|
||||
|
||||
self.cleanUp()
|
||||
self?.cleanUp()
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user