Skip to content

Commit 971cee4

Browse files
committed
Port Inlay hints for name parameters
1 parent 96de70c commit 971cee4

File tree

2 files changed

+203
-73
lines changed

2 files changed

+203
-73
lines changed

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

Lines changed: 81 additions & 15 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,16 +437,11 @@ 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, args) if SkipRules.shouldSkipSelect(sel, args) =>
425443
None
426-
case Apply(fun, args) =>
444+
case Apply(fun, args) if !SkipRules.shouldSkipInfix(fun, args) =>
427445
val funTp = fun.typeOpt.widenTermRefExpr
428446
val params = funTp.paramInfoss.flatten
429447
Some(
@@ -436,3 +454,51 @@ 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 isRealApply(tree: Tree) =
461+
!tree.symbol.isOneOf(Flags.GivenOrImplicit) && !tree.span.isZeroExtent
462+
if (params.namedParameters()){
463+
tree match
464+
case FlattenApplies(sel: Select, args) if SkipRules.shouldSkipSelect(sel, args) =>
465+
None
466+
case Apply(fun, args) if isRealApply(fun) && !SkipRules.shouldSkipInfix(fun, args) =>
467+
val funTp = fun.typeOpt.widenTermRefExpr
468+
val params = funTp.paramNamess.flatten
469+
Some(
470+
args
471+
.zip(params)
472+
.collect {
473+
case (tree, paramName) if !tree.span.isZeroExtent => (paramName.fieldName, tree.sourcePos)
474+
}
475+
)
476+
case _ => None
477+
} else None
478+
end NamedParameters
479+
480+
private object SkipRules:
481+
def shouldSkipSelect(sel: Select, args: List[Tree])(using Context): Boolean =
482+
isForComprehensionMethod(sel) || sel.symbol.name == nme.unapply || sel.isInfix
483+
484+
def shouldSkipInfix(fun: Tree, args: List[Tree])(using Context): Boolean =
485+
val source = fun.source
486+
if args.isEmpty then false
487+
else
488+
val isInfixExtensionMethod =
489+
!(fun.span.end until args.head.span.start)
490+
.map(source.apply)
491+
.contains('.')
492+
isInfixExtensionMethod && fun.symbol.is(Flags.ExtensionMethod)
493+
494+
private object FlattenApplies:
495+
/*
496+
* Extractor that strips away a (possibly nested) chain of `Apply` / `TypeApply`
497+
* wrappers and returns the underlying function tree.
498+
*/
499+
def unapply(tree: Tree): (Tree, List[Tree]) =
500+
tree match
501+
case Apply(FlattenApplies(fun, argss), args) => (fun, argss ++: args)
502+
case TypeApply(FlattenApplies(fun, argss), args) => (fun, argss ++: args)
503+
case t => (t, Nil)
504+
end FlattenApplies

0 commit comments

Comments
 (0)