Skip to content

Commit 9a35969

Browse files
committed
Port Inlay hints for name parameters
1 parent 96de70c commit 9a35969

File tree

2 files changed

+172
-67
lines changed

2 files changed

+172
-67
lines changed

presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import scala.meta.pc.SymbolSearch
1717
import dotty.tools.dotc.ast.tpd.*
1818
import dotty.tools.dotc.core.Contexts.Context
1919
import dotty.tools.dotc.core.Flags
20+
import dotty.tools.dotc.core.Names.Name
2021
import dotty.tools.dotc.core.StdNames.*
2122
import dotty.tools.dotc.core.Symbols.*
2223
import dotty.tools.dotc.core.Types.*
@@ -29,6 +30,7 @@ import dotty.tools.dotc.util.Spans.Span
2930
import org.eclipse.lsp4j.InlayHint
3031
import org.eclipse.lsp4j.InlayHintKind
3132
import org.eclipse.{lsp4j as l}
33+
import dotty.tools.dotc.core.NameOps.fieldName
3234

3335
class PcInlayHintsProvider(
3436
driver: InteractiveDriver,
@@ -116,8 +118,8 @@ class PcInlayHintsProvider(
116118
InlayHintKind.Type,
117119
)
118120
.addDefinition(adjustedPos.start)
119-
case ByNameParameters(byNameParams) =>
120-
def adjustByNameParameterPos(pos: SourcePosition): SourcePosition =
121+
case NamedParameters(_) | ByNameParameters(_) =>
122+
def adjustBlockParameterPos(pos: SourcePosition): SourcePosition =
121123
val adjusted = adjustPos(pos)
122124
val start = text.indexWhere(!_.isWhitespace, adjusted.start)
123125
val end = text.lastIndexWhere(!_.isWhitespace, adjusted.end - 1)
@@ -130,12 +132,33 @@ class PcInlayHintsProvider(
130132
else
131133
adjusted
132134

133-
byNameParams.foldLeft(inlayHints) {
134-
case (ih, pos) =>
135-
val adjusted = adjustByNameParameterPos(pos)
135+
val namedParams = NamedParameters.unapply(tree).getOrElse(Nil)
136+
val byNameParams = ByNameParameters.unapply(tree).getOrElse(Nil)
137+
138+
val namedAndByNameInlayHints =
139+
namedParams.collect {
140+
case (name, pos) if byNameParams.contains(pos) =>
141+
(name.toString() + " = => ", adjustBlockParameterPos(pos))
142+
}
143+
144+
val namedInlayHints =
145+
namedParams.collect {
146+
case (name, pos) if !byNameParams.contains(pos) =>
147+
(name.toString() + " = ", adjustBlockParameterPos(pos))
148+
}
149+
150+
val namedParamsPos = namedParams.map(_._2)
151+
val byNameInlayHints =
152+
byNameParams.collect {
153+
case pos if !namedParamsPos.contains(pos) =>
154+
("=> ", adjustBlockParameterPos(pos))
155+
}
156+
157+
(namedAndByNameInlayHints ++ namedInlayHints ++ byNameInlayHints).foldLeft(inlayHints) {
158+
case (ih, (labelStr, pos)) =>
136159
ih.add(
137-
adjusted.startPos.toLsp,
138-
List(LabelPart("=> ")),
160+
pos.startPos.toLsp,
161+
List(LabelPart(labelStr)),
139162
InlayHintKind.Parameter
140163
)
141164
}
@@ -414,14 +437,9 @@ end InferredType
414437

415438
object ByNameParameters:
416439
def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context): Option[List[SourcePosition]] =
417-
def shouldSkipSelect(sel: Select) =
418-
isForComprehensionMethod(sel) || sel.symbol.name == nme.unapply
419-
420-
if (params.byNameParameters()){
440+
if (params.byNameParameters()) {
421441
tree match
422-
case Apply(TypeApply(sel: Select, _), _) if shouldSkipSelect(sel) =>
423-
None
424-
case Apply(sel: Select, _) if shouldSkipSelect(sel) =>
442+
case FlattenApplies(sel: Select) if SkipRules.shouldSkipSelect(sel) =>
425443
None
426444
case Apply(fun, args) =>
427445
val funTp = fun.typeOpt.widenTermRefExpr
@@ -436,3 +454,50 @@ object ByNameParameters:
436454
case _ => None
437455
} else None
438456
end ByNameParameters
457+
458+
object NamedParameters:
459+
def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context): Option[List[(Name, SourcePosition)]] =
460+
def isSingleBlock(args: List[Tree]): Boolean =
461+
args.size == 1 && args.forall {
462+
case _: Block => true
463+
case Typed(_: Block, _) => true
464+
case _ => false
465+
}
466+
467+
def isRealApply(tree: Tree) =
468+
!tree.symbol.isOneOf(Flags.GivenOrImplicit) && !tree.span.isZeroExtent
469+
470+
if (params.namedParameters()){
471+
tree match
472+
case FlattenApplies(sel: Select) if SkipRules.shouldSkipSelect(sel) =>
473+
None
474+
case Apply(fun, args) if isRealApply(fun) =>
475+
val funTp = fun.typeOpt.widenTermRefExpr
476+
val params = funTp.paramNamess.flatten
477+
Some(
478+
args
479+
.zip(params)
480+
.collect {
481+
case (tree, paramName) if !tree.span.isZeroExtent => (paramName.fieldName, tree.sourcePos)
482+
}
483+
)
484+
case _ => None
485+
} else None
486+
end NamedParameters
487+
488+
object SkipRules:
489+
def shouldSkipSelect(sel: Select)(using Context): Boolean =
490+
isForComprehensionMethod(sel) || sel.symbol.name == nme.unapply || sel.isInfix
491+
492+
493+
object FlattenApplies:
494+
/*
495+
* Extractor that strips away a (possibly nested) chain of `Apply` / `TypeApply`
496+
* wrappers and returns the underlying function tree.
497+
*/
498+
def unapply(tree: Tree): Option[Tree] =
499+
tree match
500+
case Apply(FlattenApplies(fun), _) => Some(fun)
501+
case TypeApply(FlattenApplies(fun), _) => Some(fun)
502+
case t => Some(t)
503+
end FlattenApplies

0 commit comments

Comments
 (0)