Skip to content

Commit

Permalink
wip improve sbt completion
Browse files Browse the repository at this point in the history
  • Loading branch information
MasseGuillaume committed Dec 19, 2017
1 parent 3f63b5b commit 51abefa
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 26 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ val cli = MultiScalaProject(
"com.github.alexarchambault" %% "case-app" % "1.2.0",
"org.typelevel" %% "paiges-core" % "0.2.0",
"com.martiansoftware" % "nailgun-server" % "0.9.1",
"org.eclipse.jgit" % "org.eclipse.jgit" % "4.5.4.201711221230-r",
jgit,
"ch.qos.logback" % "logback-classic" % "1.2.3"
)
)
Expand Down Expand Up @@ -179,7 +179,7 @@ lazy val `scalafix-sbt` = project
}
},
sbtPlugin := true,
libraryDependencies ++= coursierDeps,
libraryDependencies ++= jgit +: coursierDeps,
testQuick := {}, // these test are slow.
// scripted tests needs scalafix 2.12
// semanticdb-scala will generate the semantic db for both scala 2.11 and scala 2.12
Expand Down
2 changes: 2 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ object Dependencies {
def sbt013 = "0.13.6"
def sbt1 = "1.0.4"

val jgit = "org.eclipse.jgit" % "org.eclipse.jgit" % "4.5.4.201711221230-r"

var testClasspath: String = "empty"
def semanticdb: ModuleID = "org.scalameta" % "semanticdb-scalac" % scalametaV cross CrossVersion.full
def metaconfig: ModuleID = "com.geirsson" %% "metaconfig-typesafe-config" % metaconfigV
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package scalafix.internal.sbt

import java.nio.file.Path
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.RefDatabase
import org.eclipse.jgit.lib.Repository
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)

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)"
}

def all: Iterable[String] = branchAndTags ++ last20Commits
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ package scalafix.internal.sbt
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
import java.util.regex.Pattern

import scala.util.control.NonFatal

import sbt.complete.DefaultParsers
import sbt.complete.DefaultParsers._
import sbt.complete.FileExamples
import sbt.complete.Parser
import sbt.complete.FixedSetExamples

object ScalafixCompletions {
private val names = ScalafixRuleNames.all
Expand Down Expand Up @@ -35,30 +40,139 @@ object ScalafixCompletions {
}
}

private def fileRule(cwd: Path): Parser[String] =
token("file:") ~>
StringBasic
.examples(new AbsolutePathExamples(cwd))
.map { f =>
val path = toAbsolutePath(Paths.get(f), cwd).toString
"file:" + path
}

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]] = {
val all =
namedRule |
fileRule(cwd) |
uri("github") |
uri("replace") |
uri("http") |
uri("https") |
uri("scala")
(token(Space) ~> token(all)).* <~ SpaceClass.*

// 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



flatten(joinSeq(space, all).*)
}
}
8 changes: 2 additions & 6 deletions scalafix-sbt/src/main/scala/scalafix/sbt/ScalafixPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -270,16 +270,12 @@ object ScalafixPlugin extends AutoPlugin {
scalafixConfig.value
.map(x => "--config" :: x.getAbsolutePath :: Nil)
.getOrElse(Nil)
val ruleArgs =
if (inputArgs.nonEmpty)
inputArgs.flatMap("-r" :: _ :: Nil)
else Nil

val sourceroot = scalafixSourceroot.value.getAbsolutePath
// only fix unmanaged sources, skip code generated files.
verbose ++
config ++
ruleArgs ++
baseArgs ++
inputArgs ++
options ++
List(
"--sourceroot",
Expand Down

0 comments on commit 51abefa

Please sign in to comment.