Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deep suggestions based on task names #2731

Merged
merged 6 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@ trait MillStableScalaModule extends MillPublishScalaModule with Mima {
ProblemFilter.exclude[Problem]("mill.eval.ProfileLogger*"),
ProblemFilter.exclude[Problem]("mill.eval.GroupEvaluator*"),
ProblemFilter.exclude[Problem]("mill.eval.Tarjans*"),
ProblemFilter.exclude[Problem]("mill.define.Ctx#Impl*")
ProblemFilter.exclude[Problem]("mill.define.Ctx#Impl*"),
ProblemFilter.exclude[Problem]("mill.resolve.ResolveNotFoundHandler*")
)
def mimaPreviousVersions: T[Seq[String]] = Settings.mimaBaseVersions

Expand Down
13 changes: 7 additions & 6 deletions example/tasks/2-primary-tasks/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@

import mill._

def allSources = T { os.walk(sources().path).map(PathRef(_)) }
def allSources = T {
os.walk(sources().path)
.filter(_.ext == "java")
.map(PathRef(_))
}

def lineCount: T[Int] = T {
println("Computing line count")
allSources()
.map(_.path)
.filter(_.ext == "java")
.map(os.read.lines(_))
.map(_.size)
.map(p => os.read.lines(p.path).size)
.sum
}

Expand Down Expand Up @@ -57,7 +58,7 @@ Computing line count
def classFiles = T {
println("Generating classfiles")

os.proc("javac", allSources().map(_.path.toString()), "-d", T.dest)
os.proc("javac", allSources().map(_.path), "-d", T.dest)
.call(cwd = T.dest)

PathRef(T.dest)
Expand Down
46 changes: 25 additions & 21 deletions main/define/src/mill/define/Discover.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@ import scala.reflect.macros.blackbox
* the `T.command` methods we find. This mapping from `Class[_]` to `MainData`
* can then be used later to look up the `MainData` for any module.
*/
case class Discover[T] private (value: Map[Class[_], Seq[mainargs.MainData[_, _]]]) {
private[mill] def copy(value: Map[Class[_], Seq[mainargs.MainData[_, _]]] = value): Discover[T] =
new Discover[T](value)
}
case class Discover[T] private (val value: Map[
Class[_],
(Seq[String], Seq[mainargs.MainData[_, _]])
])

object Discover {
def apply[T](value: Map[Class[_], Seq[mainargs.MainData[_, _]]]): Discover[T] =
def apply2[T](value: Map[Class[_], (Seq[String], Seq[mainargs.MainData[_, _]])]): Discover[T] =
new Discover[T](value)
def apply[T]: Discover[T] = macro Router.applyImpl[T]

private def unapply[T](discover: Discover[T])
: Option[Map[Class[_], Seq[mainargs.MainData[_, _]]]] = Some(discover.value)
def apply[T]: Discover[T] = macro Router.applyImpl[T]

private class Router(val ctx: blackbox.Context) extends mainargs.Macros(ctx) {
import c.universe._
Expand Down Expand Up @@ -91,19 +90,25 @@ object Discover {
(weakTypeOf[mill.define.Target[_]], 0, "Target")
)

for {
m <- methods.toList.sortBy(_.fullName)
if m.returnType <:< weakTypeOf[mill.define.Command[_]]
} yield extractMethod(
m.name,
m.paramLists.flatten,
m.pos,
m.annotations.find(_.tree.tpe =:= typeOf[mainargs.main]),
curCls,
weakTypeOf[Any]
Tuple2(
for {
m <- methods.toList.sortBy(_.fullName)
if m.returnType <:< weakTypeOf[mill.define.NamedTask[_]]
} yield m.name.decoded,
for {
m <- methods.toList.sortBy(_.fullName)
if m.returnType <:< weakTypeOf[mill.define.Command[_]]
} yield extractMethod(
m.name,
m.paramLists.flatten,
m.pos,
m.annotations.find(_.tree.tpe =:= typeOf[mainargs.main]),
curCls,
weakTypeOf[Any]
)
)
}
if overridesRoutes.nonEmpty
if overridesRoutes._1.nonEmpty || overridesRoutes._2.nonEmpty
} yield {
// by wrapping the `overridesRoutes` in a lambda function we kind of work around
// the problem of generating a *huge* macro method body that finally exceeds the
Expand All @@ -114,9 +119,8 @@ object Discover {
}

c.Expr[Discover[T]](
q"import _root_.mill.main.TokenReaders._; _root_.mill.define.Discover(_root_.scala.collection.immutable.Map(..$mapping))"
q"import _root_.mill.main.TokenReaders._; _root_.mill.define.Discover.apply2(_root_.scala.collection.immutable.Map(..$mapping))"
)
}
}

}
3 changes: 2 additions & 1 deletion main/define/src/mill/define/Reflect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ private[mill] object Reflect {
// return type, so we can identify the most specific override
res
.sortWith((m1, m2) =>
m1.getDeclaringClass.isAssignableFrom(m2.getDeclaringClass)
if (m1.getDeclaringClass.equals(m2.getDeclaringClass)) false
else m1.getDeclaringClass.isAssignableFrom(m2.getDeclaringClass)
)
.sortWith((m1, m2) =>
m1.getReturnType.isAssignableFrom(m2.getReturnType)
Expand Down
6 changes: 3 additions & 3 deletions main/resolve/src/mill/resolve/Resolve.scala
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ object Resolve {
rest: Seq[String],
nullCommandDefaults: Boolean
): Iterable[Either[String, Command[_]]] = for {
(cls, entryPoints) <- discover.value
(cls, (names, entryPoints)) <- discover.value
if cls.isAssignableFrom(target.getClass)
ep <- entryPoints
if ep.name == name
Expand Down Expand Up @@ -227,12 +227,12 @@ trait Resolve[T] {
nullCommandDefaults: Boolean
): Either[String, Seq[T]] = {
val rootResolved = ResolveCore.Resolved.Module(Segments(), rootModule.getClass)

val allPossibleNames = rootModule.millDiscover.value.values.flatMap(_._1).toSet
val resolved =
ResolveCore.resolve(rootModule, sel.value.toList, rootResolved, Segments()) match {
case ResolveCore.Success(value) => Right(value)
case ResolveCore.NotFound(segments, found, next, possibleNexts) =>
Left(ResolveNotFoundHandler(sel, segments, found, next, possibleNexts))
Left(ResolveNotFoundHandler(sel, segments, found, next, possibleNexts, allPossibleNames))
case ResolveCore.Error(value) => Left(value)
}

Expand Down
40 changes: 29 additions & 11 deletions main/resolve/src/mill/resolve/ResolveNotFoundHandler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@ private object ResolveNotFoundHandler {
segments: Segments,
found: Set[Resolved],
next: Segment,
possibleNexts: Set[Segment]
possibleNexts: Set[Segment],
allPossibleNames: Set[String]
): String = {

if (found.head.isInstanceOf[Resolved.Module]) {
next match {
case Segment.Label(s) =>
val possibleStrings = possibleNexts.collect { case Segment.Label(s) => s }
errorMsgLabel(s, possibleStrings, segments, selector)
errorMsgLabel(s, possibleStrings, segments, selector, allPossibleNames)

case Segment.Cross(keys) =>
val possibleCrossKeys = possibleNexts.collect { case Segment.Cross(keys) => keys }
errorMsgCross(keys, possibleCrossKeys, segments, selector)
errorMsgCross(keys, possibleCrossKeys, segments, selector, allPossibleNames)
}
} else {
unableToResolve((segments ++ Seq(next)).render) +
Expand All @@ -34,13 +35,28 @@ private object ResolveNotFoundHandler {

def unableToResolve(segments: String): String = "Cannot resolve " + segments + "."

def hintList(revSelectorsSoFar: Segments) = {
def hintList(revSelectorsSoFar: Segments, lastSegment: Segment, allPossibleNames: Set[String]) = {
val search = revSelectorsSoFar.render
s" Try `mill resolve $search` to see what's available."

val lastSearchOpt = for {
Segment.Label(s) <- Option(lastSegment)
if s != "_" && s != "__"
possibility <- findMostSimilar(s, allPossibleNames)
} yield "__." + possibility

val searchStr = (Seq(search) ++ lastSearchOpt)
.map { s => s"`mill resolve $s`" }
.mkString(" or ")

s" Try $searchStr to see what's available."
}

def hintListLabel(revSelectorsSoFar: Segments) = {
hintList(revSelectorsSoFar ++ Segment.Label("_"))
def hintListLabel(
revSelectorsSoFar: Segments,
lastSegment: Segment,
allPossibleNames: Set[String]
) = {
hintList(revSelectorsSoFar ++ Segment.Label("_"), lastSegment, allPossibleNames)
}

def findMostSimilar(`given`: String, options: Set[String]): Option[String] = {
Expand All @@ -55,10 +71,11 @@ private object ResolveNotFoundHandler {
given: String,
possibleMembers: Set[String],
prefixSegments: Segments,
fullSegments: Segments
fullSegments: Segments,
allPossibleNames: Set[String]
) = {
val suggestion = findMostSimilar(given, possibleMembers) match {
case None => hintListLabel(prefixSegments)
case None => hintListLabel(prefixSegments, fullSegments.value.last, allPossibleNames)
case Some(similar) =>
" Did you mean " +
(prefixSegments ++ Segment.Label(similar)).render +
Expand All @@ -74,14 +91,15 @@ private object ResolveNotFoundHandler {
givenKeys: Seq[String],
possibleCrossKeys: Set[Seq[String]],
prefixSegments: Segments,
fullSegments: Segments
fullSegments: Segments,
allPossibleNames: Set[String]
) = {

val suggestion = findMostSimilar(
givenKeys.mkString(","),
possibleCrossKeys.map(_.mkString(","))
) match {
case None => hintListLabel(prefixSegments)
case None => hintListLabel(prefixSegments, fullSegments.value.last, allPossibleNames)
case Some(similar) =>
" Did you mean " +
(prefixSegments ++ Segment.Cross(similar.split(','))).render +
Expand Down
13 changes: 11 additions & 2 deletions main/resolve/test/src/mill/main/ResolveTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -879,13 +879,22 @@ object ResolveTests extends TestSuite {
test - check(
"niled.inner.target",
Left(
"Cannot resolve niled.inner.target. Try `mill resolve niled._` to see what's available."
"Cannot resolve niled.inner.target. Try `mill resolve niled._` or `mill resolve __.target` to see what's available."
),
Set()
)
test - check(
"niled._.target",
Left("Cannot resolve niled._.target. Try `mill resolve niled._` to see what's available."),
Left(
"Cannot resolve niled._.target. Try `mill resolve niled._` or `mill resolve __.target` to see what's available."
),
Set()
)
test - check(
"niled._.tttarget",
Left(
"Cannot resolve niled._.tttarget. Try `mill resolve niled._` or `mill resolve __.target` to see what's available."
),
Set()
)
test - check(
Expand Down
2 changes: 1 addition & 1 deletion main/src/mill/main/MainModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ trait MainModule extends mill.define.Module {
.millDiscover
.value
.get(t.ctx.enclosingCls)
.flatMap(_.find(_.name == t.ctx.segments.parts.last))
.flatMap(_._2.find(_.name == t.ctx.segments.parts.last))

mainDataOpt match {
case Some(mainData) if mainData.renderedArgSigs.nonEmpty =>
Expand Down