@@ -5,12 +5,16 @@ import scala.annotation.tailrec
55
66import dotc .*
77import ast .* , tpd .*
8+ import dotty .tools .dotc .core .Constants .*
89import core .* , Contexts .* , Flags .* , Names .* , Symbols .* , Types .*
10+ import dotty .tools .dotc .core .StdNames .*
911import interactive .*
1012import util .*
1113import util .SourcePosition
14+ import dotty .tools .pc .utils .InteractiveEnrichments .*
1215
1316object MetalsInteractive :
17+ type NamedTupleArg = String
1418
1519 def contextOfStat (
1620 stats : List [Tree ],
@@ -110,67 +114,80 @@ object MetalsInteractive:
110114 pos : SourcePosition ,
111115 indexed : IndexedContext ,
112116 skipCheckOnName : Boolean = false
113- ): List [(Symbol , Type )] =
117+ ): List [(Symbol , Type , Option [ String ] )] =
114118 import indexed .ctx
115119 path match
120+ // Handle select on named tuples
121+ case (Apply (Apply (TypeApply (fun, List (t1, t2)), List (ddef)), List (Literal (Constant (i : Int ))))) :: _
122+ if fun.symbol.exists && fun.symbol.name == nme.apply &&
123+ fun.symbol.owner.exists && fun.symbol.owner == getModuleIfDefined(" scala.NamedTuple" ).moduleClass =>
124+ def getIndex (t : Tree ): Option [Type ] =
125+ t.tpe.dealias match
126+ case AppliedType (_, args) => args.get(i)
127+ case _ => None
128+ val name = getIndex(t1) match
129+ case Some (c : ConstantType ) => c.value.stringValue
130+ case _ => " "
131+ val tpe = getIndex(t2).getOrElse(NoType )
132+ List ((ddef.symbol, tpe, Some (name)))
116133 // For a named arg, find the target `DefDef` and jump to the param
117134 case NamedArg (name, _) :: Apply (fn, _) :: _ =>
118135 val funSym = fn.symbol
119136 if funSym.is(Synthetic ) && funSym.owner.is(CaseClass ) then
120137 val sym = funSym.owner.info.member(name).symbol
121- List ((sym, sym.info))
138+ List ((sym, sym.info, None ))
122139 else
123140 val paramSymbol =
124141 for param <- funSym.paramSymss.flatten.find(_.name == name)
125142 yield param
126143 val sym = paramSymbol.getOrElse(fn.symbol)
127- List ((sym, sym.info))
144+ List ((sym, sym.info, None ))
128145
129146 case (_ : untpd.ImportSelector ) :: (imp : Import ) :: _ =>
130147 importedSymbols(imp, _.span.contains(pos.span)).map(sym =>
131- (sym, sym.info)
148+ (sym, sym.info, None )
132149 )
133150
134151 case (imp : Import ) :: _ =>
135152 importedSymbols(imp, _.span.contains(pos.span)).map(sym =>
136- (sym, sym.info)
153+ (sym, sym.info, None )
137154 )
138155
139156 // wildcard param
140157 case head :: _ if (head.symbol.is(Param ) && head.symbol.is(Synthetic )) =>
141- List ((head.symbol, head.typeOpt))
158+ List ((head.symbol, head.typeOpt, None ))
142159
143160 case (head @ Select (target, name)) :: _
144161 if head.symbol.is(Synthetic ) && name == StdNames .nme.apply =>
145162 val sym = target.symbol
146163 if sym.is(Synthetic ) && sym.is(Module ) then
147- List ((sym.companionClass, sym.companionClass.info))
148- else List ((target.symbol, target.typeOpt))
164+ List ((sym.companionClass, sym.companionClass.info, None ))
165+ else List ((target.symbol, target.typeOpt, None ))
149166
150167 // L@@ft(...)
151168 case (head @ ApplySelect (select)) :: _
152169 if select.qualifier.sourcePos.contains(pos) &&
153170 select.name == StdNames .nme.apply =>
154- List ((head.symbol, head.typeOpt))
171+ List ((head.symbol, head.typeOpt, None ))
155172
156173 // for Inlined we don't have a symbol, but it's needed to show proper type
157174 case (head @ Inlined (call, bindings, expansion)) :: _ =>
158- List ((call.symbol, head.typeOpt))
175+ List ((call.symbol, head.typeOpt, None ))
159176
160177 // for comprehension
161178 case (head @ ApplySelect (select)) :: _ if isForSynthetic(head) =>
162179 // If the cursor is on the qualifier, return the symbol for it
163180 // `for { x <- List(1).head@@Option }` returns the symbol of `headOption`
164181 if select.qualifier.sourcePos.contains(pos) then
165- List ((select.qualifier.symbol, select.qualifier.typeOpt))
182+ List ((select.qualifier.symbol, select.qualifier.typeOpt, None ))
166183 // Otherwise, returns the symbol of for synthetics such as "withFilter"
167- else List ((head.symbol, head.typeOpt))
184+ else List ((head.symbol, head.typeOpt, None ))
168185
169186 // f@@oo.bar
170187 case Select (target, _) :: _
171188 if target.span.isSourceDerived &&
172189 target.sourcePos.contains(pos) =>
173- List ((target.symbol, target.typeOpt))
190+ List ((target.symbol, target.typeOpt, None ))
174191
175192 /* In some cases type might be represented by TypeTree, however it's possible
176193 * that the type tree will not be marked properly as synthetic even if it doesn't
@@ -185,7 +202,7 @@ object MetalsInteractive:
185202 */
186203 case (tpt : TypeTree ) :: parent :: _
187204 if tpt.span != parent.span && ! tpt.symbol.is(Synthetic ) =>
188- List ((tpt.symbol, tpt.typeOpt))
205+ List ((tpt.symbol, tpt.typeOpt, None ))
189206
190207 /* TypeTest class https://dotty.epfl.ch/docs/reference/other-new-features/type-test.html
191208 * compiler automatically adds unapply if possible, we need to find the type symbol
@@ -195,14 +212,14 @@ object MetalsInteractive:
195212 pat match
196213 case UnApply (fun, _, pats) =>
197214 val tpeSym = pats.head.typeOpt.typeSymbol
198- List ((tpeSym, tpeSym.info))
215+ List ((tpeSym, tpeSym.info, None ))
199216 case _ =>
200217 Nil
201218
202219 case path @ head :: tail =>
203220 if head.symbol.is(Exported ) then
204221 val sym = head.symbol.sourceSymbol
205- List ((sym, sym.info))
222+ List ((sym, sym.info, None ))
206223 else if head.symbol.is(Synthetic ) then
207224 enclosingSymbolsWithExpressionType(
208225 tail,
@@ -217,7 +234,7 @@ object MetalsInteractive:
217234 pos,
218235 indexed.ctx.source
219236 )
220- then List ((head.symbol, head.typeOpt))
237+ then List ((head.symbol, head.typeOpt, None ))
221238 /* Type tree for List(1) has an Int type variable, which has span
222239 * but doesn't exist in code.
223240 * https://github.com/scala/scala3/issues/15937
@@ -234,7 +251,7 @@ object MetalsInteractive:
234251 indexed,
235252 skipCheckOnName
236253 )
237- else recovered.map(sym => (sym, sym.info))
254+ else recovered.map(sym => (sym, sym.info, None ))
238255 end if
239256 case Nil => Nil
240257 end match
0 commit comments