@@ -2,22 +2,25 @@ package org.jetbrains.plugins.scala.lang.psi.impl.toplevel.synthetic
2
2
3
3
import com .intellij .navigation .ItemPresentation
4
4
import com .intellij .openapi .components .Service
5
- import com .intellij .openapi .project .ProjectManagerListener
5
+ import com .intellij .openapi .project .{Project , ProjectManagerListener }
6
+ import com .intellij .openapi .roots .ProjectRootManager
6
7
import com .intellij .openapi .startup .StartupActivity
7
8
import com .intellij .psi ._
8
9
import com .intellij .psi .impl .light .LightElement
9
10
import com .intellij .psi .search .GlobalSearchScope
10
11
import com .intellij .util .IncorrectOperationException
11
12
import com .intellij .util .containers .MultiMap
13
+ import org .jetbrains .annotations .TestOnly
14
+ import org .jetbrains .plugins .scala .caches .cachedInUserData
12
15
import org .jetbrains .plugins .scala .extensions ._
13
16
import org .jetbrains .plugins .scala .icons .Icons
14
17
import org .jetbrains .plugins .scala .lang .psi .adapters .PsiClassAdapter
15
18
import org .jetbrains .plugins .scala .lang .psi .api .statements .params .ScTypeParam
16
- import org .jetbrains .plugins .scala .lang .psi .api .statements .{ScFun , ScTypeAlias }
17
- import org .jetbrains .plugins .scala .lang .psi .api .toplevel .typedef .ScObject
19
+ import org .jetbrains .plugins .scala .lang .psi .api .statements .{ScFun , ScFunction , ScTypeAlias }
20
+ import org .jetbrains .plugins .scala .lang .psi .api .toplevel .typedef .{ ScObject , ScTemplateDefinition }
18
21
import org .jetbrains .plugins .scala .lang .psi .api .{ScalaFile , ScalaPsiElement }
19
- import org .jetbrains .plugins .scala .lang .psi .impl .ScalaPsiElementFactory
20
22
import org .jetbrains .plugins .scala .lang .psi .impl .toplevel .PsiClassFake
23
+ import org .jetbrains .plugins .scala .lang .psi .impl .{ScalaPsiElementFactory , ScalaPsiManager }
21
24
import org .jetbrains .plugins .scala .lang .psi .implicits .ImplicitProcessor
22
25
import org .jetbrains .plugins .scala .lang .psi .types ._
23
26
import org .jetbrains .plugins .scala .lang .psi .types .api ._
@@ -83,14 +86,16 @@ final class ScSyntheticTypeParameter(override val name: String, override val own
83
86
84
87
// we could try and implement all type system related stuff
85
88
// with class types, but it is simpler to indicate types corresponding to synthetic classes explicitly
86
- sealed class ScSyntheticClass (
89
+ final class ScSyntheticClass (
87
90
val className : String ,
88
91
val stdType : StdType
89
92
)(implicit projectContext : ProjectContext )
90
93
extends SyntheticNamedElement (className)
91
94
with PsiClassAdapter
92
95
with PsiClassFake {
93
96
97
+ override def getQualifiedName : String = " scala." + className
98
+
94
99
override def getPresentation : ItemPresentation = {
95
100
new ItemPresentation {
96
101
val This : ScSyntheticClass = ScSyntheticClass .this
@@ -102,13 +107,38 @@ sealed class ScSyntheticClass(
102
107
}
103
108
}
104
109
110
+ override def getNavigationElement : PsiElement = cachedInUserData(" ScSyntheticClass.getNavigationElement" , this , ProjectRootManager .getInstance(getProject)) {
111
+ val syntheticClassSourceMirror = for {
112
+ scalaPackagePsiDirectory <- findScalaPackageSourcesPsiDirectory(this .stdType.projectContext.project)
113
+ // class Any -> Any.scala
114
+ psiFile <- Option (scalaPackagePsiDirectory.findFile(s " $className.scala " )).map(_.asInstanceOf [ScalaFile ])
115
+ classDef <- psiFile.typeDefinitions.headOption // expecting single class definition in the file
116
+ } yield classDef
117
+ syntheticClassSourceMirror.getOrElse(super .getNavigationElement)
118
+ }
119
+
120
+ // TODO: current implementation might not work in a project with multiple scala versions. It depends on SCL-22349.
121
+ private def findScalaPackageSourcesPsiDirectory (project : Project ): Option [PsiDirectory ] = cachedInUserData(" ScSyntheticClass.findScalaPackageSourcesPsiDirectory" , project, ProjectRootManager .getInstance(project)) {
122
+ // Get some representative class from Scala standard library
123
+ val classFromStdLib = ScalaPsiManager .instance(this .stdType.projectContext).getCachedClass(GlobalSearchScope .allScope(this .stdType.projectContext.project), " scala.Array" )
124
+ classFromStdLib.map { clazz =>
125
+ // .../scala-library-2.13.11-sources.jar!/scala/Array.scala
126
+ val navigationFile = clazz.getContainingFile.getNavigationElement.asInstanceOf [ScalaFile ]
127
+ // .../scala-library-2.13.11-sources.jar!/scala
128
+ navigationFile.getParent;
129
+ }
130
+ }
131
+
105
132
override def getNameIdentifier : PsiIdentifier = null
106
133
107
134
override def toString = " Synthetic class"
108
135
109
136
val syntheticMethods = new MultiMap [String , ScSyntheticFunction ]()
110
137
111
- def addMethod (method : ScSyntheticFunction ): Unit = syntheticMethods.putValue(method.name, method)
138
+ def addMethod (method : ScSyntheticFunction ): Unit = {
139
+ syntheticMethods.putValue(method.name, method)
140
+ method.setContainingSyntheticClass(this )
141
+ }
112
142
113
143
override def processDeclarations (
114
144
processor : com.intellij.psi.scope.PsiScopeProcessor ,
@@ -152,6 +182,14 @@ sealed class ScSyntheticFunction(
152
182
typeParameterNames : Seq [String ]
153
183
)(implicit projectContext : ProjectContext )
154
184
extends SyntheticNamedElement (name) with ScFun {
185
+
186
+ private var containingSyntheticClass : Option [ScSyntheticClass ] = None
187
+
188
+ def setContainingSyntheticClass (value : ScSyntheticClass ): Unit = {
189
+ assert(containingSyntheticClass.isEmpty, s " Containing synthetic class was already assigned to method $name" )
190
+ containingSyntheticClass = Some (value)
191
+ }
192
+
155
193
def isStringPlusMethod : Boolean = {
156
194
if (name != " +" ) return false
157
195
retType.extractClass match {
@@ -186,6 +224,18 @@ sealed class ScSyntheticFunction(
186
224
}
187
225
null
188
226
}
227
+
228
+ override def getNavigationElement : PsiElement = cachedInUserData(" ScSyntheticFunction.getNavigationElement" , this , ProjectRootManager .getInstance(retType.projectContext)) {
229
+ val syntheticFunctionSourceMirror = containingSyntheticClass.flatMap(_.getNavigationElement match {
230
+ case classInSources : ScTemplateDefinition =>
231
+ // NOTE: we search for the function with the same name ignoring overloaded functions
232
+ // in principle this is not entirely correct, but for the synthetic classes in Scala library
233
+ // it should work fine because it's known that there are no overloaded methods in those classes
234
+ classInSources.members.filterByType[ScFunction ].find(_.name == name)
235
+ case _ => None
236
+ })
237
+ syntheticFunctionSourceMirror.getOrElse(super .getNavigationElement)
238
+ }
189
239
}
190
240
191
241
final class ScSyntheticValue (val name : String , val tp : ScType )
@@ -196,8 +246,6 @@ final class ScSyntheticValue(val name: String, val tp: ScType)
196
246
override def toString = " Synthetic value"
197
247
}
198
248
199
- import com .intellij .openapi .project .Project
200
-
201
249
@ Service (Array (Service .Level .PROJECT ))
202
250
final class SyntheticClasses (project : Project ) {
203
251
implicit def ctx : ProjectContext = project
@@ -327,6 +375,7 @@ final class SyntheticClasses(project: Project) {
327
375
}
328
376
329
377
// register synthetic objects
378
+ // TODO: drop it (see https://youtrack.jetbrains.com/issue/SCL-20932)
330
379
def registerObject (debugName : String , fileText : String ): Unit = {
331
380
val dummyFile = createDummyFile(debugName, fileText)
332
381
val obj = dummyFile.typeDefinitions.head.asInstanceOf [ScObject ]
@@ -344,7 +393,7 @@ final class SyntheticClasses(project: Project) {
344
393
val contextParameters = (1 to n).map(i => s " x $i: T $i" ).mkString(" , " )
345
394
346
395
registerContextFunctionClass(" ContextFunction" ,
347
- s """
396
+ s """
348
397
|package scala
349
398
|
350
399
|trait ContextFunction $n[ $typeParameters, +R] {
@@ -355,7 +404,7 @@ final class SyntheticClasses(project: Project) {
355
404
}
356
405
357
406
registerObject(" Boolean" ,
358
- """
407
+ """
359
408
package scala
360
409
361
410
object Boolean {
@@ -367,7 +416,7 @@ object Boolean {
367
416
)
368
417
369
418
registerObject(" Byte" ,
370
- """
419
+ """
371
420
package scala
372
421
373
422
object Byte {
@@ -383,7 +432,7 @@ object Byte {
383
432
)
384
433
385
434
registerObject(" Char" ,
386
- """
435
+ """
387
436
package scala
388
437
389
438
object Char {
@@ -399,7 +448,7 @@ object Char {
399
448
)
400
449
401
450
registerObject(" Double" ,
402
- """
451
+ """
403
452
package scala
404
453
405
454
object Double {
@@ -429,7 +478,7 @@ object Double {
429
478
)
430
479
431
480
registerObject(" Float" ,
432
- """
481
+ """
433
482
package scala
434
483
435
484
object Float {
@@ -459,7 +508,7 @@ object Float {
459
508
)
460
509
461
510
registerObject(" Int" ,
462
- """
511
+ """
463
512
package scala
464
513
465
514
object Int {
@@ -475,7 +524,7 @@ object Int {
475
524
)
476
525
477
526
registerObject(" Long" ,
478
- """
527
+ """
479
528
package scala
480
529
481
530
object Long {
@@ -491,7 +540,7 @@ object Long {
491
540
)
492
541
493
542
registerObject(" Short" ,
494
- """
543
+ """
495
544
package scala
496
545
497
546
object Short {
@@ -507,7 +556,7 @@ object Short {
507
556
)
508
557
509
558
registerObject(" Unit" ,
510
- """
559
+ """
511
560
package scala
512
561
513
562
object Unit
@@ -556,9 +605,7 @@ object Unit
556
605
}
557
606
558
607
def registerClass (t : StdType , name : String , isScala3 : Boolean = false ): ScSyntheticClass = {
559
- val cls = new ScSyntheticClass (name, t) {
560
- override def getQualifiedName : String = " scala." + name
561
- }
608
+ val cls = new ScSyntheticClass (name, t)
562
609
563
610
if (isScala3)
564
611
scala3Classes += ((name, cls))
@@ -572,6 +619,8 @@ object Unit
572
619
def registerNumericClass (clazz : ScSyntheticClass ): ScSyntheticClass = {numeric += clazz; clazz}
573
620
574
621
def all : Iterable [PsiClass ] = sharedClasses.values ++ scala3Classes.values
622
+ @ TestOnly
623
+ def getScala3Classes : Iterable [PsiClass ] = scala3Classes.values
575
624
576
625
def sharedClassesOnly : Iterable [PsiClass ] = sharedClasses.values
577
626
0 commit comments