mirror of https://github.com/sbt/sbt.git
94 lines
3.2 KiB
Scala
94 lines
3.2 KiB
Scala
/* sbt -- Simple Build Tool
|
|
* Copyright 2011 Mark Harrah
|
|
*/
|
|
package sbt.parse
|
|
|
|
import jline.{CandidateListCompletionHandler,Completor,CompletionHandler,ConsoleReader}
|
|
import scala.annotation.tailrec
|
|
import collection.JavaConversions
|
|
|
|
object JLineCompletion
|
|
{
|
|
def installCustomCompletor(reader: ConsoleReader, parser: Parser[_]): Unit =
|
|
installCustomCompletor(parserAsCompletor(parser), reader)
|
|
def installCustomCompletor(reader: ConsoleReader)(complete: String => (Seq[String], Seq[String])): Unit =
|
|
installCustomCompletor(customCompletor(complete), reader)
|
|
def installCustomCompletor(complete: ConsoleReader => Boolean, reader: ConsoleReader): Unit =
|
|
{
|
|
reader.removeCompletor(DummyCompletor)
|
|
reader.addCompletor(DummyCompletor)
|
|
reader.setCompletionHandler(new CustomHandler(complete))
|
|
}
|
|
|
|
private[this] final class CustomHandler(completeImpl: ConsoleReader => Boolean) extends CompletionHandler
|
|
{
|
|
override def complete(reader: ConsoleReader, candidates: java.util.List[_], position: Int) = completeImpl(reader)
|
|
}
|
|
|
|
// always provides dummy completions so that the custom completion handler gets called
|
|
// (ConsoleReader doesn't call the handler if there aren't any completions)
|
|
// the custom handler will then throw away the candidates and call the custom function
|
|
private[this] final object DummyCompletor extends Completor
|
|
{
|
|
override def complete(buffer: String, cursor: Int, candidates: java.util.List[_]): Int =
|
|
{
|
|
candidates.asInstanceOf[java.util.List[String]] add "dummy"
|
|
0
|
|
}
|
|
}
|
|
|
|
def parserAsCompletor(p: Parser[_]): ConsoleReader => Boolean =
|
|
customCompletor(str => convertCompletions(Parser.completions(p, str)))
|
|
def convertCompletions(c: Completions): (Seq[String], Seq[String]) =
|
|
{
|
|
( (Seq[String](), Seq[String]()) /: c.get) { case ( t @ (insert,display), comp) =>
|
|
if(comp.isEmpty) t else (insert :+ comp.append, insert :+ comp.display)
|
|
}
|
|
}
|
|
|
|
def customCompletor(f: String => (Seq[String], Seq[String])): ConsoleReader => Boolean =
|
|
reader => {
|
|
val success = complete(beforeCursor(reader), f, reader, false)
|
|
reader.flushConsole()
|
|
success
|
|
}
|
|
|
|
def beforeCursor(reader: ConsoleReader): String =
|
|
{
|
|
val b = reader.getCursorBuffer
|
|
b.getBuffer.substring(0, b.cursor)
|
|
}
|
|
|
|
def complete(beforeCursor: String, completions: String => (Seq[String],Seq[String]), reader: ConsoleReader, inserted: Boolean): Boolean =
|
|
{
|
|
val (insert,display) = completions(beforeCursor)
|
|
if(insert.isEmpty)
|
|
inserted
|
|
else
|
|
{
|
|
lazy val common = commonPrefix(insert)
|
|
if(inserted || common.isEmpty)
|
|
{
|
|
showCompletions(display, reader)
|
|
reader.drawLine()
|
|
true
|
|
}
|
|
else
|
|
{
|
|
reader.getCursorBuffer.write(common)
|
|
reader.redrawLine()
|
|
complete(beforeCursor + common, completions, reader, true)
|
|
}
|
|
}
|
|
}
|
|
def showCompletions(cs: Seq[String], reader: ConsoleReader): Unit =
|
|
if(cs.isEmpty) () else CandidateListCompletionHandler.printCandidates(reader, JavaConversions.asJavaList(cs), true)
|
|
|
|
def commonPrefix(s: Seq[String]): String = if(s.isEmpty) "" else s reduceLeft commonPrefix
|
|
def commonPrefix(a: String, b: String): String =
|
|
{
|
|
val len = a.length min b.length
|
|
@tailrec def loop(i: Int): Int = if(i >= len) len else if(a(i) != b(i)) i else loop(i+1)
|
|
a.substring(0, loop(0))
|
|
}
|
|
} |