@@ -5,12 +5,16 @@ import scala.annotation.tailrec
5
5
6
6
import dotc .*
7
7
import ast .* , tpd .*
8
+ import dotty .tools .dotc .core .Constants .*
8
9
import core .* , Contexts .* , Flags .* , Names .* , Symbols .* , Types .*
10
+ import dotty .tools .dotc .core .StdNames .*
9
11
import interactive .*
10
12
import util .*
11
13
import util .SourcePosition
14
+ import dotty .tools .pc .utils .InteractiveEnrichments .*
12
15
13
16
object MetalsInteractive :
17
+ type NamedTupleArg = String
14
18
15
19
def contextOfStat (
16
20
stats : List [Tree ],
@@ -110,67 +114,67 @@ object MetalsInteractive:
110
114
pos : SourcePosition ,
111
115
indexed : IndexedContext ,
112
116
skipCheckOnName : Boolean = false
113
- ): List [(Symbol , Type )] =
117
+ ): List [(Symbol , Type , Option [ String ] )] =
114
118
import indexed .ctx
115
119
path match
116
120
// For a named arg, find the target `DefDef` and jump to the param
117
121
case NamedArg (name, _) :: Apply (fn, _) :: _ =>
118
122
val funSym = fn.symbol
119
123
if funSym.is(Synthetic ) && funSym.owner.is(CaseClass ) then
120
124
val sym = funSym.owner.info.member(name).symbol
121
- List ((sym, sym.info))
125
+ List ((sym, sym.info, None ))
122
126
else
123
127
val paramSymbol =
124
128
for param <- funSym.paramSymss.flatten.find(_.name == name)
125
129
yield param
126
130
val sym = paramSymbol.getOrElse(fn.symbol)
127
- List ((sym, sym.info))
131
+ List ((sym, sym.info, None ))
128
132
129
133
case (_ : untpd.ImportSelector ) :: (imp : Import ) :: _ =>
130
134
importedSymbols(imp, _.span.contains(pos.span)).map(sym =>
131
- (sym, sym.info)
135
+ (sym, sym.info, None )
132
136
)
133
137
134
138
case (imp : Import ) :: _ =>
135
139
importedSymbols(imp, _.span.contains(pos.span)).map(sym =>
136
- (sym, sym.info)
140
+ (sym, sym.info, None )
137
141
)
138
142
139
143
// wildcard param
140
144
case head :: _ if (head.symbol.is(Param ) && head.symbol.is(Synthetic )) =>
141
- List ((head.symbol, head.typeOpt))
145
+ List ((head.symbol, head.typeOpt, None ))
142
146
143
147
case (head @ Select (target, name)) :: _
144
148
if head.symbol.is(Synthetic ) && name == StdNames .nme.apply =>
145
149
val sym = target.symbol
146
150
if sym.is(Synthetic ) && sym.is(Module ) then
147
- List ((sym.companionClass, sym.companionClass.info))
148
- else List ((target.symbol, target.typeOpt))
151
+ List ((sym.companionClass, sym.companionClass.info, None ))
152
+ else List ((target.symbol, target.typeOpt, None ))
149
153
150
154
// L@@ft(...)
151
155
case (head @ ApplySelect (select)) :: _
152
156
if select.qualifier.sourcePos.contains(pos) &&
153
157
select.name == StdNames .nme.apply =>
154
- List ((head.symbol, head.typeOpt))
158
+ List ((head.symbol, head.typeOpt, None ))
155
159
156
160
// for Inlined we don't have a symbol, but it's needed to show proper type
157
161
case (head @ Inlined (call, bindings, expansion)) :: _ =>
158
- List ((call.symbol, head.typeOpt))
162
+ List ((call.symbol, head.typeOpt, None ))
159
163
160
164
// for comprehension
161
165
case (head @ ApplySelect (select)) :: _ if isForSynthetic(head) =>
162
166
// If the cursor is on the qualifier, return the symbol for it
163
167
// `for { x <- List(1).head@@Option }` returns the symbol of `headOption`
164
168
if select.qualifier.sourcePos.contains(pos) then
165
- List ((select.qualifier.symbol, select.qualifier.typeOpt))
169
+ List ((select.qualifier.symbol, select.qualifier.typeOpt, None ))
166
170
// Otherwise, returns the symbol of for synthetics such as "withFilter"
167
- else List ((head.symbol, head.typeOpt))
171
+ else List ((head.symbol, head.typeOpt, None ))
168
172
169
173
// f@@oo.bar
170
174
case Select (target, _) :: _
171
175
if target.span.isSourceDerived &&
172
176
target.sourcePos.contains(pos) =>
173
- List ((target.symbol, target.typeOpt))
177
+ List ((target.symbol, target.typeOpt, None ))
174
178
175
179
/* In some cases type might be represented by TypeTree, however it's possible
176
180
* that the type tree will not be marked properly as synthetic even if it doesn't
@@ -185,7 +189,7 @@ object MetalsInteractive:
185
189
*/
186
190
case (tpt : TypeTree ) :: parent :: _
187
191
if tpt.span != parent.span && ! tpt.symbol.is(Synthetic ) =>
188
- List ((tpt.symbol, tpt.typeOpt))
192
+ List ((tpt.symbol, tpt.typeOpt, None ))
189
193
190
194
/* TypeTest class https://dotty.epfl.ch/docs/reference/other-new-features/type-test.html
191
195
* compiler automatically adds unapply if possible, we need to find the type symbol
@@ -195,14 +199,28 @@ object MetalsInteractive:
195
199
pat match
196
200
case UnApply (fun, _, pats) =>
197
201
val tpeSym = pats.head.typeOpt.typeSymbol
198
- List ((tpeSym, tpeSym.info))
202
+ List ((tpeSym, tpeSym.info, None ))
199
203
case _ =>
200
204
Nil
201
205
206
+ // Handle select on named tuples
207
+ case (Apply (Apply (TypeApply (fun, List (t1, t2)), List (ddef)), List (Literal (Constant (i : Int ))))) :: _
208
+ if fun.symbol.exists && fun.symbol.name == nme.apply &&
209
+ fun.symbol.owner.exists && fun.symbol.owner == getModuleIfDefined(" scala.NamedTuple" ).moduleClass =>
210
+ def getIndex (t : Tree ): Option [Type ] =
211
+ t.tpe.dealias match
212
+ case AppliedType (_, args) => args.get(i)
213
+ case _ => None
214
+ val name = getIndex(t1) match
215
+ case Some (c : ConstantType ) => c.value.stringValue
216
+ case _ => " "
217
+ val tpe = getIndex(t2).getOrElse(NoType )
218
+ List ((ddef.symbol, tpe, Some (name)))
219
+
202
220
case path @ head :: tail =>
203
221
if head.symbol.is(Exported ) then
204
222
val sym = head.symbol.sourceSymbol
205
- List ((sym, sym.info))
223
+ List ((sym, sym.info, None ))
206
224
else if head.symbol.is(Synthetic ) then
207
225
enclosingSymbolsWithExpressionType(
208
226
tail,
@@ -217,7 +235,7 @@ object MetalsInteractive:
217
235
pos,
218
236
indexed.ctx.source
219
237
)
220
- then List ((head.symbol, head.typeOpt))
238
+ then List ((head.symbol, head.typeOpt, None ))
221
239
/* Type tree for List(1) has an Int type variable, which has span
222
240
* but doesn't exist in code.
223
241
* https://github.com/scala/scala3/issues/15937
@@ -234,7 +252,7 @@ object MetalsInteractive:
234
252
indexed,
235
253
skipCheckOnName
236
254
)
237
- else recovered.map(sym => (sym, sym.info))
255
+ else recovered.map(sym => (sym, sym.info, None ))
238
256
end if
239
257
case Nil => Nil
240
258
end match
0 commit comments