diff --git a/scalafix-sbt/src/main/scala/scalafix/internal/sbt/JGitCompletions.scala b/scalafix-sbt/src/main/scala/scalafix/internal/sbt/JGitCompletions.scala index 5059da20a2..87ad8dd1fe 100644 --- a/scalafix-sbt/src/main/scala/scalafix/internal/sbt/JGitCompletions.scala +++ b/scalafix-sbt/src/main/scala/scalafix/internal/sbt/JGitCompletions.scala @@ -9,24 +9,22 @@ import org.eclipse.jgit.util.GitDateFormatter import scala.collection.JavaConverters._ class JGitCompletion(cwd: Path) { - val builder = new FileRepositoryBuilder() - val repo = builder.readEnvironment().setWorkTree(cwd.toFile).build() - val refList = repo.getRefDatabase().getRefs(RefDatabase.ALL).asScala - val branchAndTags = refList.map { - case (a, b) => Repository.shortenRefName(a) - } - val git = new Git(repo) - val refs = git.log().setMaxCount(20).call().asScala.toList - val dateFormatter = new GitDateFormatter(GitDateFormatter.Format.RELATIVE) + private val builder = new FileRepositoryBuilder() + private val repo = builder.readEnvironment().setWorkTree(cwd.toFile).build() + private val refList = repo.getRefDatabase().getRefs(RefDatabase.ALL).asScala + private val git = new Git(repo) + private val refs = git.log().setMaxCount(20).call().asScala.toList + private val dateFormatter = new GitDateFormatter(GitDateFormatter.Format.RELATIVE) - val last20Commits = refs.map { ref => - val relativeCommitTime = - dateFormatter.formatDate(refs.head.getCommitterIdent) - val abrev = ref.abbreviate(8).name - val short = ref.getShortMessage - // TODO: figure out how to display the whole message but only auto-complete on abrev - s"$abrev -- $short ($relativeCommitTime)" - } + val branchesAndTags: List[String] = + refList.map{ case (a, b) => Repository.shortenRefName(a)}.toList - def all: Iterable[String] = branchAndTags ++ last20Commits + val last20Commits: List[(String, String)] = + refs.map { ref => + val relativeCommitTime = + dateFormatter.formatDate(refs.head.getCommitterIdent) + val abrev = ref.abbreviate(8).name + val short = ref.getShortMessage + (s"$abrev -- $short ($relativeCommitTime)", abrev) + } } diff --git a/scalafix-sbt/src/main/scala/scalafix/internal/sbt/ScalafixCompletions.scala b/scalafix-sbt/src/main/scala/scalafix/internal/sbt/ScalafixCompletions.scala index 36a5c4f4ee..892d59d8c9 100644 --- a/scalafix-sbt/src/main/scala/scalafix/internal/sbt/ScalafixCompletions.scala +++ b/scalafix-sbt/src/main/scala/scalafix/internal/sbt/ScalafixCompletions.scala @@ -7,14 +7,12 @@ import java.util.regex.Pattern import scala.util.control.NonFatal -import sbt.complete.DefaultParsers +import sbt.complete._ import sbt.complete.DefaultParsers._ -import sbt.complete.FileExamples -import sbt.complete.Parser -import sbt.complete.FixedSetExamples object ScalafixCompletions { private val names = ScalafixRuleNames.all + private val namedRule: Parser[String] = names.map(literal).reduceLeft(_ | _) private def toAbsolutePath(path: Path, cwd: Path): Path = { if (path.isAbsolute) path @@ -43,136 +41,40 @@ object ScalafixCompletions { private def uri(protocol: String) = token(protocol + ":") ~> NotQuoted.map(x => s"$protocol:$x") - private val namedRule: Parser[String] = - names.map(literal).reduceLeft(_ | _) + def parser(cwd: Path): Parser[Seq[String]] = { - - // like repsep but keep the sep and flatten everything back - def repsep[T](rep: Parser[String], sep: Parser[String]): Parser[String] = - (rep ~ (sep ~ rep).*).map { - case x ~ xs => - x + xs.map { case s ~ y => s + y }.mkString("") - } - - def single[T](p: Parser[T]): Parser[Seq[T]] = - p.map(Seq(_)) - - def join[T](a: Parser[T], b: Parser[T]): Parser[Seq[T]] = - (a ~ b).map { case (ra, rb) => Seq(ra, rb) } - - def joinSeq[T](a: Parser[Seq[T]], b: Parser[Seq[T]]): Parser[Seq[T]] = - (a ~ b).map { case (ra, rb) => ra ++ rb } - - def flatten[T](p: Parser[Seq[Seq[T]]]): Parser[Seq[T]] = - p.map(_.flatten) - - def mapOrFail[S, T](p: Parser[S])(f: S => T): Parser[T] = - p.flatMap(s => - try { success(f(s)) } catch { case NonFatal(e) => failure(e.toString) }) - - val string: Parser[String] = StringBasic - - // val pathExamples: Parser[String] = - // string - // .examples(new AbsolutePathExamples(cwd)) - // .map { f => - // toAbsolutePath(Paths.get(f), cwd).toString - // } - - // val regexPath: Parser[String] = mapOrFail(pathExamples) { regex => - // Pattern.compile(regex) - // regex - // } - - // val fileRule: Parser[String] = token("file:") ~> pathExamples.map( - // "file:" + _) - - // val classpathExamples: Parser[String] = - // repsep(pathExamples, ":") - val jgitCompletion = new JGitCompletion(cwd) - val gitBase: Parser[String] = - string.examples(new FixedSetExamples(jgitCompletion.all)) - - type P = Parser[Seq[String]] - val space: P = single(" ") - val usage: P = single("--usage") - val help: P = single("--help") | single("-h") - val version: P = single("--version") | single("-v") - val verbose: P = single("--verbose") - // val config: P = join(("--config" | "-c"), pathExamples) - val configStr: P = join(("--config-str" | "-c"), string) - // val sourceroot: P = join(("--config-str" | "-c"), pathExamples) - // val classpath: P = join("--classpath", classpathExamples) - // val classpathAutoRoots: P = join("--classpath-auto-roots", pathExamples) - // val toolClasspath: P = join("--tool-classpath", classpathExamples) - val noStrictSemanticdb: P = single("--no-strict-semanticdb") - val rules: P = - flatten( - join( - "--rule" | "-r", - token( - namedRule | - // fileRule | - uri("github") | - uri("replace") | - uri("http") | - uri("https") | - uri("scala") - ) - ).* - ) - // val files: P = join("--files" | "-f", pathExamples) // extra - val stdout: P = single("--stdout") - val test: P = single("--test") - // val outFrom: P = join("--out-from", regexPath) - // val outTo: P = join("--out-to", regexPath) - // val exclude: P = join("exclude", regexPath) - val singleThread: P = single("--single-thread") - val noSysExit: P = single("--no-sys-exit") - val inPlace: P = single("-i" | "in-place") - val quietParseErrors: P = single("--quiet-parse-errors") - val bash: P = single("--bash") - val zsh: P = single("--zsh") - val nonInteractive: P = single("--non-interactive") - val projectId: P = join("--project-id", string) - val diff: P = single("--diff") - val diffBase: P = join("--diff-base", gitBase) - val all = usage | help - - // | - // help | - // version | - // verbose | - // // config | - // configStr | - // // sourceroot | - // // classpath | - // // classpathAutoRoots | - // // toolClasspath | - // noStrictSemanticdb | - // rules | - // // files | - // stdout | - // test | - // // outFrom | - // // outTo | - // // exclude | - // singleThread | - // noSysExit | - // inPlace | - // quietParseErrors | - // bash | - // zsh | - // nonInteractive | - // projectId | - // diff | - // diffBase - + val space: Parser[String] = token(Space).map(_.toString) + + val diffBase: Parser[String] = + ("--diff-base" ~ space ~ token(NotQuoted, TokenCompletions.fixed( + (seen, level) => { + val last20Commits = + jgitCompletion.last20Commits + .filter{ case (_, sha1) => sha1.startsWith(seen)} + .zipWithIndex + .map{ + case ((log, sha1), i) => { + val idx = if(i < 10) "0" + i.toString else i.toString + new Token(display = s"|$idx| $log", append = sha1.stripPrefix(seen)) + } + } + .toSet + + val branchesAndTags = + jgitCompletion.branchesAndTags + .filter(info => info.startsWith(seen)) + .map(info => new Token(display = info, append = info.stripPrefix(seen))) + .toSet + + Completions.strict(last20Commits ++ branchesAndTags) + } + ))).map { + case a ~ b ~ c => a + b + c + } - - flatten(joinSeq(space, all).*) + (token(Space) ~> diffBase).* <~ SpaceClass.* } } diff --git a/scalafix-sbt/src/test/scala/scalafix/internal/sbt/ScalafixCompletionsTest.scala b/scalafix-sbt/src/test/scala/scalafix/internal/sbt/ScalafixCompletionsTest.scala index 7184914150..9da3504717 100644 --- a/scalafix-sbt/src/test/scala/scalafix/internal/sbt/ScalafixCompletionsTest.scala +++ b/scalafix-sbt/src/test/scala/scalafix/internal/sbt/ScalafixCompletionsTest.scala @@ -4,20 +4,37 @@ import java.nio.file.Paths import org.scalatest.FunSuite import sbt.complete.Parser + +import sbt.complete.JLineCompletion + class ScalafixCompletionsTest extends FunSuite { - val cwd = Paths.get("").toAbsolutePath - println(cwd) - val parser = ScalafixCompletions.parser(cwd) - val expected = Set("-diff", "-testkit", "-sbt", "-core", "-cli") - def check(path: String): Unit = { - test(path) { - val completions = Parser.completions(parser, " file:" + path, 0) - val obtained = completions.get.map(_.append).intersect(expected) - assert(obtained == expected) - } - } + // val cwd = Paths.get("").toAbsolutePath + // println(cwd) + // val parser = ScalafixCompletions.parser(cwd) + // val expected = Set("-diff", "-testkit", "-sbt", "-core", "-cli") + // def check(path: String): Unit = { + // test(path) { + // val completions = Parser.completions(parser, " file:" + path, 0) + // val obtained = completions.get.map(_.append).intersect(expected) + // assert(obtained == expected) + // } + // } + + // check("scalafix") // relative path + // check(cwd.resolve("scalafix").toString) // absolute path + // check(Paths.get("..").resolve("scalafix").resolve("scalafix").toString) + - check("scalafix") // relative path - check(cwd.resolve("scalafix").toString) // absolute path - check(Paths.get("..").resolve("scalafix").resolve("scalafix").toString) + + test("completion") { + val cwd = Paths.get("").toAbsolutePath + val parser = ScalafixCompletions.parser(cwd) + + val completions = Parser.completions(parser, " --diff-base ", 0) + + val (insert, display) = JLineCompletion.convertCompletions(completions) + // insert.zip(display).foreach(println) + display.foreach(println) + + } }