@@ -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 ,
@@ -1521,35 +1520,65 @@ suggestNewImport packageExportsMap ps fileContents Diagnostic{_message}
15211520 , Just (range, indent) <- newImportInsertRange ps fileContents
15221521 , extendImportSuggestions <- matchRegexUnifySpaces msg
15231522 " Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’"
1524- = sortOn fst3 [(imp, kind, TextEdit range (imp <> " \n " <> T. replicate indent " " ))
1525- | (kind, unNewImport -> imp) <- constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions
1526- ]
1523+ = let suggestions = nubSort
1524+ ( constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions) in
1525+ map ( \ ( ImportSuggestion _ kind (unNewImport -> imp)) -> (imp, kind, TextEdit range (imp <> " \n " <> T. replicate indent " " ))) suggestions
15271526 where
15281527 L _ HsModule {.. } = astA ps
15291528suggestNewImport _ _ _ _ = []
15301529
15311530constructNewImportSuggestions
1532- :: ExportsMap -> (Maybe T. Text , NotInScope ) -> Maybe [T. Text ] -> [( CodeActionKind , NewImport ) ]
1533- constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrdOn snd
1531+ :: ExportsMap -> (Maybe T. Text , NotInScope ) -> Maybe [T. Text ] -> [ImportSuggestion ]
1532+ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrd
15341533 [ suggestion
1535- | Just name <- [T. stripPrefix (maybe " " (<> " ." ) qual) $ notInScope thingMissing]
1536- , identInfo <- maybe [] Set. toList $ Map. lookup name (getExportsMap exportsMap)
1537- , canUseIdent thingMissing identInfo
1538- , moduleNameText identInfo `notElem` fromMaybe [] notTheseModules
1539- , suggestion <- renderNewImport identInfo
1534+ | Just name <- [T. stripPrefix (maybe " " (<> " ." ) qual) $ notInScope thingMissing] -- strip away qualified module names from the unknown name
1535+ , identInfo <- maybe [] Set. toList $ Map. lookup name (getExportsMap exportsMap) -- look up the modified unknown name in the export map
1536+ , canUseIdent thingMissing identInfo -- check if the identifier information retrieved can be used
1537+ , moduleNameText identInfo `notElem` fromMaybe [] notTheseModules -- check if the module of the identifier is allowed
1538+ , suggestion <- renderNewImport identInfo -- creates a list of import suggestions for the retrieved identifier information
15401539 ]
15411540 where
1542- renderNewImport :: IdentInfo -> [( CodeActionKind , NewImport ) ]
1541+ renderNewImport :: IdentInfo -> [ImportSuggestion ]
15431542 renderNewImport identInfo
15441543 | Just q <- qual
1545- = [(quickFixImportKind " new.qualified" , newQualImport m q)]
1544+ = [ImportSuggestion importanceScore (quickFixImportKind " new.qualified" ) ( newQualImport m q)]
15461545 | otherwise
1547- = [(quickFixImportKind' " new" importStyle, newUnqualImport m (renderImportStyle importStyle) False )
1546+ = [ImportSuggestion importanceScore (quickFixImportKind' " new" importStyle) ( newUnqualImport m (renderImportStyle importStyle) False )
15481547 | importStyle <- NE. toList $ importStyles identInfo] ++
1549- [(quickFixImportKind " new.all" , newImportAll m)]
1548+ [ImportSuggestion importanceScore (quickFixImportKind " new.all" ) ( newImportAll m)]
15501549 where
1550+ -- The importance score takes 2 metrics into account. The first being the similarity using
1551+ -- the Text.Fuzzy.Parallel.match function. The second is a factor of the relation between
1552+ -- the modules prefix import suggestion and the unknown identifier names.
1553+ importanceScore
1554+ | Just q <- qual
1555+ = let
1556+ similarityScore = fromIntegral $ unpackMatchScore (TFP. match (T. toLower q) (T. toLower m)) :: Double
1557+ (maxLength, minLength) = case (T. length q, T. length m) of
1558+ (la, lb)
1559+ | la >= lb -> (fromIntegral la, fromIntegral lb)
1560+ | otherwise -> (fromIntegral lb, fromIntegral la)
1561+ lengthPenaltyFactor = 100 * minLength / maxLength
1562+ in max 0 (floor (similarityScore * lengthPenaltyFactor))
1563+ | otherwise
1564+ = 0
1565+ where
1566+ unpackMatchScore pScore
1567+ | Just score <- pScore = score
1568+ | otherwise = 0
15511569 m = moduleNameText identInfo
15521570
1571+ -- | Implements a lexicographic order for import suggestions.
1572+ -- First compares the importance score in DESCENDING order.
1573+ -- If the scores are equal it compares the import names alphabetical order.
1574+ data ImportSuggestion = ImportSuggestion ! Int ! CodeActionKind ! NewImport
1575+ deriving ( Eq )
1576+
1577+ instance Ord ImportSuggestion where
1578+ compare (ImportSuggestion s1 _ i1) (ImportSuggestion s2 _ i2)
1579+ | s1 == s2 = compare i1 i2
1580+ | otherwise = flip compare s1 s2
1581+
15531582newtype NewImport = NewImport { unNewImport :: T. Text}
15541583 deriving (Show , Eq , Ord )
15551584
0 commit comments