@@ -25,19 +25,46 @@ import ast.Trees._
2525import ast .untpd
2626import ast .tpd
2727import transform .SymUtils ._
28-
29- /** Messages
30- * ========
31- * The role of messages is to provide the necessary details for a simple to
32- * understand diagnostic event. Each message can be turned into a message
33- * container (one of the above) by calling the appropriate method on them.
34- * For instance:
35- *
36- * ```scala
37- * EmptyCatchBlock(tree).error(pos) // res: Error
38- * EmptyCatchBlock(tree).warning(pos) // res: Warning
39- * ```
28+ import dotty .tools .dotc .printing .Formatting .ShownDef .Show
29+ import dotty .tools .dotc .printing .Showable
30+
31+ /* Messages
32+ * ========
33+ * The role of messages is to provide the necessary details for a simple to
34+ * understand diagnostic event. Each message can be turned into a message
35+ * container (one of the above) by calling the appropriate method on them.
36+ * For instance:
37+ *
38+ * ```scala
39+ * EmptyCatchBlock(tree).error(pos) // res: Error
40+ * EmptyCatchBlock(tree).warning(pos) // res: Warning
41+ * ```
42+ */
43+
44+ /** A list of possible candidate options with their Levenstein distances
45+ * to the name of the missing member.
4046 */
47+ def closestNamed [T ](candidates : List [T ], missing : String , format : T => String , maxDist : Int = 3 ): List [(Int , T )] =
48+
49+ def levenshteinDistance (s1 : String , s2 : String ): Int =
50+ val dist = Array .ofDim[Int ](s2.length + 1 , s1.length + 1 )
51+ for
52+ j <- 0 to s2.length
53+ i <- 0 to s1.length
54+ do
55+ dist(j)(i) =
56+ if j == 0 then i
57+ else if i == 0 then j
58+ else if s2(j - 1 ) == s1(i - 1 ) then dist(j - 1 )(i - 1 )
59+ else (dist(j - 1 )(i) min dist(j)(i - 1 ) min dist(j - 1 )(i - 1 )) + 1
60+ dist(s2.length)(s1.length)
61+ end levenshteinDistance
62+
63+ candidates
64+ .map(candidate => (levenshteinDistance(format(candidate), missing), candidate))
65+ .filter((d, candidate) => d <= maxDist && d < missing.length && d < format(candidate).length)
66+ .sortBy((d, candidate) => (d, format(candidate))) // sort by distance first, alphabetically second
67+ end closestNamed
4168
4269 abstract class SyntaxMsg (errorId : ErrorMessageID ) extends Message (errorId):
4370 def kind = MessageKind .Syntax
@@ -318,27 +345,10 @@ import transform.SymUtils._
318345 && ! sym.flagsUNSAFE.isOneOf(Synthetic | Private ))
319346 yield sym
320347
321- // Calculate Levenshtein distance
322- def distance (s1 : String , s2 : String ): Int =
323- val dist = Array .ofDim[Int ](s2.length + 1 , s1.length + 1 )
324- for
325- j <- 0 to s2.length
326- i <- 0 to s1.length
327- do
328- dist(j)(i) =
329- if j == 0 then i
330- else if i == 0 then j
331- else if s2(j - 1 ) == s1(i - 1 ) then dist(j - 1 )(i - 1 )
332- else (dist(j - 1 )(i) min dist(j)(i - 1 ) min dist(j - 1 )(i - 1 )) + 1
333- dist(s2.length)(s1.length)
334-
335348 // A list of possible candidate symbols with their Levenstein distances
336349 // to the name of the missing member
337- def closest : List [(Int , Symbol )] = candidates
338- .toList
339- .map(sym => (distance(sym.name.show, missing), sym))
340- .filter((d, sym) => d <= maxDist && d < missing.length && d < sym.name.show.length)
341- .sortBy((d, sym) => (d, sym.name.show)) // sort by distance first, alphabetically second
350+ def closest : List [(Int , Symbol )] =
351+ closestNamed(candidates.toList, missing, format = _.name.show, maxDist)
342352
343353 val enumClause =
344354 if ((name eq nme.values) || (name eq nme.valueOf)) && site.classSymbol.companionClass.isEnumClass then
0 commit comments