how to work back from a psiElement to the versioned library that provided the file?

I noticed that that libraries that use gobject introspection, such as gtk, glib and gimp have gobject introspection files containing xml of all their types, classes and methods: This would be useful to generate on-the-fly documentation for these libraries. 

However the problem that I am running into, is that clion workspace/project model doesn't seem to know anything about which package (pkg-config, from meson in this case) provided the file.

See this one from gimp for example
 

 

and this can be used to created html docs on the fly like so:

 

 

At the moment, the docs are pretty basic:

 

 

So I think it would be straightforward to display that html in the doc hover, but I need to work back from (for example) hovering a “gtk_box_new” function, whcih is provided by gtk+-3.0 :
 

Dump of properties of psiElement above:



acquire: {CACHED_SMART_POINTER_KEY=java.lang.ref.SoftReference@30a10704, Original element=psi:range=(436,450),type=Identikit(class='com.intellij.psi.impl.source.tree.LeafPsiElement', elementType=IDENTIFIER, fileLanguage='ObjectiveC'), SEM=SemData{count=0}}
argumentList: null
arrayLengths: []
baseIcon: PatchedRasterizedImageDataLoader(classLoader=PluginClassLoader(plugin=PluginDescriptor(name=CIDR Base, id=com.intellij.cidr.base, descriptorPath=plugin.xml, path=~/.gradle/caches/8.13/transforms/043ce6f9136bf368ebef038438c7b070/transformed/CLion-2025.1.1/plugins/cidr-base-plugin, version=251.25410.104, package=null, isBundled=true), packagePrefix=null, state=active, parents=PluginDescriptor(name=Native Debugging Support, id=com.intellij.nativeDebug, descriptorPath=plugin.xml, path=~/.gradle/caches/8.13/transforms/043ce6f9136bf368ebef038438c7b070/transformed/CLion-2025.1.1/plugins/nativeDebug-plugin, version=251.25410.104, package=null, isBundled=true), ), path=icons/expui/codeAssistantFunction.svg)
children: [OCParameterList]
class: class com.jetbrains.cidr.lang.psi.impl.OCDeclaratorImpl
complexOffset: 7387343749120
containingFile: OCFile:gimpframe.h
containingOCFile: OCFile:gimpframe.h
context: OCFunctionDeclaration(gimp_frame_new)
declarationContext: com.jetbrains.cidr.lang.symbols.DeclarationContext@2fb9cfd0
empty: false
extendedContext: OCFunctionDeclaration(gimp_frame_new)
firstChild: PsiElement(OCPunctuator:*)
identifyingElement: PsiElement(IDENTIFIER)
initializerList: null
initializer: null
initializers: []
language: Language: ObjectiveC
lastChild: OCParameterList
localSymbol: FUNCTION_PREDECLARATION《》[gimp_frame_new]@gimpframe.h:1720
manager: {}
modifiersText: *
name: gimp_frame_new
nameIdentifier: PsiElement(IDENTIFIER)
namespaceQualifier: null
navigationElement: OCDeclarator(gimp_frame_new)
nextSibling: PsiElement(OCPunctuator:;)
node: Element(DECLARATOR)
opaque: {CACHED_SMART_POINTER_KEY=java.lang.ref.SoftReference@30a10704, Original element=psi:range=(436,450),type=Identikit(class='com.intellij.psi.impl.source.tree.LeafPsiElement', elementType=IDENTIFIER, fileLanguage='ObjectiveC'), SEM=SemData{count=0}}
originalElement: OCDeclarator(gimp_frame_new)
ownDeclarations: []
ownReferences: []
parameterList: OCParameterList
parent: OCFunctionDeclaration(gimp_frame_new)
physical: true
plain: {CACHED_SMART_POINTER_KEY=java.lang.ref.SoftReference@30a10704, Original element=psi:range=(436,450),type=Identikit(class='com.intellij.psi.impl.source.tree.LeafPsiElement', elementType=IDENTIFIER, fileLanguage='ObjectiveC'), SEM=SemData{count=0}}
pointerToFunction: false
possibleStructMember: false
....presentation: com.jetbrains.cidr.lang.navigation.OCSymbolPresentation@7c57192f
prevSibling: PsiWhiteSpace
project: Project(name=gimp-lqr-plugin, containerState=COMPONENT_CREATED, componentStore=/home/user/git/gimp-lqr-plugin)
rangeInCallElement: (2,16)
rangeWithMacros: (1718,1761)
rawType: OCFunctionType[GtkWidget *(const gchar *)]
reference: com.jetbrains.cidr.lang.psi.impl.OCDeclaratorImpl$MyReference@373bdc0c
references: [com.jetbrains.cidr.lang.psi.impl.OCDeclaratorImpl$MyReference@373bdc0c]
resolvedType: OCFunctionType[GtkWidget *(const gchar *)]
resolveScope: com.intellij.openapi.roots.impl.LibraryScopeCache$1[com.intellij.openapi.module.impl.scopes.LibraryRuntimeClasspathScope@4154f]
startOffsetInParent: 10
symbol: FUNCTION_PREDECLARATION《》[gimp_frame_new]@gimpframe.h:1720
symbolName: gimp_frame_new
templateArgumentList: null
text: * gimp_frame_new       (const gchar *label)
textLength: 43
textOffset: 1720
textRange: (1718,1761)
textRangeInParent: (10,53)
textWithMacros: * gimp_frame_new       (const gchar *label)
type: OCFunctionType[GtkWidget *(const gchar *)]
userDataEmpty: false
userDataString: {CACHED_SMART_POINTER_KEY=java.lang.ref.SoftReference@30a10704, Original element=psi:range=(436,450),type=Identikit(class='com.intellij.psi.impl.source.tree.LeafPsiElement', elementType=IDENTIFIER, fileLanguage='ObjectiveC'), SEM=SemData{count=0}}
userMap: {CACHED_SMART_POINTER_KEY=java.lang.ref.SoftReference@30a10704, Original element=psi:range=(436,450),type=Identikit(class='com.intellij.psi.impl.source.tree.LeafPsiElement', elementType=IDENTIFIER, fileLanguage='ObjectiveC'), SEM=SemData{count=0}}
useScope: Union: (com.jetbrains.cidr.lang.search.scopes.OCSearchScopeService$1[com.intellij.core.CoreProjectScopeBuilder$ContentSearchScope@3],Libraries)
valid: true
visibilitySupported: false
writable: false



I can extract from the containingOCFile the absolute path to the providing library like so:
 


val virtualFile = element.containingFile?.virtualFile
println("containingFile?.virtualFile = $virtualFile")


and that will give me:


/usr/include/gimp-3.0/libgimpwidgets/gimpframe.h



 Which is a good start. I could do some regular expression to pull the “gimp-3.0” out of the string. But it seems to me that projects build with meson or Makefile in clion, already know where the file came from, so clion, knows which library that path maps to? 

How to pull this information out of clion in a plugin?

The idea being, that if this can be done for gimp-3.0, it can also be done for all the libraries that have gobject introspection data, which is quite a lot:

 

[gimp-lqr-plugin] $ find /usr/share/ -type f -name ‘*.gir’
/usr/share/gir-1.0/Libxfce4util-1.0.gir
/usr/share/gir-1.0/Xfconf-0.gir
/usr/share/gir-1.0/Libxfce4ui-2.0.gir
/usr/share/gir-1.0/Garcon-1.0.gir
/usr/share/gir-1.0/GarconGtk-1.0.gir
/usr/share/gir-1.0/Libxfce4panel-2.0.gir
/usr/share/gir-1.0/ICal-3.0.gir
/usr/share/gir-1.0/ICalGLib-3.0.gir
/usr/share/gir-1.0/GIRepository-3.0.gir
/usr/share/gir-1.0/GLib-2.0.gir
/usr/share/gir-1.0/GLibUnix-2.0.gir
/usr/share/gir-1.0/GModule-2.0.gir
/usr/share/gir-1.0/GObject-2.0.gir
/usr/share/gir-1.0/Gio-2.0.gir
/usr/share/gir-1.0/GioUnix-2.0.gir
/usr/share/gir-1.0/DBus-1.0.gir
/usr/share/gir-1.0/DBusGLib-1.0.gir
/usr/share/gir-1.0/GIRepository-2.0.gir
/usr/share/gir-1.0/GL-1.0.gir
/usr/share/gir-1.0/Vulkan-1.0.gir
/usr/share/gir-1.0/cairo-1.0.gir
/usr/share/gir-1.0/fontconfig-2.0.gir
/usr/share/gir-1.0/freetype2-2.0.gir
/usr/share/gir-1.0/libxml2-2.0.gir
/usr/share/gir-1.0/win32-1.0.gir
/usr/share/gir-1.0/xfixes-4.0.gir
/usr/share/gir-1.0/xft-2.0.gir
/usr/share/gir-1.0/xlib-2.0.gir
/usr/share/gir-1.0/xrandr-1.3.gir
/usr/share/gir-1.0/GdkPixbuf-2.0.gir

 

 

 

 

0
4 comments

The issue seems to be that according to the docs here : https://plugins.jetbrains.com/docs/intellij/library.html?from=jetbrains.org

val module = ModuleUtilCore.findModuleForPsiElement(element)

should return something, but its always null for me. It seems like meson must know this information, but clion doesn't have access to it? 

0

ok, I see I was looking at the element and not the containingFile, but even looking at the right element, I get
 

 

So its just a generic container for all the include paths. However, clion clearly does know which library is which, because I have 4 different gtkbox.h on my system in the include paths:

$ find /usr/include/ -name gtkbox.h
/usr/include/gtk-3.0/gtk/gtkbox.h
/usr/include/gtk-2.0/gtk/gtkbox.h
/usr/include/gtk-1.2/gtk/gtkbox.h
/usr/include/gtk-4.0/gtk/gtkbox.h
 

and its picking the right one, so its either got the meson external model available, or its making a guess, or something

0

Hello!

Sorry for the delay in response. Could you please clarify if you're creating a plugin for CLion Nova or CLion Classic?

0

I am using classic
 

dependencies {
    testImplementation(libs.junit)
    testImplementation(libs.opentest4j)
    intellijPlatform {
        clion("2025.1.2")
        bundledPlugin("com.intellij.cidr.base")
        bundledPlugin("com.intellij.cidr.lang")
        bundledPlugin("com.intellij.clion")
        bundledPlugin("com.intellij.clion.meson")
        bundledPlugin("com.intellij.cidr.lang.clangd")

 

Currently I don't see any documentation or examples on how one would go about developing a plugin for nova, and nova seems buggy (indexing failing and hanging) so would leave that until it was stable and documented

0

Please sign in to leave a comment.