通过具有委托闭包参数的GDSL脚本创建方法

时间:2017-03-24 20:17:17

标签: intellij-idea groovy

使用Intellij的(几乎未记录的)gdsl脚本,可以向类中添加动态方法:

contributor(context(ctype: "my.Type")) {
  method name: "doIt", params: [body: {}], type: void
}

还可以配置闭包的委托:

contributor(context(scope: closureScope())) {
  def call = enclosingCall("doIt")
  if (call) {
    def method = call.bind()
    def clazz = method?.containingClass
    if (clazz?.qualName == 'my.Type') {
      delegatesTo(findClass('my.Inner'))
    }
  }
}

doIt是代码中定义的方法(未动态添加)时,也可以按设计工作。

但是,将closureScope与之前的创建的方法一起使用时,containing类方法始终为null,这意味着我无法安全地委托对已解决的my.Inner类的闭包。

我想要的是添加一个等同于:

的动态方法
void doIt(@DelegatesTo(my.Inner) Closure)...

即。我希望该方法在代码完成中可用(这是有效的),并且在创建的闭包内部,我希望在解决my.Inner的方法时正确完成代码。

到目前为止,我尝试了各种方法:

  • 在参数定义
  • 中包含@DelegatesTo注释
  • 尝试更多深奥的方法来寻找闭包的所有者,因为GrMethodCall根本就没有父
  • 无条件地将名为doIt的所有闭包委托给my.Inner,但这不是可行的解决方案,因为我有多个doIt方法(在不同的类上)委托给不同的目标。

那么,我如何让IDEA按预期行事并委托给正确的目标呢?

编辑以使其更清晰:

鉴于以下类别:

package my
class Type {
    void doIt(Closure) {}
}
class Inner {
    void inInner() {}
}

以及以下gdsl:

contributor(context(scope: closureScope())) {
  def call = enclosingCall("doIt")
  if (call) {
    def method = call.bind()
    def clazz = method?.containingClass
    println clazz?.qualName
    if (clazz?.qualName == 'my.Type') {
      delegatesTo(findClass('my.Inner'))
    }
  }
}

当我开始输入新脚本时:

new Type().doIt {
    inInner()
}

当在封闭内部时,我得到以下内容:

  • inInner
  • 的代码完成情况
  • inInner显示为有效
  • 从命令行使用idea.bat启动时的控制台输出显示行my.Type(来自println
  • inInner上的Ctrl-B正确链接到源代码。

(当使用doIt @DelegatesTo(Inner)方法注释闭包参数时,可以在没有gdsl的情况下达到相同的行为

但是,我不想在Type的源代码中手动包含doIt方法,它是由AST Transformation生成的,所以我的源文件现在看起来像这样:

package my
class Type {
}
class Inner {
    void inInner() {}
}

我可以使用以下gdsl片段

告诉IntelliJ有关新方法的信息
contributor(context(ctype: "my.Type")) {
  method name: "doIt", params: [body: {}], type: void
}

现在IDE正确识别带闭包参数的doIt方法。但是,在Closure中,会发生以下情况:

  • 有时代码完成显示inInner,有时在更改内容之后,它没有(当使用原始代码修复类型时,它显示,但后来声明为“未解析”),经过代码更改后这个编辑过的例子,它不再显示了......)
  • 即使显示,inInner也会显示“无法解析符号”装饰
  • 控制台将null显示为clazz,即找到方法但未链接到所有者ASTNode
  • Ctrl-B未链接到Inner
  • 中的相应方法

所以我想要的是注入doIt方法(通过Gdsl)与源代码中包含的方法相同的行为,即我希望gdsl向委托注入doIt方法关闭(到Inner)进入type类。

1 个答案:

答案 0 :(得分:1)

这有助于我将ctype添加到从方法

中查找类类型的范围
contributor(context(scope: closureScope(), ctype: 'my.Type')) {
  def call = enclosingCall("doIt")
  if (call) {
      delegatesTo(findClass('my.Inner'))
   }
}