fix: dispatch editor events in background

This commit is contained in:
Akash Yadav
2023-10-24 19:14:26 +05:30
parent 540e33734b
commit a2af3bb050
2 changed files with 132 additions and 17 deletions
@@ -0,0 +1,121 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.editor.ui
import com.itsaky.androidide.eventbus.events.editor.DocumentChangeEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentCloseEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentOpenEvent
import com.itsaky.androidide.eventbus.events.editor.DocumentSaveEvent
import com.itsaky.androidide.projects.FileManager.onDocumentClose
import com.itsaky.androidide.projects.FileManager.onDocumentContentChange
import com.itsaky.androidide.projects.FileManager.onDocumentOpen
import com.itsaky.androidide.utils.ILogger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import java.util.concurrent.CancellationException
import java.util.concurrent.LinkedBlockingQueue
/**
* Dispatches events for the editor.
*
* @author Akash Yadav
*/
class EditorEventDispatcher(
var editor: IDEEditor? = null
) {
private val eventQueue = LinkedBlockingQueue<DocumentEvent>()
private var eventDispatcherJob: Job? = null
companion object {
private val log = ILogger.newInstance("EditorEventDispatcher")
}
fun init(scope: CoroutineScope) {
eventDispatcherJob = scope.launch(Dispatchers.Default) {
while (isActive) {
dispatchNextEvent()
}
}.also {
it.invokeOnCompletion { error ->
if (error != null && error !is CancellationException) {
log.error("Failed to dispatch editor events", error)
}
}
}
}
fun dispatch(event: DocumentEvent) {
check(eventQueue.offer(event)) {
"Failed to dispatch event: $event"
}
}
private suspend fun dispatchNextEvent() {
val event = withContext(Dispatchers.IO) {
eventQueue.take()
}
if (editor?.isReleased != false) {
return
}
when (event) {
is DocumentOpenEvent -> dispatchOpen(event)
is DocumentChangeEvent -> dispatchChange(event)
is DocumentSaveEvent -> dispatchSave(event)
is DocumentCloseEvent -> dispatchClose(event)
else -> throw IllegalArgumentException("Unknown document event: $event")
}
}
private fun dispatchOpen(event: DocumentOpenEvent) {
onDocumentOpen(event)
post(event)
}
private fun dispatchChange(event: DocumentChangeEvent) {
onDocumentContentChange(event)
post(event)
}
private fun dispatchSave(event: DocumentSaveEvent) {
post(event)
}
private fun dispatchClose(event: DocumentCloseEvent) {
onDocumentClose(event)
post(event)
}
private fun post(event: DocumentEvent) {
EventBus.getDefault().post(event)
}
fun destroy() {
editor = null
eventDispatcherJob?.cancel(CancellationException("Cancellation requested"))
}
}
@@ -64,9 +64,6 @@ import com.itsaky.androidide.models.Range
import com.itsaky.androidide.preferences.internal.tabSize
import com.itsaky.androidide.preferences.internal.visiblePasswordFlag
import com.itsaky.androidide.progress.ICancelChecker
import com.itsaky.androidide.projects.FileManager.onDocumentClose
import com.itsaky.androidide.projects.FileManager.onDocumentContentChange
import com.itsaky.androidide.projects.FileManager.onDocumentOpen
import com.itsaky.androidide.syntax.colorschemes.DynamicColorScheme
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE
import com.itsaky.androidide.tasks.cancelIfActive
@@ -132,6 +129,7 @@ open class IDEEditor @JvmOverloads constructor(
}
protected val editorScope = CoroutineScope(Dispatchers.Default)
protected val eventDispatcher = EditorEventDispatcher()
var languageServer: ILanguageServer? = null
private set
@@ -189,6 +187,8 @@ open class IDEEditor @JvmOverloads constructor(
init {
run {
editorFeatures.editor = this
eventDispatcher.editor = this
eventDispatcher.init(editorScope)
initEditor()
}
}
@@ -378,6 +378,9 @@ open class IDEEditor @JvmOverloads constructor(
markUnmodified()
editorFeatures.editor = null
eventDispatcher.editor = null
eventDispatcher.destroy()
selectionChangeRunner?.also { selectionChangeHandler.removeCallbacks(it) }
selectionChangeRunner = null
@@ -502,8 +505,7 @@ open class IDEEditor @JvmOverloads constructor(
if (file == null) {
return
}
val saveEvent = DocumentSaveEvent(file!!.toPath())
EventBus.getDefault().post(saveEvent)
eventDispatcher.dispatch(DocumentSaveEvent(file!!.toPath()))
}
/**
@@ -757,9 +759,7 @@ open class IDEEditor @JvmOverloads constructor(
fileVersion
)
// Notify FileManager first
onDocumentOpen(openEvent)
EventBus.getDefault().post(openEvent)
eventDispatcher.dispatch(openEvent)
}
protected open fun dispatchDocumentChangeEvent(event: ContentChangeEvent) {
@@ -786,9 +786,7 @@ open class IDEEditor @JvmOverloads constructor(
val changeEvent = DocumentChangeEvent(file, changedText, text.toString(),
++fileVersion, type, changeDelta, changeRange)
// Notify FileManager first
onDocumentContentChange(changeEvent)
EventBus.getDefault().post(changeEvent)
eventDispatcher.dispatch(changeEvent)
}
protected open fun dispatchDocumentSelectedEvent() {
@@ -796,8 +794,7 @@ open class IDEEditor @JvmOverloads constructor(
return
}
val file = file ?: return
val selectedEvent = DocumentSelectedEvent(file.toPath())
EventBus.getDefault().post(selectedEvent)
eventDispatcher.dispatch(DocumentSelectedEvent(file.toPath()))
}
protected open fun dispatchDocumentCloseEvent() {
@@ -805,11 +802,8 @@ open class IDEEditor @JvmOverloads constructor(
return
}
val file = file ?: return
val closeEvent = DocumentCloseEvent(file.toPath(), cursorLSPRange)
// Notify FileManager first
onDocumentClose(closeEvent)
EventBus.getDefault().post(closeEvent)
eventDispatcher.dispatch(DocumentCloseEvent(file.toPath(), cursorLSPRange))
}
/**