Skip to content

Commit 338ee1f

Browse files
authored
Make mill.tabcomplete work for nested files, scripts, script tasks, external module tasks (#6225)
Fixes #6204 and adds unit tests for the new cases
1 parent 487e481 commit 338ee1f

File tree

11 files changed

+280
-100
lines changed

11 files changed

+280
-100
lines changed

core/api/src/mill/api/internal/Resolved.scala

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ import mill.api.daemon.Segments
44

55
private[mill] sealed trait Resolved {
66
def rootModule: RootModule0
7-
def segments: Segments
7+
8+
/**
9+
* What segments selector was used to resolve this root module. Might not be
10+
* `rootModule.moduleSegments` as it might be an alias that we want to preserve
11+
*/
12+
def rootModuleSegments: Segments
13+
def taskSegments: Segments
814
def cls: Class[?]
9-
def fullSegments: Segments = rootModule.moduleSegments ++ segments
15+
def fullSegments: Segments = rootModuleSegments ++ taskSegments
1016
}
1117

1218
private[mill] object Resolved {
@@ -20,12 +26,27 @@ private[mill] object Resolved {
2026
override def compare(x: Resolved, y: Resolved): Int = {
2127
val keyX = orderingKey(x)
2228
val keyY = orderingKey(y)
23-
if (keyX == keyY) Segments.ordering.compare(x.segments, y.segments)
29+
if (keyX == keyY) Segments.ordering.compare(x.fullSegments, y.fullSegments)
2430
else Ordering.Int.compare(keyX, keyY)
2531
}
2632
}
2733

28-
case class Module(rootModule: RootModule0, segments: Segments, cls: Class[?]) extends Resolved
29-
case class Command(rootModule: RootModule0, segments: Segments, cls: Class[?]) extends Resolved
30-
case class NamedTask(rootModule: RootModule0, segments: Segments, cls: Class[?]) extends Resolved
34+
case class Module(
35+
rootModule: RootModule0,
36+
rootModuleSegments: Segments,
37+
taskSegments: Segments,
38+
cls: Class[?]
39+
) extends Resolved
40+
case class Command(
41+
rootModule: RootModule0,
42+
rootModuleSegments: Segments,
43+
taskSegments: Segments,
44+
cls: Class[?]
45+
) extends Resolved
46+
case class NamedTask(
47+
rootModule: RootModule0,
48+
rootModuleSegments: Segments,
49+
taskSegments: Segments,
50+
cls: Class[?]
51+
) extends Resolved
3152
}

core/resolve/src/mill/resolve/ParseArgs.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ object ParseArgs {
111111
case (h, rest) => Segments(h +: rest)
112112
}
113113

114-
P(simpleQuery ~ (("/" | ":") ~ simpleQuery.?).? ~ End).map {
114+
P(simpleQuery ~ (("/" | ":").! ~ simpleQuery.?).? ~ End).map {
115115
case (q, None) => (None, Some(q))
116-
case (q, Some(q2)) => (Some(q), q2)
116+
case (q, Some((sep, q2))) => (Some(Segments.labels(q.render + sep)), q2)
117117
}
118118
}
119119
}

core/resolve/src/mill/resolve/Resolve.scala

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ object Resolve {
1919
object Segments extends Resolve[Segments] {
2020
def handleResolved(
2121
rootModule: RootModule0,
22+
rootModuleSegments: Segments,
2223
resolved: Seq[Resolved],
2324
args: Seq[String],
2425
selector: Segments,
@@ -36,6 +37,7 @@ object Resolve {
3637
object Raw extends Resolve[Resolved] {
3738
def handleResolved(
3839
rootModule: RootModule0,
40+
rootModuleSegments: Segments,
3941
resolved: Seq[Resolved],
4042
args: Seq[String],
4143
selector: Segments,
@@ -53,6 +55,7 @@ object Resolve {
5355
object Inspect extends Resolve[Either[Module, Task.Named[Any]]] {
5456
def handleResolved(
5557
rootModule: RootModule0,
58+
rootModuleSegments: Segments,
5659
resolved: Seq[Resolved],
5760
args: Seq[String],
5861
selector: Segments,
@@ -64,12 +67,25 @@ object Resolve {
6467

6568
val taskList: Seq[Result[Either[Module, Option[Task.Named[?]]]]] = resolved.map {
6669
case m: Resolved.Module =>
67-
ResolveCore.instantiateModule(rootModule, m.segments, cache).map(Left(_))
70+
ResolveCore.instantiateModule(
71+
rootModule,
72+
rootModuleSegments,
73+
m.taskSegments,
74+
cache
75+
).map(Left(_))
6876

6977
case t =>
7078
Resolve
7179
.Tasks
72-
.handleTask(rootModule, args, nullCommandDefaults, allowPositionalCommandArgs, cache, t)
80+
.handleTask(
81+
rootModule,
82+
rootModuleSegments,
83+
args,
84+
nullCommandDefaults,
85+
allowPositionalCommandArgs,
86+
cache,
87+
t
88+
)
7389
.map(Right(_))
7490
}
7591

@@ -89,6 +105,7 @@ object Resolve {
89105
object Tasks extends Resolve[Task.Named[Any]] {
90106
private[Resolve] def handleTask(
91107
rootModule: RootModule0,
108+
rootModuleSegments: Segments,
92109
args: Seq[String],
93110
nullCommandDefaults: Boolean,
94111
allowPositionalCommandArgs: Boolean,
@@ -97,13 +114,13 @@ object Resolve {
97114
) = task match {
98115
case r: Resolved.NamedTask =>
99116
val instantiated = ResolveCore
100-
.instantiateModule(rootModule, r.segments.init, cache)
117+
.instantiateModule(rootModule, rootModuleSegments, r.taskSegments.init, cache)
101118
.flatMap(instantiateNamedTask(r, _, cache))
102119
instantiated.map(Some(_))
103120

104121
case r: Resolved.Command =>
105122
val instantiated = ResolveCore
106-
.instantiateModule(rootModule, r.segments.init, cache)
123+
.instantiateModule(rootModule, rootModuleSegments, r.taskSegments.init, cache)
107124
.flatMap { mod =>
108125
instantiateCommand(
109126
rootModule,
@@ -117,10 +134,16 @@ object Resolve {
117134
instantiated.map(Some(_))
118135

119136
case r: Resolved.Module =>
120-
ResolveCore.instantiateModule(rootModule, r.segments, cache).flatMap {
137+
ResolveCore.instantiateModule(
138+
rootModule,
139+
rootModuleSegments,
140+
r.taskSegments,
141+
cache
142+
).flatMap {
121143
case value: DefaultTaskModule =>
122144
val directChildrenOrErr = ResolveCore.resolveDirectChildren(
123145
rootModule,
146+
r.rootModuleSegments,
124147
value.getClass,
125148
Some(value.defaultTask()),
126149
value.moduleSegments,
@@ -148,6 +171,7 @@ object Resolve {
148171
}
149172
def handleResolved(
150173
rootModule: RootModule0,
174+
rootModuleSegments: Segments,
151175
resolved: Seq[Resolved],
152176
args: Seq[String],
153177
selector: Segments,
@@ -159,6 +183,7 @@ object Resolve {
159183

160184
val taskList: Seq[Result[Option[Task.Named[?]]]] = resolved.map(handleTask(
161185
rootModule,
186+
rootModuleSegments,
162187
args,
163188
nullCommandDefaults,
164189
allowPositionalCommandArgs,
@@ -187,7 +212,7 @@ object Resolve {
187212
.reflect(
188213
p.getClass,
189214
classOf[Task.Named[?]],
190-
_ == r.segments.last.value,
215+
_ == r.taskSegments.last.value,
191216
true,
192217
getMethods = cache.getMethods
193218
)
@@ -209,7 +234,7 @@ object Resolve {
209234
mill.api.ExecResult.catchWrapException {
210235
val invoked = invokeCommand0(
211236
p,
212-
r.segments.last.value,
237+
r.taskSegments.last.value,
213238
p match {
214239
case e: ExternalModule => e.moduleCtx.discover
215240
case _ => rootModule.moduleCtx.discover
@@ -295,6 +320,7 @@ object Resolve {
295320
trait Resolve[T] {
296321
def handleResolved(
297322
rootModule: RootModule0,
323+
rootModuleSegments: Segments,
298324
resolved: Seq[Resolved],
299325
args: Seq[String],
300326
segments: Segments,
@@ -329,6 +355,7 @@ trait Resolve[T] {
329355
resolveNonEmptyAndHandle(
330356
remaining,
331357
scriptModule,
358+
Segments.labels(s"$first:"),
332359
Segments.labels(segments*),
333360
nullCommandDefaults,
334361
allowPositionalCommandArgs,
@@ -346,14 +373,21 @@ trait Resolve[T] {
346373
ParseArgs.extractAndValidate(group, selectMode == SelectMode.Multi) match {
347374
case f: Result.Failure => handleScriptModule(group, f)
348375
case Result.Success((selectors, args)) =>
349-
val selected: Seq[Result[Seq[T]]] = selectors.map { case (scopedSel, sel) =>
350-
resolveRootModule(rootModule, scopedSel) match {
376+
val selected: Seq[Result[Seq[T]]] = selectors.map { case (rootModuleSegments0, sel) =>
377+
val rootModuleSegments = rootModuleSegments0.getOrElse(Segments())
378+
resolveRootModule(rootModule, rootModuleSegments0) match {
351379
case f: Result.Failure => handleScriptModule(group, f)
352380
case Result.Success(rootModuleSels) =>
353381
val res =
354-
resolveNonEmptyAndHandle1(rootModuleSels, sel.getOrElse(Segments()), cache)
382+
resolveNonEmptyAndHandle1(
383+
rootModuleSels,
384+
rootModuleSegments,
385+
sel.getOrElse(Segments()),
386+
cache
387+
)
355388
def notFoundResult = resolveNonEmptyAndHandle2(
356389
rootModuleSels,
390+
rootModuleSegments,
357391
args,
358392
sel.getOrElse(Segments()),
359393
nullCommandDefaults,
@@ -378,6 +412,7 @@ trait Resolve[T] {
378412
def resolveNonEmptyAndHandle(
379413
args: Seq[String],
380414
rootModule: RootModule0,
415+
rootModuleSegments: Segments,
381416
sel: Segments,
382417
nullCommandDefaults: Boolean,
383418
allowPositionalCommandArgs: Boolean,
@@ -386,23 +421,27 @@ trait Resolve[T] {
386421
val cache = new ResolveCore.Cache()
387422
resolveNonEmptyAndHandle2(
388423
rootModule,
424+
rootModuleSegments: Segments,
389425
args,
390426
sel,
391427
nullCommandDefaults,
392428
allowPositionalCommandArgs,
393429
resolveToModuleTasks,
394430
cache,
395-
resolveNonEmptyAndHandle1(rootModule, sel, cache)
431+
resolveNonEmptyAndHandle1(rootModule, rootModuleSegments, sel, cache)
396432
)
397433
}
398434
def resolveNonEmptyAndHandle1(
399435
rootModule: RootModule0,
436+
rootModuleSegments: Segments,
400437
sel: Segments,
401438
cache: ResolveCore.Cache
402439
): ResolveCore.Result = {
403-
val rootResolved = Resolved.Module(rootModule, Segments(), rootModule.getClass)
440+
val rootResolved =
441+
Resolved.Module(rootModule, rootModuleSegments, Segments(), rootModule.getClass)
404442
ResolveCore.resolve(
405443
rootModule = rootModule,
444+
rootModuleSegments,
406445
remainingQuery = sel.value.toList,
407446
current = rootResolved,
408447
querySoFar = Segments(),
@@ -413,6 +452,7 @@ trait Resolve[T] {
413452

414453
def resolveNonEmptyAndHandle2(
415454
rootModule: RootModule0,
455+
rootModuleSegments: Segments,
416456
args: Seq[String],
417457
sel: Segments,
418458
nullCommandDefaults: Boolean,
@@ -448,6 +488,7 @@ trait Resolve[T] {
448488
val sorted = r.sorted
449489
handleResolved(
450490
rootModule,
491+
rootModuleSegments,
451492
sorted,
452493
args,
453494
sel,
@@ -471,11 +512,13 @@ trait Resolve[T] {
471512
case Some(scoping) =>
472513
for {
473514
moduleCls <-
474-
try Result.Success(rootModule.getClass.getClassLoader.loadClass(scoping.render + "$"))
515+
try Result.Success(
516+
rootModule.getClass.getClassLoader.loadClass(scoping.render.stripSuffix("/") + "$")
517+
)
475518
catch {
476519
case _: ClassNotFoundException =>
477520
try Result.Success(rootModule.getClass.getClassLoader.loadClass(
478-
scoping.render + ".package$"
521+
scoping.render.stripSuffix("/") + ".package$"
479522
))
480523
catch {
481524
case _: ClassNotFoundException =>

0 commit comments

Comments
 (0)