Skip to content

Commit fba6b35

Browse files
Perl99WojciechMazur
authored andcommitted
Improve symbol order in completions provided by the presentation compiler (#23888)
Extension methods that are not in the same file are placed after all Product methods and even after extension methods like "ensuring". This PR penalizes the following methods, so that they are no longer at the top of the suggestions: - scala.Product.* - scala.Equals.* - scala.Predef.ArrowAssoc.* - scala.Predef.Ensuring.* - scala.Predef.StringFormat.* - scala.Predef.nn - scala.Predef.runtimeChecked Resolves scalameta/metals#7642 [Cherry-picked 7d27633]
1 parent 7015a77 commit fba6b35

File tree

3 files changed

+71
-11
lines changed

3 files changed

+71
-11
lines changed

presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import dotty.tools.dotc.ast.untpd
1515
import dotty.tools.dotc.core.Comments.Comment
1616
import dotty.tools.dotc.core.Constants.Constant
1717
import dotty.tools.dotc.core.Contexts.*
18+
import dotty.tools.dotc.core.Decorators.toTermName
1819
import dotty.tools.dotc.core.Denotations.SingleDenotation
1920
import dotty.tools.dotc.core.Flags
2021
import dotty.tools.dotc.core.Flags.*
@@ -765,6 +766,13 @@ class Completions(
765766
).flatMap(_.alternatives.map(_.symbol)).toSet
766767
)
767768

769+
private lazy val EqualsClass: ClassSymbol = requiredClass("scala.Equals")
770+
private lazy val ArrowAssocClass: ClassSymbol = requiredClass("scala.Predef.ArrowAssoc")
771+
private lazy val EnsuringClass: ClassSymbol = requiredClass("scala.Predef.Ensuring")
772+
private lazy val StringFormatClass: ClassSymbol = requiredClass("scala.Predef.StringFormat")
773+
private lazy val nnMethod: Symbol = defn.ScalaPredefModule.info.member("nn".toTermName).symbol
774+
private lazy val runtimeCheckedMethod: Symbol = defn.ScalaPredefModule.info.member("runtimeChecked".toTermName).symbol
775+
768776
private def isNotLocalForwardReference(sym: Symbol)(using Context): Boolean =
769777
!sym.isLocalToBlock ||
770778
!sym.srcPos.isAfter(completionPos.originalCursorPosition) ||
@@ -783,6 +791,17 @@ class Completions(
783791
(sym.isField && !isJavaClass && !isModuleOrClass) || sym.getter != NoSymbol
784792
catch case _ => false
785793

794+
def isInheritedFromScalaLibrary(sym: Symbol) =
795+
sym.owner == defn.AnyClass ||
796+
sym.owner == defn.ObjectClass ||
797+
sym.owner == defn.ProductClass ||
798+
sym.owner == EqualsClass ||
799+
sym.owner == ArrowAssocClass ||
800+
sym.owner == EnsuringClass ||
801+
sym.owner == StringFormatClass ||
802+
sym == nnMethod ||
803+
sym == runtimeCheckedMethod
804+
786805
def symbolRelevance(sym: Symbol): Int =
787806
var relevance = 0
788807
// symbols defined in this file are more relevant
@@ -800,7 +819,7 @@ class Completions(
800819
case _ =>
801820

802821
// symbols whose owner is a base class are less relevant
803-
if sym.owner == defn.AnyClass || sym.owner == defn.ObjectClass
822+
if isInheritedFromScalaLibrary(sym)
804823
then relevance |= IsInheritedBaseMethod
805824
// symbols not provided via an implicit are more relevant
806825
if sym.is(Implicit) ||
@@ -812,7 +831,7 @@ class Completions(
812831
// accessors of case class members are more relevant
813832
if !sym.is(CaseAccessor) then relevance |= IsNotCaseAccessor
814833
// public symbols are more relevant
815-
if !sym.isPublic then relevance |= IsNotCaseAccessor
834+
if !sym.isPublic then relevance |= IsNotPublic
816835
// synthetic symbols are less relevant (e.g. `copy` on case classes)
817836
if sym.is(Synthetic) && !sym.isAllOf(EnumCase) then
818837
relevance |= IsSynthetic

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,44 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
437437
|""".stripMargin,
438438
assertSingleItem = false
439439
)
440+
441+
@Test def `extension-for-case-class` =
442+
check(
443+
"""|case class Bar():
444+
| def baz(): Unit = ???
445+
|
446+
|object Bar:
447+
| extension (f: Bar)
448+
| def qux: Unit = ???
449+
|
450+
|object Main:
451+
| val _ = Bar().@@
452+
|""".stripMargin,
453+
"""|baz(): Unit
454+
|copy(): Bar
455+
|qux: Unit
456+
|asInstanceOf[X0]: X0
457+
|canEqual(that: Any): Boolean
458+
|equals(x$0: Any): Boolean
459+
|getClass[X0 >: Bar](): Class[? <: X0]
460+
|hashCode(): Int
461+
|isInstanceOf[X0]: Boolean
462+
|productArity: Int
463+
|productElement(n: Int): Any
464+
|productElementName(n: Int): String
465+
|productElementNames: Iterator[String]
466+
|productIterator: Iterator[Any]
467+
|productPrefix: String
468+
|synchronized[X0](x$0: X0): X0
469+
|toString(): String
470+
|->[B](y: B): (Bar, B)
471+
|ensuring(cond: Boolean): Bar
472+
|ensuring(cond: Bar => Boolean): Bar
473+
|ensuring(cond: Boolean, msg: => Any): Bar
474+
|ensuring(cond: Bar => Boolean, msg: => Any): Bar
475+
|nn: `?1`.type
476+
|runtimeChecked: `?2`.type
477+
|formatted(fmtstr: String): String
478+
|→[B](y: B): (Bar, B)
479+
| """.stripMargin
480+
)

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,9 @@ class CompletionSuite extends BaseCompletionSuite:
109109
|tabulate[A](n: Int)(f: Int => A): List[A]
110110
|unapplySeq[A](x: List[A] @uncheckedVariance): UnapplySeqWrapper[A]
111111
|unfold[A, S](init: S)(f: S => Option[(A, S)]): List[A]
112-
|->[B](y: B): (List.type, B)
113-
|ensuring(cond: Boolean): List.type
114-
|ensuring(cond: List.type => Boolean): List.type
115-
|ensuring(cond: Boolean, msg: => Any): List.type
116-
|ensuring(cond: List.type => Boolean, msg: => Any): List.type
117112
|fromSpecific(from: Any)(it: IterableOnce[Nothing]): List[Nothing]
118113
|fromSpecific(it: IterableOnce[Nothing]): List[Nothing]
119-
|nn: List.type
120-
|runtimeChecked scala.collection.immutable
121114
|toFactory(from: Any): Factory[Nothing, List[Nothing]]
122-
|formatted(fmtstr: String): String
123-
|→[B](y: B): (List.type, B)
124115
|iterableFactory[A]: Factory[A, List[A]]
125116
|asInstanceOf[X0]: X0
126117
|equals(x$0: Any): Boolean
@@ -129,6 +120,15 @@ class CompletionSuite extends BaseCompletionSuite:
129120
|isInstanceOf[X0]: Boolean
130121
|synchronized[X0](x$0: X0): X0
131122
|toString(): String
123+
|->[B](y: B): (List.type, B)
124+
|ensuring(cond: Boolean): List.type
125+
|ensuring(cond: List.type => Boolean): List.type
126+
|ensuring(cond: Boolean, msg: => Any): List.type
127+
|ensuring(cond: List.type => Boolean, msg: => Any): List.type
128+
|nn: List.type
129+
|runtimeChecked scala.collection.immutable
130+
|formatted(fmtstr: String): String
131+
|→[B](y: B): (List.type, B)
132132
|""".stripMargin
133133
)
134134

0 commit comments

Comments
 (0)