@@ -21,6 +21,7 @@ import dotty.tools.dotc.core.TypeComparer
21
21
import dotty .tools .dotc .core .TypeError
22
22
import dotty .tools .dotc .core .Types .{ExprType , MethodOrPoly , NameFilter , NamedType , NoType , PolyType , TermRef , Type }
23
23
import dotty .tools .dotc .printing .Texts ._
24
+ import dotty .tools .dotc .util .Chars .{isOperatorPart , isScalaLetter }
24
25
import dotty .tools .dotc .util .{NameTransformer , NoSourcePosition , SourcePosition }
25
26
26
27
import scala .collection .mutable
@@ -59,7 +60,7 @@ object Completion {
59
60
*
60
61
* Otherwise, provide no completion suggestion.
61
62
*/
62
- private def completionMode (path : List [Tree ], pos : SourcePosition ): Mode =
63
+ def completionMode (path : List [Tree ], pos : SourcePosition ): Mode =
63
64
path match {
64
65
case (ref : RefTree ) :: _ =>
65
66
if (ref.name.isTermName) Mode .Term
@@ -81,7 +82,7 @@ object Completion {
81
82
* Inspect `path` to determine the completion prefix. Only symbols whose name start with the
82
83
* returned prefix should be considered.
83
84
*/
84
- private def completionPrefix (path : List [untpd.Tree ], pos : SourcePosition ): String =
85
+ def completionPrefix (path : List [untpd.Tree ], pos : SourcePosition ): String =
85
86
path match {
86
87
case (sel : untpd.ImportSelector ) :: _ =>
87
88
completionPrefix(sel.imported :: Nil , pos)
@@ -100,7 +101,7 @@ object Completion {
100
101
}
101
102
102
103
/** Inspect `path` to determine the offset where the completion result should be inserted. */
103
- private def completionOffset (path : List [Tree ]): Int =
104
+ def completionOffset (path : List [Tree ]): Int =
104
105
path match {
105
106
case (ref : RefTree ) :: _ => ref.span.point
106
107
case _ => 0
@@ -134,14 +135,14 @@ object Completion {
134
135
* If several denotations share the same name, the type denotations appear before term denotations inside
135
136
* the same `Completion`.
136
137
*/
137
- private def describeCompletions (completions : CompletionMap )(using Context ): List [Completion ] = {
138
+ def describeCompletions (completions : CompletionMap )(using Context ): List [Completion ] = {
138
139
completions
139
140
.toList.groupBy(_._1.toTermName) // don't distinguish between names of terms and types
140
141
.toList.map { (name, namedDenots) =>
141
142
val denots = namedDenots.flatMap(_._2)
142
143
val typesFirst = denots.sortWith((d1, d2) => d1.isType && ! d2.isType)
143
144
val desc = description(typesFirst)
144
- Completion (name.show , desc, typesFirst.map(_.symbol))
145
+ Completion (label( name) , desc, typesFirst.map(_.symbol))
145
146
}
146
147
}
147
148
@@ -174,7 +175,7 @@ object Completion {
174
175
* For the results of all `xyzCompletions` methods term names and type names are always treated as different keys in the same map
175
176
* and they never conflict with each other.
176
177
*/
177
- private class Completer (val mode : Mode , val prefix : String , pos : SourcePosition ) {
178
+ class Completer (val mode : Mode , val prefix : String , pos : SourcePosition ) {
178
179
/** Completions for terms and types that are currently in scope:
179
180
* the members of the current class, local definitions and the symbols that have been imported,
180
181
* recursively adding completions from outer scopes.
@@ -442,11 +443,11 @@ object Completion {
442
443
* The completion mode: defines what kinds of symbols should be included in the completion
443
444
* results.
444
445
*/
445
- private class Mode (val bits : Int ) extends AnyVal {
446
+ class Mode (val bits : Int ) extends AnyVal {
446
447
def is (other : Mode ): Boolean = (bits & other.bits) == other.bits
447
448
def | (other : Mode ): Mode = new Mode (bits | other.bits)
448
449
}
449
- private object Mode {
450
+ object Mode {
450
451
/** No symbol should be included */
451
452
val None : Mode = new Mode (0 )
452
453
@@ -459,5 +460,39 @@ object Completion {
459
460
/** Both term and type symbols are allowed */
460
461
val Import : Mode = new Mode (4 ) | Term | Type
461
462
}
463
+
464
+ private val bslash = '\\ '
465
+ private val isDot = (x : Char ) => x == '.'
466
+ private val brackets = List ('[' ,']' ,'(' ,')' ,'{' ,'}' )
467
+
468
+ def label (name : Name ): String = {
469
+
470
+ def maybeQuote (name : Name , recurse : Boolean ): String =
471
+ if (recurse && name.isTermName)
472
+ name.asTermName.qualToString(maybeQuote(_, true ), maybeQuote(_, false ))
473
+ // initially adapted from
474
+ // https://github.com/scala/scala/blob/decbd53f1bde4600c8ff860f30a79f028a8e431d/
475
+ // src/reflect/scala/reflect/internal/Printers.scala#L573-L584
476
+ else if (name == nme.CONSTRUCTOR ) " this"
477
+ else {
478
+ val decName = name.decode.toString
479
+ val hasSpecialChar = decName.exists { ch =>
480
+ brackets.contains(ch) || ch.isWhitespace || isDot(ch)
481
+ }
482
+ def isOperatorLike = (name.isOperatorName || decName.exists(isOperatorPart)) &&
483
+ decName.exists(isScalaLetter) &&
484
+ ! decName.contains(bslash)
485
+ lazy val term = name.toTermName
486
+
487
+ val needsBackTicks = hasSpecialChar ||
488
+ isOperatorLike ||
489
+ nme.keywords(term) && term != nme.USCOREkw
490
+
491
+ if (needsBackTicks) s " ` $decName` "
492
+ else decName
493
+ }
494
+
495
+ maybeQuote(name, true )
496
+ }
462
497
}
463
498
0 commit comments