在Eclipse插件中加载Clojure代码

lis*_*aak 2 eclipse clojure

我有这种问题.我在*.clj文件中包含Eclipse插件(表示为A)中包含的Clojure代码.我不想要AOT编译.但是,我需要从另一个Clojure插件B加载clojure代码.当B依赖于A时,这是可能的.Clojure可以轻松访问类路径,一切正常.但我希望插件A作为B的扩展插件.但是有一个问题,因为我找不到如何从B中包含的*.clj文件加载A中包含的Clojure文件的方法.我想使用Clojure'load'函数,它可以从类路径加载*.clj文件但是当我明确地启动这样的插件时,这个函数只是看不到插件A事件的内容

 (org.eclipse.core.runtime.Platform/getBundle "A")
Run Code Online (Sandbox Code Playgroud)

对洛朗回答的反应

洛朗,非常感谢你!这很有趣.但是,我认为这可能解决了我原来的问题.你描述了如何从java插件中调用clojure代码,这完全是非常棒的.我需要从clojure插件调用clojure代码,我认为这可能更容易.我想我会创建扩展点并提供像这样的clojure函数

<extension point="transforms">
  <function namespace="my.nemaspace" fn="my-transform"/>
</extension>
Run Code Online (Sandbox Code Playgroud)

所以IExecutableExtensionFactory我不需要任何魔法.我可以从clojure代码中读取扩展注册表.我不能做的是加载扩展中指定的功能.这是可行的还是我误解了什么?我注意到你正在使用clojure.osgi.这看起来很酷,该项目有任何文档吗?

Lau*_*tit 6

关于类加载器的问题是可以预料到的:如果Eclipse插件/ OSGi包的依赖关系是A - > B,Clojure jar从B引导,那么就无法从B看到A的资源是正常的.

Eclipse插件的依赖项之间不存在循环,因此类加载器层次结构之间不存在循环.

如果您使用常规Eclipse机制从A编写B的扩展,则会出现同样的问题:插件B将声明一个接口和一个扩展点.然后插件A可以实现接口,并声明扩展点的扩展.最后一部分允许Eclipse框架使用bundle做一些技巧:它看到A声明了B的扩展,因此实现了A中实现B中声明的接口的类(这是因为A依赖于B),并给出了B来自A的实现实例也可以,因为它实现了B中的接口!

(不确定这是否清楚).

无论如何,回到用Clojure编写的插件.
使用Clojure,您没有开箱即用的类加载器提供的独立环境,因为所有内容都聚集在一个"Clojure环境"中,该环境位于嵌入clojure jar的插件的类加载器领域中.因此,当插件A启动时,一种可能性就是从A加载相关的命名空间.然后,它们将在正确的时间加载,并可用于任何其他Clojure代码.另一种可能性是使用扩展点/扩展机制.Eclipse提供了一种使用"工厂"来创建扩展点实例的方法.在逆时针方面,我们利用这个功能,并拥有一个通用的工厂类(用java编写,所以没有AOT),它负责从正确的包中加载正确的命名空间.

以下是有关如何扩展扩展点的更多详细信息.

逆时针的一个例子:
Eclipse框架中有一个现有的扩展点,用于为控制台内容提供超链接检测器.逆时针扩展此扩展点以添加nrepl超链接.
在java世界中,您必须在扩展中直接声明实现接口IPatternMatchListenerDelegate的某些类.
但是对于CCW,可能与你的原因相同,我试图不惜一切代价避免使用AOT,所以我不能在扩展中给出一个java类名,否则我要么必须在java中编写并编译它,或者gen-class在Clojure和AOT中编写一个- 编译它.

相反,CCW利用了plugin.xml的隐藏gem:在几乎每个地方,当你必须提供类名时,你可以提供一个IExecutableExtensionFactory实例,其create()方法将被Eclipse框架调用以创建一个实例.期望的课程.

这允许我编写一个用于调用Clojure世界的泛型类:我只使用代替我应该编写的类名,类名ccw.util.GenericExecutableExtension

plugin.xml中提取:

<extension point="org.eclipse.ui.console.consolePatternMatchListeners">
<consolePatternMatchListener
   id="ccw.editors.clojure.nREPLHyperlink"
   regex="nrepl://[^':',' ']+:\d+">
    <class class="ccw.util.GenericExecutableExtension">
       <parameter
             name="factory"
             value="ccw.editors.clojure.nrepl-hyperlink/factory">
       </parameter>
    </class>
</consolePatternMatchListener>
Run Code Online (Sandbox Code Playgroud)

注意class属性,以及如何通过parameter元素向工厂提供参数(工厂必须实现接口IExecutableExtension才能使用参数进行初始化).

最后,您可以看到在命名空间中ccw.editors.clojure.nrepl-hyperlink,函数工厂非常简单,只需调用make函数:

(defn make []
  (let [state (atom nil)]
    (reify org.eclipse.ui.console.IPatternMatchListenerDelegate
      (connect [this console]  (dosync (reset! state console)))
      (disconnect [this]       (reset! state nil))
      (matchFound [this event] (match-found event @state)))))

(defn factory "plugin.xml hook" [ _ ] (make))
Run Code Online (Sandbox Code Playgroud)

请注意,我将此作为示例显示,并且逆时针中的相关代码尚未准备好作为"准备好消费"的独立库发布.
但是你应该能够推出自己的解决方案(一旦你把所有的部分都放在脑中就很容易了).

希望有所帮助,

- 劳伦特