mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[editor] IJPL-54 Replace AdDocumentEntity with fleet common DocumentEntity
GitOrigin-RevId: 8dfd8e39a86702c59f9dc514dbbbea2a7729201c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f821e02859
commit
439fbffe94
@@ -17,5 +17,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.coroutines" />
|
||||
<orderEntry type="module" module-name="intellij.platform.kernel" />
|
||||
<orderEntry type="module" module-name="intellij.platform.pasta" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -4,14 +4,15 @@ package com.intellij.platform.editor.backend
|
||||
import com.intellij.codeWithMe.clientId
|
||||
import com.intellij.openapi.application.isRhizomeAdEnabled
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.impl.ad.AdDocumentEntity
|
||||
import com.intellij.openapi.editor.impl.editorId
|
||||
import com.intellij.platform.editor.EditorEntity
|
||||
import com.intellij.platform.kernel.KernelService
|
||||
import com.intellij.platform.kernel.util.kernelCoroutineContext
|
||||
import com.intellij.platform.pasta.common.DocumentEntity
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import fleet.kernel.change
|
||||
import fleet.kernel.shared
|
||||
import fleet.util.UID
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
internal class BackendEditorState(
|
||||
@@ -32,7 +33,7 @@ internal class BackendEditorState(
|
||||
val clientId = requireNotNull(currentCoroutineContext().clientId())
|
||||
val editorEntity = change {
|
||||
shared {
|
||||
val document = AdDocumentEntity.fromString(editor.document.text, editor.document.modificationStamp)
|
||||
val document = DocumentEntity.fromText(UID.random(), editor.document.text)
|
||||
EditorEntity.new {
|
||||
it[EditorEntity.idAttr] = editor.editorId()
|
||||
it[EditorEntity.clientIdAttr] = clientId
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.intellij.platform.editor
|
||||
|
||||
import com.intellij.codeWithMe.ClientId
|
||||
import com.intellij.openapi.editor.impl.EditorId
|
||||
import com.intellij.openapi.editor.impl.ad.AdDocumentEntity
|
||||
import com.intellij.platform.pasta.common.DocumentEntity
|
||||
import com.jetbrains.rhizomedb.EID
|
||||
import com.jetbrains.rhizomedb.Entity
|
||||
import com.jetbrains.rhizomedb.Indexing
|
||||
@@ -16,7 +16,7 @@ import org.jetbrains.annotations.ApiStatus.Internal
|
||||
@Internal
|
||||
data class EditorEntity(override val eid: EID) : Entity {
|
||||
|
||||
val document: AdDocumentEntity by documentAttr
|
||||
val document: DocumentEntity by documentAttr
|
||||
val id: EditorId by idAttr
|
||||
val clientId: ClientId by clientIdAttr
|
||||
|
||||
@@ -27,7 +27,7 @@ data class EditorEntity(override val eid: EID) : Entity {
|
||||
) {
|
||||
|
||||
val idAttr: Required<EditorId> = requiredValue("id", EditorId.serializer(), Indexing.UNIQUE)
|
||||
val documentAttr: Required<AdDocumentEntity> = requiredRef("document", RefFlags.CASCADE_DELETE_BY)
|
||||
val documentAttr: Required<DocumentEntity> = requiredRef("document", RefFlags.CASCADE_DELETE_BY)
|
||||
val clientIdAttr: Required<ClientId> = requiredValue("clientId", ClientIdSerializer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
package com.intellij.platform.editor
|
||||
|
||||
import com.intellij.openapi.application.isRhizomeAdEnabled
|
||||
import com.intellij.openapi.editor.impl.ad.AdDocumentEntity
|
||||
import com.intellij.platform.kernel.EntityTypeProvider
|
||||
import com.intellij.platform.pasta.common.DocumentEntity
|
||||
import com.intellij.platform.pasta.common.EditLogEntity
|
||||
import com.intellij.platform.kernel.EntityTypeProvider
|
||||
import com.jetbrains.rhizomedb.EntityType
|
||||
|
||||
internal class EditorEntityTypeProvider : EntityTypeProvider {
|
||||
@@ -13,7 +12,6 @@ internal class EditorEntityTypeProvider : EntityTypeProvider {
|
||||
override fun entityTypes(): List<EntityType<*>> {
|
||||
if (!isRhizomeAdEnabled) return emptyList()
|
||||
return listOf(
|
||||
AdDocumentEntity,
|
||||
EditorEntity,
|
||||
DocumentEntity,
|
||||
EditLogEntity,
|
||||
|
||||
@@ -40,6 +40,16 @@ class DocumentEntity(override val eid: EID) : Entity {
|
||||
val WritableAttr: Required<Boolean> = requiredValue("writable", Boolean.serializer())
|
||||
val EditLogAttr: Required<EditLogEntity> = requiredRef<EditLogEntity>("editLogEntity", RefFlags.UNIQUE)
|
||||
val SharedAnchorStorageAttr: Required<AnchorStorage> = requiredValue("sharedAnchorStorage", AnchorStorage.serializer())
|
||||
|
||||
fun fromText(uid: UID, text: CharSequence): DocumentEntity = requireChangeScope {
|
||||
DocumentEntity.new {
|
||||
it[Durable.Id] = uid
|
||||
it[TextAttr] = Text.fromString(text.toString())
|
||||
it[WritableAttr] = true
|
||||
it[EditLogAttr] = createEmptyEditLog()
|
||||
it[SharedAnchorStorageAttr] = AnchorStorage.empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val timestamp: Long
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.editor.ex;
|
||||
|
||||
import com.intellij.openapi.editor.impl.ad.AdTheManagerImpl;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.Internal
|
||||
public interface AdTheManager {
|
||||
|
||||
static @NotNull AdTheManager getInstance(@NotNull Project project) {
|
||||
return project.getService(AdTheManagerImpl.class);
|
||||
}
|
||||
|
||||
@Nullable EditorModel createEditorModel(@NotNull EditorEx editor);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import com.intellij.openapi.editor.event.EditorFactoryListener
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory
|
||||
import com.intellij.openapi.editor.impl.ad.v2.AdTheManagerV2
|
||||
import com.intellij.openapi.editor.impl.ad.AdTheManager
|
||||
import com.intellij.openapi.editor.impl.event.EditorEventMulticasterImpl
|
||||
import com.intellij.openapi.editor.impl.view.EditorPainter
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
@@ -209,7 +209,7 @@ class EditorFactoryImpl(coroutineScope: CoroutineScope?) : EditorFactory() {
|
||||
highlighter: EditorHighlighter?,
|
||||
afterCreation: ((EditorImpl) -> Unit)?,
|
||||
): EditorImpl {
|
||||
AdTheManagerV2.getInstance().bindLocalDocEntity(document)
|
||||
AdTheManager.getInstance().bindLocalDocEntity(document)
|
||||
val editor = EditorImpl(document, isViewer, project, kind, file, highlighter)
|
||||
editor.putEditorId()
|
||||
// must be _before_ event firing
|
||||
|
||||
@@ -37,7 +37,7 @@ import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.editor.ex.util.EmptyEditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterClient;
|
||||
import com.intellij.openapi.editor.impl.ad.v2.AdTheManagerV2;
|
||||
import com.intellij.openapi.editor.impl.ad.AdTheManager;
|
||||
import com.intellij.openapi.editor.impl.event.MarkupModelListener;
|
||||
import com.intellij.openapi.editor.impl.stickyLines.StickyLinesManager;
|
||||
import com.intellij.openapi.editor.impl.stickyLines.StickyLinesModel;
|
||||
@@ -342,7 +342,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
final EditorView myView;
|
||||
final @Nullable EditorView myAdView;
|
||||
private static final boolean AD_V2 = Registry.is("ijpl.rhizome.ad.v2.enabled", false);
|
||||
|
||||
private boolean myCharKeyPressed;
|
||||
private boolean myNeedToSelectPreviousChar;
|
||||
@@ -479,27 +478,22 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
myEditorModel = new EditorModelImpl(this);
|
||||
|
||||
if (AD_V2) {
|
||||
DocumentEx adDocumentV2 = AdTheManagerV2.getInstance().getAdDocument(myDocument);
|
||||
myAdEditorModel = adDocumentV2 == null ? null : new EditorModel() {
|
||||
@Override public DocumentEx getDocument() { return adDocumentV2; }
|
||||
@Override public MarkupModelEx getEditorMarkupModel() { return myMarkupModel; }
|
||||
@Override public MarkupModelEx getDocumentMarkupModel() { return myEditorFilteringMarkupModel; }
|
||||
@Override public EditorHighlighter getHighlighter() { return myHighlighter; }
|
||||
@Override public InlayModelEx getInlayModel() { return myInlayModel; }
|
||||
@Override public FoldingModelEx getFoldingModel() { return myFoldingModel; }
|
||||
@Override public SoftWrapModelEx getSoftWrapModel() { return mySoftWrapModel; }
|
||||
@Override public CaretModel getCaretModel() { return myCaretModel; }
|
||||
@Override public SelectionModel getSelectionModel() { return mySelectionModel; }
|
||||
@Override public ScrollingModel getScrollingModel() { return myScrollingModel; }
|
||||
@Override public FocusModeModel getFocusModel() { return myFocusModeModel; }
|
||||
@Override public boolean isAd() { return true; }
|
||||
@Override public void dispose() { AdTheManagerV2.getInstance().releaseDocEntity(myDocument); }
|
||||
};
|
||||
}
|
||||
else {
|
||||
myAdEditorModel = project == null ? null : AdTheManager.getInstance(project).createEditorModel(this);
|
||||
}
|
||||
DocumentEx adDocument = AdTheManager.getInstance().getAdDocument(myDocument);
|
||||
myAdEditorModel = adDocument == null ? null : new EditorModel() {
|
||||
@Override public DocumentEx getDocument() { return adDocument; }
|
||||
@Override public MarkupModelEx getEditorMarkupModel() { return myMarkupModel; }
|
||||
@Override public MarkupModelEx getDocumentMarkupModel() { return myEditorFilteringMarkupModel; }
|
||||
@Override public EditorHighlighter getHighlighter() { return myHighlighter; }
|
||||
@Override public InlayModelEx getInlayModel() { return myInlayModel; }
|
||||
@Override public FoldingModelEx getFoldingModel() { return myFoldingModel; }
|
||||
@Override public SoftWrapModelEx getSoftWrapModel() { return mySoftWrapModel; }
|
||||
@Override public CaretModel getCaretModel() { return myCaretModel; }
|
||||
@Override public SelectionModel getSelectionModel() { return mySelectionModel; }
|
||||
@Override public ScrollingModel getScrollingModel() { return myScrollingModel; }
|
||||
@Override public FocusModeModel getFocusModel() { return myFocusModeModel; }
|
||||
@Override public boolean isAd() { return true; }
|
||||
@Override public void dispose() { AdTheManager.getInstance().releaseDocEntity(myDocument); }
|
||||
};
|
||||
|
||||
myView = new EditorView(this, myEditorModel);
|
||||
myAdView = myAdEditorModel == null ? null : new EditorView(this, myAdEditorModel);
|
||||
@@ -532,8 +526,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
addListeners();
|
||||
|
||||
if (AD_V2 && myAdEditorModel != null) {
|
||||
AdTheManagerV2.getInstance().bindEditor(this);
|
||||
if (myAdEditorModel != null) {
|
||||
AdTheManager.getInstance().bindEditor(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1188,9 +1182,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
Disposer.dispose(myView);
|
||||
if (myAdView != null) {
|
||||
Disposer.dispose(myAdView);
|
||||
if (AD_V2) {
|
||||
Disposer.dispose(myAdEditorModel);
|
||||
}
|
||||
Disposer.dispose(myAdEditorModel);
|
||||
}
|
||||
clearCaretThread();
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
// Copyright 2000-2025 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
|
||||
|
||||
import andel.editor.RangeMarkerId
|
||||
import andel.intervals.Interval
|
||||
import andel.operation.Operation
|
||||
import andel.operation.captureOperation
|
||||
import andel.operation.rebase
|
||||
import andel.text.*
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.editor.RangeMarker
|
||||
@@ -14,65 +9,16 @@ import com.intellij.openapi.editor.ex.DocumentEx
|
||||
import com.intellij.openapi.editor.ex.EditReadOnlyListener
|
||||
import com.intellij.openapi.editor.ex.LineIterator
|
||||
import com.intellij.openapi.editor.ex.RangeMarkerEx
|
||||
import com.intellij.platform.pasta.common.DocumentEntity
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.util.concurrency.ThreadingAssertions
|
||||
import com.jetbrains.rhizomedb.ChangeScope
|
||||
import com.jetbrains.rhizomedb.DbContext
|
||||
import com.jetbrains.rhizomedb.asOf
|
||||
import com.jetbrains.rhizomedb.requireChangeScope
|
||||
import fleet.util.UID
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental
|
||||
import java.beans.PropertyChangeListener
|
||||
import java.util.*
|
||||
|
||||
@Experimental
|
||||
internal class AdDocument(
|
||||
private val name: String,
|
||||
private val entity: AdDocumentEntity,
|
||||
) : DocumentEx {
|
||||
|
||||
internal fun replaceString(startOffset: Int, endOffset: Int, chars: CharSequence, modStamp: Long) {
|
||||
performTextOperation(modStamp) {
|
||||
textView().replaceOperation(startOffset, endOffset, chars.toString())
|
||||
}
|
||||
}
|
||||
|
||||
internal fun queryRangeMarkers(startOffset: Long, endOffset: Long): Sequence<Interval<UID, Unit>> {
|
||||
return documentRead {
|
||||
entity.anchorStorage.intervals.query(startOffset, endOffset)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun addRangeMarkers(rangeIds: List<RangeMarkerId>, ranges: List<andel.text.TextRange>) {
|
||||
documentChange {
|
||||
entity[AdDocumentEntity.AnchorStorageAttr] = entity.anchorStorage.batchUpdate(
|
||||
emptyList(),
|
||||
LongArray(0),
|
||||
rangeIds,
|
||||
ranges, // TODO: greedy
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun addRangeMarker(rangeId: RangeMarkerId, startOffset: Long, endOffset: Long) {
|
||||
documentChange {
|
||||
entity[AdDocumentEntity.AnchorStorageAttr] = entity.anchorStorage.addRangeMarker(
|
||||
rangeId,
|
||||
startOffset,
|
||||
endOffset,
|
||||
false, // TODO: greedy
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun removeRangeMarker(rangeId: RangeMarkerId) {
|
||||
documentChange {
|
||||
entity[AdDocumentEntity.AnchorStorageAttr] = entity.anchorStorage.removeRangeMarker(rangeId)
|
||||
}
|
||||
}
|
||||
internal class AdDocument(private val entity: DocumentEntity) : DocumentEx {
|
||||
|
||||
// region TEXT
|
||||
|
||||
@@ -214,7 +160,7 @@ internal class AdDocument(
|
||||
|
||||
override fun getModificationStamp(): Long {
|
||||
return documentRead {
|
||||
entity.modStamp
|
||||
entity.editLog.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,50 +307,8 @@ internal class AdDocument(
|
||||
return text().view()
|
||||
}
|
||||
|
||||
private fun textMutableView(): MutableTextView {
|
||||
return text().mutableView()
|
||||
}
|
||||
|
||||
private fun <T> documentRead(block: () -> T): T {
|
||||
if (DbContext.threadLocal.get() != null) {
|
||||
return block()
|
||||
}
|
||||
|
||||
ThreadingAssertions.assertBackgroundThread() // DB always exists w/i EDT
|
||||
|
||||
// TODO: lastKnownOrPendingDb
|
||||
return asOf(ThreadLocalRhizomeDB.lastKnownDb()) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> documentChange(block: ChangeScope.() -> T): T {
|
||||
return requireChangeScope {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
private fun performTextOperation(modStamp: Long, operationLambda: ChangeScope.() -> Operation) {
|
||||
documentChange {
|
||||
val operationId = UID.random()
|
||||
val textBefore = entity.text
|
||||
val doc = entity.asAndelDocument()
|
||||
val versionedOperation = doc.captureOperation(operationLambda())
|
||||
val operation = versionedOperation.rebase(doc)
|
||||
|
||||
val textAfter = textMutableView().apply { edit(operation) }.text()
|
||||
val editLogAfter = entity.editLog.append(operationId, operation).trim()
|
||||
val anchorStorageAfter = entity.anchorStorage.edit(textBefore, textAfter, operation)
|
||||
|
||||
entity[AdDocumentEntity.TextAttr] = textAfter
|
||||
entity[AdDocumentEntity.ModStampAttr] = modStamp
|
||||
entity[AdDocumentEntity.EditLogAttr] = editLogAfter
|
||||
entity[AdDocumentEntity.AnchorStorageAttr] = anchorStorageAfter
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return name
|
||||
return block()
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright 2000-2025 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
|
||||
|
||||
import andel.editor.AnchorId
|
||||
import andel.editor.RangeMarkerId
|
||||
import andel.intervals.AnchorStorage
|
||||
import andel.operation.EditLog
|
||||
import andel.text.Text
|
||||
import andel.text.TextRange
|
||||
import com.jetbrains.rhizomedb.Attributes
|
||||
import com.jetbrains.rhizomedb.EID
|
||||
import com.jetbrains.rhizomedb.Entity
|
||||
import com.jetbrains.rhizomedb.requireChangeScope
|
||||
import fleet.kernel.DurableEntityType
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
private typealias Att<T> = Attributes<AdDocumentEntity>.Required<T>
|
||||
|
||||
@Experimental
|
||||
@Internal
|
||||
class AdDocumentEntity(override val eid: EID) : Entity {
|
||||
val text: Text by TextAttr
|
||||
val modStamp: Long by ModStampAttr
|
||||
val editLog: EditLog by EditLogAttr
|
||||
val anchorStorage: AnchorStorage by AnchorStorageAttr
|
||||
|
||||
companion object : DurableEntityType<AdDocumentEntity>(AdDocumentEntity::class, ::AdDocumentEntity) {
|
||||
val TextAttr: Att<Text> = requiredValue("text", Text.serializer())
|
||||
val ModStampAttr: Att<Long> = requiredValue("modStamp", Long.serializer())
|
||||
val EditLogAttr: Att<EditLog> = requiredValue("editLog", EditLog.serializer())
|
||||
val AnchorStorageAttr: Att<AnchorStorage> = requiredValue("anchorStorage", AnchorStorage.serializer())
|
||||
|
||||
fun fromString(text: String, modStamp: Long): AdDocumentEntity = requireChangeScope {
|
||||
AdDocumentEntity.new {
|
||||
it[TextAttr] = Text.fromString(text)
|
||||
it[ModStampAttr] = modStamp
|
||||
it[EditLogAttr] = EditLog.empty()
|
||||
it[AnchorStorageAttr] = AnchorStorage.empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun asAndelDocument(): andel.editor.Document {
|
||||
return object : andel.editor.Document {
|
||||
override val text: Text get() = this@AdDocumentEntity.text
|
||||
override val timestamp: Long = editLog.timestamp
|
||||
override val edits: EditLog get() = editLog
|
||||
override fun resolveAnchor(anchorId: AnchorId): Long? = anchorStorage.resolveAnchor(anchorId)
|
||||
override fun resolveRangeMarker(markerId: RangeMarkerId): TextRange? = anchorStorage.resolveRangeMarker(markerId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "AdDocumentEntity(eid=$eid, modStamp=$modStamp, text=$text)"
|
||||
}
|
||||
}
|
||||
+39
-40
@@ -1,62 +1,61 @@
|
||||
// Copyright 2000-2025 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
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import andel.operation.Operation
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.ex.PrioritizedDocumentListener
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.pasta.common.DocumentEntity
|
||||
import fleet.kernel.awaitCommitted
|
||||
import fleet.kernel.change
|
||||
import fleet.kernel.shared
|
||||
import fleet.util.UID
|
||||
import fleet.util.openmap.OpenMap
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental
|
||||
|
||||
|
||||
@Experimental
|
||||
internal class AdDocumentSynchronizer(
|
||||
private val adDocument: AdDocument,
|
||||
internal data class AdDocumentSynchronizer(
|
||||
private val documentId: Any, // RdDocumentId
|
||||
private val entityId: UID,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val repaintLambda: suspend () -> Unit,
|
||||
) : PrioritizedDocumentListener, Disposable.Default {
|
||||
private val entityDeferred: Deferred<DocumentEntity>,
|
||||
): PrioritizedDocumentListener {
|
||||
|
||||
override fun getPriority(): Int {
|
||||
return Integer.MIN_VALUE + 1
|
||||
}
|
||||
override fun getPriority(): Int = Int.MIN_VALUE + 1
|
||||
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
val realChars = event.document.immutableCharSequence
|
||||
coroutineScope.launch {
|
||||
if (isDebugMode()) {
|
||||
delay(500)
|
||||
}
|
||||
change {
|
||||
shared {
|
||||
adDocument.replaceString(
|
||||
startOffset = event.offset,
|
||||
endOffset = event.offset + event.oldLength,
|
||||
chars = event.newFragment,
|
||||
modStamp = event.document.modificationStamp,
|
||||
)
|
||||
val entityChange = coroutineScope.async {
|
||||
val operation = operation(event)
|
||||
val entity = entityDeferred.await()
|
||||
change { // it is a shared change
|
||||
entity.mutate(this, OpenMap.empty()) {
|
||||
edit(operation)
|
||||
}
|
||||
}
|
||||
val adChars = adDocument.immutableCharSequence
|
||||
if (realChars.hashCode() != adChars.hashCode()) {
|
||||
assert(realChars.toString() == adChars.toString()) {
|
||||
"""
|
||||
AdDocument is out of sync, expected chars:
|
||||
$realChars
|
||||
|
||||
but encountered:
|
||||
$adChars
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
repaintLambda.invoke()
|
||||
awaitCommitted()
|
||||
}
|
||||
// TODO: pumping events in runWithModalProgressBlocking ruins the models
|
||||
//@Suppress("HardCodedStringLiteral")
|
||||
//runWithModalProgressBlocking(
|
||||
// ModalTaskOwner.guess(),
|
||||
// "Shared document entity synchronization $entityId",
|
||||
//) { entityChange.await() }
|
||||
runBlocking { entityChange.await() }
|
||||
ThreadLocalRhizomeDB.setThreadLocalDb(ThreadLocalRhizomeDB.lastKnownDb())
|
||||
}
|
||||
|
||||
private fun isDebugMode(): Boolean {
|
||||
return Registry.`is`("ijpl.rhizome.ad.debug.enabled", false)
|
||||
private fun operation(event: DocumentEvent): Operation {
|
||||
val oldFragment = event.oldFragment.toString()
|
||||
val newFragment = event.newFragment.toString()
|
||||
val lengthBefore = event.document.textLength - newFragment.length + oldFragment.length
|
||||
return Operation.Companion.replaceAt(
|
||||
offset = event.offset.toLong(),
|
||||
oldText = oldFragment,
|
||||
newText = newFragment,
|
||||
totalLength = lengthBefore.toLong(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,276 +0,0 @@
|
||||
// Copyright 2000-2025 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
|
||||
|
||||
import andel.editor.RangeMarkerId
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.editor.*
|
||||
import com.intellij.openapi.editor.ex.InlayModelEx
|
||||
import com.intellij.openapi.editor.impl.InlayModelImpl
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.jetbrains.rhizomedb.ChangeScope
|
||||
import fleet.kernel.change
|
||||
import fleet.kernel.shared
|
||||
import fleet.util.UID
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental
|
||||
import java.awt.Point
|
||||
import java.awt.Rectangle
|
||||
import andel.text.TextRange as AndelTextRange
|
||||
|
||||
|
||||
@Experimental
|
||||
internal class AdInlayModel private constructor(
|
||||
private val adDocument: AdDocument,
|
||||
private val mapper: AdRangeMapper<Inlay<*>>,
|
||||
private val inlayModel: InlayModelImpl,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val repaintLambda: suspend () -> Unit,
|
||||
) : InlayModelEx, Disposable {
|
||||
|
||||
companion object {
|
||||
fun fromInlays(
|
||||
inlayModel: InlayModelImpl,
|
||||
adDocument: AdDocument,
|
||||
coroutineScope: CoroutineScope,
|
||||
repaintLambda: suspend () -> Unit,
|
||||
): AdInlayModel {
|
||||
val mapper = AdRangeMapper<Inlay<*>>()
|
||||
val rangesIds = mutableListOf<RangeMarkerId>()
|
||||
val ranges = mutableListOf<AndelTextRange>()
|
||||
for (inlay in inlayModel.getInlineElementsInRange(0, Integer.MAX_VALUE)) {
|
||||
val rangeId = UID.random()
|
||||
rangesIds.add(RangeMarkerId(rangeId))
|
||||
ranges.add(AndelTextRange(inlay.offset, inlay.offset))
|
||||
mapper.register(rangeId, inlay)
|
||||
}
|
||||
adDocument.addRangeMarkers(rangesIds, ranges)
|
||||
return AdInlayModel(adDocument, mapper, inlayModel, coroutineScope, repaintLambda)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val listener = object : InlayModel.Listener {
|
||||
override fun onAdded(inlay: Inlay<*>) {
|
||||
onInlayUpdate {
|
||||
// TODO: incorrect offset -> ui offset
|
||||
val rangeId = UID.random()
|
||||
adDocument.addRangeMarker(
|
||||
RangeMarkerId(rangeId),
|
||||
inlay.offset.toLong(),
|
||||
inlay.offset.toLong(),
|
||||
)
|
||||
mapper.register(rangeId, inlay)
|
||||
}
|
||||
}
|
||||
override fun onRemoved(inlay: Inlay<*>) {
|
||||
onInlayUpdate {
|
||||
val rangeId = mapper.unregister(inlay)
|
||||
if (rangeId != null) {
|
||||
adDocument.removeRangeMarker(RangeMarkerId(rangeId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
coroutineScope.launch(Dispatchers.EDT) {
|
||||
inlayModel.addListener(listener, this@AdInlayModel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onInlayUpdate(body: ChangeScope.() -> Unit) {
|
||||
coroutineScope.launch {
|
||||
change {
|
||||
shared {
|
||||
body()
|
||||
}
|
||||
}
|
||||
repaintLambda.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBlockElementsForVisualLine(visualLine: Int, above: Boolean): List<Inlay<*>?> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun getAfterLineEndElementsForLogicalLine(logicalLine: Int): List<Inlay<*>?> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun getInlineElementsInRange(startOffset: Int, endOffset: Int): List<Inlay<*>> {
|
||||
val inlays = mutableListOf<Inlay<*>>()
|
||||
for (interval in adDocument.queryRangeMarkers(startOffset.toLong(), endOffset.toLong())) {
|
||||
val inlay = mapper.resolveRange(interval.id)
|
||||
if (inlay != null) {
|
||||
val offset = interval.from.toInt()
|
||||
inlays.add(AdInlay(offset, inlay))
|
||||
}
|
||||
}
|
||||
return inlays
|
||||
}
|
||||
|
||||
override fun getAfterLineEndElementsInRange(startOffset: Int, endOffset: Int): List<Inlay<*>?> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun getBlockElementsInRange(startOffset: Int, endOffset: Int): List<Inlay<*>?> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* FoldingModel
|
||||
* CaretModel
|
||||
* EditorImpl
|
||||
* SoftWrapModel
|
||||
* EditorSizeManager
|
||||
*/
|
||||
override fun addListener(listener: InlayModel.Listener, disposable: Disposable) {
|
||||
}
|
||||
|
||||
override fun isInBatchMode(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getHeightOfBlockElementsBeforeVisualLine(visualLine: Int, startOffset: Int, prevFoldRegionIndex: Int): Int {
|
||||
return inlayModel.getHeightOfBlockElementsBeforeVisualLine(visualLine, startOffset, prevFoldRegionIndex)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
|
||||
// region Not yet implemented
|
||||
|
||||
override fun <T : EditorCustomElementRenderer?> addInlineElement(offset: Int, relatesToPrecedingText: Boolean, renderer: T & Any): Inlay<T?>? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : EditorCustomElementRenderer?> addInlineElement(offset: Int, relatesToPrecedingText: Boolean, priority: Int, renderer: T & Any): Inlay<T?>? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : EditorCustomElementRenderer?> addInlineElement(offset: Int, properties: InlayProperties, renderer: T & Any): Inlay<T?>? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : EditorCustomElementRenderer?> addBlockElement(offset: Int, relatesToPrecedingText: Boolean, showAbove: Boolean, priority: Int, renderer: T & Any): Inlay<T?>? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : EditorCustomElementRenderer?> addBlockElement(offset: Int, properties: InlayProperties, renderer: T & Any): Inlay<T?>? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : EditorCustomElementRenderer?> addAfterLineEndElement(offset: Int, relatesToPrecedingText: Boolean, renderer: T & Any): Inlay<T?>? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : EditorCustomElementRenderer?> addAfterLineEndElement(offset: Int, properties: InlayProperties, renderer: T & Any): Inlay<T?>? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun hasInlineElementAt(offset: Int): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getInlineElementAt(visualPosition: VisualPosition): Inlay<*>? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getElementAt(point: Point): Inlay<*>? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setConsiderCaretPositionOnDocumentUpdates(enabled: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun execute(batchMode: Boolean, operation: Runnable) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
private class AdInlay<T : EditorCustomElementRenderer>(
|
||||
private val offset: Int,
|
||||
private val origin: Inlay<T>,
|
||||
) : Inlay<T> {
|
||||
|
||||
override fun getOffset(): Int {
|
||||
return offset
|
||||
}
|
||||
|
||||
override fun getHeightInPixels(): Int {
|
||||
return origin.getHeightInPixels()
|
||||
}
|
||||
|
||||
override fun getRenderer(): T {
|
||||
return origin.getRenderer()
|
||||
}
|
||||
|
||||
override fun getWidthInPixels(): Int {
|
||||
return origin.getWidthInPixels()
|
||||
}
|
||||
|
||||
override fun getPlacement(): Inlay.Placement {
|
||||
return origin.getPlacement()
|
||||
}
|
||||
|
||||
override fun isRelatedToPrecedingText(): Boolean {
|
||||
return origin.isRelatedToPrecedingText()
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
return origin.isValid
|
||||
}
|
||||
|
||||
override fun <T : Any?> getUserData(key: Key<T?>): T? {
|
||||
return origin.getUserData(key)
|
||||
}
|
||||
|
||||
override fun getEditor(): Editor {
|
||||
return origin.editor
|
||||
}
|
||||
|
||||
// region Not yet implemented
|
||||
|
||||
override fun getVisualPosition(): VisualPosition {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getBounds(): Rectangle? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getGutterIconRenderer(): GutterIconRenderer? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun update() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun repaint() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : Any?> putUserDataIfAbsent(key: Key<T?>, value: T & Any): T & Any {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : Any?> replace(key: Key<T?>, oldValue: T?, newValue: T?): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : Any?> putUserData(key: Key<T?>, value: T?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
@@ -1,408 +0,0 @@
|
||||
// Copyright 2000-2025 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
|
||||
|
||||
import andel.editor.RangeMarkerId
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import com.intellij.openapi.editor.ex.MarkupIterator
|
||||
import com.intellij.openapi.editor.ex.MarkupModelEx
|
||||
import com.intellij.openapi.editor.ex.RangeHighlighterEx
|
||||
import com.intellij.openapi.editor.impl.event.MarkupModelListener
|
||||
import com.intellij.openapi.editor.markup.*
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.util.Consumer
|
||||
import com.intellij.util.Processor
|
||||
import com.jetbrains.rhizomedb.ChangeScope
|
||||
import fleet.kernel.change
|
||||
import fleet.kernel.shared
|
||||
import fleet.util.UID
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.awt.Color
|
||||
import andel.text.TextRange as AndelTextRange
|
||||
|
||||
|
||||
@ApiStatus.Experimental
|
||||
internal class AdMarkupModel private constructor(
|
||||
private val adDocument: AdDocument,
|
||||
private val mapper: AdRangeMapper<RangeHighlighterEx>,
|
||||
markupModel: MarkupModelEx,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val repaintLambda: suspend () -> Unit,
|
||||
) : MarkupModelEx, Disposable {
|
||||
|
||||
companion object {
|
||||
fun fromMarkup(
|
||||
markupModel: MarkupModelEx,
|
||||
adDocument: AdDocument,
|
||||
coroutineScope: CoroutineScope,
|
||||
repaintLambda: suspend () -> Unit,
|
||||
): AdMarkupModel {
|
||||
val mapper = addExistingHighlighters(markupModel, adDocument)
|
||||
val adMarkupModel = AdMarkupModel(adDocument, mapper, markupModel, coroutineScope, repaintLambda)
|
||||
return adMarkupModel
|
||||
}
|
||||
|
||||
private fun addExistingHighlighters(markupModel: MarkupModelEx, adDocument: AdDocument): AdRangeMapper<RangeHighlighterEx> {
|
||||
// TODO: there is a gap between allHighlighters and addMarkupModelListener, a highlighter may be missed
|
||||
val mapper = AdRangeMapper<RangeHighlighterEx>()
|
||||
val rangesIds = mutableListOf<RangeMarkerId>()
|
||||
val ranges = mutableListOf<AndelTextRange>()
|
||||
for (highlighter in markupModel.allHighlighters) {
|
||||
if (highlighter.isValid) {
|
||||
val rangeId = UID.random()
|
||||
rangesIds.add(RangeMarkerId(rangeId))
|
||||
ranges.add(AndelTextRange(highlighter.startOffset, highlighter.endOffset))
|
||||
mapper.register(rangeId, highlighter as RangeHighlighterEx)
|
||||
}
|
||||
}
|
||||
adDocument.addRangeMarkers(rangesIds, ranges)
|
||||
return mapper
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
markupModel.addMarkupModelListener(this, object : MarkupModelListener {
|
||||
override fun afterAdded(highlighter: RangeHighlighterEx) {
|
||||
onHighlightUpdate {
|
||||
// TODO: incorrect offset -> ui offset
|
||||
val rangeId = UID.random()
|
||||
adDocument.addRangeMarker(
|
||||
RangeMarkerId(rangeId),
|
||||
highlighter.startOffset.toLong(),
|
||||
highlighter.endOffset.toLong(),
|
||||
)
|
||||
mapper.register(rangeId, highlighter)
|
||||
}
|
||||
}
|
||||
override fun afterRemoved(highlighter: RangeHighlighterEx) {
|
||||
onHighlightUpdate {
|
||||
val rangeId = mapper.unregister(highlighter)
|
||||
if (rangeId != null) {
|
||||
adDocument.removeRangeMarker(RangeMarkerId(rangeId))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun processRangeHighlightersOverlappingWith(
|
||||
start: Int,
|
||||
end: Int,
|
||||
processor: Processor<in RangeHighlighterEx>,
|
||||
): Boolean {
|
||||
for (interval in adDocument.queryRangeMarkers(start.toLong(), end.toLong())) {
|
||||
val rh = mapper.resolveRange(interval.id)
|
||||
if (rh != null && rh.isValid) {
|
||||
val startOffset = interval.from.toInt()
|
||||
val endOffset = interval.to.toInt()
|
||||
val proceed = processor.process(AdRangeHighlighter(
|
||||
startOffset,
|
||||
endOffset,
|
||||
affectedStartOffset(startOffset, rh.targetArea),
|
||||
affectedEndOffset(endOffset, rh.targetArea),
|
||||
rh
|
||||
))
|
||||
if (!proceed) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
|
||||
private fun onHighlightUpdate(body: ChangeScope.() -> Unit) {
|
||||
coroutineScope.launch {
|
||||
change {
|
||||
shared {
|
||||
body()
|
||||
}
|
||||
}
|
||||
repaintLambda.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun affectedStartOffset(startOffset: Int, area: HighlighterTargetArea): Int {
|
||||
if (area == HighlighterTargetArea.EXACT_RANGE) {
|
||||
return startOffset
|
||||
}
|
||||
val line = adDocument.getLineNumber(startOffset)
|
||||
return adDocument.getLineStartOffset(line)
|
||||
}
|
||||
|
||||
private fun affectedEndOffset(endOffset: Int, area: HighlighterTargetArea): Int {
|
||||
if (area == HighlighterTargetArea.EXACT_RANGE) {
|
||||
return endOffset
|
||||
}
|
||||
val line = adDocument.getLineNumber(endOffset)
|
||||
return adDocument.getLineEndOffset(line)
|
||||
}
|
||||
|
||||
// region Not yet implemented
|
||||
|
||||
override fun addPersistentLineHighlighter(textAttributesKey: TextAttributesKey?, lineNumber: Int, layer: Int): RangeHighlighterEx? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addPersistentLineHighlighter(lineNumber: Int, layer: Int, textAttributes: TextAttributes?): RangeHighlighterEx? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun containsHighlighter(highlighter: RangeHighlighter): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addMarkupModelListener(parentDisposable: Disposable, listener: MarkupModelListener) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setRangeHighlighterAttributes(highlighter: RangeHighlighter, textAttributes: TextAttributes) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun processRangeHighlightersOutside(start: Int, end: Int, processor: Processor<in RangeHighlighterEx>): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun overlappingIterator(startOffset: Int, endOffset: Int): MarkupIterator<RangeHighlighterEx?> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addRangeHighlighterAndChangeAttributes(textAttributesKey: TextAttributesKey?, startOffset: Int, endOffset: Int, layer: Int, targetArea: HighlighterTargetArea, isPersistent: Boolean, changeAttributesAction: Consumer<in RangeHighlighterEx>?): RangeHighlighterEx {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun changeAttributesInBatch(highlighter: RangeHighlighterEx, changeAttributesAction: Consumer<in RangeHighlighterEx>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getDocument(): Document {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addRangeHighlighter(textAttributesKey: TextAttributesKey?, startOffset: Int, endOffset: Int, layer: Int, targetArea: HighlighterTargetArea): RangeHighlighter {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addRangeHighlighter(startOffset: Int, endOffset: Int, layer: Int, textAttributes: TextAttributes?, targetArea: HighlighterTargetArea): RangeHighlighter {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addLineHighlighter(textAttributesKey: TextAttributesKey?, line: Int, layer: Int): RangeHighlighter {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addLineHighlighter(line: Int, layer: Int, textAttributes: TextAttributes?): RangeHighlighter {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun removeHighlighter(rangeHighlighter: RangeHighlighter) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun removeAllHighlighters() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getAllHighlighters(): Array<out RangeHighlighter> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : Any?> getUserData(key: Key<T?>): T? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : Any?> putUserData(key: Key<T?>, value: T?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
private class AdRangeHighlighter(
|
||||
private val startOffset: Int,
|
||||
private val endOffset: Int,
|
||||
private val startAffectedOffset: Int,
|
||||
private val endAffectedOffset: Int,
|
||||
private val origin: RangeHighlighterEx,
|
||||
) : RangeHighlighterEx by origin {
|
||||
|
||||
override fun isValid(): Boolean = true
|
||||
override fun getStartOffset(): Int = startOffset
|
||||
override fun getEndOffset(): Int = endOffset
|
||||
override fun getAffectedAreaStartOffset(): Int = startAffectedOffset
|
||||
override fun getAffectedAreaEndOffset(): Int = endAffectedOffset
|
||||
|
||||
override fun getCustomRenderer(): CustomHighlighterRenderer? = origin.customRenderer // TODO
|
||||
override fun getLineSeparatorPlacement(): SeparatorPlacement? = origin.lineSeparatorPlacement // TODO
|
||||
override fun getLineSeparatorRenderer(): LineSeparatorRenderer? = origin.lineSeparatorRenderer // TODO
|
||||
|
||||
override fun getLineSeparatorColor(): Color? = origin.lineSeparatorColor
|
||||
override fun isAfterEndOfLine(): Boolean = origin.isAfterEndOfLine
|
||||
override fun getTextAttributes(scheme: EditorColorsScheme?): TextAttributes? = origin.getTextAttributes(scheme)
|
||||
override fun isVisibleIfFolded(): Boolean = origin.isVisibleIfFolded
|
||||
override fun getLayer(): Int = origin.layer
|
||||
override fun getTargetArea(): HighlighterTargetArea = origin.targetArea
|
||||
override fun getErrorStripeTooltip(): Any? = origin.errorStripeTooltip
|
||||
|
||||
// region Not yet implemented
|
||||
|
||||
override fun getId(): Long {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getTextRange(): TextRange {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getForcedTextAttributes(): TextAttributes? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getForcedErrorStripeMarkColor(): Color? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setAfterEndOfLine(value: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun fireChanged(renderersChanged: Boolean, fontStyleChanged: Boolean, foregroundColorChanged: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setTextAttributes(textAttributes: TextAttributes?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setVisibleIfFolded(value: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun isPersistent(): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun isRenderedInGutter(): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun copyFrom(other: RangeHighlighterEx) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getTextAttributesKey(): TextAttributesKey? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setTextAttributesKey(textAttributesKey: TextAttributesKey) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
override fun getTextAttributes(): TextAttributes? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getLineMarkerRenderer(): LineMarkerRenderer? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setLineMarkerRenderer(renderer: LineMarkerRenderer?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setCustomRenderer(renderer: CustomHighlighterRenderer?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getGutterIconRenderer(): GutterIconRenderer? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setGutterIconRenderer(renderer: GutterIconRenderer?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getErrorStripeMarkColor(scheme: EditorColorsScheme?): Color? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setErrorStripeMarkColor(color: Color?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setErrorStripeTooltip(tooltipObject: @NlsContexts.Tooltip Any?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun isThinErrorStripeMark(): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setThinErrorStripeMark(value: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setLineSeparatorColor(color: Color?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setLineSeparatorRenderer(renderer: LineSeparatorRenderer?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setLineSeparatorPlacement(placement: SeparatorPlacement?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setEditorFilter(filter: MarkupEditorFilter) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getEditorFilter(): MarkupEditorFilter {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getDocument(): Document {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setGreedyToLeft(greedy: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setGreedyToRight(greedy: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun isGreedyToRight(): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun isGreedyToLeft(): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : Any?> getUserData(key: Key<T?>): T? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : Any?> putUserData(key: Key<T?>, value: T?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2000-2025 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
|
||||
|
||||
import fleet.util.UID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
internal class AdRangeMapper<T> {
|
||||
private val idToRange = ConcurrentHashMap<UID, T>()
|
||||
private val rangeToId = ConcurrentHashMap<T, UID>()
|
||||
|
||||
fun resolveRange(id: UID): T? {
|
||||
return idToRange[id]
|
||||
}
|
||||
|
||||
fun register(id: UID, range: T) {
|
||||
idToRange.put(id, range)
|
||||
rangeToId.put(range, id)
|
||||
}
|
||||
|
||||
fun unregister(range: T): UID? {
|
||||
val id = rangeToId.remove(range)
|
||||
if (id != null) {
|
||||
idToRange.remove(id)
|
||||
}
|
||||
return id
|
||||
}
|
||||
}
|
||||
+7
-23
@@ -1,8 +1,6 @@
|
||||
// Copyright 2000-2025 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.v2
|
||||
package com.intellij.openapi.editor.impl.ad
|
||||
|
||||
import andel.intervals.AnchorStorage
|
||||
import andel.text.Text
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.isRhizomeAdEnabled
|
||||
@@ -13,24 +11,16 @@ import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ex.DocumentEx
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||
import com.intellij.openapi.editor.impl.ad.ThreadLocalRhizomeDB
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.platform.ide.progress.ModalTaskOwner
|
||||
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
|
||||
import com.intellij.platform.pasta.common.DocumentEntity
|
||||
import com.intellij.platform.pasta.common.DocumentEntity.Companion.EditLogAttr
|
||||
import com.intellij.platform.pasta.common.DocumentEntity.Companion.SharedAnchorStorageAttr
|
||||
import com.intellij.platform.pasta.common.DocumentEntity.Companion.TextAttr
|
||||
import com.intellij.platform.pasta.common.DocumentEntity.Companion.WritableAttr
|
||||
import com.intellij.platform.pasta.common.createEmptyEditLog
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import com.intellij.util.awaitCancellationAndInvoke
|
||||
import com.intellij.util.concurrency.AppExecutorUtil
|
||||
import com.intellij.util.concurrency.ThreadingAssertions
|
||||
import com.intellij.util.ui.EDT
|
||||
import fleet.kernel.Durable
|
||||
import fleet.kernel.change
|
||||
import fleet.kernel.rete.each
|
||||
import fleet.kernel.rete.filter
|
||||
@@ -49,11 +39,11 @@ private val AD_DISPATCHER by lazy {
|
||||
|
||||
@Experimental
|
||||
@Service(Level.APP)
|
||||
class AdTheManagerV2(private val coroutineScope: CoroutineScope) {
|
||||
class AdTheManager(private val coroutineScope: CoroutineScope) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(): AdTheManagerV2 = service()
|
||||
fun getInstance(): AdTheManager = service()
|
||||
}
|
||||
|
||||
private val docToHandle = IdentityHashMap<DocumentEx, DocumentEntityHandle>()
|
||||
@@ -119,7 +109,7 @@ class AdTheManagerV2(private val coroutineScope: CoroutineScope) {
|
||||
}?.entity()
|
||||
if (entity != null) {
|
||||
ThreadLocalRhizomeDB.setThreadLocalDb(ThreadLocalRhizomeDB.lastKnownDb())
|
||||
return AdDocumentV2(entity)
|
||||
return AdDocument(entity)
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -157,13 +147,7 @@ class AdTheManagerV2(private val coroutineScope: CoroutineScope) {
|
||||
createEntity = { entityId ->
|
||||
change {
|
||||
shared {
|
||||
DocumentEntity.new {
|
||||
it[Durable.Id] = entityId
|
||||
it[TextAttr] = Text.fromString(text.toString())
|
||||
it[WritableAttr] = true
|
||||
it[EditLogAttr] = createEmptyEditLog()
|
||||
it[SharedAnchorStorageAttr] = AnchorStorage.empty()
|
||||
}
|
||||
DocumentEntity.fromText(entityId, text.toString())
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -203,7 +187,7 @@ class AdTheManagerV2(private val coroutineScope: CoroutineScope) {
|
||||
entity
|
||||
}
|
||||
if (bindType != BindType.FRONTEND) {
|
||||
val documentListener = AdDocumentSynchronizerV2(documentId, entityId, cs, entityDeferred)
|
||||
val documentListener = AdDocumentSynchronizer(documentId, entityId, cs, entityDeferred)
|
||||
document.addDocumentListener(documentListener)
|
||||
@Suppress("OPT_IN_USAGE")
|
||||
cs.awaitCancellationAndInvoke {
|
||||
@@ -240,7 +224,7 @@ class AdTheManagerV2(private val coroutineScope: CoroutineScope) {
|
||||
}
|
||||
|
||||
private fun isEnabled(): Boolean {
|
||||
return isRhizomeAdEnabled && Registry.`is`("ijpl.rhizome.ad.v2.enabled", false)
|
||||
return isRhizomeAdEnabled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
// Copyright 2000-2025 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
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.isRhizomeAdEnabled
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.Service.Level
|
||||
import com.intellij.openapi.editor.CaretModel
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.ScrollingModel
|
||||
import com.intellij.openapi.editor.SelectionModel
|
||||
import com.intellij.openapi.editor.ex.*
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl
|
||||
import com.intellij.openapi.editor.impl.EditorImpl
|
||||
import com.intellij.openapi.editor.impl.FocusModeModel
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.platform.kernel.withKernel
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import com.intellij.util.concurrency.AppExecutorUtil
|
||||
import com.intellij.util.concurrency.ThreadingAssertions
|
||||
import com.jetbrains.rhizomedb.ChangeScope
|
||||
import com.jetbrains.rhizomedb.asOf
|
||||
import fleet.kernel.change
|
||||
import fleet.kernel.lastKnownDb
|
||||
import fleet.kernel.shared
|
||||
import fleet.kernel.transactor
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
|
||||
private val LIMITED_DISPATCHER by lazy {
|
||||
AppExecutorUtil.createBoundedApplicationPoolExecutor("AD_DISPATCHER", 1).asCoroutineDispatcher()
|
||||
}
|
||||
|
||||
@Service(Level.PROJECT)
|
||||
internal class AdTheManagerImpl(private val coroutineScope: CoroutineScope) : AdTheManager {
|
||||
|
||||
override fun createEditorModel(editor: EditorEx): EditorModel? {
|
||||
ThreadingAssertions.assertEventDispatchThread()
|
||||
if (isRhizomeAdEnabled && editor is EditorImpl && isDocumentGuardedByLock(editor.document)) {
|
||||
val editorScope = this.coroutineScope.childScope("CoroutineScope for $editor", LIMITED_DISPATCHER)
|
||||
val modStampBefore = editor.document.modificationStamp
|
||||
@Suppress("RAW_RUN_BLOCKING")
|
||||
val editorModel = runBlocking {
|
||||
withKernel {
|
||||
change {
|
||||
shared {
|
||||
createEditorModelImpl(editor, editorScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ThreadLocalRhizomeDB.setThreadLocalDb(ThreadLocalRhizomeDB.lastKnownDb())
|
||||
|
||||
val documentSynchronizer = AdDocumentSynchronizer(editorModel.document as AdDocument, editorScope) {
|
||||
repaintEditor(editor)
|
||||
}
|
||||
editor.document.addDocumentListener(documentSynchronizer, documentSynchronizer)
|
||||
|
||||
Disposer.register(editorModel, documentSynchronizer)
|
||||
Disposer.register(editorModel, editorModel.editorMarkupModel as Disposable)
|
||||
Disposer.register(editorModel, editorModel.documentMarkupModel as Disposable)
|
||||
Disposer.register(editorModel, editorModel.inlayModel as Disposable)
|
||||
Disposer.register(editor.disposable, editorModel)
|
||||
|
||||
assert(editor.document.modificationStamp == modStampBefore)
|
||||
|
||||
return editorModel
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun ChangeScope.createEditorModelImpl(editor: EditorImpl, coroutineScope: CoroutineScope): EditorModel {
|
||||
register(AdDocumentEntity)
|
||||
val document = editor.document
|
||||
val entity = AdDocumentEntity.fromString(
|
||||
document.immutableCharSequence.toString(),
|
||||
document.modificationStamp,
|
||||
)
|
||||
val adDocument = AdDocument(debugName(document), entity)
|
||||
|
||||
fun createMarkup(markupModel: MarkupModelEx) = AdMarkupModel.fromMarkup(
|
||||
markupModel,
|
||||
adDocument,
|
||||
coroutineScope
|
||||
) { repaintEditor(editor) }
|
||||
|
||||
val editorMarkup = createMarkup(editor.markupModel)
|
||||
val documentMarkup = createMarkup(editor.filteredDocumentMarkupModel)
|
||||
|
||||
val inlayModel = AdInlayModel.fromInlays(
|
||||
editor.inlayModel,
|
||||
adDocument,
|
||||
coroutineScope
|
||||
) { repaintEditor(editor) }
|
||||
|
||||
return object : EditorModel {
|
||||
override fun getDocument(): DocumentEx = adDocument
|
||||
override fun getEditorMarkupModel(): MarkupModelEx = editorMarkup
|
||||
override fun getDocumentMarkupModel(): MarkupModelEx = documentMarkup
|
||||
override fun getHighlighter(): EditorHighlighter = editor.highlighter
|
||||
override fun getInlayModel(): InlayModelEx = inlayModel
|
||||
override fun getFoldingModel(): FoldingModelEx = editor.foldingModel
|
||||
override fun getSoftWrapModel(): SoftWrapModelEx = editor.softWrapModel
|
||||
override fun getCaretModel(): CaretModel = editor.caretModel
|
||||
override fun getSelectionModel(): SelectionModel = editor.selectionModel
|
||||
override fun getScrollingModel(): ScrollingModel = editor.scrollingModel
|
||||
override fun getFocusModel(): FocusModeModel = editor.focusModeModel
|
||||
override fun isAd(): Boolean = true
|
||||
override fun dispose() {
|
||||
coroutineScope.cancel()
|
||||
this@AdTheManagerImpl.coroutineScope.launch {
|
||||
change {
|
||||
shared {
|
||||
entity.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun repaintEditor(editor: EditorImpl) {
|
||||
val lastKnownDb = transactor().lastKnownDb
|
||||
withContext(Dispatchers.EDT) {
|
||||
if (!editor.isDisposed) {
|
||||
asOf(lastKnownDb) {
|
||||
editor.component.repaint()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isDocumentGuardedByLock(document: Document): Boolean {
|
||||
return document is DocumentImpl && document.isWriteThreadOnly
|
||||
}
|
||||
|
||||
private fun debugName(document: Document): String {
|
||||
val hash = Integer.toHexString(System.identityHashCode(document))
|
||||
return document.toString().replace("DocumentImpl", "AdDocument@$hash")
|
||||
}
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
// Copyright 2000-2025 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.v2
|
||||
|
||||
import andel.operation.Operation
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.ex.PrioritizedDocumentListener
|
||||
import com.intellij.openapi.editor.impl.ad.ThreadLocalRhizomeDB
|
||||
import com.intellij.platform.pasta.common.DocumentEntity
|
||||
import fleet.kernel.awaitCommitted
|
||||
import fleet.kernel.change
|
||||
import fleet.util.UID
|
||||
import fleet.util.openmap.OpenMap
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental
|
||||
|
||||
|
||||
@Experimental
|
||||
internal data class AdDocumentSynchronizerV2(
|
||||
private val documentId: Any, // RdDocumentId
|
||||
private val entityId: UID,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val entityDeferred: Deferred<DocumentEntity>,
|
||||
): PrioritizedDocumentListener {
|
||||
|
||||
override fun getPriority(): Int = Int.MIN_VALUE + 1
|
||||
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
val entityChange = coroutineScope.async {
|
||||
val operation = operation(event)
|
||||
val entity = entityDeferred.await()
|
||||
change { // it is a shared change
|
||||
entity.mutate(this, OpenMap.empty()) {
|
||||
edit(operation)
|
||||
}
|
||||
}
|
||||
awaitCommitted()
|
||||
}
|
||||
// TODO: pumping events in runWithModalProgressBlocking ruins the models
|
||||
//@Suppress("HardCodedStringLiteral")
|
||||
//runWithModalProgressBlocking(
|
||||
// ModalTaskOwner.guess(),
|
||||
// "Shared document entity synchronization $entityId",
|
||||
//) { entityChange.await() }
|
||||
runBlocking { entityChange.await() }
|
||||
ThreadLocalRhizomeDB.setThreadLocalDb(ThreadLocalRhizomeDB.lastKnownDb())
|
||||
}
|
||||
|
||||
private fun operation(event: DocumentEvent): Operation {
|
||||
val oldFragment = event.oldFragment.toString()
|
||||
val newFragment = event.newFragment.toString()
|
||||
val lengthBefore = event.document.textLength - newFragment.length + oldFragment.length
|
||||
return Operation.Companion.replaceAt(
|
||||
offset = event.offset.toLong(),
|
||||
oldText = oldFragment,
|
||||
newText = newFragment,
|
||||
totalLength = lengthBefore.toLong(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,315 +0,0 @@
|
||||
// Copyright 2000-2025 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.v2
|
||||
|
||||
import andel.text.*
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.editor.RangeMarker
|
||||
import com.intellij.openapi.editor.event.DocumentListener
|
||||
import com.intellij.openapi.editor.ex.DocumentEx
|
||||
import com.intellij.openapi.editor.ex.EditReadOnlyListener
|
||||
import com.intellij.openapi.editor.ex.LineIterator
|
||||
import com.intellij.openapi.editor.ex.RangeMarkerEx
|
||||
import com.intellij.platform.pasta.common.DocumentEntity
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental
|
||||
import java.beans.PropertyChangeListener
|
||||
import java.util.*
|
||||
|
||||
@Experimental
|
||||
internal class AdDocumentV2(private val entity: DocumentEntity) : DocumentEx {
|
||||
|
||||
// region TEXT
|
||||
|
||||
override fun getText(range: TextRange): String {
|
||||
return textView().string(range.startOffset, range.endOffset)
|
||||
}
|
||||
|
||||
override fun getImmutableCharSequence(): CharSequence {
|
||||
return textView().charSequence()
|
||||
}
|
||||
|
||||
override fun getTextLength(): Int {
|
||||
return text().charCount
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
|
||||
//region LINES
|
||||
|
||||
override fun getLineCount(): Int {
|
||||
val text = text()
|
||||
if (text.charCount == 0) {
|
||||
return 0
|
||||
}
|
||||
return text.lineCount.line
|
||||
}
|
||||
|
||||
override fun getLineNumber(offset: Int): Int {
|
||||
return textView().lineAt(offset).line
|
||||
}
|
||||
|
||||
override fun getLineStartOffset(line: Int): Int {
|
||||
return textView().lineStartOffset(line.line)
|
||||
}
|
||||
|
||||
override fun getLineEndOffset(line: Int): Int {
|
||||
return textView().lineEndOffset(line.line)
|
||||
}
|
||||
|
||||
override fun getLineSeparatorLength(line: Int): Int {
|
||||
// TODO: optimize
|
||||
return textView().lineEndOffset(line.line, includeLineSeparator=true) -
|
||||
textView().lineEndOffset(line.line, includeLineSeparator=false)
|
||||
}
|
||||
|
||||
override fun createLineIterator(): LineIterator {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region MUTATE TEXT
|
||||
|
||||
override fun insertString(offset: Int, chars: CharSequence) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun deleteString(startOffset: Int, endOffset: Int) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun replaceString(startOffset: Int, endOffset: Int, chars: CharSequence) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setText(chars: CharSequence) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun replaceText(chars: CharSequence, newModificationStamp: Long) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region RANGE MARKERS
|
||||
|
||||
override fun createRangeMarker(startOffset: Int, endOffset: Int, surviveOnExternalChange: Boolean): RangeMarkerEx {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun registerRangeMarker(rangeMarker: RangeMarkerEx, start: Int, end: Int, greedyToLeft: Boolean, greedyToRight: Boolean, layer: Int) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun removeRangeMarker(rangeMarker: RangeMarkerEx): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun processRangeMarkers(processor: Processor<in RangeMarker>): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun processRangeMarkersOverlappingWith(start: Int, end: Int, processor: Processor<in RangeMarker>): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region DOC LISTENERS
|
||||
|
||||
override fun addDocumentListener(listener: DocumentListener, parentDisposable: Disposable) {
|
||||
//TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addDocumentListener(listener: DocumentListener) {
|
||||
//TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun removeDocumentListener(listener: DocumentListener) {
|
||||
//TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region PROP LISTENER
|
||||
|
||||
override fun addPropertyChangeListener(listener: PropertyChangeListener) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun removePropertyChangeListener(listener: PropertyChangeListener) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region TRACK MODIFICATIONS
|
||||
|
||||
override fun isLineModified(line: Int): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getModificationStamp(): Long {
|
||||
return documentRead {
|
||||
entity.editLog.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
override fun setModificationStamp(modificationStamp: Long) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun clearLineModificationFlags() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getModificationSequence(): Int {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region GUARDED BLOCKS
|
||||
|
||||
override fun createGuardedBlock(startOffset: Int, endOffset: Int): RangeMarker {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun removeGuardedBlock(block: RangeMarker) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getOffsetGuard(offset: Int): RangeMarker? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getRangeGuard(start: Int, end: Int): RangeMarker? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getGuardedBlocks(): List<RangeMarker> {
|
||||
// TODO("Not yet implemented")
|
||||
return Collections.emptyList()
|
||||
}
|
||||
|
||||
override fun startGuardedBlockChecking() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun stopGuardedBlockChecking() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun suppressGuardedExceptions() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun unSuppressGuardedExceptions() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region BULK MODE
|
||||
|
||||
override fun isInBulkUpdate(): Boolean {
|
||||
return false // TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun setInBulkUpdate(value: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun isInEventsHandling(): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region READ-ONLY MODE
|
||||
|
||||
override fun setReadOnly(isReadOnly: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun isWritable(): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region SPECIAL MODES
|
||||
|
||||
override fun setCyclicBufferSize(bufferSize: Int) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setStripTrailingSpacesEnabled(isEnabled: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region READ ATTEMPT
|
||||
|
||||
override fun fireReadOnlyModificationAttempt() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun addEditReadOnlyListener(listener: EditReadOnlyListener) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun removeEditReadOnlyListener(listener: EditReadOnlyListener) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region USER DATA
|
||||
|
||||
override fun <T : Any?> getUserData(key: Key<T>): T? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun <T : Any?> putUserData(key: Key<T>, value: T?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//region PRIVATE
|
||||
|
||||
private fun text(): Text {
|
||||
return documentRead {
|
||||
entity.text
|
||||
}
|
||||
}
|
||||
|
||||
private fun textView(): TextView {
|
||||
return text().view()
|
||||
}
|
||||
|
||||
private fun <T> documentRead(block: () -> T): T {
|
||||
return block()
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.Internal
|
||||
package com.intellij.openapi.editor.impl.ad.v2;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -2481,11 +2481,6 @@ ide.show.files.action=true
|
||||
ide.showing.scope.compatibility.mode=false
|
||||
ide.showing.scope.compatibility.mode.description=Turns on compatibility implementation of `showingScope` using newer `launchOnShow`.
|
||||
|
||||
ijpl.rhizome.ad.debug.enabled=false
|
||||
ijpl.rhizome.ad.debug.enabled.description=Debug mode for IJPL-54
|
||||
ijpl.rhizome.ad.v2.enabled=false
|
||||
ijpl.rhizome.ad.v2.enabled.description=Second version of IJPL-54 based on common fleet entity. NOTE: must be enabled on FE and BE
|
||||
|
||||
thumbnails.toolwindow.enabled=false
|
||||
thumbnails.toolwindow.enabled.description=Enables the Thumbnails toolwindows and related functionality
|
||||
|
||||
|
||||
Reference in New Issue
Block a user