@@ -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
@@ -87,6 +86,7 @@ import Language.LSP.Types (ApplyWorkspa
8786 uriToFilePath )
8887import Language.LSP.VFS (VirtualFile ,
8988 _file_text )
89+ import qualified Text.Fuzzy.Parallel as TFP
9090import Text.Regex.TDFA (mrAfter ,
9191 (=~) , (=~~) )
9292#if MIN_VERSION_ghc(9,2,0)
@@ -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,65 @@ 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+ ( 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, minLength) = case (T. length q, T. length m) of
1532+ (la, lb)
1533+ | la >= lb -> (fromIntegral la, fromIntegral lb)
1534+ | otherwise -> (fromIntegral lb, fromIntegral la)
1535+ lengthPenaltyFactor = 100 * minLength / maxLength
1536+ in max 0 (floor (similarityScore * lengthPenaltyFactor))
1537+ | otherwise
1538+ = 0
1539+ where
1540+ unpackMatchScore pScore
1541+ | Just score <- pScore = score
1542+ | otherwise = 0
15251543 m = moduleNameText identInfo
15261544
1545+ -- | Implements a lexicographic order for import suggestions.
1546+ -- First compares the importance score in DESCENDING order.
1547+ -- If the scores are equal it compares the import names alphabetical order.
1548+ data ImportSuggestion = ImportSuggestion ! Int ! CodeActionKind ! NewImport
1549+ deriving ( Eq )
1550+
1551+ instance Ord ImportSuggestion where
1552+ compare (ImportSuggestion s1 _ i1) (ImportSuggestion s2 _ i2)
1553+ | s1 == s2 = compare i1 i2
1554+ | otherwise = flip compare s1 s2
1555+
15271556newtype NewImport = NewImport { unNewImport :: T. Text}
15281557 deriving (Show , Eq , Ord )
15291558
0 commit comments