Skip to content

Instantly share code, notes, and snippets.

@dkandalov
Last active May 9, 2021 16:26
Show Gist options
  • Save dkandalov/db4c400d69be423545e294654475c9aa to your computer and use it in GitHub Desktop.
Save dkandalov/db4c400d69be423545e294654475c9aa to your computer and use it in GitHub Desktop.
Markdown links with custom actions (see https://github.com/dkandalov/live-plugin)
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler
import com.intellij.lang.LanguageAnnotators
import com.intellij.lang.LanguageExtensionPoint
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity.INFORMATION
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.colors.CodeInsightColors.INACTIVE_HYPERLINK_ATTRIBUTES
import com.intellij.openapi.extensions.DefaultPluginDescriptor
import com.intellij.openapi.util.TextRange
import com.intellij.patterns.PsiElementPattern
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReferenceBase
import com.intellij.psi.PsiReferenceProvider
import com.intellij.psi.SyntheticElement
import com.intellij.psi.impl.FakePsiElement
import com.intellij.psi.impl.source.resolve.reference.PsiReferenceRegistrarImpl
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
import com.intellij.util.ProcessingContext
import liveplugin.show
val markdown = com.intellij.lang.Language.findLanguageByID("Markdown")!!
val psiPattern = object : PsiElementPattern.Capture<PsiElement>(PsiElement::class.java) {
override fun accepts(o: Any?, context: ProcessingContext?) = (o as? PsiElement)?.text == "hello"
}
val psiReferenceProvider = object : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext) =
arrayOf(PsiReferenceTo(PsiElementWithAction(element, "action text") {
show("hello from element: $element")
}))
}
(ReferenceProvidersRegistry.getInstance().getRegistrar(markdown) as PsiReferenceRegistrarImpl)
.registerReferenceProvider(psiPattern, psiReferenceProvider, 1.0, pluginDisposable)
class PsiReferenceTo(private val ref: PsiElement) : PsiReferenceBase<PsiElement>(ref.parent, true) {
override fun resolve(): PsiElement = ref
}
class PsiElementWithAction(
private val _parent: PsiElement,
private val _text: String,
private val action: () -> Unit
) : FakePsiElement(), SyntheticElement {
override fun navigate(requestFocus: Boolean) = action()
override fun getParent() = _parent
override fun getPresentableText() = _text
override fun getName() = _text
override fun getTextRange(): TextRange = _parent.textRange
}
if (false) {
fun annotator() = Annotator { element, holder ->
val text = element.text
text.indices
.filter { i -> text.substring(i, minOf(i + 5, text.length)) == "hello" }
.forEach { i ->
holder.newAnnotation(INFORMATION, "Hello!")
.range(TextRange(i, i + 5))
.textAttributes(INACTIVE_HYPERLINK_ATTRIBUTES)
.create()
}
}
val pluginDescriptor = DefaultPluginDescriptor("hello-world")
ApplicationManager.getApplication().extensionArea.getExtensionPoint(LanguageAnnotators.EP_NAME)
.registerExtension(
LanguageExtensionPoint(markdown.id, annotator()).also { it.pluginDescriptor = pluginDescriptor },
pluginDescriptor,
pluginDisposable
)
LanguageAnnotators.INSTANCE.clearCache(markdown)
val declarationHandler = GotoDeclarationHandler { element: PsiElement?, _: Int, _: Editor ->
val i = element?.text?.indexOf("hello")
if (i == null || i == -1) arrayOf()
else arrayOf(PsiElementWithAction(element, "foooo") {
show("hello")
})
}
ApplicationManager.getApplication().extensionArea.getExtensionPoint(GotoDeclarationHandler.EP_NAME)
.registerExtension(declarationHandler, pluginDisposable)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment