@@ -38,7 +38,6 @@ import Data.Ord (comparing)
3838import qualified Data.Set as S
3939import qualified Data.Text as T
4040import qualified Data.Text.Utf16.Rope as Rope
41- import Data.Tuple.Extra (fst3 )
4241import Development.IDE.Core.Rules
4342import Development.IDE.Core.RuleTypes
4443import Development.IDE.Core.Service
@@ -89,6 +88,7 @@ import Language.LSP.VFS (VirtualFile,
8988 _file_text )
9089import Text.Regex.TDFA (mrAfter ,
9190 (=~) , (=~~) )
91+ import qualified Text.Fuzzy.Parallel as TFP
9292#if MIN_VERSION_ghc(9,2,0)
9393import GHC (AddEpAnn (AddEpAnn ),
9494 Anchor (anchor_op ),
@@ -99,7 +99,6 @@ import GHC (AddEpAnn (Ad
9999 EpaLocation (.. ),
100100 LEpaComment ,
101101 LocatedA )
102-
103102#else
104103import Language.Haskell.GHC.ExactPrint.Types (Annotation (annsDP ),
105104 DeltaPos ,
@@ -1495,35 +1494,63 @@ suggestNewImport packageExportsMap ps fileContents Diagnostic{_message}
14951494 , Just (range, indent) <- newImportInsertRange ps fileContents
14961495 , extendImportSuggestions <- matchRegexUnifySpaces msg
14971496 " Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’"
1498- = sortOn fst3 [(imp, kind, TextEdit range (imp <> " \n " <> T. replicate indent " " ))
1499- | (kind, unNewImport -> imp) <- constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions
1500- ]
1497+ = let suggestions = nubSort
1498+ [ s | s <- constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions ] in
1499+ map ( \ ( ImportSuggestion (_, kind, unNewImport -> imp)) -> (imp, kind, TextEdit range (imp <> " \n " <> T. replicate indent " " ))) suggestions
15011500 where
15021501 L _ HsModule {.. } = astA ps
15031502suggestNewImport _ _ _ _ = []
15041503
15051504constructNewImportSuggestions
1506- :: ExportsMap -> (Maybe T. Text , NotInScope ) -> Maybe [T. Text ] -> [( CodeActionKind , NewImport ) ]
1507- constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrdOn snd
1505+ :: ExportsMap -> (Maybe T. Text , NotInScope ) -> Maybe [T. Text ] -> [ImportSuggestion ]
1506+ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrd
15081507 [ suggestion
1509- | Just name <- [T. stripPrefix (maybe " " (<> " ." ) qual) $ notInScope thingMissing]
1510- , identInfo <- maybe [] Set. toList $ Map. lookup name (getExportsMap exportsMap)
1511- , canUseIdent thingMissing identInfo
1512- , moduleNameText identInfo `notElem` fromMaybe [] notTheseModules
1513- , suggestion <- renderNewImport identInfo
1508+ | Just name <- [T. stripPrefix (maybe " " (<> " ." ) qual) $ notInScope thingMissing] -- strip away qualified module names from the unknown name
1509+ , identInfo <- maybe [] Set. toList $ Map. lookup name (getExportsMap exportsMap) -- look up the modified unknown name in the export map
1510+ , canUseIdent thingMissing identInfo -- check if the identifier information retrieved can be used
1511+ , moduleNameText identInfo `notElem` fromMaybe [] notTheseModules -- check if the module of the identifier is allowed
1512+ , suggestion <- renderNewImport identInfo -- creates a list of import suggestions for the retrieved identifier information
15141513 ]
15151514 where
1516- renderNewImport :: IdentInfo -> [( CodeActionKind , NewImport ) ]
1515+ renderNewImport :: IdentInfo -> [ImportSuggestion ]
15171516 renderNewImport identInfo
15181517 | Just q <- qual
1519- = [( quickFixImportKind " new.qualified" , newQualImport m q)]
1518+ = [ImportSuggestion (importanceScore, quickFixImportKind " new.qualified" , newQualImport m q)]
15201519 | otherwise
1521- = [( quickFixImportKind' " new" importStyle, newUnqualImport m (renderImportStyle importStyle) False )
1520+ = [ImportSuggestion (importanceScore, quickFixImportKind' " new" importStyle, newUnqualImport m (renderImportStyle importStyle) False )
15221521 | importStyle <- NE. toList $ importStyles identInfo] ++
1523- [( quickFixImportKind " new.all" , newImportAll m)]
1522+ [ImportSuggestion (importanceScore, quickFixImportKind " new.all" , newImportAll m)]
15241523 where
1524+ -- The importance score takes 2 metrics into account. The first being the similarity using
1525+ -- the Text.Fuzzy.Parallel.match function. The second is a factor of the relation between
1526+ -- the modules prefix import suggestion and the unknown identifier names.
1527+ importanceScore
1528+ | Just q <- qual
1529+ = let
1530+ similarityScore = fromIntegral $ unpackMatchScore (TFP. match (T. toLower q) (T. toLower m)) :: Double
1531+ maxLength = fromIntegral (max (T. length q) (T. length m)) :: Double
1532+ minLength = fromIntegral (min (T. length q) (T. length m)) :: Double
1533+ lengthPenaltyFactor = 100 * minLength / maxLength
1534+ in max 0 (floor (similarityScore * lengthPenaltyFactor))
1535+ | otherwise
1536+ = 0
1537+ where
1538+ unpackMatchScore pScore
1539+ | Just score <- pScore = score
1540+ | otherwise = 0
15251541 m = moduleNameText identInfo
15261542
1543+ -- | Implements a lexicographic order for import suggestions.
1544+ -- First compares the importance score in DESCENDING order.
1545+ -- If the scores are equal it compares the import names alphabetical order.
1546+ data ImportSuggestion = ImportSuggestion (Int , CodeActionKind , NewImport )
1547+ deriving ( Eq )
1548+
1549+ instance Ord ImportSuggestion where
1550+ compare (ImportSuggestion (s1, _, i1)) (ImportSuggestion (s2, _, i2))
1551+ | s1 == s2 = compare i1 i2
1552+ | otherwise = flip compare s1 s2
1553+
15271554newtype NewImport = NewImport { unNewImport :: T. Text}
15281555 deriving (Show , Eq , Ord )
15291556
0 commit comments