@@ -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,80 @@ 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
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)))
116
133
// For a named arg, find the target `DefDef` and jump to the param
117
134
case NamedArg (name, _) :: Apply (fn, _) :: _ =>
118
135
val funSym = fn.symbol
119
136
if funSym.is(Synthetic ) && funSym.owner.is(CaseClass ) then
120
137
val sym = funSym.owner.info.member(name).symbol
121
- List ((sym, sym.info))
138
+ List ((sym, sym.info, None ))
122
139
else
123
140
val paramSymbol =
124
141
for param <- funSym.paramSymss.flatten.find(_.name == name)
125
142
yield param
126
143
val sym = paramSymbol.getOrElse(fn.symbol)
127
- List ((sym, sym.info))
144
+ List ((sym, sym.info, None ))
128
145
129
146
case (_ : untpd.ImportSelector ) :: (imp : Import ) :: _ =>
130
147
importedSymbols(imp, _.span.contains(pos.span)).map(sym =>
131
- (sym, sym.info)
148
+ (sym, sym.info, None )
132
149
)
133
150
134
151
case (imp : Import ) :: _ =>
135
152
importedSymbols(imp, _.span.contains(pos.span)).map(sym =>
136
- (sym, sym.info)
153
+ (sym, sym.info, None )
137
154
)
138
155
139
156
// wildcard param
140
157
case head :: _ if (head.symbol.is(Param ) && head.symbol.is(Synthetic )) =>
141
- List ((head.symbol, head.typeOpt))
158
+ List ((head.symbol, head.typeOpt, None ))
142
159
143
160
case (head @ Select (target, name)) :: _
144
161
if head.symbol.is(Synthetic ) && name == StdNames .nme.apply =>
145
162
val sym = target.symbol
146
163
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 ))
149
166
150
167
// L@@ft(...)
151
168
case (head @ ApplySelect (select)) :: _
152
169
if select.qualifier.sourcePos.contains(pos) &&
153
170
select.name == StdNames .nme.apply =>
154
- List ((head.symbol, head.typeOpt))
171
+ List ((head.symbol, head.typeOpt, None ))
155
172
156
173
// for Inlined we don't have a symbol, but it's needed to show proper type
157
174
case (head @ Inlined (call, bindings, expansion)) :: _ =>
158
- List ((call.symbol, head.typeOpt))
175
+ List ((call.symbol, head.typeOpt, None ))
159
176
160
177
// for comprehension
161
178
case (head @ ApplySelect (select)) :: _ if isForSynthetic(head) =>
162
179
// If the cursor is on the qualifier, return the symbol for it
163
180
// `for { x <- List(1).head@@Option }` returns the symbol of `headOption`
164
181
if select.qualifier.sourcePos.contains(pos) then
165
- List ((select.qualifier.symbol, select.qualifier.typeOpt))
182
+ List ((select.qualifier.symbol, select.qualifier.typeOpt, None ))
166
183
// 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 ))
168
185
169
186
// f@@oo.bar
170
187
case Select (target, _) :: _
171
188
if target.span.isSourceDerived &&
172
189
target.sourcePos.contains(pos) =>
173
- List ((target.symbol, target.typeOpt))
190
+ List ((target.symbol, target.typeOpt, None ))
174
191
175
192
/* In some cases type might be represented by TypeTree, however it's possible
176
193
* that the type tree will not be marked properly as synthetic even if it doesn't
@@ -185,7 +202,7 @@ object MetalsInteractive:
185
202
*/
186
203
case (tpt : TypeTree ) :: parent :: _
187
204
if tpt.span != parent.span && ! tpt.symbol.is(Synthetic ) =>
188
- List ((tpt.symbol, tpt.typeOpt))
205
+ List ((tpt.symbol, tpt.typeOpt, None ))
189
206
190
207
/* TypeTest class https://dotty.epfl.ch/docs/reference/other-new-features/type-test.html
191
208
* compiler automatically adds unapply if possible, we need to find the type symbol
@@ -195,14 +212,14 @@ object MetalsInteractive:
195
212
pat match
196
213
case UnApply (fun, _, pats) =>
197
214
val tpeSym = pats.head.typeOpt.typeSymbol
198
- List ((tpeSym, tpeSym.info))
215
+ List ((tpeSym, tpeSym.info, None ))
199
216
case _ =>
200
217
Nil
201
218
202
219
case path @ head :: tail =>
203
220
if head.symbol.is(Exported ) then
204
221
val sym = head.symbol.sourceSymbol
205
- List ((sym, sym.info))
222
+ List ((sym, sym.info, None ))
206
223
else if head.symbol.is(Synthetic ) then
207
224
enclosingSymbolsWithExpressionType(
208
225
tail,
@@ -217,7 +234,7 @@ object MetalsInteractive:
217
234
pos,
218
235
indexed.ctx.source
219
236
)
220
- then List ((head.symbol, head.typeOpt))
237
+ then List ((head.symbol, head.typeOpt, None ))
221
238
/* Type tree for List(1) has an Int type variable, which has span
222
239
* but doesn't exist in code.
223
240
* https://github.com/scala/scala3/issues/15937
@@ -234,7 +251,7 @@ object MetalsInteractive:
234
251
indexed,
235
252
skipCheckOnName
236
253
)
237
- else recovered.map(sym => (sym, sym.info))
254
+ else recovered.map(sym => (sym, sym.info, None ))
238
255
end if
239
256
case Nil => Nil
240
257
end match
0 commit comments