Remove inlay created through InlayHintsSink

Answered

I'm extending InlayHintsProvider and adding inlay hints through InlayHintsSink, but I don't see how to remove those inlays once they're added. Using InlayModel I get an instance of Inlay which I could call dispose() on, but I don't get that ability when using InlayHintsSink. The best solution I've come up with is:

editor.inlayModel.getBlockElementsInRange(0, Integer.MAX_VALUE)?.forEach { it.dispose() }

This is obviously not the correct way to remove them.

Should I not be using InlayHintsProvider to create inlay hints that are not permanent?

5 comments
Comment actions Permalink

Hi Brandon,

Your approach is almost correct, just use document length instead of Integer.MAX_VALUE:

editor.inlayModel.getBlockElementsInRange(0, editor.document.textLength)?.forEach { it.dispose() }
0
Comment actions Permalink

Thanks for the improved solution, but I think I should have been a bit more specific. That approach removes all inlays and I'm only interested in removing specific inlays. Is there a way to add something to filter the inlays? I've looked through them in debug mode and I see I can do it via reflection, but again that doesn't seem like how they were designed to be removed.

For example, let's say I added an Inlay through InlayHintsSink that I only wanted to last for 5 minutes. Once the timer is up, how would I then re-target that inlay?

0
Comment actions Permalink

You can filter returned Inlay objects and remove only the ones that are older than 5 minutes. Similar filtering is done in:

getBlockElementsInRange(int startOffset, int endOffset, @NotNull Class<T> type)

It is implemented as:

return (List)ContainerUtil.filter(getBlockElementsInRange(startOffset, endOffset), inlay -> type.isInstance(inlay.getRenderer()));

It is just the filter that differs.

0
Comment actions Permalink

The above is similar to how I was planning on implementing this via reflection. Using InlayHintsSink I'm only able to provide a InlayPresentation. This automatically gets wrapped in a BlockInlayRenderer. I could technically access the presentation I passed, like so:

(((it.renderer as BlockInlayRenderer).getCachedPresentation() as RecursivelyUpdatingRootPresentation).content).presentation

The only issue is "presentation" is a private field so I would have to use reflection to get at the presentation I created.

I'm sure that will work, but is there no other way to do it?

0
Comment actions Permalink

Turns out I don't need to use reflection, I can get my presentation via:

editor.inlayModel.getBlockElementsInRange(0, editor.document.textLength).forEach {
if (it.renderer is BlockInlayRenderer) {
if ((it.renderer as BlockInlayRenderer).getCachedPresentation() is RecursivelyUpdatingRootPresentation) {
val rootPresentation = (it.renderer as BlockInlayRenderer).getCachedPresentation() as RecursivelyUpdatingRootPresentation
if (rootPresentation.content is StaticDelegatePresentation) {
val delegatePresentation = rootPresentation.content as StaticDelegatePresentation
val myPresentation = delegatePresentation.presentation
println(myPresentation) //can dispose my inlay
}
}
}
}

Seems a bit much but I can work with this.

Edit: I actually will need to resort to reflection with this approach for older versions of IntelliJ that haven't exposed BlockInlayRenderer.getCachedPresentation().

0

Please sign in to leave a comment.