Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move documentation support to new "Documentation Target API" #207

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<MvNamedAddress>()
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<MvNamedAddress>()
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<out DocumentationTarget> {
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 (
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@
<stubIndex implementation="org.move.lang.index.MvModuleIndex" />
<stubIndex implementation="org.move.lang.index.MvModuleSpecIndex" />

<lang.documentationProvider language="Move"
implementationClass="org.move.ide.docs.MvDocumentationProvider" />
<platform.backend.documentation.psiTargetProvider
implementation="org.move.ide.docs.MvPsiDocumentationTargetProvider"/>

<lang.importOptimizer language="Move" implementationClass="org.move.ide.refactoring.MvImportOptimizer" />

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.move.ide.docs

import org.intellij.lang.annotations.Language
import org.move.utils.tests.MvDocumentationProviderTestCase

class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
Expand Down Expand Up @@ -232,7 +231,4 @@ module 0x1::M {}
<div class='definition'><pre>module 0x1::M</pre></div>
<div class='content'><ol><li>The number of &quot;items&quot; in global storage.</li><li>The number of bytes in global storage.</li></ol></div>
""")

private fun doTest(@Language("Move") code: String, @Language("Html") expected: String?) =
doTest(code, expected, block = MvDocumentationProvider::generateDoc)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ class MvNamedModulePathDocumentationTest : MvDocumentationProviderProjectTestCas
)
}
},
"Std = \"0x42\"", block = MvDocumentationProvider::generateDoc
"Std = \"0x42\""
)
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
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

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<MvElement>()
@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 {
Expand All @@ -34,10 +38,9 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() {
@Language("Move") code: String,
@Language("Html") expected: String?,
findElement: () -> Pair<PsiElement, Int> = { 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)
}
}
Expand All @@ -58,17 +61,18 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() {
@Language("Move") code: String,
expected: T?,
findElement: () -> Pair<PsiElement, Int> = { 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 {
Expand All @@ -77,3 +81,6 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() {
}
}
}

@Suppress("UnstableApiUsage")
fun DocumentationResult.content(): String? = (this as? DocumentationData)?.html?.trim()
Loading