Skip to content

Commit 22ec3a6

Browse files
committed
Port Inlay hints for name parameters
1 parent 96de70c commit 22ec3a6

File tree

2 files changed

+233
-83
lines changed

2 files changed

+233
-83
lines changed

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

Lines changed: 107 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ 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.NameOps.fieldName
21+
import dotty.tools.dotc.core.Names.Name
2022
import dotty.tools.dotc.core.StdNames.*
2123
import dotty.tools.dotc.core.Symbols.*
2224
import dotty.tools.dotc.core.Types.*
@@ -116,26 +118,69 @@ class PcInlayHintsProvider(
116118
InlayHintKind.Type,
117119
)
118120
.addDefinition(adjustedPos.start)
119-
case ByNameParameters(byNameParams) =>
120-
def adjustByNameParameterPos(pos: SourcePosition): SourcePosition =
121-
val adjusted = adjustPos(pos)
122-
val start = text.indexWhere(!_.isWhitespace, adjusted.start)
123-
val end = text.lastIndexWhere(!_.isWhitespace, adjusted.end - 1)
121+
case Parameters(isInfixFun, args) =>
122+
def isNamedParam(pos: SourcePosition): Boolean =
123+
val start = text.indexWhere(!_.isWhitespace, pos.start)
124+
val end = text.lastIndexWhere(!_.isWhitespace, pos.end - 1)
124125

126+
text.slice(start, end).contains('=')
127+
128+
def isBlockParam(pos: SourcePosition): Boolean =
129+
val start = text.indexWhere(!_.isWhitespace, pos.start)
130+
val end = text.lastIndexWhere(!_.isWhitespace, pos.end - 1)
125131
val startsWithBrace = text.lift(start).contains('{')
126132
val endsWithBrace = text.lift(end).contains('}')
127133

128-
if startsWithBrace && endsWithBrace then
129-
adjusted.withStart(start + 1)
134+
startsWithBrace && endsWithBrace
135+
136+
def adjustBlockParamPos(pos: SourcePosition): SourcePosition =
137+
if isBlockParam(pos) then
138+
pos.withStart(pos.start + 1)
130139
else
131-
adjusted
140+
pos
141+
142+
val adjustedArgs = args.map {
143+
case (name, pos, isByName) => (name, adjustPos(pos), isByName)
144+
}
145+
146+
val namedParams =
147+
if params.namedParameters() && !isInfixFun then
148+
adjustedArgs.collect {
149+
// We don't want to show parameter names for block parameters or named parameters
150+
case (name, pos, _) if !isBlockParam(pos) && !isNamedParam(pos) => (name, pos)
151+
}
152+
else Nil
132153

133-
byNameParams.foldLeft(inlayHints) {
134-
case (ih, pos) =>
135-
val adjusted = adjustByNameParameterPos(pos)
154+
val byNameParams =
155+
if params.byNameParameters() then
156+
adjustedArgs.collect {
157+
case (name, pos, isByName) if isByName => (name, pos)
158+
}
159+
else Nil
160+
161+
val namedAndByNameInlayHints =
162+
namedParams.collect {
163+
case param@(name, pos) if byNameParams.contains(param) =>
164+
(name.toString() + " = => ", adjustBlockParamPos(pos))
165+
}
166+
167+
val namedInlayHints =
168+
namedParams.collect {
169+
case param@(name, pos) if !byNameParams.contains(param) =>
170+
(name.toString() + " = ", pos)
171+
}
172+
173+
val byNameInlayHints =
174+
byNameParams.collect {
175+
case param@(_, pos) if !namedParams.contains(param) && (!isInfixFun || (isInfixFun && isBlockParam(pos))) =>
176+
("=> ", adjustBlockParamPos(pos))
177+
}
178+
179+
(namedAndByNameInlayHints ++ namedInlayHints ++ byNameInlayHints).foldLeft(inlayHints) {
180+
case (ih, (labelStr, pos)) =>
136181
ih.add(
137-
adjusted.startPos.toLsp,
138-
List(LabelPart("=> ")),
182+
pos.startPos.toLsp,
183+
List(LabelPart(labelStr)),
139184
InlayHintKind.Parameter
140185
)
141186
}
@@ -412,27 +457,55 @@ object InferredType:
412457

413458
end InferredType
414459

415-
object ByNameParameters:
416-
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
460+
object Parameters:
461+
def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context): Option[(Boolean, List[(Name, SourcePosition, Boolean)])] =
462+
def shouldSkipFun(fun: Tree)(using Context): Boolean =
463+
fun match
464+
case sel: Select => isForComprehensionMethod(sel) || sel.symbol.name == nme.unapply
465+
case _ => false
466+
467+
def isInfixFun(fun: Tree, args: List[Tree])(using Context): Boolean =
468+
val isInfixSelect = fun match
469+
case Select(sel, _) => sel.isInfix
470+
case _ => false
471+
val source = fun.source
472+
if args.isEmpty then isInfixSelect
473+
else
474+
(!(fun.span.end until args.head.span.start)
475+
.map(source.apply)
476+
.contains('.') && fun.symbol.is(Flags.ExtensionMethod)) || isInfixSelect
477+
478+
def isRealApply(tree: Tree) =
479+
!tree.symbol.isOneOf(Flags.GivenOrImplicit) && !tree.span.isZeroExtent
480+
481+
def getUnderlyingFun(tree: Tree): Tree =
482+
tree match
483+
case Apply(fun, _) => getUnderlyingFun(fun)
484+
case TypeApply(fun, _) => getUnderlyingFun(fun)
485+
case t => t
419486

420-
if (params.byNameParameters()){
487+
if (params.namedParameters() || params.byNameParameters()) then
421488
tree match
422-
case Apply(TypeApply(sel: Select, _), _) if shouldSkipSelect(sel) =>
423-
None
424-
case Apply(sel: Select, _) if shouldSkipSelect(sel) =>
425-
None
426-
case Apply(fun, args) =>
427-
val funTp = fun.typeOpt.widenTermRefExpr
428-
val params = funTp.paramInfoss.flatten
429-
Some(
430-
args
431-
.zip(params)
432-
.collect {
433-
case (tree, param) if param.isByName => tree.sourcePos
434-
}
435-
)
489+
case Apply(fun, args) if isRealApply(fun) =>
490+
val underlyingFun = getUnderlyingFun(fun)
491+
if shouldSkipFun(underlyingFun) then
492+
None
493+
else
494+
val funTp = fun.typeOpt.widenTermRefExpr
495+
val paramNames = funTp.paramNamess.flatten
496+
val paramInfos = funTp.paramInfoss.flatten
497+
Some(
498+
// Check if the function is an infix function or the underlying function is an infix function
499+
isInfixFun(fun, args) || underlyingFun.isInfix,
500+
(
501+
args
502+
.zip(paramNames)
503+
.zip(paramInfos)
504+
.collect {
505+
case ((arg, paramName), paramInfo) if !arg.span.isZeroExtent => (paramName.fieldName, arg.sourcePos, paramInfo.isByName)
506+
}
507+
)
508+
)
436509
case _ => None
437-
} else None
438-
end ByNameParameters
510+
else None
511+
end Parameters

0 commit comments

Comments
 (0)