From 2b83e391ab8d578c7c881dede0e030fe7369bb22 Mon Sep 17 00:00:00 2001
From: Maksim Kurnikov
Date: Fri, 22 Nov 2024 17:46:11 +0100
Subject: [PATCH 1/6] separate markdown docs tests
---
.../ide/docs/MoveDocumentationProviderTest.kt | 70 -----------------
.../ide/docs/MvMarkdownDocsRendererTest.kt | 76 +++++++++++++++++++
2 files changed, 76 insertions(+), 70 deletions(-)
create mode 100644 src/test/kotlin/org/move/ide/docs/MvMarkdownDocsRendererTest.kt
diff --git a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
index 535c9b04..d8994ccc 100644
--- a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
+++ b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
@@ -161,74 +161,4 @@ module 0x1::m {
""", """
value parameter result: &mut T
""")
-
- fun `test markdown text styles`() = doTest("""
-/// This string contains some *bold* and **italic** words.
-module 0x1::M {}
- //^
- """, """
-
-This string contains some bold and italic words.
- """)
-
- fun `test markdown inline code`() = doTest("""
-/// Maybe some `inline` keyword
-module 0x1::M {}
- //^
- """, """
-
-Maybe some inline
keyword
- """)
-
- fun `test markdown multiline code`() = doTest("""
-/// Move code:
-/// ```
-/// module 0x1::M {}
-/// ```
-module 0x1::M {}
- //^
- """, """
-
-Move code:
module 0x1::M {}
-
-
- """)
-
- fun `test markdown multiline code with extra spaces`() = doTest("""
-/// Move code:
-/// ```
-/// module 0x1::M {
-/// // comment
-/// }
-/// ```
-module 0x1::M {}
- //^
- """, """
-
-Move code:
module 0x1::M {
- // comment
-}
-
-
- """)
-
- fun `test markdown list`() = doTest("""
-/// - The number of "items" in global storage.
-/// - The number of bytes in global storage.
-module 0x1::M {}
- //^
- """, """
-
-- The number of "items" in global storage.
- The number of bytes in global storage.
- """)
-
- fun `test markdown numbered list`() = doTest("""
-/// 1. The number of "items" in global storage.
-/// 2. The number of bytes in global storage.
-module 0x1::M {}
- //^
- """, """
-
-- The number of "items" in global storage.
- The number of bytes in global storage.
- """)
}
diff --git a/src/test/kotlin/org/move/ide/docs/MvMarkdownDocsRendererTest.kt b/src/test/kotlin/org/move/ide/docs/MvMarkdownDocsRendererTest.kt
new file mode 100644
index 00000000..736931fc
--- /dev/null
+++ b/src/test/kotlin/org/move/ide/docs/MvMarkdownDocsRendererTest.kt
@@ -0,0 +1,76 @@
+package org.move.ide.docs
+
+import org.move.utils.tests.MvDocumentationProviderTestCase
+
+class MvMarkdownDocsRendererTest: MvDocumentationProviderTestCase() {
+
+ fun `test markdown text styles`() = doTest("""
+/// This string contains some *bold* and **italic** words.
+module 0x1::M {}
+ //^
+ """, """
+
+This string contains some bold and italic words.
+ """)
+
+ fun `test markdown inline code`() = doTest("""
+/// Maybe some `inline` keyword
+module 0x1::M {}
+ //^
+ """, """
+
+Maybe some inline
keyword
+ """)
+
+ fun `test markdown multiline code`() = doTest("""
+/// Move code:
+/// ```
+/// module 0x1::M {}
+/// ```
+module 0x1::M {}
+ //^
+ """, """
+
+Move code:
module 0x1::M {}
+
+
+ """)
+
+ fun `test markdown multiline code with extra spaces`() = doTest("""
+/// Move code:
+/// ```
+/// module 0x1::M {
+/// // comment
+/// }
+/// ```
+module 0x1::M {}
+ //^
+ """, """
+
+Move code:
module 0x1::M {
+ // comment
+}
+
+
+ """)
+
+ fun `test markdown list`() = doTest("""
+/// - The number of "items" in global storage.
+/// - The number of bytes in global storage.
+module 0x1::M {}
+ //^
+ """, """
+
+- The number of "items" in global storage.
- The number of bytes in global storage.
+ """)
+
+ fun `test markdown numbered list`() = doTest("""
+/// 1. The number of "items" in global storage.
+/// 2. The number of bytes in global storage.
+module 0x1::M {}
+ //^
+ """, """
+
+- The number of "items" in global storage.
- The number of bytes in global storage.
+ """)
+}
\ No newline at end of file
From 977be0f93a9f2d76931d109d399bc7a4db08a16d Mon Sep 17 00:00:00 2001
From: Maksim Kurnikov
Date: Sat, 23 Nov 2024 17:30:27 +0100
Subject: [PATCH 2/6] add docs for missing module items, coloring for the
documentation
---
src/main/grammars/MoveParser.bnf | 1 +
.../kotlin/org/move/ide/docs/MvColorUtils.kt | 55 +++++
.../docs/MvPsiDocumentationTargetProvider.kt | 165 +++++----------
.../org/move/ide/docs/RenderSignature.kt | 165 +++++++++++++++
.../kotlin/org/move/ide/presentation/Utils.kt | 10 +
.../kotlin/org/move/ide/presentation/ty.kt | 192 ++++++++++++------
.../move/lang/core/psi/ext/MvEnumVariant.kt | 2 -
.../org/move/lang/core/psi/ext/MvFunction.kt | 16 ++
.../move/lang/core/psi/ext/MvSpecFunction.kt | 30 ++-
.../kotlin/org/move/stdext/Collections.kt | 25 ++-
.../kotlin/org/move/utils/SignatureUtils.kt | 2 +-
.../ide/docs/MoveDocumentationProviderTest.kt | 137 ++++++++++---
.../ide/docs/MvMarkdownDocsRendererTest.kt | 56 +++--
.../MoveDocumentationProviderTestCase.kt | 15 +-
14 files changed, 637 insertions(+), 234 deletions(-)
create mode 100644 src/main/kotlin/org/move/ide/docs/MvColorUtils.kt
create mode 100644 src/main/kotlin/org/move/ide/docs/RenderSignature.kt
diff --git a/src/main/grammars/MoveParser.bnf b/src/main/grammars/MoveParser.bnf
index ee482d8e..dd7c4723 100644
--- a/src/main/grammars/MoveParser.bnf
+++ b/src/main/grammars/MoveParser.bnf
@@ -338,6 +338,7 @@ fake SpecInlineFunction ::= Attr* native? fun IDENTIFIER? TypeParameterList?
(SpecCodeBlock | ';')?
{
implements = [
+ "org.move.lang.core.psi.MvQualNamedElement"
"org.move.lang.core.psi.MvFunctionLike"
"org.move.lang.core.types.infer.MvInferenceContextOwner"
"org.move.lang.core.psi.ext.MvItemElement"
diff --git a/src/main/kotlin/org/move/ide/docs/MvColorUtils.kt b/src/main/kotlin/org/move/ide/docs/MvColorUtils.kt
new file mode 100644
index 00000000..ea199f82
--- /dev/null
+++ b/src/main/kotlin/org/move/ide/docs/MvColorUtils.kt
@@ -0,0 +1,55 @@
+package org.move.ide.docs
+
+import com.intellij.lang.documentation.DocumentationSettings
+import com.intellij.openapi.editor.colors.EditorColorsManager
+import com.intellij.openapi.editor.colors.TextAttributesKey
+import com.intellij.openapi.editor.markup.TextAttributes
+import com.intellij.openapi.editor.richcopy.HtmlSyntaxInfoUtil
+import io.ktor.util.escapeHTML
+import org.move.ide.colors.MvColor
+
+@Suppress("UnstableApiUsage")
+object MvColorUtils {
+
+ val asKeyword get() = loadKey(MvColor.KEYWORD)
+ val asFunction get() = loadKey(MvColor.FUNCTION)
+ val asStruct get() = loadKey(MvColor.STRUCT)
+ val asField get() = loadKey(MvColor.FIELD)
+ val asEnum get() = loadKey(MvColor.ENUM)
+ val asEnumVariant get() = loadKey(MvColor.ENUM_VARIANT)
+ val asConst get() = loadKey(MvColor.CONSTANT)
+ val asTypeParam get() = loadKey(MvColor.TYPE_PARAMETER)
+ val asAbility get() = loadKey(MvColor.ABILITY)
+
+ val asPrimitiveType get() = loadKey(MvColor.PRIMITIVE_TYPE)
+ val asBuiltinType get() = loadKey(MvColor.BUILTIN_TYPE)
+ val asStructType get() = loadKey(MvColor.STRUCT)
+ val asEnumType get() = loadKey(MvColor.ENUM)
+ val asEnumVariantType get() = loadKey(MvColor.ENUM_VARIANT)
+
+ val asBraces get() = loadKey(MvColor.BRACES)
+ val asBrackets get() = loadKey(MvColor.BRACKETS)
+ val asParens get() = loadKey(MvColor.PARENTHESES)
+ val asComma get() = loadKey(MvColor.COMMA)
+ val asSemicolon get() = loadKey(MvColor.SEMICOLON)
+
+ fun StringBuilder.keyword(text: String) = colored(text, asKeyword)
+ fun StringBuilder.comma() = colored(",", asComma)
+ fun StringBuilder.semicolon() = colored(";", asSemicolon)
+
+ fun StringBuilder.colored(text: String?, color: TextAttributes, noHtml: Boolean = false) {
+ if (noHtml) {
+ append(text)
+ return
+ }
+ HtmlSyntaxInfoUtil.appendStyledSpan(
+ this, color, text?.escapeHTML() ?: "",
+ DocumentationSettings.getHighlightingSaturation(false)
+ )
+ }
+
+ private fun loadKey(color: MvColor): TextAttributes = loadKey(color.textAttributesKey)
+
+ private fun loadKey(key: TextAttributesKey): TextAttributes =
+ EditorColorsManager.getInstance().globalScheme.getAttributes(key)!!
+}
\ No newline at end of file
diff --git a/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt
index cc470128..649a8162 100644
--- a/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt
+++ b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt
@@ -9,36 +9,34 @@ import com.intellij.platform.backend.documentation.PsiDocumentationTargetProvide
import com.intellij.platform.backend.presentation.TargetPresentation
import com.intellij.psi.PsiElement
import com.intellij.psi.createSmartPointer
+import org.move.ide.docs.MvColorUtils.asAbility
+import org.move.ide.docs.MvColorUtils.asTypeParam
+import org.move.ide.docs.MvColorUtils.colored
+import org.move.ide.docs.MvColorUtils.keyword
+import org.move.ide.presentation.declaringModule
import org.move.ide.presentation.presentationInfo
import org.move.ide.presentation.text
-import org.move.ide.presentation.typeLabel
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.containingModule
import org.move.lang.core.psi.ext.*
-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
+import kotlin.collections.isNotEmpty
-class MvPsiDocumentationTargetProvider : PsiDocumentationTargetProvider {
+class MvPsiDocumentationTargetProvider: PsiDocumentationTargetProvider {
override fun documentationTarget(element: PsiElement, originalElement: PsiElement?): DocumentationTarget? {
if (element is MvElement) {
return MvDocumentationTarget(element, originalElement)
@@ -53,7 +51,10 @@ class MvPsiDocumentationTargetProvider : PsiDocumentationTargetProvider {
}
@Suppress("UnstableApiUsage")
-class MvDocumentationTarget(val element: PsiElement, private val originalElement: PsiElement?) : DocumentationTarget {
+class MvDocumentationTarget(
+ val element: PsiElement,
+ private val originalElement: PsiElement?
+): DocumentationTarget {
override fun computePresentation(): TargetPresentation {
val project = element.project
val file = element.containingFile?.virtualFile
@@ -86,8 +87,8 @@ class MvDocumentationTarget(val element: PsiElement, private val originalElement
}
when (docElement) {
+ is MvDocAndAttributeOwner -> generateDocumentationOwnerDoc(docElement, buffer)
is MvNamedAddress -> {
- // TODO: add docs for both [addresses] and [dev-addresses]
val moveProject = docElement.moveProject ?: return null
val refName = docElement.referenceName
val named = moveProject.getNamedAddressTestAware(refName) ?: return null
@@ -95,17 +96,18 @@ class MvDocumentationTarget(val element: PsiElement, private val originalElement
named.addressLit()?.original ?: angleWrapped("unassigned")
return "$refName = \"$address\""
}
- is MvDocAndAttributeOwner -> generateOwnerDoc(docElement, buffer)
is MvPatBinding -> {
val presentationInfo = docElement.presentationInfo ?: return null
+ buffer.keyword(presentationInfo.type)
+ buffer += " "
+ buffer += presentationInfo.name
val msl = docElement.isMslOnlyItem
val inference = docElement.inference(msl) ?: return null
- val type = inference.getBindingType(docElement).renderForDocs(true)
- buffer += presentationInfo.type
- buffer += " "
- buffer.b { it += presentationInfo.name }
+ val tyText = inference
+ .getBindingType(docElement)
+ .text(fq = true, colors = true)
buffer += ": "
- buffer += type
+ buffer += tyText
}
is MvTypeParameter -> {
@@ -115,18 +117,20 @@ class MvDocumentationTarget(val element: PsiElement, private val originalElement
buffer.b { it += presentationInfo.name }
val abilities = docElement.abilityBounds
if (abilities.isNotEmpty()) {
- abilities.joinToWithBuffer(buffer, " + ", ": ") { generateDocumentation(it) }
+ abilities.joinToWithBuffer(buffer, " + ", ": ") { generateDoc(it) }
}
}
}
return if (buffer.isEmpty()) null else buffer.toString()
}
- private fun generateOwnerDoc(element: MvDocAndAttributeOwner, buffer: StringBuilder) {
+ private fun generateDocumentationOwnerDoc(element: MvDocAndAttributeOwner, buffer: StringBuilder) {
definition(buffer) {
+ element.header(it)
element.signature(it)
}
val text = element.documentationAsHtml()
+ if (text.isEmpty()) return
buffer += "\n" // Just for more pretty html text representation
content(buffer) { it += text }
}
@@ -137,118 +141,50 @@ fun MvDocAndAttributeOwner.documentationAsHtml(): String {
return documentationAsHtml(commentText, this)
}
-fun generateFunction(function: MvFunction, buffer: StringBuilder) {
- val module = function.module
- if (module != null) {
- buffer += module.qualName?.editorText() ?: "unknown"
- buffer += "\n"
- }
- if (function.isNative) buffer += "native "
- buffer += "fun "
- buffer.b { it += function.name }
- function.typeParameterList?.generateDocumentation(buffer)
- function.functionParameterList?.generateDocumentation(buffer)
- function.returnType?.generateDocumentation(buffer)
-}
-
-fun MvElement.signature(builder: StringBuilder) {
- val buffer = StringBuilder()
- // no need for msl type conversion in docs
- val msl = false
-// val msl = this.isMslLegacy()
- when (this) {
- is MvFunction -> generateFunction(this, buffer)
- is MvModule -> {
- buffer += "module "
- buffer += this.qualName?.editorText() ?: "unknown"
- }
-
- is MvStruct -> {
- buffer += this.module.qualName?.editorText() ?: "unknown"
- buffer += "\n"
-
- buffer += "struct "
- buffer.b { it += this.name }
- this.typeParameterList?.generateDocumentation(buffer)
- this.abilitiesList?.abilityList
- ?.joinToWithBuffer(buffer, ", ", " has ") { generateDocumentation(it) }
- }
-
- is MvNamedFieldDecl -> {
- val module = this.fieldOwner.itemElement.module
-// val itemContext = this.structItem.outerItemContext(msl)
- buffer += module.qualName?.editorText() ?: "unknown"
- buffer += "::"
- buffer += this.fieldOwner.name ?: angleWrapped("anonymous")
- buffer += "\n"
- buffer.b { it += this.name }
- buffer += ": ${(this.type?.loweredType(msl) ?: TyUnknown).renderForDocs(true)}"
-// buffer += ": ${itemContext.getStructFieldItemTy(this).renderForDocs(true)}"
- }
-
- is MvConst -> {
-// val itemContext = this.outerItemContext(msl)
- buffer += this.module?.qualName?.editorText() ?: angleWrapped("unknown")
- buffer += "\n"
- buffer += "const "
- buffer.b { it += this.name ?: angleWrapped("unknown") }
- buffer += ": ${(this.type?.loweredType(msl) ?: TyUnknown).renderForDocs(false)}"
-// buffer += ": ${itemContext.getConstTy(this).renderForDocs(false)}"
- this.initializer?.let { buffer += " ${it.text}" }
- }
-
- else -> return
- } ?: return
- listOf(buffer.toString()).joinTo(builder, "
")
-}
-
-private fun PsiElement.generateDocumentation(
- buffer: StringBuilder,
- prefix: String = "",
- suffix: String = ""
-) {
- buffer += prefix
+fun PsiElement.generateDoc(buf: StringBuilder) {
when (this) {
is MvType -> {
val msl = this.isMsl()
- buffer += this.loweredType(msl)
- .typeLabel(this)
- .replace("<", "<")
- .replace(">", ">")
+ val ty = this.loweredType(msl)
+
+ val tyItemModule = ty.declaringModule()
+ val fq = tyItemModule != null && tyItemModule != this.containingModule
+ buf += ty.text(fq, colors = true)
}
is MvFunctionParameterList ->
this.functionParameterList
- .joinToWithBuffer(buffer, ", ", "(", ")") { generateDocumentation(it) }
+ .joinToWithBuffer(buf, ", ", "(", ")") { generateDoc(it) }
is MvFunctionParameter -> {
- buffer += this.patBinding.identifier.text
- this.type?.generateDocumentation(buffer, ": ")
+ buf += this.patBinding.identifier.text
+ this.type?.let {
+ buf += ": "
+ it.generateDoc(buf)
+ }
}
is MvTypeParameterList ->
this.typeParameterList
- .joinToWithBuffer(buffer, ", ", "<", ">") { generateDocumentation(it) }
+ .joinToWithBuffer(buf, ", ", "<", ">") { generateDoc(it) }
is MvTypeParameter -> {
if (this.isPhantom) {
- buffer += "phantom"
- buffer += " "
+ buf.keyword("phantom")
+ buf += " "
}
- buffer += this.identifier?.text
+ buf.colored(this.name, asTypeParam)
val bound = this.typeParamBound
if (bound != null) {
- abilityBounds.joinToWithBuffer(buffer, " + ", ": ") { generateDocumentation(it) }
+ abilityBounds.joinToWithBuffer(buf, " + ", ": ") { generateDoc(it) }
}
}
-
- is MvAbility -> {
- buffer += this.text
+ is MvAbility -> buf.colored(this.text, asAbility)
+ is MvReturnType -> this.type?.let {
+ buf += ": "
+ it.generateDoc(buf)
}
-
- is MvReturnType -> this.type?.generateDocumentation(buffer, ": ")
}
- buffer += suffix
}
private inline fun definition(buffer: StringBuilder, block: (StringBuilder) -> Unit) {
@@ -263,16 +199,13 @@ private inline fun content(buffer: StringBuilder, block: (StringBuilder) -> Unit
buffer += DocumentationMarkup.CONTENT_END
}
-private fun angleWrapped(text: String): String = "<$text>"
-
-private fun Ty.renderForDocs(fq: Boolean): String {
- val original = this.text(fq)
- return original
+fun String.escapeForHtml(): String {
+ return this
.replace("<", "<")
.replace(">", ">")
}
-private operator fun StringBuilder.plusAssign(value: String?) {
+operator fun StringBuilder.plusAssign(value: String?) {
if (value != null) {
append(value)
}
@@ -283,3 +216,5 @@ private inline fun StringBuilder.b(action: (StringBuilder) -> Unit) {
action(this)
append("")
}
+
+private fun angleWrapped(text: String): String = "<$text>"
diff --git a/src/main/kotlin/org/move/ide/docs/RenderSignature.kt b/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
new file mode 100644
index 00000000..58b9225d
--- /dev/null
+++ b/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
@@ -0,0 +1,165 @@
+package org.move.ide.docs
+
+import org.move.ide.docs.MvColorUtils.asConst
+import org.move.ide.docs.MvColorUtils.asEnum
+import org.move.ide.docs.MvColorUtils.asEnumVariant
+import org.move.ide.docs.MvColorUtils.asField
+import org.move.ide.docs.MvColorUtils.asFunction
+import org.move.ide.docs.MvColorUtils.asStruct
+import org.move.ide.docs.MvColorUtils.colored
+import org.move.ide.docs.MvColorUtils.keyword
+import org.move.ide.presentation.presentableQualifiedName
+import org.move.ide.presentation.text
+import org.move.lang.core.psi.MvConst
+import org.move.lang.core.psi.MvEnum
+import org.move.lang.core.psi.MvEnumVariant
+import org.move.lang.core.psi.MvFunction
+import org.move.lang.core.psi.MvFunctionLike
+import org.move.lang.core.psi.MvModule
+import org.move.lang.core.psi.MvNamedFieldDecl
+import org.move.lang.core.psi.MvSpecFunction
+import org.move.lang.core.psi.MvSpecInlineFunction
+import org.move.lang.core.psi.MvStruct
+import org.move.lang.core.psi.ext.MvDocAndAttributeOwner
+import org.move.lang.core.psi.ext.MvStructOrEnumItemElement
+import org.move.lang.core.psi.ext.enumItem
+import org.move.lang.core.psi.ext.fieldOwner
+import org.move.lang.core.psi.ext.modifiers
+import org.move.lang.core.types.ty.TyUnknown
+import org.move.stdext.joinToWithBuffer
+
+fun MvDocAndAttributeOwner.header(buffer: StringBuilder) {
+ val rawLines = when (this) {
+ is MvNamedFieldDecl -> listOfNotNull((fieldOwner as? MvDocAndAttributeOwner)?.presentableQualifiedName)
+ is MvStructOrEnumItemElement,
+ is MvFunctionLike,
+ is MvConst -> listOfNotNull(presentableQualifiedModName)
+ else -> emptyList()
+ }
+ rawLines.joinTo(buffer, "
")
+ if (rawLines.isNotEmpty()) {
+ buffer += "\n"
+ }
+}
+
+fun MvDocAndAttributeOwner.signature(builder: StringBuilder) {
+ // no need for msl type conversion in docs
+// val msl = false
+ val buffer = StringBuilder()
+ when (this) {
+ is MvFunction -> buffer.generateFunction(this)
+ is MvSpecFunction -> buffer.generateSpecFunction(this)
+ is MvSpecInlineFunction -> buffer.generateSpecInlineFunction(this)
+ is MvModule -> buffer.generateModule(this)
+ is MvStructOrEnumItemElement -> buffer.generateStructOrEnum(this)
+ is MvNamedFieldDecl -> buffer.generateNamedField(this)
+ is MvConst -> buffer.generateConst(this)
+ is MvEnumVariant -> buffer.generateEnumVariant(this)
+ else -> return
+ }
+ listOf(buffer.toString()).joinTo(builder, "
")
+}
+
+private fun StringBuilder.generateFunction(fn: MvFunction) {
+ for (modifier in fn.modifiers) {
+ keyword(modifier)
+ this += " "
+ }
+ keyword("fun")
+ this += " "
+ colored(fn.name, asFunction)
+
+ fn.typeParameterList?.generateDoc(this)
+ fn.functionParameterList?.generateDoc(this)
+ fn.returnType?.generateDoc(this)
+}
+
+private fun StringBuilder.generateSpecFunction(specFn: MvSpecFunction) {
+ for (modifier in specFn.modifiers) {
+ keyword(modifier)
+ this += " "
+ }
+ keyword("spec")
+ this += " "
+ keyword("fun")
+ this += " "
+ colored(specFn.name, asFunction)
+
+ specFn.typeParameterList?.generateDoc(this)
+ specFn.functionParameterList?.generateDoc(this)
+ specFn.returnType?.generateDoc(this)
+}
+
+private fun StringBuilder.generateSpecInlineFunction(specInlineFn: MvSpecInlineFunction) {
+ for (modifier in specInlineFn.modifiers) {
+ keyword(modifier)
+ this += " "
+ }
+ keyword("fun")
+ this += " "
+ colored(specInlineFn.name, asFunction)
+
+ specInlineFn.typeParameterList?.generateDoc(this)
+ specInlineFn.functionParameterList?.generateDoc(this)
+ specInlineFn.returnType?.generateDoc(this)
+}
+
+private fun StringBuilder.generateModule(mod: MvModule) {
+ keyword("module")
+ this += " "
+ this += mod.qualName?.editorText() ?: "unknown"
+}
+
+private fun StringBuilder.generateStructOrEnum(structOrEnum: MvStructOrEnumItemElement) {
+ when (structOrEnum) {
+ is MvStruct -> {
+ keyword("struct")
+ this += " "
+ colored(structOrEnum.name, asStruct)
+ }
+ is MvEnum -> {
+ keyword("enum")
+ this += " "
+ colored(structOrEnum.name, asEnum)
+ }
+ }
+ structOrEnum.typeParameterList?.generateDoc(this)
+ structOrEnum.abilitiesList?.abilityList
+ ?.joinToWithBuffer(this, ", ", " has ") { generateDoc(it) }
+}
+
+private fun StringBuilder.generateEnumVariant(variant: MvEnumVariant) {
+ this += variant.enumItem.presentableQualifiedName
+ this += "::"
+ this.colored(variant.name, asEnumVariant)
+}
+
+private fun StringBuilder.generateNamedField(field: MvNamedFieldDecl) {
+ colored(field.name, asField)
+ this += ": "
+ val fieldType = field.type
+ if (fieldType == null) {
+ this += TyUnknown.text(colors = true)
+ return
+ }
+ fieldType.generateDoc(this)
+}
+
+private fun StringBuilder.generateConst(const: MvConst) {
+ keyword("const")
+ this += " "
+ colored(const.name, asConst)
+
+ this += ": "
+ val constType = const.type
+ if (constType == null) {
+ this += TyUnknown.text(colors = true)
+ return
+ }
+ constType.generateDoc(this)
+
+ const.initializer?.let { this += " ${it.text}" }
+}
+
+private val MvDocAndAttributeOwner.presentableQualifiedModName: String?
+ get() = presentableQualifiedName?.removeSuffix("::$name")
diff --git a/src/main/kotlin/org/move/ide/presentation/Utils.kt b/src/main/kotlin/org/move/ide/presentation/Utils.kt
index ef478bea..f32799c8 100644
--- a/src/main/kotlin/org/move/ide/presentation/Utils.kt
+++ b/src/main/kotlin/org/move/ide/presentation/Utils.kt
@@ -12,6 +12,9 @@ import org.move.lang.core.psi.MvFunctionLike
import org.move.lang.core.psi.MvModule
import org.move.lang.core.psi.MvNamedElement
import org.move.lang.core.psi.MvNamedFieldDecl
+import org.move.lang.core.psi.MvQualNamedElement
+import org.move.lang.core.psi.ext.MvDocAndAttributeOwner
+import org.move.lang.core.psi.ext.enumItem
import org.move.lang.core.psi.signatureText
import org.move.lang.core.types.address
import org.move.lang.moveProject
@@ -69,6 +72,13 @@ fun PsiElement.locationString(tryRelative: Boolean): String? = when (this) {
else -> containingFilePath(tryRelative)?.toString()
}
+val MvDocAndAttributeOwner.presentableQualifiedName: String?
+ get() {
+ val qName = (this as? MvQualNamedElement)?.qualName?.editorText()
+ if (qName != null) return qName
+ return name
+ }
+
private fun PsiElement.containingFilePath(tryRelative: Boolean): Path? {
val containingFilePath = this.containingFile.toNioPathOrNull() ?: return null
if (tryRelative) {
diff --git a/src/main/kotlin/org/move/ide/presentation/ty.kt b/src/main/kotlin/org/move/ide/presentation/ty.kt
index d201859c..05eff36f 100644
--- a/src/main/kotlin/org/move/ide/presentation/ty.kt
+++ b/src/main/kotlin/org/move/ide/presentation/ty.kt
@@ -1,9 +1,16 @@
package org.move.ide.presentation
-import org.move.lang.core.psi.MvElement
+import com.intellij.openapi.editor.markup.TextAttributes
+import org.move.ide.docs.MvColorUtils.asBuiltinType
+import org.move.ide.docs.MvColorUtils.asKeyword
+import org.move.ide.docs.MvColorUtils.asPrimitiveType
+import org.move.ide.docs.MvColorUtils.asTypeParam
+import org.move.ide.docs.MvColorUtils.colored
+import org.move.ide.docs.escapeForHtml
import org.move.lang.core.psi.MvModule
import org.move.lang.core.psi.containingModule
import org.move.lang.core.types.ty.*
+import org.move.stdext.chainIf
// null -> builtin module
fun Ty.declaringModule(): MvModule? = when (this) {
@@ -16,35 +23,27 @@ fun Ty.nameNoArgs(): String {
return this.name().replace(Regex("<.*>"), "")
}
-fun Ty.name(): String {
- return text(fq = false)
+fun Ty.name(colors: Boolean = false): String {
+ return text(fq = false, colors = colors)
}
fun Ty.fullnameNoArgs(): String {
return this.fullname().replace(Regex("<.*>"), "")
}
-fun Ty.fullname(): String {
- return text(fq = true)
-}
-
-fun Ty.typeLabel(relativeTo: MvElement): String {
- val typeModule = this.declaringModule()
- if (typeModule != null && typeModule != relativeTo.containingModule) {
- return this.fullname()
- } else {
- return this.name()
- }
+fun Ty.fullname(colors: Boolean = false): String {
+ return text(fq = true, colors = colors)
}
fun Ty.hintText(): String =
render(this, level = 3, unknown = "?", tyVar = { "?" })
-fun Ty.text(fq: Boolean = false): String =
+fun Ty.text(fq: Boolean = false, colors: Boolean = false): String =
render(
this,
level = 3,
- fq = fq
+ fq = fq,
+ toHtml = colors,
)
fun Ty.expectedTyText(): String {
@@ -71,14 +70,14 @@ fun Ty.expectedTyText(): String {
)
}
-val Ty.insertionSafeText: String
- get() = render(
- this,
- level = Int.MAX_VALUE,
- unknown = "_",
- anonymous = "_",
- integer = "_"
- )
+//val Ty.insertionSafeText: String
+// get() = render(
+// this,
+// level = Int.MAX_VALUE,
+// unknown = "_",
+// anonymous = "_",
+// integer = "_"
+// )
fun tyToString(ty: Ty) = render(ty, Int.MAX_VALUE)
@@ -88,35 +87,28 @@ private fun render(
unknown: String = "",
anonymous: String = "",
integer: String = "integer",
- typeParam: (TyTypeParameter) -> String = { it.name ?: anonymous },
- tyVar: (TyInfer.TyVar) -> String = { "?${it.origin?.name ?: "_"}" },
- fq: Boolean = false
+ fq: Boolean = false,
+ toHtml: Boolean = false,
+ typeParam: (TyTypeParameter) -> String = {
+ it.name?.chainIf(toHtml) { colored(this, asTypeParam) }
+ ?: anonymous
+// colored(it.name, asTypeParam, toHtml) ?: anonymous
+ },
+ tyVar: (TyInfer.TyVar) -> String = {
+ val varName =
+ it.origin?.name?.chainIf(toHtml) { colored(this, asTypeParam) }
+ ?: "_"
+ "?$varName"
+// colored(it.origin?.name, asTypeParam, toHtml) ?: "_"
+ },
): String {
check(level >= 0)
- if (ty is TyUnknown) return unknown
- if (ty is TyPrimitive) {
- return when (ty) {
- is TyBool -> "bool"
- is TyAddress -> "address"
- is TySigner -> "signer"
- is TyUnit -> "()"
- is TyNum -> "num"
- is TySpecBv -> "bv"
- is TyInteger -> {
- if (ty.kind == TyInteger.DEFAULT_KIND) {
- integer
- } else {
- ty.kind.toString()
- }
- }
- is TyNever -> ""
- else -> error("unreachable")
- }
- }
+ if (ty is TyUnknown) return unknown.chainIf(toHtml) { escapeForHtml() }
+ if (ty is TyPrimitive) return renderPrimitive(ty, integer, toHtml = toHtml)
if (level == 0) return "_"
- val r = { subTy: Ty -> render(subTy, level - 1, unknown, anonymous, integer, typeParam, tyVar, fq) }
+ val r = { subTy: Ty -> render(subTy, level - 1, unknown, anonymous, integer, fq, toHtml, typeParam, tyVar) }
return when (ty) {
is TyFunction -> {
@@ -128,26 +120,42 @@ private fun render(
s
}
is TyTuple -> ty.types.joinToString(", ", "(", ")", transform = r)
- is TyVector -> "vector<${r(ty.item)}>"
- is TyRange -> "range<${r(ty.item)}>"
+ is TyVector -> {
+ val buf = StringBuilder()
+ buf.colored("vector", asBuiltinType, noHtml = !toHtml)
+ buf += "<".escapeIf(toHtml) + r(ty.item) + ">".escapeIf(toHtml)
+ buf.toString()
+ }
+ is TyRange -> {
+ val buf = StringBuilder()
+ buf.colored("range", asBuiltinType, noHtml = !toHtml)
+ buf += "<".escapeIf(toHtml) + r(ty.item) + ">".escapeIf(toHtml)
+ buf.toString()
+ }
is TyReference -> {
- val prefix = if (ty.mutability.isMut) "&mut " else "&"
- "$prefix${r(ty.referenced)}"
+ val buf = StringBuilder()
+ // todo: escape?
+ buf += "&"
+ if (ty.mutability.isMut) {
+ buf += colored("mut", asKeyword, toHtml)
+ buf += " "
+ }
+ buf += r(ty.referenced)
+ buf.toString()
}
is TyTypeParameter -> typeParam(ty)
-// is TyStruct -> {
-// val name = if (fq) ty.item.qualName?.editorText() ?: anonymous else (ty.item.name ?: anonymous)
-// val args =
-// if (ty.typeArguments.isEmpty()) ""
-// else ty.typeArguments.joinToString(", ", "<", ">", transform = r)
-// name + args
-// }
is TyAdt -> {
- val name = if (fq) ty.item.qualName?.editorText() ?: anonymous else (ty.item.name ?: anonymous)
- val args =
+ val itemName = if (fq) ty.item.qualName?.editorText() ?: anonymous else (ty.item.name ?: anonymous)
+ val typeArgs =
if (ty.typeArguments.isEmpty()) ""
- else ty.typeArguments.joinToString(", ", "<", ">", transform = r)
- name + args
+ else ty.typeArguments.joinToString(
+ ", ",
+ "<".escapeIf(toHtml),
+ ">".escapeIf(toHtml),
+ transform = r
+ )
+ itemName + typeArgs
+// itemName + typeArgs.chainIf(toHtml) { escapeForHtml() }
}
is TyInfer -> when (ty) {
is TyInfer.TyVar -> tyVar(ty)
@@ -163,11 +171,61 @@ private fun render(
}
is TySchema -> {
val name = if (fq) ty.item.qualName?.editorText() ?: anonymous else (ty.item.name ?: anonymous)
- val args =
- if (ty.typeArguments.isEmpty()) ""
- else ty.typeArguments.joinToString(", ", "<", ">", transform = r)
- name + args
+ val typeArgs =
+ if (ty.typeArguments.isEmpty()) {
+ ""
+ } else {
+ ty.typeArguments.joinToString(
+ ", ",
+ "<".escapeIf(toHtml),
+ ">".escapeIf(toHtml),
+ transform = r
+ )
+ }
+ name + typeArgs
}
else -> error("unimplemented for type ${ty.javaClass.name}")
}
}
+
+private fun renderPrimitive(ty: TyPrimitive, integer: String = "integer", toHtml: Boolean = false): String {
+ val tyText = when (ty) {
+ is TyBool -> "bool"
+ is TyAddress -> "address"
+ is TySigner -> "signer"
+ is TyUnit -> "()"
+ is TyNum -> "num"
+ is TySpecBv -> "bv"
+ is TyInteger -> {
+ if (ty.kind == TyInteger.DEFAULT_KIND) {
+ integer
+ } else {
+ ty.kind.toString()
+ }
+ }
+ is TyNever -> ""
+ else -> error("unreachable")
+ }
+ .chainIf(toHtml) { escapeForHtml() }
+
+ return colored(tyText, asPrimitiveType, toHtml)!!
+}
+
+private fun String.escapeIf(toHtml: Boolean) = this.chainIf(toHtml) { escapeForHtml() }
+
+private fun colored(text: String, color: TextAttributes): String {
+ return colored(text, color, html = true)!!
+}
+
+private fun colored(text: String?, color: TextAttributes, html: Boolean = true): String? {
+ if (text == null) return null
+ val buf = StringBuilder()
+ buf.colored(text, color, !html)
+ return buf.toString()
+}
+
+operator fun StringBuilder.plusAssign(value: String?) {
+ if (value != null) {
+ append(value)
+ }
+}
diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvEnumVariant.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvEnumVariant.kt
index e00e6f84..cba128e2 100644
--- a/src/main/kotlin/org/move/lang/core/psi/ext/MvEnumVariant.kt
+++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvEnumVariant.kt
@@ -1,8 +1,6 @@
package org.move.lang.core.psi.ext
-import com.intellij.ide.projectView.PresentationData
import com.intellij.lang.ASTNode
-import com.intellij.navigation.ItemPresentation
import com.intellij.psi.stubs.IStubElementType
import org.move.ide.MoveIcons
import org.move.lang.core.psi.MvEnum
diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvFunction.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvFunction.kt
index e886d32c..df2f10da 100644
--- a/src/main/kotlin/org/move/lang/core/psi/ext/MvFunction.kt
+++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvFunction.kt
@@ -35,6 +35,22 @@ val MvFunction.isView: Boolean
return stub?.isView ?: queryAttributes.isView
}
+val MvFunctionLike.modifiers: List get() {
+ // todo: order of appearance
+ val item = this
+ return buildList {
+ if (item is MvFunction) {
+ val vis = item.visibilityModifier
+ if (vis != null) {
+ add(vis.stubVisKind.keyword)
+ }
+ }
+ if (item is MvFunction && item.isEntry) add("entry")
+ if (item.isNative) add("native")
+ if (item is MvFunction && item.isInline) add("inline")
+ }
+}
+
fun MvFunction.functionId(): String? = qualName?.cmdText()
val MvFunction.testAttrItem: MvAttrItem? get() = queryAttributes.getAttrItem("test")
diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvSpecFunction.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvSpecFunction.kt
index ee6e6a63..09df16eb 100644
--- a/src/main/kotlin/org/move/lang/core/psi/ext/MvSpecFunction.kt
+++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvSpecFunction.kt
@@ -4,15 +4,33 @@ import com.intellij.lang.ASTNode
import com.intellij.psi.stubs.IStubElementType
import org.move.ide.MoveIcons
import org.move.lang.core.psi.MvModule
+import org.move.lang.core.psi.MvModuleItemSpec
+import org.move.lang.core.psi.MvModuleSpec
+import org.move.lang.core.psi.MvModuleSpecBlock
+import org.move.lang.core.psi.MvSpecCodeBlock
import org.move.lang.core.psi.MvSpecFunction
import org.move.lang.core.psi.MvSpecInlineFunction
+import org.move.lang.core.psi.containingModule
import org.move.lang.core.psi.impl.MvNameIdentifierOwnerImpl
+import org.move.lang.core.psi.namespaceModule
import org.move.lang.core.stubs.MvSpecFunctionStub
import org.move.lang.core.stubs.MvStubbedNamedElementImpl
import org.move.lang.core.types.ItemQualName
import javax.swing.Icon
-val MvSpecFunction.module: MvModule? get() = this.parent as? MvModule
+val MvSpecFunction.parentModule: MvModule? get() {
+ val parent = this.parent
+ if (parent is MvModule) return parent
+ if (parent is MvModuleSpecBlock) {
+ return parent.moduleSpec.moduleItem
+ }
+ return null
+}
+val MvSpecInlineFunction.parentModule: MvModule? get() {
+ val specCodeBlock = this.parent.parent as MvSpecCodeBlock
+ val moduleSpec = specCodeBlock.parent as? MvModuleItemSpec ?: return null
+ return moduleSpec.namespaceModule
+}
abstract class MvSpecFunctionMixin: MvStubbedNamedElementImpl,
MvSpecFunction {
@@ -24,7 +42,7 @@ abstract class MvSpecFunctionMixin: MvStubbedNamedElementImpl Set.containsAny(vararg items: T): Boolean = items.any { this.contains
inline fun Iterable.joinToWithBuffer(
buffer: StringBuilder,
- separator: CharSequence = ", ",
+ sep: CharSequence = ", ",
prefix: CharSequence = "",
postfix: CharSequence = "",
action: T.(StringBuilder) -> Unit,
@@ -144,7 +144,7 @@ inline fun Iterable.joinToWithBuffer(
var needInsertSeparator = false
for (element in this) {
if (needInsertSeparator) {
- buffer.append(separator)
+ buffer.append(sep)
}
element.action(buffer)
needInsertSeparator = true
@@ -223,3 +223,24 @@ inline fun T.asMap() : Map {
return props.keys.associateWith { props[it]?.get(this) }
}
+inline fun T.applyIf(condition: Boolean, body: T.() -> Unit) {
+ if (!condition) {
+ this.body()
+ }
+}
+
+inline fun T.chainIf(condition: Boolean, body: T.() -> T): T {
+ return if (!condition) {
+ this
+ } else {
+ this.body()
+ }
+}
+
+//inline fun T.mapIf(condition: Boolean, fn: (T) -> T): T {
+// if (!condition) {
+// return this
+// } else {
+// return this.body()
+// }
+//}
diff --git a/src/main/kotlin/org/move/utils/SignatureUtils.kt b/src/main/kotlin/org/move/utils/SignatureUtils.kt
index bc09a54c..269310c3 100644
--- a/src/main/kotlin/org/move/utils/SignatureUtils.kt
+++ b/src/main/kotlin/org/move/utils/SignatureUtils.kt
@@ -6,7 +6,7 @@ object SignatureUtils {
fun joinParameters(params: List>): String =
buildString {
append("(")
- params.joinToWithBuffer(this, separator = ", ") { sb ->
+ params.joinToWithBuffer(this, sep = ", ") { sb ->
val (name, type) = this
sb.append(name)
if (type != null) {
diff --git a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
index d8994ccc..2e099cac 100644
--- a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
+++ b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
@@ -1,5 +1,6 @@
package org.move.ide.docs
+import org.move.utils.tests.MoveV2
import org.move.utils.tests.MvDocumentationProviderTestCase
class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
@@ -11,10 +12,10 @@ class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
}
}
""", expected = """
- 0x0::builtins
- native fun move_from<T: key>(addr: address): T
- Removes T
from address and returns it.
- Aborts if address does not hold a T
.
+0x0::builtins
+native fun move_from<T: key>(addr: address): T
+Removes T
from address and returns it.
+Aborts if address does not hold a T
.
""")
fun `test show doc comment for module`() = doTest("""
@@ -22,8 +23,8 @@ class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
module 0x1::M {}
//^
""", expected = """
-
-
+
+
"""
)
@@ -34,9 +35,9 @@ class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
//^
}
""", expected = """
- 0x1::M
- const ERR_COLLECTION_IS_ALREADY_EXISTS: u64 = 1
-
+0x1::M
+const ERR_COLLECTION_IS_ALREADY_EXISTS: u64 = 1
+
""")
fun `test show doc comments and signature for function`() = doTest("""
@@ -49,9 +50,9 @@ class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
}
}
""", expected = """
- 0x1::M
- fun add(a: u8, b: u8): u8
- Adds two numbers.
Returns their sum.
+0x1::M
+fun add(a: u8, b: u8): u8
+Adds two numbers.
Returns their sum.
""")
fun `test show signature for function parameter`() = doTest("""
@@ -62,7 +63,7 @@ class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
}
}
""", expected = """
- value parameter a: u8
+ value parameter a: u8
""")
fun `test show signature for type parameter`() = doTest("""
@@ -72,7 +73,7 @@ class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
}
}
""", expected = """
- type parameter R: store + drop
+ type parameter R: store + drop
""")
fun `test show signature for simple let variable`() = doTest("""
@@ -84,7 +85,7 @@ class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
}
}
""", expected = """
- variable a: vector<u8>
+variable a: vector<u8>
""")
fun `test struct docstring`() = doTest("""
@@ -97,9 +98,9 @@ class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
}
}
""", expected = """
- 0x1::M
- struct S<R: store, phantom PH> has copy, drop, store
-
+0x1::M
+struct S<R: store, phantom PH> has copy, drop, store
+
""")
fun `test struct field as vector`() = doTest(
@@ -117,9 +118,10 @@ class MvDocumentationProviderTest : MvDocumentationProviderTestCase() {
}
}
""", expected = """
- 0x1::M::Collection
- nfts: vector<0x1::M::NFT>
- """
+0x1::M::Collection
+nfts: vector<NFT>
+
+ """
)
fun `test function signature with return generic`() = doTest("""
@@ -134,8 +136,7 @@ module 0x1::main {
}
""", expected = """
0x1::main
-fun box3<T>(x: T): Box3<T>
-
+fun box3<T>(x: T): Box3<T>
""")
fun `test result type documentation`() = doTest("""
@@ -147,7 +148,7 @@ module 0x1::m {
}
}
""", """
-value parameter result: num
+value parameter result: num
""")
fun `test generic result type documentation`() = doTest("""
@@ -159,6 +160,92 @@ module 0x1::m {
}
}
""", """
-value parameter result: &mut T
+value parameter result: &mut T
+ """)
+
+ @MoveV2
+ fun `test enum`() = doTest("""
+ module 0x1::m {
+ /// enum S documentation
+ enum S { Inner(T), Outer(T) }
+ //^
+ }
+ """, """
+
+
+ """)
+
+ @MoveV2
+ fun `test enum variant`() = doTest("""
+ module 0x1::m {
+ enum S {
+ /// i am a well documented enum variant
+ Inner(T),
+ Outer(T)
+ }
+ fun main() {
+ let _ = S::Inner(1);
+ //^
+ }
+ }
+ """, """
+
+i am a well documented enum variant
+ """)
+
+ fun `test function docs through spec reference`() = doTest("""
+ module 0x1::m {
+ /// main function
+ fun main() {}
+ }
+ spec 0x1::m {
+ spec main {}
+ //^
+ }
+ """, """
+
+
+ """)
+
+ fun `test struct docs through spec reference`() = doTest("""
+ module 0x1::m {
+ /// main struct
+ struct S { val: u8 }
+ }
+ spec 0x1::m {
+ spec S {}
+ //^
+ }
+ """, """
+
+
+ """)
+
+ fun `test spec fun docs`() = doTest("""
+ module 0x1::m {
+ }
+ spec 0x1::m {
+ /// my specification function
+ spec fun ident(x: u8): u8 { x }
+ //^
+ }
+ """, """
+0x1::m
+spec fun ident(x: num): num
""")
+
+ fun `test inline spec fun docs`() = doTest("""
+ module 0x1::m {
+ spec module {
+ /// my inline spec fun
+ fun inline_spec_fun();
+ //^
+ }
+ }
+ """, """
+0x1::m
+fun inline_spec_fun()
""")
}
diff --git a/src/test/kotlin/org/move/ide/docs/MvMarkdownDocsRendererTest.kt b/src/test/kotlin/org/move/ide/docs/MvMarkdownDocsRendererTest.kt
index 736931fc..24c51d8e 100644
--- a/src/test/kotlin/org/move/ide/docs/MvMarkdownDocsRendererTest.kt
+++ b/src/test/kotlin/org/move/ide/docs/MvMarkdownDocsRendererTest.kt
@@ -1,28 +1,34 @@
package org.move.ide.docs
+import org.intellij.lang.annotations.Language
import org.move.utils.tests.MvDocumentationProviderTestCase
class MvMarkdownDocsRendererTest: MvDocumentationProviderTestCase() {
- fun `test markdown text styles`() = doTest("""
+ fun `test markdown text styles`() = doTest(
+ """
/// This string contains some *bold* and **italic** words.
module 0x1::M {}
//^
""", """
-
+
This string contains some bold and italic words.
- """)
+ """
+ )
- fun `test markdown inline code`() = doTest("""
+ fun `test markdown inline code`() = doTest(
+ """
/// Maybe some `inline` keyword
module 0x1::M {}
//^
""", """
-
+
Maybe some inline
keyword
- """)
+ """
+ )
- fun `test markdown multiline code`() = doTest("""
+ fun `test markdown multiline code`() = doTest(
+ """
/// Move code:
/// ```
/// module 0x1::M {}
@@ -30,13 +36,15 @@ module 0x1::M {}
module 0x1::M {}
//^
""", """
-
+
Move code:
module 0x1::M {}
- """)
+ """
+ )
- fun `test markdown multiline code with extra spaces`() = doTest("""
+ fun `test markdown multiline code with extra spaces`() = doTest(
+ """
/// Move code:
/// ```
/// module 0x1::M {
@@ -46,31 +54,43 @@ module 0x1::M {}
module 0x1::M {}
//^
""", """
-
+
Move code:
module 0x1::M {
// comment
}
- """)
+ """
+ )
- fun `test markdown list`() = doTest("""
+ fun `test markdown list`() = doTest(
+ """
/// - The number of "items" in global storage.
/// - The number of bytes in global storage.
module 0x1::M {}
//^
""", """
-
+
- The number of "items" in global storage.
- The number of bytes in global storage.
- """)
+ """
+ )
- fun `test markdown numbered list`() = doTest("""
+ fun `test markdown numbered list`() = doTest(
+ """
/// 1. The number of "items" in global storage.
/// 2. The number of bytes in global storage.
module 0x1::M {}
//^
""", """
-
+
- The number of "items" in global storage.
- The number of bytes in global storage.
- """)
+ """
+ )
+
+ private fun doTest(
+ @Language("Move") code: String,
+ @Language("Html") expected: String?,
+ ) {
+ doTest(code, expected, hideStyles = false)
+ }
}
\ No newline at end of file
diff --git a/src/test/kotlin/org/move/utils/tests/MoveDocumentationProviderTestCase.kt b/src/test/kotlin/org/move/utils/tests/MoveDocumentationProviderTestCase.kt
index 821c76f7..0d5c7f6a 100644
--- a/src/test/kotlin/org/move/utils/tests/MoveDocumentationProviderTestCase.kt
+++ b/src/test/kotlin/org/move/utils/tests/MoveDocumentationProviderTestCase.kt
@@ -5,8 +5,10 @@ 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.MvDocumentationTarget
import org.move.ide.docs.MvPsiDocumentationTargetProvider
import org.move.lang.core.psi.MvElement
+import org.move.stdext.chainIf
import org.move.utils.tests.base.findElementAndOffsetInEditor
abstract class MvDocumentationProviderProjectTestCase : MvProjectTestBase() {
@@ -37,10 +39,11 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() {
protected fun doTest(
@Language("Move") code: String,
@Language("Html") expected: String?,
+ hideStyles: Boolean = true,
findElement: () -> Pair = { myFixture.findElementAndOffsetInEditor() },
) {
@Suppress("NAME_SHADOWING")
- doTest(code, expected, findElement) { actual, expected ->
+ doTest(code, expected, findElement, hideStyles) { actual, expected ->
assertSameLines(expected.trimIndent(), actual)
}
}
@@ -57,10 +60,12 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() {
// }
// }
+ @Suppress("OverrideOnly")
protected fun doTest(
@Language("Move") code: String,
expected: T?,
findElement: () -> Pair = { myFixture.findElementAndOffsetInEditor() },
+ hideStyles: Boolean = true,
check: (String, T) -> Unit
) {
InlineFile(myFixture, code, "main.move")
@@ -72,7 +77,7 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() {
val target = provider.documentationTarget(element, originalElement)!!
val doc = target.computeDocumentation()
- val actual = doc?.content()
+ val actual = doc?.content()?.chainIf(hideStyles) { hideSpecificStyles() }
if (expected == null) {
check(actual == null) { "Expected null, got `$actual`" }
} else {
@@ -80,6 +85,12 @@ abstract class MvDocumentationProviderTestCase : MvTestBase() {
check(actual, expected)
}
}
+
+ protected fun String.hideSpecificStyles(): String = replace(STYLE_REGEX, """style="..."""")
+
+ companion object {
+ private val STYLE_REGEX = Regex("""style=".*?"""")
+ }
}
@Suppress("UnstableApiUsage")
From aea7dfdacb2cc2ef4d5a112538e855c40e3aa610 Mon Sep 17 00:00:00 2001
From: Maksim Kurnikov
Date: Sat, 23 Nov 2024 17:58:38 +0100
Subject: [PATCH 3/6] highlight const initializer
---
.../kotlin/org/move/ide/docs/MvColorUtils.kt | 2 ++
.../org/move/ide/docs/RenderSignature.kt | 19 +++++++++++++++++--
.../ide/docs/MoveDocumentationProviderTest.kt | 4 ++--
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/src/main/kotlin/org/move/ide/docs/MvColorUtils.kt b/src/main/kotlin/org/move/ide/docs/MvColorUtils.kt
index ea199f82..971c2820 100644
--- a/src/main/kotlin/org/move/ide/docs/MvColorUtils.kt
+++ b/src/main/kotlin/org/move/ide/docs/MvColorUtils.kt
@@ -32,8 +32,10 @@ object MvColorUtils {
val asParens get() = loadKey(MvColor.PARENTHESES)
val asComma get() = loadKey(MvColor.COMMA)
val asSemicolon get() = loadKey(MvColor.SEMICOLON)
+ val asOperator get() = loadKey(MvColor.OPERATORS)
fun StringBuilder.keyword(text: String) = colored(text, asKeyword)
+ fun StringBuilder.op(text: String) = colored(text, asOperator)
fun StringBuilder.comma() = colored(",", asComma)
fun StringBuilder.semicolon() = colored(";", asSemicolon)
diff --git a/src/main/kotlin/org/move/ide/docs/RenderSignature.kt b/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
index 58b9225d..2712330a 100644
--- a/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
+++ b/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
@@ -1,5 +1,9 @@
package org.move.ide.docs
+import com.intellij.codeEditor.printing.HTMLTextPainter
+import com.intellij.openapi.util.NlsSafe
+import com.intellij.psi.PsiElement
+import org.intellij.plugins.markdown.lang.psi.impl.MarkdownLeafPsiElement
import org.move.ide.docs.MvColorUtils.asConst
import org.move.ide.docs.MvColorUtils.asEnum
import org.move.ide.docs.MvColorUtils.asEnumVariant
@@ -8,6 +12,7 @@ import org.move.ide.docs.MvColorUtils.asFunction
import org.move.ide.docs.MvColorUtils.asStruct
import org.move.ide.docs.MvColorUtils.colored
import org.move.ide.docs.MvColorUtils.keyword
+import org.move.ide.docs.MvColorUtils.op
import org.move.ide.presentation.presentableQualifiedName
import org.move.ide.presentation.text
import org.move.lang.core.psi.MvConst
@@ -125,7 +130,7 @@ private fun StringBuilder.generateStructOrEnum(structOrEnum: MvStructOrEnumItemE
}
structOrEnum.typeParameterList?.generateDoc(this)
structOrEnum.abilitiesList?.abilityList
- ?.joinToWithBuffer(this, ", ", " has ") { generateDoc(it) }
+ ?.joinToWithBuffer(this, ", ", " ${keyword("has")} ") { generateDoc(it) }
}
private fun StringBuilder.generateEnumVariant(variant: MvEnumVariant) {
@@ -158,7 +163,17 @@ private fun StringBuilder.generateConst(const: MvConst) {
}
constType.generateDoc(this)
- const.initializer?.let { this += " ${it.text}" }
+ const.initializer?.expr?.let { expr ->
+ this += " = "
+ this += highlightWithLexer(expr, expr.text)
+ }
+}
+
+private fun highlightWithLexer(context: PsiElement, text: String): String {
+ val highlighed =
+ HTMLTextPainter.convertCodeFragmentToHTMLFragmentWithInlineStyles(context, text)
+ return highlighed.trimEnd()
+ .removeSurrounding("", "
")
}
private val MvDocAndAttributeOwner.presentableQualifiedModName: String?
diff --git a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
index 2e099cac..d7067022 100644
--- a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
+++ b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
@@ -31,12 +31,12 @@ Aborts if address does not hold a T
.
fun `test show doc comment for const`() = doTest("""
module 0x1::M {
/// const docstring
- const ERR_COLLECTION_IS_ALREADY_EXISTS: u64 = 1;
+ const ERR_COLLECTION_IS_ALREADY_EXISTS: u64 = 0x1;
//^
}
""", expected = """
0x1::M
-const ERR_COLLECTION_IS_ALREADY_EXISTS: u64 = 1
+const ERR_COLLECTION_IS_ALREADY_EXISTS: u64 = 0x1
""")
From 09524dcc0a07ace36bc0f175380f2ebb4af6b1b9 Mon Sep 17 00:00:00 2001
From: Maksim Kurnikov
Date: Sat, 23 Nov 2024 18:08:09 +0100
Subject: [PATCH 4/6] bug with abilities highlighting
---
.../ide/docs/MvPsiDocumentationTargetProvider.kt | 3 +--
.../kotlin/org/move/ide/docs/RenderSignature.kt | 13 ++++++++-----
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt
index 649a8162..854e8296 100644
--- a/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt
+++ b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt
@@ -93,7 +93,7 @@ class MvDocumentationTarget(
val refName = docElement.referenceName
val named = moveProject.getNamedAddressTestAware(refName) ?: return null
val address =
- named.addressLit()?.original ?: angleWrapped("unassigned")
+ named.addressLit()?.original ?: "".escapeForHtml()
return "$refName = \"$address\""
}
is MvPatBinding -> {
@@ -217,4 +217,3 @@ private inline fun StringBuilder.b(action: (StringBuilder) -> Unit) {
append("")
}
-private fun angleWrapped(text: String): String = "<$text>"
diff --git a/src/main/kotlin/org/move/ide/docs/RenderSignature.kt b/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
index 2712330a..41ee39ac 100644
--- a/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
+++ b/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
@@ -1,9 +1,7 @@
package org.move.ide.docs
import com.intellij.codeEditor.printing.HTMLTextPainter
-import com.intellij.openapi.util.NlsSafe
import com.intellij.psi.PsiElement
-import org.intellij.plugins.markdown.lang.psi.impl.MarkdownLeafPsiElement
import org.move.ide.docs.MvColorUtils.asConst
import org.move.ide.docs.MvColorUtils.asEnum
import org.move.ide.docs.MvColorUtils.asEnumVariant
@@ -12,7 +10,6 @@ import org.move.ide.docs.MvColorUtils.asFunction
import org.move.ide.docs.MvColorUtils.asStruct
import org.move.ide.docs.MvColorUtils.colored
import org.move.ide.docs.MvColorUtils.keyword
-import org.move.ide.docs.MvColorUtils.op
import org.move.ide.presentation.presentableQualifiedName
import org.move.ide.presentation.text
import org.move.lang.core.psi.MvConst
@@ -129,8 +126,14 @@ private fun StringBuilder.generateStructOrEnum(structOrEnum: MvStructOrEnumItemE
}
}
structOrEnum.typeParameterList?.generateDoc(this)
- structOrEnum.abilitiesList?.abilityList
- ?.joinToWithBuffer(this, ", ", " ${keyword("has")} ") { generateDoc(it) }
+
+ val abilities = structOrEnum.abilitiesList?.abilityList
+ if (abilities != null && abilities.isNotEmpty()) {
+ this += " "
+ this.keyword("has")
+ this += " "
+ abilities.joinToWithBuffer(this, ", ") { generateDoc(it) }
+ }
}
private fun StringBuilder.generateEnumVariant(variant: MvEnumVariant) {
From 08be196c6432fba4dc90f0b10ded4ef8e4f1659c Mon Sep 17 00:00:00 2001
From: Maksim Kurnikov
Date: Sat, 23 Nov 2024 18:15:03 +0100
Subject: [PATCH 5/6] parameter / value parameter / type parameter docs is
monospaced
---
.../docs/MvPsiDocumentationTargetProvider.kt | 46 +++++++++++--------
1 file changed, 27 insertions(+), 19 deletions(-)
diff --git a/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt
index 854e8296..57876fb8 100644
--- a/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt
+++ b/src/main/kotlin/org/move/ide/docs/MvPsiDocumentationTargetProvider.kt
@@ -9,6 +9,7 @@ import com.intellij.platform.backend.documentation.PsiDocumentationTargetProvide
import com.intellij.platform.backend.presentation.TargetPresentation
import com.intellij.psi.PsiElement
import com.intellij.psi.createSmartPointer
+import io.ktor.http.quote
import org.move.ide.docs.MvColorUtils.asAbility
import org.move.ide.docs.MvColorUtils.asTypeParam
import org.move.ide.docs.MvColorUtils.colored
@@ -90,34 +91,41 @@ class MvDocumentationTarget(
is MvDocAndAttributeOwner -> generateDocumentationOwnerDoc(docElement, buffer)
is MvNamedAddress -> {
val moveProject = docElement.moveProject ?: return null
- val refName = docElement.referenceName
- val named = moveProject.getNamedAddressTestAware(refName) ?: return null
+ val addressName = docElement.referenceName
+ val named = moveProject.getNamedAddressTestAware(addressName) ?: return null
val address =
named.addressLit()?.original ?: "".escapeForHtml()
- return "$refName = \"$address\""
+ definition(buffer) {
+ it += "$addressName = ${address.quote()}"
+ }
+// return "$refName = \"$address\""
}
is MvPatBinding -> {
val presentationInfo = docElement.presentationInfo ?: return null
- buffer.keyword(presentationInfo.type)
- buffer += " "
- buffer += presentationInfo.name
- val msl = docElement.isMslOnlyItem
- val inference = docElement.inference(msl) ?: return null
- val tyText = inference
- .getBindingType(docElement)
- .text(fq = true, colors = true)
- buffer += ": "
- buffer += tyText
+ definition(buffer) {
+ it.keyword(presentationInfo.type)
+ it += " "
+ it += presentationInfo.name
+ val msl = docElement.isMslOnlyItem
+ val inference = docElement.inference(msl) ?: return null
+ val tyText = inference
+ .getBindingType(docElement)
+ .text(fq = true, colors = true)
+ it += ": "
+ it += tyText
+ }
}
is MvTypeParameter -> {
val presentationInfo = docElement.presentationInfo ?: return null
- buffer += presentationInfo.type
- buffer += " "
- buffer.b { it += presentationInfo.name }
- val abilities = docElement.abilityBounds
- if (abilities.isNotEmpty()) {
- abilities.joinToWithBuffer(buffer, " + ", ": ") { generateDoc(it) }
+ definition(buffer) {
+ it.keyword(presentationInfo.type)
+ it += " "
+ it += presentationInfo.name
+ val abilities = docElement.abilityBounds
+ if (abilities.isNotEmpty()) {
+ abilities.joinToWithBuffer(it, " + ", ": ") { generateDoc(it) }
+ }
}
}
}
From 05a3037dab2f81d56854202c5eeadf52cfb1bfca Mon Sep 17 00:00:00 2001
From: Maksim Kurnikov
Date: Sat, 23 Nov 2024 18:38:17 +0100
Subject: [PATCH 6/6] docs for schema
---
src/main/grammars/MoveParser.bnf | 12 ++++---
.../org/move/ide/docs/RenderSignature.kt | 14 +++++++-
.../org/move/lang/core/psi/ext/MvSchema.kt | 11 ++++--
.../move/lang/core/psi/ext/MvSpecFunction.kt | 1 +
.../ide/docs/MoveDocumentationProviderTest.kt | 34 +++++++++++++++----
.../docs/MoveNamedAddressDocumentationTest.kt | 2 +-
6 files changed, 59 insertions(+), 15 deletions(-)
diff --git a/src/main/grammars/MoveParser.bnf b/src/main/grammars/MoveParser.bnf
index dd7c4723..108ff6a7 100644
--- a/src/main/grammars/MoveParser.bnf
+++ b/src/main/grammars/MoveParser.bnf
@@ -325,12 +325,14 @@ SpecFunctionInner ::= Attr* spec fun IDENTIFIER TypeParameterList?
{
pin = 3
elementType = SpecFunction
+ hooks = [ leftBinder = "ADJACENT_LINE_COMMENTS" ]
}
NativeSpecFunctionInner ::= Attr* spec (native fun) IDENTIFIER TypeParameterList?
FunctionParameterList ReturnType? ';'
{
pin = 3
elementType = SpecFunction
+ hooks = [ leftBinder = "ADJACENT_LINE_COMMENTS" ]
}
fake SpecInlineFunction ::= Attr* native? fun IDENTIFIER? TypeParameterList?
@@ -351,17 +353,19 @@ fake SpecInlineFunction ::= Attr* native? fun IDENTIFIER? TypeParameterList?
// | NativeSpecInlineFunctionInner
// | UninterpretedSpecInlineFunctionInner
-SpecInlineFunctionInner ::= fun IDENTIFIER TypeParameterList?
+SpecInlineFunctionInner ::= Attr* fun IDENTIFIER TypeParameterList?
FunctionParameterList ReturnType? (<> | ';')
{
- pin = 1
+ pin = 2
elementType = SpecInlineFunction
+ hooks = [ leftBinder = "ADJACENT_LINE_COMMENTS" ]
}
-NativeSpecInlineFunctionInner ::= (native fun) IDENTIFIER TypeParameterList?
+NativeSpecInlineFunctionInner ::= Attr* (native fun) IDENTIFIER TypeParameterList?
FunctionParameterList ReturnType? ';'
{
+ pin = 2
elementType = SpecInlineFunction
- pin = 1
+ hooks = [ leftBinder = "ADJACENT_LINE_COMMENTS" ]
}
SpecInlineFunctionStmt ::= SpecInlineFunctionInner
diff --git a/src/main/kotlin/org/move/ide/docs/RenderSignature.kt b/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
index 41ee39ac..baffc6ee 100644
--- a/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
+++ b/src/main/kotlin/org/move/ide/docs/RenderSignature.kt
@@ -19,6 +19,7 @@ import org.move.lang.core.psi.MvFunction
import org.move.lang.core.psi.MvFunctionLike
import org.move.lang.core.psi.MvModule
import org.move.lang.core.psi.MvNamedFieldDecl
+import org.move.lang.core.psi.MvSchema
import org.move.lang.core.psi.MvSpecFunction
import org.move.lang.core.psi.MvSpecInlineFunction
import org.move.lang.core.psi.MvStruct
@@ -35,7 +36,8 @@ fun MvDocAndAttributeOwner.header(buffer: StringBuilder) {
is MvNamedFieldDecl -> listOfNotNull((fieldOwner as? MvDocAndAttributeOwner)?.presentableQualifiedName)
is MvStructOrEnumItemElement,
is MvFunctionLike,
- is MvConst -> listOfNotNull(presentableQualifiedModName)
+ is MvConst,
+ is MvSchema -> listOfNotNull(presentableQualifiedModName)
else -> emptyList()
}
rawLines.joinTo(buffer, "
")
@@ -54,6 +56,7 @@ fun MvDocAndAttributeOwner.signature(builder: StringBuilder) {
is MvSpecInlineFunction -> buffer.generateSpecInlineFunction(this)
is MvModule -> buffer.generateModule(this)
is MvStructOrEnumItemElement -> buffer.generateStructOrEnum(this)
+ is MvSchema -> buffer.generateSchema(this)
is MvNamedFieldDecl -> buffer.generateNamedField(this)
is MvConst -> buffer.generateConst(this)
is MvEnumVariant -> buffer.generateEnumVariant(this)
@@ -136,6 +139,15 @@ private fun StringBuilder.generateStructOrEnum(structOrEnum: MvStructOrEnumItemE
}
}
+private fun StringBuilder.generateSchema(schema: MvSchema) {
+ this.keyword("spec")
+ this += " "
+ this.keyword("schema")
+ this += " "
+ this += schema.name
+ schema.typeParameterList?.generateDoc(this)
+}
+
private fun StringBuilder.generateEnumVariant(variant: MvEnumVariant) {
this += variant.enumItem.presentableQualifiedName
this += "::"
diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvSchema.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvSchema.kt
index ed28342e..e14d44ae 100644
--- a/src/main/kotlin/org/move/lang/core/psi/ext/MvSchema.kt
+++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvSchema.kt
@@ -15,7 +15,14 @@ import org.move.lang.core.types.ty.TyUnknown
val MvSchema.specBlock: MvSpecCodeBlock? get() = this.childOfType()
-val MvSchema.module: MvModule? get() = this.parent as? MvModule
+val MvSchema.parentModule: MvModule? get() {
+ val parent = this.parent
+ if (parent is MvModule) return parent
+ if (parent is MvModuleSpecBlock) {
+ return parent.moduleSpec.moduleItem
+ }
+ return null
+}
val MvSchema.requiredTypeParams: List
get() {
@@ -46,7 +53,7 @@ abstract class MvSchemaMixin: MvStubbedNamedElementImpl,
override val qualName: ItemQualName?
get() {
val itemName = this.name ?: return null
- val moduleFQName = this.module?.qualName ?: return null
+ val moduleFQName = this.parentModule?.qualName ?: return null
return ItemQualName(this, moduleFQName.address, moduleFQName.itemName, itemName)
}
diff --git a/src/main/kotlin/org/move/lang/core/psi/ext/MvSpecFunction.kt b/src/main/kotlin/org/move/lang/core/psi/ext/MvSpecFunction.kt
index 09df16eb..ffdb5296 100644
--- a/src/main/kotlin/org/move/lang/core/psi/ext/MvSpecFunction.kt
+++ b/src/main/kotlin/org/move/lang/core/psi/ext/MvSpecFunction.kt
@@ -26,6 +26,7 @@ val MvSpecFunction.parentModule: MvModule? get() {
}
return null
}
+
val MvSpecInlineFunction.parentModule: MvModule? get() {
val specCodeBlock = this.parent.parent as MvSpecCodeBlock
val moduleSpec = specCodeBlock.parent as? MvModuleItemSpec ?: return null
diff --git a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
index d7067022..56440cd6 100644
--- a/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
+++ b/src/test/kotlin/org/move/ide/docs/MoveDocumentationProviderTest.kt
@@ -63,7 +63,7 @@ Aborts if address does not hold a T
.
}
}
""", expected = """
- value parameter a: u8
+
""")
fun `test show signature for type parameter`() = doTest("""
@@ -73,7 +73,7 @@ Aborts if address does not hold a T
.
}
}
""", expected = """
- type parameter R: store + drop
+type parameter R: store + drop
""")
fun `test show signature for simple let variable`() = doTest("""
@@ -85,7 +85,7 @@ Aborts if address does not hold a T
.
}
}
""", expected = """
-variable a: vector<u8>
+
""")
fun `test struct docstring`() = doTest("""
@@ -99,7 +99,7 @@ Aborts if address does not hold a T
.
}
""", expected = """
0x1::M
-struct S<R: store, phantom PH> has copy, drop, store
+struct S<R: store, phantom PH> has copy, drop, store
""")
@@ -148,7 +148,7 @@ module 0x1::m {
}
}
""", """
-value parameter result: num
+value parameter result: num
""")
fun `test generic result type documentation`() = doTest("""
@@ -160,7 +160,7 @@ module 0x1::m {
}
}
""", """
-value parameter result: &mut T
+value parameter result: &mut T
""")
@MoveV2
@@ -234,8 +234,11 @@ module 0x1::m {
}
""", """
0x1::m
-spec fun ident(x: num): num
""")
+spec fun ident(x: num): num
+my specification function
+""")
+ // todo: add context support
fun `test inline spec fun docs`() = doTest("""
module 0x1::m {
spec module {
@@ -248,4 +251,21 @@ module 0x1::m {
0x1::m
fun inline_spec_fun()
""")
+
+ fun `test schema docs`() = doTest("""
+ module 0x1::m {
+ }
+ spec 0x1::m {
+ /// my schema
+ spec schema CreateAccountAbortsIf {
+ //^
+ addr: address;
+ val: T;
+ }
+ }
+ """, """
+0x1::m
+spec schema CreateAccountAbortsIf<T>
+
+ """)
}
diff --git a/src/test/kotlin/org/move/ide/docs/MoveNamedAddressDocumentationTest.kt b/src/test/kotlin/org/move/ide/docs/MoveNamedAddressDocumentationTest.kt
index eba5e7ec..891f523a 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\""
+ ""
)
}