1
1
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:
Tae Won Ha 2023-12-18 21:59:33 +01:00
parent ad17863496
commit 87017f8df0
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
4 changed files with 92 additions and 70 deletions

View File

@ -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)

View File

@ -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()

View File

@ -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>

View File

@ -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()
}
}