@@ -38,7 +38,6 @@ import Data.Ord (comparing)
38
38
import qualified Data.Set as S
39
39
import qualified Data.Text as T
40
40
import qualified Data.Text.Utf16.Rope as Rope
41
- import Data.Tuple.Extra (fst3 )
42
41
import Development.IDE.Core.Rules
43
42
import Development.IDE.Core.RuleTypes
44
43
import Development.IDE.Core.Service
@@ -87,6 +86,7 @@ import Language.LSP.Types (ApplyWorkspa
87
86
uriToFilePath )
88
87
import Language.LSP.VFS (VirtualFile ,
89
88
_file_text )
89
+ import qualified Text.Fuzzy.Parallel as TFP
90
90
import Text.Regex.TDFA (mrAfter ,
91
91
(=~) , (=~~) )
92
92
#if MIN_VERSION_ghc(9,2,0)
@@ -99,7 +99,6 @@ import GHC (AddEpAnn (Ad
99
99
EpaLocation (.. ),
100
100
LEpaComment ,
101
101
LocatedA )
102
-
103
102
#else
104
103
import Language.Haskell.GHC.ExactPrint.Types (Annotation (annsDP ),
105
104
DeltaPos ,
@@ -1521,35 +1520,65 @@ suggestNewImport packageExportsMap ps fileContents Diagnostic{_message}
1521
1520
, Just (range, indent) <- newImportInsertRange ps fileContents
1522
1521
, extendImportSuggestions <- matchRegexUnifySpaces msg
1523
1522
" 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
1527
1526
where
1528
1527
L _ HsModule {.. } = astA ps
1529
1528
suggestNewImport _ _ _ _ = []
1530
1529
1531
1530
constructNewImportSuggestions
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
1534
1533
[ 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
1540
1539
]
1541
1540
where
1542
- renderNewImport :: IdentInfo -> [( CodeActionKind , NewImport ) ]
1541
+ renderNewImport :: IdentInfo -> [ImportSuggestion ]
1543
1542
renderNewImport identInfo
1544
1543
| Just q <- qual
1545
- = [(quickFixImportKind " new.qualified" , newQualImport m q)]
1544
+ = [ImportSuggestion importanceScore (quickFixImportKind " new.qualified" ) ( newQualImport m q)]
1546
1545
| otherwise
1547
- = [(quickFixImportKind' " new" importStyle, newUnqualImport m (renderImportStyle importStyle) False )
1546
+ = [ImportSuggestion importanceScore (quickFixImportKind' " new" importStyle) ( newUnqualImport m (renderImportStyle importStyle) False )
1548
1547
| importStyle <- NE. toList $ importStyles identInfo] ++
1549
- [(quickFixImportKind " new.all" , newImportAll m)]
1548
+ [ImportSuggestion importanceScore (quickFixImportKind " new.all" ) ( newImportAll m)]
1550
1549
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
1551
1569
m = moduleNameText identInfo
1552
1570
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
+
1553
1582
newtype NewImport = NewImport { unNewImport :: T. Text}
1554
1583
deriving (Show , Eq , Ord )
1555
1584
0 commit comments