mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJPL-440 Support editor markup cache for declarative inlay hints
Merge-request: IJ-MR-122603 Merged-by: Aleksandr Trushev <aleksandr.trushev@jetbrains.com> GitOrigin-RevId: 9e60cec17e5f91cf0a6841761e13a6df59a365a0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6ba5b0d436
commit
b7cfce2b0a
+134
@@ -0,0 +1,134 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.daemon.impl
|
||||
|
||||
import com.intellij.codeInsight.hints.declarative.InlayActionPayload
|
||||
import com.intellij.codeInsight.hints.declarative.PsiPointerInlayActionPayload
|
||||
import com.intellij.codeInsight.hints.declarative.impl.*
|
||||
import com.intellij.codeInsight.hints.declarative.impl.util.TinyTree
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.readActionBlocking
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.Service.Level
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.EditorFactory
|
||||
import com.intellij.openapi.editor.EditorKind
|
||||
import com.intellij.openapi.editor.event.EditorFactoryEvent
|
||||
import com.intellij.openapi.editor.event.EditorFactoryListener
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorCache
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorInitializer
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileWithId
|
||||
import com.intellij.psi.PsiManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class DeclarativeHintsEditorInitializer : TextEditorInitializer {
|
||||
override suspend fun initializeEditor(
|
||||
project: Project,
|
||||
file: VirtualFile,
|
||||
document: Document,
|
||||
editorSupplier: suspend () -> EditorEx,
|
||||
highlighterReady: suspend () -> Unit,
|
||||
) {
|
||||
val grave = project.serviceAsync<DeclarativeHintsGrave>()
|
||||
val inlayDataList = grave.raise(file, document) ?: return
|
||||
val psiManager = project.serviceAsync<PsiManager>()
|
||||
val psiFile = readActionBlocking {
|
||||
psiManager.findFile(file)
|
||||
} ?: return
|
||||
val editor = editorSupplier()
|
||||
withContext(Dispatchers.EDT) {
|
||||
DeclarativeInlayHintsPass.applyInlayData(editor, psiFile, inlayDataList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Service(Level.PROJECT)
|
||||
internal class DeclarativeHintsGrave(private val project: Project, private val scope: CoroutineScope)
|
||||
: TextEditorCache<DeclarativeHintsState>(project, scope),
|
||||
Disposable {
|
||||
|
||||
override fun namePrefix(): String = "persistent-declarative-hints"
|
||||
override fun valueExternalizer(): DeclarativeHintsState.Externalizer = DeclarativeHintsState.Externalizer()
|
||||
override fun useHeapCache(): Boolean = true
|
||||
|
||||
init {
|
||||
subscribeEditorClosed()
|
||||
}
|
||||
|
||||
fun raise(file: VirtualFile, document: Document): List<InlayData>? {
|
||||
if (!isEnabled() || file !is VirtualFileWithId) {
|
||||
return null
|
||||
}
|
||||
val state: DeclarativeHintsState? = cache[file.id]
|
||||
if (state == null || state.contentHash != document.contentHash()) {
|
||||
return null
|
||||
}
|
||||
for (inlayData in state.inlayDataList) {
|
||||
initZombiePointers(file, inlayData.tree)
|
||||
}
|
||||
return state.inlayDataList
|
||||
}
|
||||
|
||||
private fun bury(editor: Editor) {
|
||||
val file = editor.virtualFile
|
||||
if (!isEnabled() || editor.editorKind != EditorKind.MAIN_EDITOR || file !is VirtualFileWithId) {
|
||||
return
|
||||
}
|
||||
val declarativeHints = editor.getInlayModel().getInlineElementsInRange(
|
||||
0,
|
||||
editor.getDocument().textLength,
|
||||
DeclarativeInlayRenderer::class.java
|
||||
)
|
||||
if (declarativeHints.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val contentHash = editor.document.contentHash()
|
||||
val inlayDataList = declarativeHints.map { inlay -> inlay.renderer.toInlayData() }.toList()
|
||||
scope.launch(Dispatchers.IO) {
|
||||
cache[file.id] = DeclarativeHintsState(contentHash, inlayDataList)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
|
||||
private fun subscribeEditorClosed() {
|
||||
EditorFactory.getInstance().addEditorFactoryListener(
|
||||
object : EditorFactoryListener {
|
||||
override fun editorReleased(event: EditorFactoryEvent) {
|
||||
bury(event.editor)
|
||||
}
|
||||
},
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
private fun initZombiePointers(file: VirtualFile, tree: TinyTree<Any?>, index: Byte = 0) {
|
||||
val dataPayload = tree.getDataPayload(index)
|
||||
if (dataPayload is ActionWithContent) {
|
||||
val payload: InlayActionPayload = dataPayload.actionData.payload
|
||||
if (payload is PsiPointerInlayActionPayload) {
|
||||
val pointer = payload.pointer
|
||||
if (pointer is ZombieSmartPointer) {
|
||||
pointer.projectSupp = { project }
|
||||
pointer.fileSupp = { file }
|
||||
}
|
||||
}
|
||||
}
|
||||
tree.processChildren(index) { child ->
|
||||
initZombiePointers(file, tree, child)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun isEnabled() = Registry.`is`("cache.inlay.hints.on.disk")
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.daemon.impl
|
||||
|
||||
import com.intellij.codeInsight.hints.declarative.impl.InlayData
|
||||
import com.intellij.openapi.fileEditor.impl.text.VersionedExternalizer
|
||||
import com.intellij.util.io.DataInputOutputUtil.readINT
|
||||
import com.intellij.util.io.DataInputOutputUtil.writeINT
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
|
||||
internal class DeclarativeHintsState(val contentHash: Int, val inlayDataList: List<InlayData>) {
|
||||
|
||||
class Externalizer : VersionedExternalizer<DeclarativeHintsState> {
|
||||
|
||||
private val inlayDataExternalizer: InlayData.Externalizer = InlayData.Externalizer()
|
||||
|
||||
companion object {
|
||||
// increment on format changed
|
||||
private const val SERDE_VERSION = 0
|
||||
}
|
||||
|
||||
override fun serdeVersion(): Int = SERDE_VERSION + inlayDataExternalizer.serdeVersion()
|
||||
|
||||
override fun save(output: DataOutput, state: DeclarativeHintsState) {
|
||||
writeINT(output, state.contentHash)
|
||||
writeINT(output, state.inlayDataList.size)
|
||||
for (inlayData in state.inlayDataList) {
|
||||
inlayDataExternalizer.save(output, inlayData)
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): DeclarativeHintsState {
|
||||
val contentHash = readINT(input)
|
||||
val inlayCount = readINT(input)
|
||||
val inlayDataList = buildList(inlayCount) {
|
||||
repeat(inlayCount) {
|
||||
add(inlayDataExternalizer.read(input))
|
||||
}
|
||||
}
|
||||
return DeclarativeHintsState(contentHash, inlayDataList)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -3,6 +3,7 @@ package com.intellij.codeInsight.daemon.impl
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightingMarkupGrave.FileMarkupInfo
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorCache
|
||||
import com.intellij.openapi.fileEditor.impl.text.VersionedExternalizer
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFileWithId
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -34,7 +35,7 @@ internal class HighlightingMarkupStore(project: Project, private val scope: Coro
|
||||
}
|
||||
}
|
||||
|
||||
object FileMarkupInfoExternalizer : ValueExternalizer<FileMarkupInfo> {
|
||||
object FileMarkupInfoExternalizer : VersionedExternalizer<FileMarkupInfo> {
|
||||
override fun serdeVersion() = 2
|
||||
override fun save(output: DataOutput, value: FileMarkupInfo) = value.bury(output)
|
||||
override fun read(input: DataInput) = FileMarkupInfo.exhume(input)
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import com.intellij.codeInsight.codeVision.ui.model.CounterCodeVisionEntry
|
||||
import com.intellij.codeInsight.codeVision.ui.model.ZombieCodeVisionEntry
|
||||
import com.intellij.codeInsight.daemon.impl.readGutterIcon
|
||||
import com.intellij.codeInsight.daemon.impl.writeGutterIcon
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorCache
|
||||
import com.intellij.openapi.fileEditor.impl.text.VersionedExternalizer
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.util.io.DataInputOutputUtil.readINT
|
||||
import com.intellij.util.io.DataInputOutputUtil.writeINT
|
||||
@@ -21,7 +21,7 @@ internal data class CodeVisionState(val contentHash: Int, val entries: List<Code
|
||||
Pair(TextRange(entry.startOffset, entry.endOffset), entry.asZombie())
|
||||
}
|
||||
|
||||
object Externalizer : TextEditorCache.ValueExternalizer<CodeVisionState> {
|
||||
object Externalizer : VersionedExternalizer<CodeVisionState> {
|
||||
override fun serdeVersion() = 0
|
||||
|
||||
override fun save(output: DataOutput, value: CodeVisionState) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
package com.intellij.codeInsight.hints
|
||||
|
||||
import com.intellij.codeInsight.hints.ParameterHintsPass.HintData
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorCache
|
||||
import com.intellij.openapi.fileEditor.impl.text.VersionedExternalizer
|
||||
import com.intellij.util.io.DataInputOutputUtil.readINT
|
||||
import com.intellij.util.io.DataInputOutputUtil.writeINT
|
||||
import com.intellij.util.io.IOUtil.readUTF
|
||||
@@ -12,7 +12,7 @@ import java.io.DataOutput
|
||||
|
||||
internal data class ParameterHintsState(val contentHash: Int, val hints: List<Pair<Int, HintData>>) {
|
||||
|
||||
object Externalizer : TextEditorCache.ValueExternalizer<ParameterHintsState> {
|
||||
object Externalizer : VersionedExternalizer<ParameterHintsState> {
|
||||
override fun serdeVersion() = 0
|
||||
|
||||
override fun save(output: DataOutput, value: ParameterHintsState) {
|
||||
|
||||
+74
-66
@@ -9,8 +9,10 @@ import com.intellij.openapi.editor.Inlay
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.SyntaxTraverser
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import java.util.function.IntFunction
|
||||
|
||||
@@ -53,86 +55,92 @@ class DeclarativeInlayHintsPass(
|
||||
)
|
||||
|
||||
override fun doApplyInformationToEditor() {
|
||||
val inlayDatas = sinks.flatMap { it.finish() }
|
||||
val inlayModel = editor.inlayModel
|
||||
val document = editor.document
|
||||
val existingInlineElements = inlayModel.getInlineElementsInRange(0, document.textLength, DeclarativeInlayRenderer::class.java)
|
||||
val existingEolElements = inlayModel.getAfterLineEndElementsInRange(0, document.textLength, DeclarativeInlayRenderer::class.java)
|
||||
val offsetToExistingInlineElements = Int2ObjectOpenHashMap<SmartList<Inlay<out DeclarativeInlayRenderer>>>() // either inlay or list of inlays
|
||||
val offsetToExistingEolElements = Int2ObjectOpenHashMap<SmartList<Inlay<out DeclarativeInlayRenderer>>>() // either inlay or list of inlays
|
||||
for (inlineElement in existingInlineElements) {
|
||||
val inlaysAtOffset = offsetToExistingInlineElements.computeIfAbsent(inlineElement.offset, IntFunction { SmartList() })
|
||||
inlaysAtOffset.add(inlineElement)
|
||||
}
|
||||
for (eolElement in existingEolElements) {
|
||||
val inlaysAtOffset = offsetToExistingEolElements.computeIfAbsent(eolElement.offset, IntFunction { SmartList() })
|
||||
inlaysAtOffset.add(eolElement)
|
||||
}
|
||||
val storage = InlayHintsUtils.getTextMetricStorage(editor)
|
||||
for (inlayData in inlayDatas) {
|
||||
when (val position = inlayData.position) {
|
||||
is EndOfLinePosition -> {
|
||||
val lineEndOffset = editor.document.getLineEndOffset(position.line)
|
||||
val updated = tryUpdateAndDeleteFromListInlay(offsetToExistingEolElements, inlayData, lineEndOffset)
|
||||
if (!updated) {
|
||||
val presentationList = InlayPresentationList(inlayData.tree, inlayData.hasBackground, inlayData.disabled,
|
||||
createPayloads(inlayData), inlayData.providerClass, inlayData.tooltip)
|
||||
val renderer = DeclarativeInlayRenderer(presentationList, storage, inlayData.providerId)
|
||||
val inlay = inlayModel.addAfterLineEndElement(lineEndOffset, true, renderer)
|
||||
if (inlay != null) {
|
||||
renderer.setInlay(inlay)
|
||||
applyInlayData(editor, myFile, inlayDatas = sinks.flatMap { it.finish() })
|
||||
}
|
||||
|
||||
companion object {
|
||||
@RequiresEdt
|
||||
internal fun applyInlayData(editor: Editor, file: PsiFile, inlayDatas: List<InlayData>) {
|
||||
val inlayModel = editor.inlayModel
|
||||
val document = editor.document
|
||||
val existingInlineElements = inlayModel.getInlineElementsInRange(0, document.textLength, DeclarativeInlayRenderer::class.java)
|
||||
val existingEolElements = inlayModel.getAfterLineEndElementsInRange(0, document.textLength, DeclarativeInlayRenderer::class.java)
|
||||
val offsetToExistingInlineElements = Int2ObjectOpenHashMap<SmartList<Inlay<out DeclarativeInlayRenderer>>>() // either inlay or list of inlays
|
||||
val offsetToExistingEolElements = Int2ObjectOpenHashMap<SmartList<Inlay<out DeclarativeInlayRenderer>>>() // either inlay or list of inlays
|
||||
for (inlineElement in existingInlineElements) {
|
||||
val inlaysAtOffset = offsetToExistingInlineElements.computeIfAbsent(inlineElement.offset, IntFunction { SmartList() })
|
||||
inlaysAtOffset.add(inlineElement)
|
||||
}
|
||||
for (eolElement in existingEolElements) {
|
||||
val inlaysAtOffset = offsetToExistingEolElements.computeIfAbsent(eolElement.offset, IntFunction { SmartList() })
|
||||
inlaysAtOffset.add(eolElement)
|
||||
}
|
||||
val storage = InlayHintsUtils.getTextMetricStorage(editor)
|
||||
for (inlayData in inlayDatas) {
|
||||
when (val position = inlayData.position) {
|
||||
is EndOfLinePosition -> {
|
||||
val lineEndOffset = editor.document.getLineEndOffset(position.line)
|
||||
val updated = tryUpdateAndDeleteFromListInlay(offsetToExistingEolElements, inlayData, lineEndOffset)
|
||||
if (!updated) {
|
||||
val presentationList = InlayPresentationList(inlayData.tree, inlayData.hasBackground, inlayData.disabled,
|
||||
createPayloads(inlayData), inlayData.providerClass, inlayData.tooltip)
|
||||
val renderer = DeclarativeInlayRenderer(presentationList, storage, inlayData.providerId, position)
|
||||
val inlay = inlayModel.addAfterLineEndElement(lineEndOffset, true, renderer)
|
||||
if (inlay != null) {
|
||||
renderer.setInlay(inlay)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is InlineInlayPosition -> {
|
||||
val updated = tryUpdateAndDeleteFromListInlay(offsetToExistingInlineElements, inlayData, position.offset)
|
||||
if (!updated) {
|
||||
val presentationList = InlayPresentationList(inlayData.tree, inlayData.hasBackground, inlayData.disabled,
|
||||
createPayloads(inlayData), inlayData.providerClass, inlayData.tooltip)
|
||||
val renderer = DeclarativeInlayRenderer(presentationList, storage, inlayData.providerId)
|
||||
val inlay = inlayModel.addInlineElement(position.offset, position.relatedToPrevious, position.priority, renderer)
|
||||
if (inlay != null) {
|
||||
renderer.setInlay(inlay)
|
||||
is InlineInlayPosition -> {
|
||||
val updated = tryUpdateAndDeleteFromListInlay(offsetToExistingInlineElements, inlayData, position.offset)
|
||||
if (!updated) {
|
||||
val presentationList = InlayPresentationList(inlayData.tree, inlayData.hasBackground, inlayData.disabled,
|
||||
createPayloads(inlayData), inlayData.providerClass, inlayData.tooltip)
|
||||
val renderer = DeclarativeInlayRenderer(presentationList, storage, inlayData.providerId, position)
|
||||
val inlay = inlayModel.addInlineElement(position.offset, position.relatedToPrevious, position.priority, renderer)
|
||||
if (inlay != null) {
|
||||
renderer.setInlay(inlay)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deleteNotPreservedInlays(offsetToExistingInlineElements)
|
||||
deleteNotPreservedInlays(offsetToExistingEolElements)
|
||||
|
||||
DeclarativeInlayHintsPassFactory.updateModificationStamp(editor, file)
|
||||
}
|
||||
|
||||
deleteNotPreservedInlays(offsetToExistingInlineElements)
|
||||
deleteNotPreservedInlays(offsetToExistingEolElements)
|
||||
private fun createPayloads(inlayData: InlayData) =
|
||||
inlayData.payloads?.associate { it.payloadName to it.payload }
|
||||
|
||||
DeclarativeInlayHintsPassFactory.updateModificationStamp(editor, myFile)
|
||||
}
|
||||
|
||||
private fun createPayloads(inlayData: InlayData) =
|
||||
inlayData.payloads?.associate { it.payloadName to it.payload }
|
||||
|
||||
private fun deleteNotPreservedInlays(offsetToExistingInlays: Int2ObjectOpenHashMap<SmartList<Inlay<out DeclarativeInlayRenderer>>>) {
|
||||
for (inlays in offsetToExistingInlays.values) {
|
||||
for (inlay in inlays) {
|
||||
Disposer.dispose(inlay)
|
||||
private fun deleteNotPreservedInlays(offsetToExistingInlays: Int2ObjectOpenHashMap<SmartList<Inlay<out DeclarativeInlayRenderer>>>) {
|
||||
for (inlays in offsetToExistingInlays.values) {
|
||||
for (inlay in inlays) {
|
||||
Disposer.dispose(inlay)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryUpdateAndDeleteFromListInlay(offsetToExistingInlays: Int2ObjectOpenHashMap<SmartList<Inlay<out DeclarativeInlayRenderer>>>,
|
||||
inlayData: InlayData,
|
||||
offset: Int): Boolean {
|
||||
val inlays = offsetToExistingInlays.get(offset)
|
||||
if (inlays == null) return false
|
||||
val iterator = inlays.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val existingInlay = iterator.next()
|
||||
val renderer = existingInlay.renderer
|
||||
if (renderer.providerId == inlayData.providerId) {
|
||||
renderer.updateState(inlayData.tree, inlayData.disabled, inlayData.hasBackground)
|
||||
existingInlay.update()
|
||||
iterator.remove()
|
||||
return true
|
||||
private fun tryUpdateAndDeleteFromListInlay(offsetToExistingInlays: Int2ObjectOpenHashMap<SmartList<Inlay<out DeclarativeInlayRenderer>>>,
|
||||
inlayData: InlayData,
|
||||
offset: Int): Boolean {
|
||||
val inlays = offsetToExistingInlays.get(offset)
|
||||
if (inlays == null) return false
|
||||
val iterator = inlays.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val existingInlay = iterator.next()
|
||||
val renderer = existingInlay.renderer
|
||||
if (renderer.providerId == inlayData.providerId) {
|
||||
renderer.updateState(inlayData.tree, inlayData.disabled, inlayData.hasBackground)
|
||||
existingInlay.update()
|
||||
iterator.remove()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun createCollector(provider: InlayHintsProvider): InlayHintsCollector? {
|
||||
|
||||
+6
-1
@@ -3,6 +3,7 @@ package com.intellij.codeInsight.hints.declarative.impl
|
||||
|
||||
import com.intellij.codeInsight.hints.declarative.InlayHintsProvider
|
||||
import com.intellij.codeInsight.hints.declarative.InlayHintsProviderFactory
|
||||
import com.intellij.codeInsight.hints.declarative.InlayPosition
|
||||
import com.intellij.codeInsight.hints.declarative.impl.util.TinyTree
|
||||
import com.intellij.codeInsight.hints.presentation.InlayTextMetricsStorage
|
||||
import com.intellij.openapi.actionSystem.ActionGroup
|
||||
@@ -28,6 +29,7 @@ class DeclarativeInlayRenderer(
|
||||
val presentationList: InlayPresentationList,
|
||||
private val fontMetricsStorage: InlayTextMetricsStorage,
|
||||
val providerId: String,
|
||||
private val position: InlayPosition,
|
||||
) : EditorCustomElementRenderer {
|
||||
private var inlay: Inlay<DeclarativeInlayRenderer>? = null
|
||||
|
||||
@@ -89,5 +91,8 @@ class DeclarativeInlayRenderer(
|
||||
override fun getContextMenuGroupId(inlay: Inlay<*>): String {
|
||||
return "DummyActionGroup"
|
||||
}
|
||||
}
|
||||
|
||||
internal fun toInlayData(): InlayData {
|
||||
return presentationList.toInlayData(position, providerId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.hints.declarative.impl
|
||||
|
||||
import com.intellij.codeInsight.hints.declarative.EndOfLinePosition
|
||||
import com.intellij.codeInsight.hints.declarative.InlayPayload
|
||||
import com.intellij.codeInsight.hints.declarative.InlayPosition
|
||||
import com.intellij.codeInsight.hints.declarative.InlineInlayPosition
|
||||
import com.intellij.codeInsight.hints.declarative.impl.util.TinyTree
|
||||
import com.intellij.openapi.fileEditor.impl.text.VersionedExternalizer
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.intellij.util.io.DataInputOutputUtil.readINT
|
||||
import com.intellij.util.io.DataInputOutputUtil.writeINT
|
||||
import com.intellij.util.io.IOUtil.readUTF
|
||||
import com.intellij.util.io.IOUtil.writeUTF
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
|
||||
data class InlayData(
|
||||
val position: InlayPosition,
|
||||
@NlsContexts.HintText val tooltip: String?,
|
||||
val hasBackground: Boolean,
|
||||
val tree: TinyTree<Any?>,
|
||||
val providerId: String,
|
||||
val disabled: Boolean,
|
||||
val payloads: List<InlayPayload>?,
|
||||
/**
|
||||
* Just for debugging purposes
|
||||
*/
|
||||
val providerClass: Class<*>,
|
||||
) {
|
||||
|
||||
class Externalizer : VersionedExternalizer<InlayData> {
|
||||
private val treeExternalizer: PresentationTreeExternalizer = PresentationTreeExternalizer()
|
||||
|
||||
companion object {
|
||||
// increment on format changed
|
||||
private const val SERDE_VERSION = 0
|
||||
}
|
||||
|
||||
override fun serdeVersion(): Int = SERDE_VERSION + treeExternalizer.serdeVersion()
|
||||
|
||||
override fun save(output: DataOutput, inlayData: InlayData) {
|
||||
writePosition(output, inlayData.position)
|
||||
writeTooltip(output, inlayData.tooltip)
|
||||
output.writeBoolean(inlayData.hasBackground)
|
||||
treeExternalizer.save(output, inlayData.tree)
|
||||
writeUTF(output, inlayData.providerId)
|
||||
output.writeBoolean(inlayData.disabled)
|
||||
writePayloads(output, inlayData.payloads)
|
||||
writeProviderClass(output, inlayData.providerClass)
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): InlayData {
|
||||
val position: InlayPosition = readPosition(input)
|
||||
val tooltip: String? = readTooltip(input)
|
||||
val hasBackground: Boolean = input.readBoolean()
|
||||
val tree: TinyTree<Any?> = treeExternalizer.read(input)
|
||||
val providerId: String = readUTF(input)
|
||||
val disabled: Boolean = input.readBoolean()
|
||||
val payloads: List<InlayPayload>? = readPayloads(input)
|
||||
val providerClass: Class<*> = readProviderClass(input)
|
||||
return InlayData(position, tooltip, hasBackground, tree, providerId, disabled, payloads, providerClass)
|
||||
}
|
||||
|
||||
private fun writePosition(output: DataOutput, position: InlayPosition) {
|
||||
when (position) {
|
||||
is InlineInlayPosition -> {
|
||||
writeINT(output, 0)
|
||||
writeINT(output, position.offset)
|
||||
output.writeBoolean(position.relatedToPrevious)
|
||||
writeINT(output, position.priority)
|
||||
}
|
||||
is EndOfLinePosition -> {
|
||||
writeINT(output, 1)
|
||||
writeINT(output, position.line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readPosition(input: DataInput): InlayPosition {
|
||||
val type = readINT(input)
|
||||
if (type == 0) {
|
||||
val offset = readINT(input)
|
||||
val related = input.readBoolean()
|
||||
val priority = readINT(input)
|
||||
return InlineInlayPosition(offset, related, priority)
|
||||
} else if (type == 1) {
|
||||
val line = readINT(input)
|
||||
return EndOfLinePosition(line)
|
||||
}
|
||||
throw IllegalStateException("unknown inlay position type: $type")
|
||||
}
|
||||
|
||||
private fun writeTooltip(output: DataOutput, tooltip: String?) {
|
||||
if (tooltip == null) {
|
||||
output.writeBoolean(false)
|
||||
} else {
|
||||
output.writeBoolean(true)
|
||||
writeUTF(output, tooltip)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readTooltip(input: DataInput): String? {
|
||||
return if (input.readBoolean()) {
|
||||
readUTF(input)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun writePayloads(output: DataOutput, payloads: List<InlayPayload>?) {
|
||||
if (payloads == null) {
|
||||
output.writeBoolean(false)
|
||||
} else {
|
||||
output.writeBoolean(true)
|
||||
writeINT(output, payloads.size)
|
||||
for (p in payloads) {
|
||||
writeInlayPayload(output, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readPayloads(input: DataInput): List<InlayPayload>? {
|
||||
if (input.readBoolean()) {
|
||||
val payloadCount = readINT(input)
|
||||
val payloads = ArrayList<InlayPayload>(payloadCount)
|
||||
repeat(payloadCount) {
|
||||
val inlayPayload = readInlayPayload(input)
|
||||
payloads.add(inlayPayload)
|
||||
}
|
||||
return payloads
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeInlayPayload(output: DataOutput, payload: InlayPayload) {
|
||||
writeUTF(output, payload.payloadName)
|
||||
treeExternalizer.writeInlayActionPayload(output, payload.payload)
|
||||
}
|
||||
|
||||
private fun readInlayPayload(input: DataInput): InlayPayload {
|
||||
val payloadName = readUTF(input)
|
||||
val inlayActionPayload = treeExternalizer.readInlayActionPayload(input)
|
||||
return InlayPayload(payloadName, inlayActionPayload)
|
||||
}
|
||||
|
||||
private fun writeProviderClass(output: DataOutput, providerClass: Class<*>) {
|
||||
writeUTF(output, providerClass.name)
|
||||
}
|
||||
|
||||
private fun readProviderClass(input: DataInput): Class<*> {
|
||||
val className = readUTF(input)
|
||||
return Class.forName(className)
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -3,6 +3,8 @@ package com.intellij.codeInsight.hints.declarative.impl
|
||||
|
||||
import com.intellij.codeInsight.hints.InlayHintsUtils
|
||||
import com.intellij.codeInsight.hints.declarative.InlayActionPayload
|
||||
import com.intellij.codeInsight.hints.declarative.InlayPayload
|
||||
import com.intellij.codeInsight.hints.declarative.InlayPosition
|
||||
import com.intellij.codeInsight.hints.declarative.impl.util.TinyTree
|
||||
import com.intellij.codeInsight.hints.presentation.InlayTextMetricsStorage
|
||||
import com.intellij.codeInsight.hints.presentation.PresentationFactory
|
||||
@@ -207,4 +209,17 @@ class InlayPresentationList(
|
||||
val entry = findEntryByPoint(fontMetricsStorage, pointInsideInlay) ?: return null
|
||||
return entry.clickArea
|
||||
}
|
||||
|
||||
internal fun toInlayData(position: InlayPosition, providerId: String): InlayData {
|
||||
return InlayData(
|
||||
position,
|
||||
tooltip,
|
||||
hasBackground,
|
||||
state,
|
||||
providerId,
|
||||
isDisabled,
|
||||
payloads?.map { (name, action) -> InlayPayload(name, action) },
|
||||
providerClass,
|
||||
)
|
||||
}
|
||||
}
|
||||
-17
@@ -5,7 +5,6 @@ import com.intellij.codeInsight.hints.declarative.InlayPayload
|
||||
import com.intellij.codeInsight.hints.declarative.InlayPosition
|
||||
import com.intellij.codeInsight.hints.declarative.InlayTreeSink
|
||||
import com.intellij.codeInsight.hints.declarative.PresentationTreeBuilder
|
||||
import com.intellij.codeInsight.hints.declarative.impl.util.TinyTree
|
||||
import com.intellij.diagnostic.PluginException
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
|
||||
@@ -73,19 +72,3 @@ class InlayTreeSinkImpl(
|
||||
return inlayDataToPresentation
|
||||
}
|
||||
}
|
||||
|
||||
data class InlayData(
|
||||
val position: InlayPosition,
|
||||
@NlsContexts.HintText val tooltip: String?,
|
||||
val hasBackground: Boolean,
|
||||
val tree: TinyTree<Any?>,
|
||||
val providerId: String,
|
||||
val disabled: Boolean,
|
||||
val payloads: List<InlayPayload>?,
|
||||
/**
|
||||
* Just for debugging purposes
|
||||
*/
|
||||
val providerClass: Class<*>
|
||||
) {
|
||||
|
||||
}
|
||||
+12
-2
@@ -2,6 +2,7 @@
|
||||
package com.intellij.codeInsight.hints.declarative.impl
|
||||
|
||||
import com.intellij.codeInsight.hints.declarative.InlayActionData
|
||||
import com.intellij.codeInsight.hints.declarative.PsiPointerInlayActionPayload
|
||||
import com.intellij.codeInsight.hints.declarative.impl.util.TinyTree
|
||||
import com.intellij.diagnostic.PluginException
|
||||
|
||||
@@ -53,10 +54,10 @@ class PresentationEntryBuilder(val state: TinyTree<Any?>, private val providerCl
|
||||
area?.entries?.add(entry)
|
||||
}
|
||||
is ActionWithContent -> {
|
||||
val area = this.currentClickArea ?: InlayMouseArea(dataPayload.actionData)
|
||||
val area = this.currentClickArea ?: mouseAreaIfNotZombie(dataPayload.actionData)
|
||||
val entry = TextInlayPresentationEntry(dataPayload.content as String, clickArea = area, parentIndexToSwitch = parentIndexToSwitch)
|
||||
addEntry(entry)
|
||||
area.entries.add(entry)
|
||||
area?.entries?.add(entry)
|
||||
}
|
||||
else -> throw IllegalStateException("Illegal payload for text tag: $dataPayload")
|
||||
}
|
||||
@@ -85,6 +86,15 @@ class PresentationEntryBuilder(val state: TinyTree<Any?>, private val providerCl
|
||||
}
|
||||
}
|
||||
|
||||
private fun mouseAreaIfNotZombie(actionData: InlayActionData): InlayMouseArea? {
|
||||
val inlayActionPayload = actionData.payload
|
||||
if (inlayActionPayload is PsiPointerInlayActionPayload && inlayActionPayload.pointer is ZombieSmartPointer) {
|
||||
// zombie pointer is not hoverable/clickable
|
||||
return null
|
||||
}
|
||||
return InlayMouseArea(actionData)
|
||||
}
|
||||
|
||||
private fun selectFromList(index: Byte, collapsed: Boolean) {
|
||||
val savedIndexOfClosestParentList = indexOfClosestParentList
|
||||
indexOfClosestParentList = index
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.hints.declarative.impl
|
||||
|
||||
import com.intellij.codeInsight.hints.declarative.InlayActionData
|
||||
import com.intellij.codeInsight.hints.declarative.InlayActionPayload
|
||||
import com.intellij.codeInsight.hints.declarative.PsiPointerInlayActionPayload
|
||||
import com.intellij.codeInsight.hints.declarative.StringInlayActionPayload
|
||||
import com.intellij.codeInsight.hints.declarative.impl.util.TinyTree
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.util.io.DataInputOutputUtil.readINT
|
||||
import com.intellij.util.io.DataInputOutputUtil.writeINT
|
||||
import com.intellij.util.io.IOUtil.readUTF
|
||||
import com.intellij.util.io.IOUtil.writeUTF
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
|
||||
internal class PresentationTreeExternalizer : TinyTree.Externalizer<Any?>() {
|
||||
|
||||
companion object {
|
||||
// increment on format changed
|
||||
private const val SERDE_VERSION = 0
|
||||
}
|
||||
|
||||
override fun serdeVersion(): Int = SERDE_VERSION + super.serdeVersion()
|
||||
|
||||
override fun writeDataPayload(output: DataOutput, payload: Any?) {
|
||||
when (payload) {
|
||||
is String? -> {
|
||||
writeINT(output, 0)
|
||||
writeNullableString(output, payload)
|
||||
}
|
||||
is ActionWithContent -> {
|
||||
writeINT(output, 1)
|
||||
writeUTF(output, payload.content as String)
|
||||
writeInlayActionData(output, payload.actionData)
|
||||
}
|
||||
else -> throw IllegalArgumentException("unknown data payload type: $payload")
|
||||
}
|
||||
}
|
||||
|
||||
override fun readDataPayload(input: DataInput): Any? {
|
||||
val type = readINT(input)
|
||||
if (type == 0) {
|
||||
val string = readNullableString(input) ?: return null
|
||||
return decorateIfDebug(string)
|
||||
} else if (type == 1) {
|
||||
val content = decorateIfDebug(readUTF(input))
|
||||
return ActionWithContent(readInlayActionData(input), content)
|
||||
}
|
||||
throw IllegalStateException("unknown data payload type: $type")
|
||||
}
|
||||
|
||||
private fun writeNullableString(output: DataOutput, value: String?) {
|
||||
if (value == null) {
|
||||
output.writeBoolean(false)
|
||||
} else {
|
||||
output.writeBoolean(true)
|
||||
writeUTF(output, value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readNullableString(input: DataInput): String? {
|
||||
return if (input.readBoolean()) {
|
||||
readUTF(input)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeInlayActionData(output: DataOutput, inlayActionData: InlayActionData) {
|
||||
writeUTF(output, inlayActionData.handlerId)
|
||||
writeInlayActionPayload(output, inlayActionData.payload)
|
||||
}
|
||||
|
||||
private fun readInlayActionData(input: DataInput): InlayActionData {
|
||||
val handlerId = readUTF(input)
|
||||
val payload = readInlayActionPayload(input)
|
||||
return InlayActionData(payload, handlerId)
|
||||
}
|
||||
|
||||
fun writeInlayActionPayload(output: DataOutput, actionPayload: InlayActionPayload) {
|
||||
when(actionPayload) {
|
||||
is StringInlayActionPayload -> {
|
||||
writeINT(output, 0)
|
||||
writeUTF(output, actionPayload.text)
|
||||
}
|
||||
is PsiPointerInlayActionPayload -> {
|
||||
writeINT(output, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun readInlayActionPayload(input: DataInput): InlayActionPayload {
|
||||
val type = readINT(input)
|
||||
if (type == 0) {
|
||||
return StringInlayActionPayload(readUTF(input))
|
||||
} else if (type == 1) {
|
||||
return PsiPointerInlayActionPayload(ZombieSmartPointer())
|
||||
}
|
||||
throw IllegalStateException("unknown inlay action payload type: $type")
|
||||
}
|
||||
|
||||
private fun decorateIfDebug(content: String): String {
|
||||
return if (Registry.`is`("cache.markup.debug")) {
|
||||
"$content?"
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.hints.declarative.impl
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Segment
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
|
||||
internal class ZombieSmartPointer : SmartPsiElementPointer<PsiElement> {
|
||||
lateinit var projectSupp: () -> Project
|
||||
lateinit var fileSupp: () -> VirtualFile
|
||||
|
||||
override fun getElement(): PsiElement? = null
|
||||
override fun getContainingFile(): PsiFile? = null
|
||||
override fun getProject(): Project = projectSupp.invoke()
|
||||
override fun getVirtualFile(): VirtualFile = fileSupp.invoke()
|
||||
override fun getRange(): Segment? = null
|
||||
override fun getPsiRange(): Segment? = null
|
||||
}
|
||||
+57
-7
@@ -1,22 +1,27 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.hints.declarative.impl.util
|
||||
|
||||
import com.intellij.openapi.fileEditor.impl.text.VersionedExternalizer
|
||||
import com.intellij.util.io.DataInputOutputUtil.readINT
|
||||
import com.intellij.util.io.DataInputOutputUtil.writeINT
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
|
||||
/**
|
||||
* Stores up to 127 elements with a single byte payload and reference data
|
||||
*/
|
||||
class TinyTree<T>(rootPayload: Byte, rootData: T) {
|
||||
class TinyTree<T> private constructor(
|
||||
private val firstChild: ByteArrayList,
|
||||
private val nextChild: ByteArrayList,
|
||||
private val payload: ByteArrayList,
|
||||
private val data: ArrayList<T>,
|
||||
) {
|
||||
companion object {
|
||||
private const val NO_ELEMENT: Byte = -1
|
||||
}
|
||||
|
||||
private val firstChild: ByteArrayList = ByteArrayList()
|
||||
private val nextChild: ByteArrayList = ByteArrayList()
|
||||
private val payload: ByteArrayList = ByteArrayList()
|
||||
private val data: ArrayList<T> = ArrayList()
|
||||
|
||||
init {
|
||||
constructor(rootPayload: Byte, rootData: T) : this(ByteArrayList(), ByteArrayList(), ByteArrayList(), ArrayList()) {
|
||||
payload.add(rootPayload)
|
||||
data.add(rootData)
|
||||
firstChild.add(NO_ELEMENT)
|
||||
@@ -101,4 +106,49 @@ class TinyTree<T>(rootPayload: Byte, rootData: T) {
|
||||
get() = payload.size
|
||||
|
||||
class TooManyElementsException : Exception()
|
||||
|
||||
abstract class Externalizer<T> : VersionedExternalizer<TinyTree<T>> {
|
||||
|
||||
companion object {
|
||||
// increment on format changed
|
||||
private const val SERDE_VERSION = 0
|
||||
}
|
||||
|
||||
override fun serdeVersion(): Int = SERDE_VERSION
|
||||
|
||||
abstract fun writeDataPayload(output: DataOutput, payload: T)
|
||||
|
||||
abstract fun readDataPayload(input: DataInput): T
|
||||
|
||||
override fun save(output: DataOutput, tree: TinyTree<T>) {
|
||||
writeINT(output, tree.size)
|
||||
writeByteArray(output, tree.firstChild)
|
||||
writeByteArray(output, tree.nextChild)
|
||||
writeByteArray(output, tree.payload)
|
||||
for (dataPayload in tree.data) {
|
||||
writeDataPayload(output, dataPayload)
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): TinyTree<T> {
|
||||
val size = readINT(input)
|
||||
val firstChild = readByteArray(input, size)
|
||||
val nextChild = readByteArray(input, size)
|
||||
val payload = readByteArray(input, size)
|
||||
val data = ArrayList<T>(size)
|
||||
repeat(size) {
|
||||
data.add(readDataPayload(input))
|
||||
}
|
||||
return TinyTree(firstChild, nextChild, payload, data)
|
||||
}
|
||||
|
||||
private fun writeByteArray(output: DataOutput, byteArray: ByteArrayList) {
|
||||
output.write(byteArray.elements(), 0, byteArray.size)
|
||||
}
|
||||
|
||||
private fun readByteArray(input: DataInput, size: Int): ByteArrayList {
|
||||
val bytes = ByteArray(size).also { input.readFully(it) }
|
||||
return ByteArrayList(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -8,8 +8,8 @@ import com.intellij.openapi.editor.FoldRegion
|
||||
import com.intellij.openapi.editor.ex.FoldingModelEx
|
||||
import com.intellij.openapi.editor.impl.FoldingModelImpl
|
||||
import com.intellij.openapi.editor.impl.FoldingModelImpl.ZOMBIE_REGION_KEY
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorCache
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorCache.Companion.contentHash
|
||||
import com.intellij.openapi.fileEditor.impl.text.VersionedExternalizer
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.intellij.util.io.DataInputOutputUtil.readINT
|
||||
import com.intellij.util.io.DataInputOutputUtil.writeINT
|
||||
@@ -80,7 +80,7 @@ internal class FoldingState(private val contentHash: Int, private val regions: L
|
||||
}
|
||||
}
|
||||
|
||||
object FoldingStateExternalizer : TextEditorCache.ValueExternalizer<FoldingState> {
|
||||
object FoldingStateExternalizer : VersionedExternalizer<FoldingState> {
|
||||
override fun serdeVersion() = 2
|
||||
override fun save(output: DataOutput, value: FoldingState) = value.save(output)
|
||||
override fun read(input: DataInput) = FoldingState.read(input)
|
||||
|
||||
+16
@@ -4,11 +4,16 @@ package com.intellij.codeInsight.hints.declarative
|
||||
import com.intellij.codeInsight.hints.declarative.impl.util.TinyTree
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import junit.framework.TestCase
|
||||
import java.io.*
|
||||
import com.intellij.codeInsight.hints.declarative.TinyTreeDebugNode as DebugNode
|
||||
|
||||
class TinyTreeTest : UsefulTestCase() {
|
||||
fun testAddToTreeInReverseOrder() {
|
||||
val tinyTree = createTree()
|
||||
assertTreeStructure(tinyTree)
|
||||
}
|
||||
|
||||
private fun assertTreeStructure(tinyTree: TinyTree<String>) {
|
||||
assertEquals(6, tinyTree.size)
|
||||
val debugTree = DebugNode.buildDebugTree(tinyTree)
|
||||
val expectedNode = DebugNode(10, "root", mutableListOf(
|
||||
@@ -46,6 +51,17 @@ class TinyTreeTest : UsefulTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
fun testTreeSerde() {
|
||||
val externalizer = object : TinyTree.Externalizer<String>() {
|
||||
override fun writeDataPayload(output: DataOutput, payload: String): Unit = output.writeUTF(payload)
|
||||
override fun readDataPayload(input: DataInput): String = input.readUTF()
|
||||
}
|
||||
val baos = ByteArrayOutputStream()
|
||||
externalizer.save(DataOutputStream(baos), createTree())
|
||||
val deserializedTree = externalizer.read(DataInputStream(ByteArrayInputStream(baos.toByteArray())))
|
||||
assertTreeStructure(deserializedTree)
|
||||
}
|
||||
|
||||
private fun createTree(): TinyTree<String> {
|
||||
// 10
|
||||
// 20 30
|
||||
|
||||
+1
-5
@@ -18,13 +18,9 @@ abstract class TextEditorCache<T>(private val project: Project, private val scop
|
||||
protected val cache: ManagedCache<Int, T> = createCache()
|
||||
|
||||
abstract fun namePrefix(): String
|
||||
abstract fun valueExternalizer(): ValueExternalizer<T>
|
||||
abstract fun valueExternalizer(): VersionedExternalizer<T>
|
||||
abstract fun useHeapCache(): Boolean
|
||||
|
||||
interface ValueExternalizer<T> : DataExternalizer<T> {
|
||||
fun serdeVersion(): Int
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun Document.contentHash(): Int = Hashing.komihash5_0().hashCharsToInt(this.immutableCharSequence)
|
||||
fun cachePath(): Path = PathManager.getSystemDir().resolve("editor")
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.fileEditor.impl.text
|
||||
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface VersionedExternalizer<T> : DataExternalizer<T> {
|
||||
fun serdeVersion(): Int
|
||||
}
|
||||
@@ -123,6 +123,7 @@
|
||||
<textEditorInitializer implementation="com.intellij.openapi.fileEditor.impl.text.FoldingTextEditorInitializer"/>
|
||||
<textEditorInitializer implementation="com.intellij.codeInsight.hints.codeVision.CodeVisionTextEditorInitializer"/>
|
||||
<textEditorInitializer implementation="com.intellij.codeInsight.daemon.impl.InlayHintsEditorInitializer"/>
|
||||
<textEditorInitializer implementation="com.intellij.codeInsight.daemon.impl.DeclarativeHintsEditorInitializer"/>
|
||||
<textEditorInitializer implementation="com.intellij.codeInsight.hints.ParameterHintsEditorInitializer"/>
|
||||
<textEditorInitializer implementation="com.intellij.openapi.fileEditor.impl.text.DocRenderTextEditorInitializer"/>
|
||||
<textEditorInitializer implementation="com.intellij.openapi.fileEditor.impl.text.FocusZoneTextEditorInitializer"/>
|
||||
|
||||
Reference in New Issue
Block a user