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

Port inlay hints to new API, add ctrl+click and lambda expr support #218

Merged
merged 4 commits into from
Oct 25, 2024
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,6 +1,7 @@
package org.move.cli.runConfigurations.aptos

import com.intellij.psi.SmartPsiElementPointer
import com.intellij.psi.createSmartPointer
import org.move.lang.core.psi.MvFunction
import org.move.lang.core.psi.parametersAsBindings
import org.move.lang.core.psi.ext.*
Expand Down Expand Up @@ -59,7 +60,7 @@ data class FunctionCall(
for (parameterName in parameterNames) {
nullParams[parameterName] = null
}
return FunctionCall(function.asSmartPointer(), nullTypeParams, nullParams)
return FunctionCall(function.createSmartPointer(), nullTypeParams, nullParams)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.intellij.openapi.diagnostic.LogLevel.WARNING
import com.intellij.openapi.diagnostic.logger
import com.intellij.psi.NavigatablePsiElement
import com.intellij.psi.PsiElement
import com.intellij.util.PsiNavigateUtil
import org.move.cli.toolwindow.MoveProjectsTreeStructure.MoveSimpleNode
import org.move.ide.notifications.logOrShowBalloon
import java.awt.event.MouseAdapter
Expand Down
126 changes: 126 additions & 0 deletions src/main/kotlin/org/move/ide/hints/type/MvTypeHintsFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package org.move.ide.hints.type

import com.intellij.codeInsight.hints.declarative.*
import com.intellij.codeInsight.hints.declarative.CollapseState.Collapsed
import com.intellij.codeInsight.hints.declarative.CollapseState.Expanded
import com.intellij.psi.createSmartPointer
import org.move.ide.presentation.hintText
import org.move.lang.core.types.ty.Ty
import org.move.lang.core.types.ty.TyAdt
import org.move.lang.core.types.ty.TyTuple
import org.move.lang.core.types.ty.TyVector

object MvTypeHintsFactory {
fun typeHint(type: Ty, treeBuilder: PresentationTreeBuilder) {
treeBuilder.typeHint(0, type)
}

// private const val UNNAMED_MARK = "<unnamed>"
private const val ANONYMOUS_MARK = "<anonymous>"

private fun PresentationTreeBuilder.typeHint(level: Int, type: Ty) {
when (type) {
is TyTuple -> tupleTypeHint(level, type)
is TyAdt -> adtTypeHint(level, type)
is TyVector -> vectorTypeHint(level, type)
else -> {
text(type.hintText())
}
}
}

private fun PresentationTreeBuilder.adtTypeHint(level: Int, tyAdt: TyAdt) {
val itemName = tyAdt.item.name ?: ANONYMOUS_MARK
text(
itemName,
InlayActionData(
PsiPointerInlayActionPayload(tyAdt.item.createSmartPointer()),
PsiPointerInlayActionNavigationHandler.HANDLER_ID
)
)
if (tyAdt.typeArguments.isEmpty()) return
collapsibleList(
state = calcCollapseState(level, tyAdt),
expandedState = {
toggleButton { text("<") }
join(
tyAdt.typeArguments,
op = {
typeHint(level + 1, it)
},
separator = { text(", ") }
)
toggleButton { text(">") }
},
collapsedState = {
toggleButton { text("<...>") }
})
}

private fun PresentationTreeBuilder.tupleTypeHint(level: Int, tyTuple: TyTuple) {
collapsibleList(
state = calcCollapseState(level, tyTuple),
expandedState = {
toggleButton { text("(") }
join(
tyTuple.types,
op = {
typeHint(level + 1, it)
},
separator = { text(", ") }
)
toggleButton { text(")") }
},
collapsedState = {
toggleButton { text("(...)") }
})
}

private fun PresentationTreeBuilder.vectorTypeHint(level: Int, tyVector: TyVector) {
text("vector")
collapsibleList(
state = calcCollapseState(level, tyVector),
expandedState = {
toggleButton { text("[") }
typeHint(level + 1, tyVector.item)
toggleButton { text("]") }
},
collapsedState = {
toggleButton { text("[...]") }
})
}

private fun <T> PresentationTreeBuilder.join(
elements: List<T>,
op: PresentationTreeBuilder.(T) -> Unit,
separator: PresentationTreeBuilder.() -> Unit
) {
var isFirst = true
for (element in elements) {
if (isFirst) {
isFirst = false
} else {
separator()
}
op(this, element)
}
}

private const val COLLAPSE_FROM_LEVEL: Int = 2
private const val TY_ADT_NAME_LENGTH_COLLAPSE_LIMIT: Int = 10

private fun calcCollapseState(level: Int, ty: Ty): CollapseState {
when (ty) {
is TyAdt -> {
// check whether the name is too long, collapse the type arguments if so
val itemName = ty.item.name
if (itemName != null) {
if (itemName.length > TY_ADT_NAME_LENGTH_COLLAPSE_LIMIT) {
return Collapsed
}
}
}
}
return if (level < COLLAPSE_FROM_LEVEL) Expanded else Collapsed
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.move.ide.hints.type

import com.intellij.codeInsight.hints.declarative.*
import com.intellij.codeInsight.hints.declarative.HintFontSize.ABitSmallerThanInEditor
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.move.ide.presentation.hintText
import org.move.lang.core.psi.MvFunctionParameter
import org.move.lang.core.psi.MvLetStmt
import org.move.lang.core.psi.MvPatBinding
import org.move.lang.core.psi.MvSchemaFieldStmt
import org.move.lang.core.psi.ext.bindingOwner
import org.move.lang.core.psi.ext.endOffset
import org.move.lang.core.psi.ext.hasAncestor
import org.move.lang.core.psi.ext.isMsl
import org.move.lang.core.types.infer.inferenceOwner
import org.move.lang.core.types.infer.inference
import org.move.lang.core.types.ty.*

class MvTypeInlayHintsProvider2: InlayHintsProvider {

override fun createCollector(file: PsiFile, editor: Editor): InlayHintsCollector = Collector()

private class Collector: SharedBypassCollector {
override fun collectFromElement(element: PsiElement, sink: InlayTreeSink) {
when (element) {
is MvPatBinding -> showTypeForPatBinding(element, sink)
}
}

private fun showTypeForPatBinding(patBinding: MvPatBinding, sink: InlayTreeSink) {
// skip private variables
if (patBinding.name.startsWith("_")) return

// does not show hints for bindings with explicit type annotations
val owner = patBinding.bindingOwner
if (owner is MvFunctionParameter || owner is MvSchemaFieldStmt) return

val contextInferenceOwner = patBinding.inferenceOwner() ?: return

val msl = patBinding.isMsl()
val ty = contextInferenceOwner.inference(msl).getBindingType(patBinding)
if (ty is TyUnknown) return

val pos = InlineInlayPosition(patBinding.endOffset, false)
val format = HintFormat.default.withFontSize(ABitSmallerThanInEditor)
sink.addPresentation(pos, hintFormat = format) {
text(": ")
MvTypeHintsFactory.typeHint(ty, this)
}
}
}

companion object {
const val PROVIDER_ID: String = "org.move.hints.types"
}
}
1 change: 1 addition & 0 deletions src/main/kotlin/org/move/lang/core/psi/ext/MvBindingPat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ val MvPatBinding.bindingOwner: PsiElement?
it is MvLetStmt
|| it is MvFunctionParameter
|| it is MvSchemaFieldStmt
|| it is MvLambdaParameter
}

sealed class RsBindingModeKind {
Expand Down
2 changes: 0 additions & 2 deletions src/main/kotlin/org/move/lang/core/psi/ext/PsiElement.kt
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,6 @@ fun PsiElement.cameBefore(element: PsiElement) =
inline val <T: StubElement<*>> StubBasedPsiElement<T>.greenStub: T?
get() = (this as? StubBasedPsiElementBase<T>)?.greenStub

fun <T: PsiElement> T.asSmartPointer() = SmartPointerManager.createPointer(this)

val PsiElement.stubParent: PsiElement?
get() {
if (this is StubBasedPsiElement<*>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,14 @@ fun MvInferenceContextOwner.inference(msl: Boolean): InferenceResult {
}
}

fun MvElement.inferenceOwner(): MvInferenceContextOwner? = this.ancestorOrSelf()

fun MvElement.inference(msl: Boolean): InferenceResult? {
val contextOwner = this.ancestorOrSelf<MvInferenceContextOwner>() ?: return null
val contextOwner = inferenceOwner() ?: return null
return contextOwner.inference(msl)
}


//data class ResolvedPath(val element: MvElement, val isVisible: Boolean) {
// companion object {
// fun from(entry: ScopeEntry, context: MvElement): ResolvedPath {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ class TypeInferenceWalker(
is MvDerefExpr -> inferDerefExprTy(expr)
is MvLitExpr -> inferLitExprTy(expr, expected)
is MvTupleLitExpr -> inferTupleLitExprTy(expr, expected)
is MvLambdaExpr -> inferLambdaExpr(expr, expected)
is MvLambdaExpr -> inferLambdaExprTy(expr, expected)

is MvMoveExpr -> expr.expr?.inferType() ?: TyUnknown
is MvCopyExpr -> expr.expr?.inferType() ?: TyUnknown
Expand Down Expand Up @@ -379,7 +379,7 @@ class TypeInferenceWalker(
return TyReference(innerRefTy, mutability, ctx.msl)
}

private fun inferLambdaExpr(lambdaExpr: MvLambdaExpr, expected: Expectation): Ty {
private fun inferLambdaExprTy(lambdaExpr: MvLambdaExpr, expected: Expectation): Ty {
val bindings = lambdaExpr.parametersAsBindings
val lambdaTy =
(expected.onlyHasTy(this.ctx) as? TyLambda) ?: TyLambda.unknown(bindings.size)
Expand All @@ -398,10 +398,7 @@ class TypeInferenceWalker(
val baseFuncTy =
when (namedItem) {
is MvFunctionLike -> {
val itemTy = instantiatePath<TyFunction>(path, namedItem) ?: return TyUnknown
// val (itemTy, _) = instantiateMethodOrPath<TyFunction>(path, namedItem)
// ?: return TyUnknown
itemTy
instantiatePath<TyFunction>(path, namedItem) ?: return TyUnknown
}
is MvFieldsOwner -> {
val tupleFields = namedItem.tupleFields
Expand Down Expand Up @@ -658,9 +655,9 @@ class TypeInferenceWalker(
formalArgs: List<Ty>,
): List<Ty> {
val resolvedFormalRet = resolveTypeVarsIfPossible(formalRet)
val retTy = expectedRet.onlyHasTy(ctx) ?: return emptyList()
val expectedRetTy = expectedRet.onlyHasTy(ctx) ?: return emptyList()
return ctx.freezeUnification {
if (ctx.combineTypes(retTy, resolvedFormalRet).isOk) {
if (ctx.combineTypes(expectedRetTy, resolvedFormalRet).isOk) {
formalArgs.map { ctx.resolveTypeVarsIfPossible(it) }
} else {
emptyList()
Expand Down
11 changes: 9 additions & 2 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,15 @@
implementationClass="org.move.ide.hints.StructLitFieldsInfoHandler" />
<codeInsight.parameterNameHints language="Move"
implementationClass="org.move.ide.hints.MvInlayParameterHintsProvider" />
<codeInsight.inlayProvider language="Move"
implementationClass="org.move.ide.hints.type.MvInlayTypeHintsProvider" />

<!-- <codeInsight.inlayProvider language="Move"-->
<!-- implementationClass="org.move.ide.hints.type.MvInlayTypeHintsProvider" />-->
<codeInsight.declarativeInlayProvider group="TYPES_GROUP"
implementationClass="org.move.ide.hints.type.MvTypeInlayHintsProvider2"
isEnabledByDefault="true"
language="Move"
providerId="org.move.hints.types"
nameKey="inlay.hints.types"/>

<defaultLiveTemplates file="liveTemplates/Move.xml" />
<liveTemplateContext implementation="org.move.ide.liveTemplates.MvContextType$Generic" />
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/messages/MvBundle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
inlay.hints.types=Types

advanced.setting.aptos.group=Aptos

advanced.setting.org.move.aptos.test.tool.window=Show test results in Test Tool Window
Expand Down
Loading
Loading