mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[editor] IJPL-54 Sync entity->documentImpl
GitOrigin-RevId: f43b36fcbab6b9b65dccd403e53c5264ffb71431
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ad9e12e51a
commit
1fba937893
@@ -6,6 +6,7 @@ import andel.editor.AnchorLifetime
|
||||
import andel.editor.DocumentComponent
|
||||
import andel.editor.DocumentComponentKey
|
||||
import andel.editor.DocumentMeta
|
||||
import andel.editor.DocumentMetaKey
|
||||
import andel.editor.MutableDocument
|
||||
import andel.editor.RangeMarkerId
|
||||
import andel.intervals.AnchorStorage
|
||||
@@ -28,6 +29,11 @@ import fleet.util.openmap.MutableBoundedOpenMap
|
||||
import fleet.util.openmap.MutableOpenMap
|
||||
|
||||
|
||||
object DocToEntityUpdate : DocumentMetaKey<Boolean>
|
||||
|
||||
internal val DbMutableDocument.docToDb: Boolean?
|
||||
get() = meta[DocToEntityUpdate]
|
||||
|
||||
internal class DbMutableDocument(
|
||||
val dbDocument: DocumentEntity,
|
||||
private val changeScope: ChangeScope,
|
||||
@@ -79,7 +85,9 @@ internal class DbMutableDocument(
|
||||
ChangeDocument(
|
||||
operationId = UID.random(),
|
||||
documentId = dbDocument.eid,
|
||||
operation = versionedOperation.rebase(this@DbMutableDocument)),
|
||||
operation = versionedOperation.rebase(this@DbMutableDocument),
|
||||
docToDb = docToDb ?: false,
|
||||
),
|
||||
)
|
||||
}
|
||||
val textAfter = text
|
||||
|
||||
@@ -27,6 +27,7 @@ data class ChangeDocument(
|
||||
val documentId: EID,
|
||||
val operationId: UID,
|
||||
val operation: Operation,
|
||||
val docToDb: Boolean,
|
||||
override val seed: Long = Random.nextLong(),
|
||||
) : Instruction {
|
||||
|
||||
@@ -49,6 +50,7 @@ data class ChangeDocument(
|
||||
documentId = documentUID,
|
||||
operationId = instruction.operationId,
|
||||
operation = instruction.operation,
|
||||
docToDb = instruction.docToDb,
|
||||
seed = instruction.seed,
|
||||
)
|
||||
)
|
||||
@@ -65,6 +67,7 @@ data class ChangeDocument(
|
||||
documentId = documentEID,
|
||||
operationId = sharedInstruction.operationId,
|
||||
operation = sharedInstruction.operation,
|
||||
docToDb = sharedInstruction.docToDb,
|
||||
seed = sharedInstruction.seed,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ data class SharedChangeDocument(
|
||||
val documentId: UID,
|
||||
val operationId: UID,
|
||||
val operation: Operation,
|
||||
val docToDb: Boolean,
|
||||
val seed: Long,
|
||||
)
|
||||
|
||||
|
||||
+84
-31
@@ -1,8 +1,13 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.editor.impl.ad.document
|
||||
|
||||
import andel.operation.Op
|
||||
import andel.operation.Operation
|
||||
import andel.operation.compose
|
||||
import andel.operation.isNotIdentity
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.edtWriteAction
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.Service.Level
|
||||
import com.intellij.openapi.components.service
|
||||
@@ -11,17 +16,27 @@ import com.intellij.openapi.editor.ex.DocumentEx
|
||||
import com.intellij.openapi.editor.ex.PrioritizedDocumentListener
|
||||
import com.intellij.openapi.editor.impl.ad.AdTheManager
|
||||
import com.intellij.openapi.editor.impl.ad.util.ThreadLocalRhizomeDB
|
||||
import com.intellij.platform.pasta.common.ChangeDocument
|
||||
import com.intellij.platform.pasta.common.DocToEntityUpdate
|
||||
import com.intellij.platform.pasta.common.DocumentEntity
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import com.intellij.util.ui.EDT
|
||||
import com.jetbrains.rhizomedb.asOf
|
||||
import fleet.kernel.SubscriptionEvent
|
||||
import fleet.kernel.byUidOrNull
|
||||
import fleet.kernel.change
|
||||
import fleet.kernel.rebase.TransactionResult
|
||||
import fleet.kernel.rebase.TransactionResultKey
|
||||
import fleet.kernel.rebase.awaitCommitted
|
||||
import fleet.kernel.rebase.shared
|
||||
import fleet.kernel.transactor
|
||||
import fleet.util.openmap.OpenMap
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.lang.ref.Reference
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
||||
@Service(Level.APP)
|
||||
@@ -42,6 +57,7 @@ class AdDocumentSynchronizer(private val coroutineScope: CoroutineScope): Dispos
|
||||
DocToEntitySynchronizer(
|
||||
debugName,
|
||||
entity,
|
||||
WeakReference(document),
|
||||
cs,
|
||||
)
|
||||
)
|
||||
@@ -52,51 +68,57 @@ class AdDocumentSynchronizer(private val coroutineScope: CoroutineScope): Dispos
|
||||
private class DocToEntitySynchronizer(
|
||||
private val debugName: String,
|
||||
private val entity: DocumentEntity,
|
||||
//private val document: DocumentEx,
|
||||
private val documentRef: Reference<DocumentEx>,
|
||||
private val coroutineScope: CoroutineScope
|
||||
) : PrioritizedDocumentListener {
|
||||
|
||||
//private var documentChanging = false
|
||||
|
||||
//init {
|
||||
// coroutineScope.launch {
|
||||
// var initial = true
|
||||
// entity.asQuery()[DocumentEntity.TextAttr].collect { text ->
|
||||
// // TODO do we need to process first change in some cases (e.g. on the frontend)?
|
||||
// if (initial) {
|
||||
// initial = false
|
||||
// return@collect
|
||||
// }
|
||||
//
|
||||
// writeAction {
|
||||
// documentChanging = true
|
||||
// try {
|
||||
// document.setText(text.view().charSequence())
|
||||
// }
|
||||
// finally {
|
||||
// documentChanging = false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
init {
|
||||
coroutineScope.launch {
|
||||
transactor().log.collect { e ->
|
||||
if (documentRef.get() == null) {
|
||||
cancel()
|
||||
return@collect
|
||||
}
|
||||
if (e is SubscriptionEvent.Next) {
|
||||
val change = e.change
|
||||
val tx = change.meta[TransactionResultKey]
|
||||
if (tx is TransactionResult.TransactionApplied) {
|
||||
val op = tx.tx.instructions.asSequence()
|
||||
.filter { it.name == ChangeDocument.instructionName }
|
||||
.map { it.instruction.get(ChangeDocument.serializer) }
|
||||
.filter {
|
||||
asOf(change.dbAfter) {
|
||||
val changedEntity = byUidOrNull<DocumentEntity>(it.documentId)
|
||||
changedEntity?.eid == entity.eid
|
||||
}
|
||||
}
|
||||
.filter { !it.docToDb }
|
||||
.map { it.operation }
|
||||
.toList()
|
||||
.compose()
|
||||
if (op.isNotIdentity()) {
|
||||
entityToDocumentImplChange(op)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPriority(): Int = Int.MIN_VALUE + 1
|
||||
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
//if (documentChanging) return
|
||||
|
||||
val entityChange = coroutineScope.async {
|
||||
val operation = operation(event)
|
||||
change {
|
||||
// shared should not be used here, otherwise an exception is going to be thrown during rebase
|
||||
// `mutate` should decide when to use `shared`
|
||||
// TODO check that markups work // shared to mutate shared document components (e.g., AdMarkupModel)
|
||||
shared { // shared to mutate shared document components (e.g., AdMarkupModel)
|
||||
entity.mutate(this, OpenMap.empty()) {
|
||||
//shared { // shared to mutate shared document components (e.g., AdMarkupModel)
|
||||
entity.mutate(this, OpenMap { set(DocToEntityUpdate, true) }) {
|
||||
edit(operation)
|
||||
}
|
||||
}
|
||||
//}
|
||||
}
|
||||
//TODO we should not wait for
|
||||
awaitCommitted()
|
||||
@@ -108,6 +130,37 @@ class AdDocumentSynchronizer(private val coroutineScope: CoroutineScope): Dispos
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun entityToDocumentImplChange(op: Operation) {
|
||||
edtWriteAction {
|
||||
CommandProcessor.getInstance().executeCommand(
|
||||
null,
|
||||
Runnable {
|
||||
val document = documentRef.get()
|
||||
if (document != null) {
|
||||
var offset = 0
|
||||
for (item in op.ops) {
|
||||
when (item) {
|
||||
is Op.Replace -> {
|
||||
document.replaceString(
|
||||
offset,
|
||||
offset + item.delete.length,
|
||||
item.insert,
|
||||
)
|
||||
offset += item.insert.length
|
||||
}
|
||||
is Op.Retain -> {
|
||||
offset += item.len.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
null,
|
||||
null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun operation(event: DocumentEvent): Operation {
|
||||
val oldFragment = event.oldFragment.toString()
|
||||
val newFragment = event.newFragment.toString()
|
||||
|
||||
Reference in New Issue
Block a user