@@ -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,46 @@ 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+ val noImplicitsCtx = ctx.retractMode(Mode .ImplicitsEnabled )
273+ for ref <- refs do
274+ var i = 0
275+ var diff = 0
276+ while i < filled && diff == 0 do
277+ diff = compare(ref, top(i))(using noImplicitsCtx)
278+ if diff > 0 then
279+ rest += top(i)
280+ top(i) = ref
281+ i += 1
282+ end while
283+ if diff == 0 && filled < n then
284+ top(filled) = ref
285+ filled += 1
286+ else if diff <= 0 then
287+ rest += ref
288+ end for
289+ val remaining =
290+ if filled < n && rest.nonEmpty then rest.toList.best(n - filled)
291+ else Nil
292+ top.take(filled).toList ++ remaining
293+ end best
294+
252295 /** An addendum to an error message where the error might be fixed
253296 * by some implicit value of type `pt` that is however not found.
254297 * The addendum suggests given imports that might fix the problem.
@@ -265,15 +308,11 @@ trait ImportSuggestions:
265308 s " import ${ctx.printer.toTextRef(ref).show}"
266309 val suggestions = suggestedRefs
267310 .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
311+ .filter((ref, str) => str.contains('.' )) // must be a real import with `.`
312+ .sortBy(_._2) // sort first alphabetically for stability
313+ .distinctRefs // TermRefs might be different but generate the same strings
314+ .best(MaxSuggestions ) // take MaxSuggestions best references according to specificity
315+ .map(importString)
277316 if suggestions.isEmpty then " "
278317 else
279318 val fix =
0 commit comments