【问题标题】:Get same completion candidates for IMain as would appear in a REPL为 IMain 获取与 REPL 中相同的完成候选
【发布时间】:2013-12-02 15:20:09
【问题描述】:

我正在尝试让 Scala 解释器工作的代码完成。理想情况下,它的工作方式与 REPL (ILoop) 提供的相同。我使用文本文档作为源,所以我不想实例化 ILoop 而只是 IMain

在以下示例中,补全仅适用于特殊情况:

import scala.tools.nsc.interpreter.{JLineCompletion, IMain}
import scala.tools.nsc.Settings

object CompletionTest extends App {
  val settings  = new Settings
  settings.usejavacp.tryToSetFromPropertyValue("true")
  val intp      = new IMain(settings)
  intp.initializeSynchronous()
  assert(intp.isInitializeComplete)
  val comp      = new JLineCompletion(intp)
  val completer = comp.completer()
  val buffer    = "val x = Indexe"
  val choices   = completer.complete(buffer, buffer.length)
  println("----BEGIN COMPLETION----")
  choices.candidates.foreach(println)
  println("----END COMPLETION----")
  intp.close()
}

预期的输出是IndexedSeq,但它是空的。如果我将缓冲区设置为 Indexe,它就可以工作。如果我将缓冲区设置为  Indexe(前导空格),则完成候选者再次为空。

因此在处理缓冲区或调用完成时必须涉及一个额外的步骤。在 REPL 中按下 <tab> 时究竟会发生什么?似乎几乎不可能弄清楚调用了哪些方法......

【问题讨论】:

  • 我尝试改用ILoop,出现同样的问题。所以一定是我调用完成者的方式是错误的或不充分的。

标签: scala autocomplete interpreter read-eval-print-loop


【解决方案1】:

JLineReader,你可以看到接线。 JLineConsoleReader 设置一个 ArgumentCompleterScalaCompleter 作为底层完成者。

所以完成者只需要参数,而不是行。

apm@mara:~$ scalam
Welcome to Scala version 2.11.0-M7 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'.          **
** scala.tools.nsc._ has been imported      **
** global._, definitions._ also imported    **
** Try  :help, :vals, power.<tab>           **

scala> val b = "Indexe"
b: String = Indexe

scala> completion.completer complete (b, b.length)
res0: scala.tools.nsc.interpreter.Completion.Candidates = Candidates(0,List(IndexedSeq))

在其他击键中,

// paste your code

scala> val buffer    = "Indexe"
buffer: String = Indexe

scala> completer.complete(buffer, buffer.length)
res6: scala.tools.nsc.interpreter.Completion.Candidates = Candidates(0,List(IndexedSeq))

scala> import tools.nsc.interpreter.Completion.Candidates
import tools.nsc.interpreter.Completion.Candidates

scala> val Candidates(_, choices) = completer.complete(buffer, buffer.length)
choices: List[String] = List(IndexedSeq)

scala> choices foreach println
IndexedSeq

把整行交给它:

scala> val argCompletor: ArgumentCompleter =new ArgumentCompleter(new JLineDelimiter, scalaToJline(comp.completer))
argCompletor: jline.console.completer.ArgumentCompleter = jline.console.completer.ArgumentCompleter@751222c7

scala> val maybes = new java.util.ArrayList[CharSequence]
maybes: java.util.ArrayList[CharSequence] = []

scala> val buffer    = "val x = Indexe"
buffer: String = val x = Indexe

scala> argCompletor.setStrict(false)

scala> argCompletor.complete(buffer, buffer.length, maybes)
res32: Int = 8

scala> maybes
res33: java.util.ArrayList[CharSequence] = [IndexedSeq]

分隔符进行行解析。

编辑——一些增值分析:

存在补全器的“严格”模式,因为您可以为行上的每个标记提供补全器,并要求每个先前的参数都是可补全的。对于 n 个完成者,第 n 个 arg 之后的所有 arg 都由最后一个完成者处理。

【讨论】:

  • 有任何线索可以将其剥离以直接与IMain 一起使用吗?
  • 它适用于val b = "Indexe",但不适用于val b = "val b = Indexe"。也就是说,ConsoleReader 中的JLine 做了一些按摩,以找到看起来合适的位置。我想如果我想以编程方式访问,我必须打开ConsoleReader
  • 感谢delimiter和strict的解释!我很好奇——因为分隔符不将解释器作为参数:你知道吗,这是一个纯粹的本地语法解析,没有调用解释器内部的任何东西/Global
  • @0__ 只是在分隔符上进行语法拆分,但引用存在错误,因此最终复杂性可能会上升。你是对的,代码堆栈很难导航。
【解决方案2】:

部分答案。我设法通过在JLineReader 中覆盖scalaToJline 在那个怪物身上钻了一个洞。该方法是使用预先按摩的字符串调用的,遵循以下跟踪:

at CompletionTest$$anon$1$$anon$2$$anon$3.complete(CompletionTest.scala:37)
at scala.tools.jline.console.completer.ArgumentCompleter.complete(ArgumentCompleter.java:150)
at scala.tools.jline.console.ConsoleReader.complete(ConsoleReader.java:1543)
at scala.tools.jline.console.ConsoleReader.readLine(ConsoleReader.java:1312)
at scala.tools.jline.console.ConsoleReader.readLine(ConsoleReader.java:1170)
at scala.tools.nsc.interpreter.JLineReader.readOneLine(JLineReader.scala:74)
at scala.tools.nsc.interpreter.InteractiveReader$$anonfun$readLine$2.apply(InteractiveReader.scala:42)
at scala.tools.nsc.interpreter.InteractiveReader$$anonfun$readLine$2.apply(InteractiveReader.scala:42)
at scala.tools.nsc.interpreter.InteractiveReader$.restartSysCalls(InteractiveReader.scala:49)
at scala.tools.nsc.interpreter.InteractiveReader$class.readLine(InteractiveReader.scala:42)
at scala.tools.nsc.interpreter.JLineReader.readLine(JLineReader.scala:19)
at scala.tools.nsc.interpreter.ILoop.readOneLine$1(ILoop.scala:568)
at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:584)
at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:587)

【讨论】:

    【解决方案3】:

    所以这是我的“拆线”,它似乎按预期工作:

    import scala.tools.nsc.interpreter._
    import scala.tools.jline.console.completer.{Completer, ArgumentCompleter}
    import scala.tools.nsc.interpreter.Completion.{Candidates, ScalaCompleter}
    import scala.tools.nsc.Settings
    import collection.JavaConverters._
    

     

    object Completion2 extends App {
      val settings  = new Settings
      settings.usejavacp.tryToSetFromPropertyValue("true")
      val intp      = new IMain(settings)
      intp.initializeSynchronous()
    
      val completion = new JLineCompletion(intp)
    
      def scalaToJline(tc: ScalaCompleter): Completer = new Completer {
        def complete(_buf: String, cursor: Int, candidates: JList[CharSequence]): Int = {
          val buf   = if (_buf == null) "" else _buf
          val Candidates(newCursor, newCandidates) = tc.complete(buf, cursor)
          newCandidates foreach (candidates add _)
          newCursor
        }
      }
    
      val argCompletor: ArgumentCompleter =
        new ArgumentCompleter(new JLineDelimiter, scalaToJline(completion.completer()))
      argCompletor.setStrict(false)
    
      val jlist: java.util.List[CharSequence] = new java.util.ArrayList
    
      val buffer = "val x = Indexe"
    
      argCompletor.complete(buffer, buffer.length, jlist)
    
      val list = jlist.asScala
    
      println("----BEGIN COMPLETION----")
      list.foreach(println)
      println("----END COMPLETION----")
      intp.close()
    }
    

    编辑:由于某种原因,通配符导入存在问题。就像我执行

    import mypackage.MySymbol
    

    然后MySymbol被完成者找到。但如果我改为执行

    import mypackage._
    

    那么mypackage的内容都找不到了。有什么想法吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-02
      • 1970-01-01
      • 2011-09-22
      • 1970-01-01
      • 2023-03-10
      • 2019-08-27
      • 1970-01-01
      相关资源
      最近更新 更多