获得与REPL中出现的相同的完成候选人

时间:2013-12-02 15:20:10

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

我正在尝试让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>时到底发生了什么?似乎几乎不可能弄清楚哪种方法被称为......

3 个答案:

答案 0 :(得分:1)

JLineReader中,您可以看到布线。 JLineConsoleReader设置ArgumentCompleter,其中ScalaCompleter为基础完成者。

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

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]

分隔符执行行解析。

编辑 - 一些增值分析:

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

答案 1 :(得分:0)

部分答案。我设法通过覆盖scalaToJline中的JLineReader在该怪物上挖洞。在跟踪此跟踪之后,使用预先按摩的字符串调用该方法:

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)

答案 2 :(得分:0)

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

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的任何内容。有什么想法吗?