1
+ package dotty .tools .pc
2
+
3
+
4
+ import java .nio .file .Paths
5
+
6
+ import scala .meta .internal .metals .ReportContext
7
+ import dotty .tools .pc .utils .MtagsEnrichments .*
8
+ import dotty .tools .pc .printer .ShortenedTypePrinter
9
+ import scala .meta .pc .SymbolSearch
10
+ import scala .meta .pc .SyntheticDecoration
11
+ import scala .meta .pc .SyntheticDecorationsParams
12
+ import scala .meta .internal .pc .DecorationKind
13
+ import scala .meta .internal .pc .Decoration
14
+
15
+
16
+ import dotty .tools .dotc .ast .tpd
17
+ import dotty .tools .dotc .ast .tpd .*
18
+ import dotty .tools .dotc .core .Contexts .Context
19
+ import dotty .tools .dotc .core .Flags
20
+ import dotty .tools .dotc .core .StdNames .*
21
+ import dotty .tools .dotc .core .Types .*
22
+ import dotty .tools .dotc .interactive .Interactive
23
+ import dotty .tools .dotc .interactive .InteractiveDriver
24
+ import dotty .tools .dotc .util .SourceFile
25
+ import dotty .tools .dotc .util .SourcePosition
26
+ import dotty .tools .dotc .util .Spans .Span
27
+ import dotty .tools .pc .IndexedContext
28
+
29
+ final class PcSyntheticDecorationsProvider (
30
+ driver : InteractiveDriver ,
31
+ params : SyntheticDecorationsParams ,
32
+ symbolSearch : SymbolSearch ,
33
+ )(using ReportContext ):
34
+
35
+ val uri = params.uri().nn
36
+ val filePath = Paths .get(uri).nn
37
+ val sourceText = params.text().nn
38
+ val text = sourceText.toCharArray().nn
39
+ val source =
40
+ SourceFile .virtual(filePath.toString, sourceText)
41
+ driver.run(uri, source)
42
+ given ctx : Context = driver.currentCtx
43
+ val unit = driver.currentCtx.run.nn.units.head
44
+
45
+ def tpdTree = unit.tpdTree
46
+
47
+ def provide (): List [SyntheticDecoration ] =
48
+ val deepFolder = DeepFolder [Synthetics ](collectDecorations)
49
+ deepFolder(Synthetics .empty, tpdTree).decorations
50
+
51
+ def collectDecorations (
52
+ decorations : Synthetics ,
53
+ tree : Tree ,
54
+ ): Synthetics =
55
+ tree match
56
+ case ImplicitConversion (name, range) if params.implicitConversions() =>
57
+ val adjusted = range.adjust(text)._1
58
+ decorations
59
+ .add(
60
+ Decoration (
61
+ adjusted.startPos.toLsp,
62
+ name + " (" ,
63
+ DecorationKind .ImplicitConversion ,
64
+ )
65
+ )
66
+ .add(
67
+ Decoration (
68
+ adjusted.endPos.toLsp,
69
+ " )" ,
70
+ DecorationKind .ImplicitConversion ,
71
+ )
72
+ )
73
+ case ImplicitParameters (names, pos, allImplicit)
74
+ if params.implicitParameters() =>
75
+ val label =
76
+ if allImplicit then names.mkString(" (" , " , " , " )" )
77
+ else names.mkString(" , " , " , " , " " )
78
+ decorations.add(
79
+ Decoration (
80
+ pos.adjust(text)._1.toLsp,
81
+ label,
82
+ DecorationKind .ImplicitParameter ,
83
+ )
84
+ )
85
+ case TypeParameters (tpes, pos, sel)
86
+ if params.typeParameters() && ! syntheticTupleApply(sel) =>
87
+ val label = tpes.map(toLabel(_, pos)).mkString(" [" , " , " , " ]" )
88
+ decorations.add(
89
+ Decoration (
90
+ pos.adjust(text)._1.endPos.toLsp,
91
+ label,
92
+ DecorationKind .TypeParameter ,
93
+ )
94
+ )
95
+ case InferredType (tpe, pos, defTree) if params.inferredTypes() =>
96
+ val adjustedPos = pos.adjust(text)._1.endPos
97
+ if decorations.containsDef(adjustedPos.start) then decorations
98
+ else
99
+ decorations.add(
100
+ Decoration (
101
+ adjustedPos.toLsp,
102
+ " : " + toLabel(tpe, pos),
103
+ DecorationKind .InferredType ,
104
+ ),
105
+ adjustedPos.start,
106
+ )
107
+ case _ => decorations
108
+
109
+ private def toLabel (
110
+ tpe : Type ,
111
+ pos : SourcePosition ,
112
+ ): String =
113
+ val tpdPath =
114
+ Interactive .pathTo(unit.tpdTree, pos.span)
115
+
116
+ val indexedCtx = IndexedContext (Interactive .contextOfPath(tpdPath))
117
+ val printer = ShortenedTypePrinter (
118
+ symbolSearch
119
+ )(using indexedCtx)
120
+ def optDealias (tpe : Type ): Type =
121
+ def isInScope (tpe : Type ): Boolean =
122
+ tpe match
123
+ case tref : TypeRef =>
124
+ indexedCtx.lookupSym(
125
+ tref.currentSymbol
126
+ ) == IndexedContext .Result .InScope
127
+ case AppliedType (tycon, args) =>
128
+ isInScope(tycon) && args.forall(isInScope)
129
+ case _ => true
130
+ if isInScope(tpe)
131
+ then tpe
132
+ else tpe.metalsDealias(using indexedCtx.ctx)
133
+
134
+ val dealiased = optDealias(tpe)
135
+ printer.tpe(dealiased)
136
+ end toLabel
137
+
138
+ private val definitions = IndexedContext (ctx).ctx.definitions
139
+ private def syntheticTupleApply (tree : Tree ): Boolean =
140
+ tree match
141
+ case sel : Select =>
142
+ if definitions.isTupleNType(sel.symbol.info.finalResultType) then
143
+ sel match
144
+ case Select (tupleClass : Ident , _)
145
+ if ! tupleClass.span.isZeroExtent &&
146
+ tupleClass.span.exists &&
147
+ tupleClass.name.startsWith(" Tuple" ) =>
148
+ val pos = tupleClass.sourcePos
149
+ ! sourceText.slice(pos.start, pos.end).mkString.startsWith(" Tuple" )
150
+ case _ => true
151
+ else false
152
+ case _ => false
153
+ end PcSyntheticDecorationsProvider
154
+
155
+ object ImplicitConversion :
156
+ def unapply (tree : Tree )(using Context ) =
157
+ tree match
158
+ case Apply (fun : Ident , args) if isSynthetic(fun) =>
159
+ implicitConversion(fun, args)
160
+ case Apply (Select (fun, name), args)
161
+ if name == nme.apply && isSynthetic(fun) =>
162
+ implicitConversion(fun, args)
163
+ case _ => None
164
+ private def isSynthetic (tree : Tree )(using Context ) =
165
+ tree.span.isSynthetic && tree.symbol.isOneOf(Flags .GivenOrImplicit )
166
+
167
+ private def implicitConversion (fun : Tree , args : List [Tree ])(using Context ) =
168
+ val lastArgPos =
169
+ args.lastOption.map(_.sourcePos).getOrElse(fun.sourcePos)
170
+ Some (
171
+ fun.symbol.decodedName,
172
+ lastArgPos.withStart(fun.sourcePos.start),
173
+ )
174
+ end ImplicitConversion
175
+
176
+ object ImplicitParameters :
177
+ def unapply (tree : Tree )(using Context ) =
178
+ tree match
179
+ case Apply (fun, args)
180
+ if args.exists(isSyntheticArg) && ! tree.sourcePos.span.isZeroExtent =>
181
+ val (implicitArgs, providedArgs) = args.partition(isSyntheticArg)
182
+ val allImplicit = providedArgs.isEmpty
183
+ val pos = implicitArgs.head.sourcePos
184
+ Some (implicitArgs.map(_.symbol.decodedName), pos, allImplicit)
185
+ case Apply (ta @ TypeApply (fun, _), _)
186
+ if fun.span.isSynthetic && isValueOf(fun) =>
187
+ Some (
188
+ List (" new " + tpnme.valueOf.decoded.capitalize + " (...)" ),
189
+ fun.sourcePos,
190
+ true ,
191
+ )
192
+ case _ => None
193
+ private def isValueOf (tree : Tree )(using Context ) =
194
+ val symbol = tree.symbol.maybeOwner
195
+ symbol.name.decoded == tpnme.valueOf.decoded.capitalize
196
+ private def isSyntheticArg (tree : Tree )(using Context ) = tree match
197
+ case tree : Ident =>
198
+ tree.span.isSynthetic && tree.symbol.isOneOf(Flags .GivenOrImplicit )
199
+ case _ => false
200
+ end ImplicitParameters
201
+
202
+ object TypeParameters :
203
+ def unapply (tree : Tree )(using Context ) =
204
+ tree match
205
+ case TypeApply (sel : Select , _) if sel.isForComprehensionMethod => None
206
+ case TypeApply (fun, args) if inferredTypeArgs(args) =>
207
+ val pos = fun match
208
+ case sel : Select if sel.isInfix =>
209
+ sel.sourcePos.withEnd(sel.nameSpan.end)
210
+ case _ => fun.sourcePos
211
+ val tpes = args.map(_.tpe.stripTypeVar.widen.finalResultType)
212
+ Some ((tpes, pos.endPos, fun))
213
+ case _ => None
214
+ private def inferredTypeArgs (args : List [Tree ]): Boolean =
215
+ args.forall {
216
+ case tt : TypeTree if tt.span.exists && ! tt.span.isZeroExtent => true
217
+ case _ => false
218
+ }
219
+ end TypeParameters
220
+
221
+ object InferredType :
222
+ def unapply (tree : Tree )(using Context ) =
223
+ tree match
224
+ case vd @ ValDef (_, tpe, _)
225
+ if isValidSpan(tpe.span, vd.nameSpan) &&
226
+ ! vd.symbol.is(Flags .Enum ) =>
227
+ if vd.symbol == vd.symbol.sourceSymbol then
228
+ Some (tpe.tpe, tpe.sourcePos.withSpan(vd.nameSpan), vd)
229
+ else None
230
+ case vd @ DefDef (_, _, tpe, _)
231
+ if isValidSpan(tpe.span, vd.nameSpan) &&
232
+ tpe.span.start >= vd.nameSpan.end &&
233
+ ! vd.symbol.isConstructor &&
234
+ ! vd.symbol.is(Flags .Mutable ) =>
235
+ if vd.symbol == vd.symbol.sourceSymbol then
236
+ Some (tpe.tpe, tpe.sourcePos, vd)
237
+ else None
238
+ case bd @ Bind (
239
+ name,
240
+ Ident (nme.WILDCARD ),
241
+ ) =>
242
+ Some (bd.symbol.info, bd.namePos, bd)
243
+ case _ => None
244
+
245
+ private def isValidSpan (tpeSpan : Span , nameSpan : Span ): Boolean =
246
+ tpeSpan.isZeroExtent &&
247
+ nameSpan.exists &&
248
+ ! nameSpan.isZeroExtent
249
+
250
+ end InferredType
251
+
252
+ case class Synthetics (
253
+ decorations : List [Decoration ],
254
+ definitions : Set [Int ],
255
+ ):
256
+ def containsDef (offset : Int ) = definitions(offset)
257
+ def add (decoration : Decoration , offset : Int ) =
258
+ copy(
259
+ decorations = decoration :: decorations,
260
+ definitions = definitions + offset,
261
+ )
262
+ def add (decoration : Decoration ) =
263
+ copy(decorations = decoration :: decorations)
264
+
265
+ object Synthetics :
266
+ def empty : Synthetics = Synthetics (Nil , Set .empty)
0 commit comments