@@ -19,6 +19,9 @@ import collection.mutable
1919trait ImportSuggestions :
2020 this : Typer =>
2121
22+ /** The maximal number of suggested imports to make */
23+ inline val MaxSuggestions = 10
24+
2225 import tpd ._
2326
2427 /** Timeout to test a single implicit value as a suggestion, in ms */
@@ -249,6 +252,45 @@ trait ImportSuggestions:
249252 finally timer.cancel()
250253 end importSuggestions
251254
255+ /** The `ref` parts of this list of pairs, discarding subsequent elements that
256+ * have the same String part. Elements are sorted by their String parts.
257+ */
258+ def (refs : List [(TermRef , String )]).distinctRefs(using Context ): List [TermRef ] = refs match
259+ case (ref, str) :: refs1 =>
260+ ref :: refs1.dropWhile(_._2 == str).distinctRefs
261+ case Nil =>
262+ Nil
263+
264+ /** The best `n` references in `refs`, according to `compare`
265+ * `compare` is a partial order. If there's a tie, we take elements
266+ * in the order thy appear in the list.
267+ */
268+ def (refs : List [TermRef ]).best(n : Int )(using Context ): List [TermRef ] =
269+ val top = new Array [TermRef ](n)
270+ var filled = 0
271+ val rest = new mutable.ListBuffer [TermRef ]
272+ for ref <- refs do
273+ var i = 0
274+ var diff = 0
275+ while i < filled && diff == 0 do
276+ diff = compare(ref, top(i))(using ctx.retractMode(Mode .ImplicitsEnabled ))
277+ if diff > 0 then
278+ rest += top(i)
279+ top(i) = ref
280+ i += 1
281+ end while
282+ if diff == 0 && filled < n then
283+ top(filled) = ref
284+ filled += 1
285+ else if diff <= 0 then
286+ rest += ref
287+ end for
288+ val remaining =
289+ if filled < n && rest.nonEmpty then rest.toList.best(n - filled)
290+ else Nil
291+ top.take(filled).toList ++ remaining
292+ end best
293+
252294 /** An addendum to an error message where the error might be fixed
253295 * by some implicit value of type `pt` that is however not found.
254296 * The addendum suggests given imports that might fix the problem.
@@ -265,15 +307,11 @@ trait ImportSuggestions:
265307 s " import ${ctx.printer.toTextRef(ref).show}"
266308 val suggestions = suggestedRefs
267309 .zip(suggestedRefs.map(importString))
268- .filter((ref, str) => str.contains('.' ))
269- .sortWith { (x, y) =>
270- // sort by specificity first, alphabetically second
271- val ((ref1, str1), (ref2, str2)) = (x, y)
272- val diff = compare(ref1, ref2)
273- diff > 0 || diff == 0 && str1 < str2
274- }
275- .map((ref, str) => str)
276- .distinct // TermRefs might be different but generate the same strings
310+ .filter((ref, str) => str.contains('.' )) // must be a real import with `.`
311+ .sortBy(_._2) // sort first alphabetically for stability
312+ .distinctRefs // TermRefs might be different but generate the same strings
313+ .best(MaxSuggestions ) // take MaxSuggestions best references according to specificity
314+ .map(importString)
277315 if suggestions.isEmpty then " "
278316 else
279317 val fix =
0 commit comments