From b78bc911d11cc9c618490ffd590009ff4916fe2a Mon Sep 17 00:00:00 2001 From: Petr Makhnev <51853996+i582@users.noreply.github.com> Date: Mon, 21 Oct 2024 00:17:25 +0400 Subject: [PATCH] move documentation support to new "Documentation Target API" (#207) See https://plugins.jetbrains.com/docs/intellij/documentation.html#documentation-target-api for more details --- ...kt => MvPsiDocumentationTargetProvider.kt} | 89 +++++++++++++++---- src/main/resources/META-INF/plugin.xml | 4 +- .../ide/docs/MoveDocumentationProviderTest.kt | 4 - .../docs/MoveNamedAddressDocumentationTest.kt | 2 +- .../MoveDocumentationProviderTestCase.kt | 33 ++++--- 5 files changed, 96 insertions(+), 36 deletions(-) rename src/main/kotlin/org/move/ide/docs/{MvDocumentationProvider.kt => MvPsiDocumentationTargetProvider.kt} (69%) diff --git a/src/main/kotlin/org/move/ide/docs/MvDocumentationProvider.kt b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt similarity index 69% rename from src/main/kotlin/org/move/ide/docs/MvDocumentationProvider.kt rename to src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt index 551ce0bf0..bbf71a944 100644 --- a/src/main/kotlin/org/move/ide/docs/MvDocumentationProvider.kt +++ b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt @@ -1,35 +1,92 @@ package org.move.ide.docs -import com.intellij.lang.documentation.AbstractDocumentationProvider import com.intellij.lang.documentation.DocumentationMarkup -import com.intellij.openapi.editor.Editor +import com.intellij.model.Pointer +import com.intellij.openapi.vfs.newvfs.VfsPresentationUtil +import com.intellij.platform.backend.documentation.DocumentationResult +import com.intellij.platform.backend.documentation.DocumentationTarget +import com.intellij.platform.backend.documentation.PsiDocumentationTargetProvider +import com.intellij.platform.backend.presentation.TargetPresentation import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile +import com.intellij.psi.createSmartPointer import org.move.ide.presentation.presentationInfo import org.move.ide.presentation.text import org.move.ide.presentation.typeLabel -import org.move.lang.core.psi.* -import org.move.lang.core.psi.ext.* +import org.move.lang.core.psi.MvAbility +import org.move.lang.core.psi.MvConst +import org.move.lang.core.psi.MvElement +import org.move.lang.core.psi.MvFunction +import org.move.lang.core.psi.MvFunctionParameter +import org.move.lang.core.psi.MvFunctionParameterList +import org.move.lang.core.psi.MvModule +import org.move.lang.core.psi.MvNamedAddress +import org.move.lang.core.psi.MvNamedFieldDecl +import org.move.lang.core.psi.MvPatBinding +import org.move.lang.core.psi.MvReturnType +import org.move.lang.core.psi.MvStruct +import org.move.lang.core.psi.MvType +import org.move.lang.core.psi.MvTypeParameter +import org.move.lang.core.psi.MvTypeParameterList +import org.move.lang.core.psi.ext.MvDocAndAttributeOwner +import org.move.lang.core.psi.ext.abilityBounds +import org.move.lang.core.psi.ext.ancestorOrSelf +import org.move.lang.core.psi.ext.bindingOwner +import org.move.lang.core.psi.ext.fieldOwner +import org.move.lang.core.psi.ext.isMsl +import org.move.lang.core.psi.ext.isMslOnlyItem +import org.move.lang.core.psi.ext.isPhantom +import org.move.lang.core.psi.ext.itemElement +import org.move.lang.core.psi.ext.module +import org.move.lang.core.psi.isNative +import org.move.lang.core.psi.module import org.move.lang.core.types.infer.inference import org.move.lang.core.types.infer.loweredType import org.move.lang.core.types.ty.Ty import org.move.lang.core.types.ty.TyUnknown import org.move.lang.moveProject import org.move.stdext.joinToWithBuffer +import org.toml.lang.psi.TomlKeySegment -class MvDocumentationProvider : AbstractDocumentationProvider() { - override fun getCustomDocumentationElement( - editor: Editor, - file: PsiFile, - contextElement: PsiElement?, - targetOffset: Int - ): PsiElement? { - val namedAddress = contextElement?.ancestorOrSelf() - if (namedAddress != null) return namedAddress - return super.getCustomDocumentationElement(editor, file, contextElement, targetOffset) +class MvPsiDocumentationTargetProvider : PsiDocumentationTargetProvider { + override fun documentationTarget(element: PsiElement, originalElement: PsiElement?): DocumentationTarget? { + if (element is MvElement) { + return MvDocumentationTarget(element, originalElement) + } + if (element is TomlKeySegment) { + val namedAddress = originalElement?.ancestorOrSelf() + if (namedAddress != null) return MvDocumentationTarget(namedAddress, originalElement) + return MvDocumentationTarget(element, originalElement) + } + return null + } +} + +@Suppress("UnstableApiUsage") +class MvDocumentationTarget(val element: PsiElement, private val originalElement: PsiElement?) : DocumentationTarget { + override fun computePresentation(): TargetPresentation { + val project = element.project + val file = element.containingFile?.virtualFile + + return TargetPresentation.builder("") + .backgroundColor(file?.let { VfsPresentationUtil.getFileBackgroundColor(project, file) }) + .presentation() + } + + override fun computeDocumentation(): DocumentationResult? { + val content = generateDoc(element) ?: return null + return DocumentationResult.documentation(content) + } + + override fun createPointer(): Pointer { + val elementPtr = element.createSmartPointer() + val originalElementPtr = originalElement?.createSmartPointer() + return Pointer { + val element = elementPtr.dereference() ?: return@Pointer null + MvDocumentationTarget(element, originalElementPtr?.dereference()) + } } - override fun generateDoc(element: PsiElement?, originalElement: PsiElement?): String? { + fun generateDoc(element: PsiElement?): String? { val buffer = StringBuilder() var docElement = element if ( diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 4aa474841..0d7fade73 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -76,8 +76,8 @@ - + diff --git a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt index a44bdbbe4..535c9b047 100644 --- a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt +++ b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt @@ -1,6 +1,5 @@ package org.move.ide.docs -import org.intellij.lang.annotations.Language import org.move.utils.tests.MvDocumentationProviderTestCase class MvDocumentationProviderTest : MvDocumentationProviderTestCase() { @@ -232,7 +231,4 @@ module 0x1::M {}
module 0x1::M
  1. The number of "items" in global storage.
  2. The number of bytes in global storage.
""") - - private fun doTest(@Language("Move") code: String, @Language("Html") expected: String?) = - doTest(code, expected, block = MvDocumentationProvider::generateDoc) } diff --git a/src/test/kotlin/org/move/ide/docs/MoveNamedAddressDocumentationTest.kt b/src/test/kotlin/org/move/ide/docs/MoveNamedAddressDocumentationTest.kt index b561dd80e..eba5e7ec5 100644 --- a/src/test/kotlin/org/move/ide/docs/MoveNamedAddressDocumentationTest.kt +++ b/src/test/kotlin/org/move/ide/docs/MoveNamedAddressDocumentationTest.kt @@ -24,6 +24,6 @@ class MvNamedModulePathDocumentationTest : MvDocumentationProviderProjectTestCas ) } }, - "Std = \"0x42\"", block = MvDocumentationProvider::generateDoc + "Std = \"0x42\"" ) } diff --git a/src/test/kotlin/org/move/utils/tests/MoveDocumentationProviderTestCase.kt b/src/test/kotlin/org/move/utils/tests/MoveDocumentationProviderTestCase.kt index 079e6e575..821c76f71 100644 --- a/src/test/kotlin/org/move/utils/tests/MoveDocumentationProviderTestCase.kt +++ b/src/test/kotlin/org/move/utils/tests/MoveDocumentationProviderTestCase.kt @@ -1,8 +1,11 @@ package org.move.utils.tests +import com.intellij.codeInsight.TargetElementUtil +import com.intellij.platform.backend.documentation.DocumentationData +import com.intellij.platform.backend.documentation.DocumentationResult import com.intellij.psi.PsiElement import org.intellij.lang.annotations.Language -import org.move.ide.docs.MvDocumentationProvider +import org.move.ide.docs.MvPsiDocumentationTargetProvider import org.move.lang.core.psi.MvElement import org.move.utils.tests.base.findElementAndOffsetInEditor @@ -10,16 +13,17 @@ abstract class MvDocumentationProviderProjectTestCase : MvProjectTestBase() { protected fun doTestByFileTree( @Language("Move") builder: TreeBuilder, @Language("Html") expected: String?, - block: MvDocumentationProvider.(PsiElement, PsiElement?) -> String? ) { testProject(builder) val (originalElement, offset) = myFixture.findElementAndOffsetInEditor() - @Suppress("DEPRECATION") - val element = com.intellij.codeInsight.documentation.DocumentationManager.getInstance(project) - .findTargetElement(myFixture.editor, offset, myFixture.file, originalElement)!! + val element = TargetElementUtil.getInstance().findTargetElement(myFixture.editor, TargetElementUtil.getInstance().getAllAccepted(), offset)!! - val actual = MvDocumentationProvider().block(element, originalElement)?.trim() + val provider = MvPsiDocumentationTargetProvider() + val target = provider.documentationTarget(element, originalElement)!! + val doc = target.computeDocumentation() + + val actual = doc?.content() if (expected == null) { check(actual == null) { "Expected null, got `$actual`" } } else { @@ -34,10 +38,9 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() { @Language("Move") code: String, @Language("Html") expected: String?, findElement: () -> Pair = { myFixture.findElementAndOffsetInEditor() }, - block: MvDocumentationProvider.(PsiElement, PsiElement?) -> String? ) { @Suppress("NAME_SHADOWING") - doTest(code, expected, findElement, block) { actual, expected -> + doTest(code, expected, findElement) { actual, expected -> assertSameLines(expected.trimIndent(), actual) } } @@ -58,17 +61,18 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() { @Language("Move") code: String, expected: T?, findElement: () -> Pair = { myFixture.findElementAndOffsetInEditor() }, - block: MvDocumentationProvider.(PsiElement, PsiElement?) -> String?, check: (String, T) -> Unit ) { InlineFile(myFixture, code, "main.move") val (originalElement, offset) = findElement() - @Suppress("DEPRECATION") - val element = com.intellij.codeInsight.documentation.DocumentationManager.getInstance(project) - .findTargetElement(myFixture.editor, offset, myFixture.file, originalElement)!! + val element = TargetElementUtil.getInstance().findTargetElement(myFixture.editor, TargetElementUtil.getInstance().getAllAccepted(), offset)!! + + val provider = MvPsiDocumentationTargetProvider() + val target = provider.documentationTarget(element, originalElement)!! + val doc = target.computeDocumentation() - val actual = MvDocumentationProvider().block(element, originalElement)?.trim() + val actual = doc?.content() if (expected == null) { check(actual == null) { "Expected null, got `$actual`" } } else { @@ -77,3 +81,6 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() { } } } + +@Suppress("UnstableApiUsage") +fun DocumentationResult.content(): String? = (this as? DocumentationData)?.html?.trim()