@@ -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
@@ -89,6 +88,7 @@ import Language.LSP.VFS (VirtualFile,
89
88
_file_text )
90
89
import Text.Regex.TDFA (mrAfter ,
91
90
(=~) , (=~~) )
91
+ import qualified Text.Fuzzy.Parallel as TFP
92
92
#if MIN_VERSION_ghc(9,2,0)
93
93
import GHC (AddEpAnn (AddEpAnn ),
94
94
Anchor (anchor_op ),
@@ -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 ,
@@ -1495,35 +1494,63 @@ suggestNewImport packageExportsMap ps fileContents Diagnostic{_message}
1495
1494
, Just (range, indent) <- newImportInsertRange ps fileContents
1496
1495
, extendImportSuggestions <- matchRegexUnifySpaces msg
1497
1496
" 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
1501
1500
where
1502
1501
L _ HsModule {.. } = astA ps
1503
1502
suggestNewImport _ _ _ _ = []
1504
1503
1505
1504
constructNewImportSuggestions
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
1508
1507
[ 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
1514
1513
]
1515
1514
where
1516
- renderNewImport :: IdentInfo -> [( CodeActionKind , NewImport ) ]
1515
+ renderNewImport :: IdentInfo -> [ImportSuggestion ]
1517
1516
renderNewImport identInfo
1518
1517
| Just q <- qual
1519
- = [( quickFixImportKind " new.qualified" , newQualImport m q)]
1518
+ = [ImportSuggestion (importanceScore, quickFixImportKind " new.qualified" , newQualImport m q)]
1520
1519
| otherwise
1521
- = [( quickFixImportKind' " new" importStyle, newUnqualImport m (renderImportStyle importStyle) False )
1520
+ = [ImportSuggestion (importanceScore, quickFixImportKind' " new" importStyle, newUnqualImport m (renderImportStyle importStyle) False )
1522
1521
| importStyle <- NE. toList $ importStyles identInfo] ++
1523
- [( quickFixImportKind " new.all" , newImportAll m)]
1522
+ [ImportSuggestion (importanceScore, quickFixImportKind " new.all" , newImportAll m)]
1524
1523
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
1525
1541
m = moduleNameText identInfo
1526
1542
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
+
1527
1554
newtype NewImport = NewImport { unNewImport :: T. Text}
1528
1555
deriving (Show , Eq , Ord )
1529
1556
0 commit comments