@@ -48,6 +48,8 @@ import Data.Aeson.Types (toJSON, fromJSON, Value(..), Result(..))
4848import Data.Char
4949import Data.Maybe
5050import Data.List.Extra
51+ import Data.List.NonEmpty (NonEmpty ((:|) ))
52+ import qualified Data.List.NonEmpty as NE
5153import qualified Data.Text as T
5254import Text.Regex.TDFA (mrAfter , (=~) , (=~~) )
5355import Outputable (ppr , showSDocUnsafe )
@@ -620,9 +622,13 @@ suggestExtendImport exportsMap contents Diagnostic{_range=_range,..}
620622 in x{_end = (_end x){_character = succ (_character (_end x))}}
621623 _ -> error " bug in srcspan parser" ,
622624 importLine <- textInRange range c,
623- Just ident <- lookupExportMap binding mod ,
624- Just result <- addBindingToImportList ident importLine
625- = [(" Add " <> renderIdentInfo ident <> " to the import list of " <> mod , [TextEdit range result])]
625+ Just ident <- lookupExportMap binding mod
626+ = [ ( " Add " <> rendered <> " to the import list of " <> mod
627+ , [TextEdit range result]
628+ )
629+ | importStyle <- NE. toList $ importStyles ident
630+ , let rendered = renderImportStyle importStyle
631+ , result <- maybeToList $ addBindingToImportList importStyle importLine]
626632 | otherwise = []
627633 lookupExportMap binding mod
628634 | Just match <- Map. lookup binding (getExportsMap exportsMap)
@@ -931,13 +937,15 @@ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules =
931937 , suggestion <- renderNewImport identInfo m
932938 ]
933939 where
940+ renderNewImport :: IdentInfo -> T. Text -> [T. Text ]
934941 renderNewImport identInfo m
935942 | Just q <- qual
936943 , asQ <- if q == m then " " else " as " <> q
937944 = [" import qualified " <> m <> asQ]
938945 | otherwise
939- = [" import " <> m <> " (" <> renderIdentInfo identInfo <> " )"
940- ," import " <> m ]
946+ = [" import " <> m <> " (" <> renderImportStyle importStyle <> " )"
947+ | importStyle <- NE. toList $ importStyles identInfo] ++
948+ [" import " <> m ]
941949
942950canUseIdent :: NotInScope -> IdentInfo -> Bool
943951canUseIdent NotInScopeDataConstructor {} = isDatacon
@@ -1078,15 +1086,18 @@ rangesForBinding' _ _ = []
10781086-- import (qualified) A (..) ..
10791087-- Places the new binding first, preserving whitespace.
10801088-- Copes with multi-line import lists
1081- addBindingToImportList :: IdentInfo -> T. Text -> Maybe T. Text
1082- addBindingToImportList IdentInfo {parent = _parent, .. } importLine =
1089+ addBindingToImportList :: ImportStyle -> T. Text -> Maybe T. Text
1090+ addBindingToImportList importStyle importLine =
10831091 case T. breakOn " (" importLine of
10841092 (pre, T. uncons -> Just (_, rest)) ->
1085- case _parent of
1086- -- the binding is not a constructor, add it to the head of import list
1087- Nothing -> Just $ T. concat [pre, " (" , rendered, addCommaIfNeeds rest]
1088- Just parent -> case T. breakOn parent rest of
1089- -- the binding is a constructor, and current import list contains its parent
1093+ case importStyle of
1094+ ImportTopLevel rendered ->
1095+ -- the binding has no parent, add it to the head of import list
1096+ Just $ T. concat [pre, " (" , rendered, addCommaIfNeeds rest]
1097+ ImportViaParent rendered parent -> case T. breakOn parent rest of
1098+ -- the binding has a parent, and the current import list contains the
1099+ -- parent
1100+ --
10901101 -- `rest'` could be 1. `,...)`
10911102 -- or 2. `(),...)`
10921103 -- or 3. `(ConsA),...)`
@@ -1178,7 +1189,43 @@ matchRegExMultipleImports message = do
11781189 imps <- regExImports imports
11791190 return (binding, imps)
11801191
1181- renderIdentInfo :: IdentInfo -> T. Text
1182- renderIdentInfo IdentInfo {parent, rendered}
1183- | Just p <- parent = p <> " (" <> rendered <> " )"
1184- | otherwise = rendered
1192+ -- | Possible import styles for an 'IdentInfo'.
1193+ --
1194+ -- The first 'Text' parameter corresponds to the 'rendered' field of the
1195+ -- 'IdentInfo'.
1196+ data ImportStyle
1197+ = ImportTopLevel T. Text
1198+ -- ^ Import a top-level export from a module, e.g., a function, a type, a
1199+ -- class.
1200+ --
1201+ -- > import M (?)
1202+ --
1203+ -- Some exports that have a parent, like a type-class method or an
1204+ -- associated type/data family, can still be imported as a top-level
1205+ -- import.
1206+ --
1207+ -- Note that this is not the case for constructors, they must always be
1208+ -- imported as part of their parent data type.
1209+
1210+ | ImportViaParent T. Text T. Text
1211+ -- ^ Import an export (first parameter) through its parent (second
1212+ -- parameter).
1213+ --
1214+ -- import M (P(?))
1215+ --
1216+ -- @P@ and @?@ can be a data type and a constructor, a class and a method,
1217+ -- a class and an associated type/data family, etc.
1218+
1219+ importStyles :: IdentInfo -> NonEmpty ImportStyle
1220+ importStyles IdentInfo {parent, rendered, isDatacon}
1221+ | Just p <- parent
1222+ -- Constructors always have to be imported via their parent data type, but
1223+ -- methods and associated type/data families can also be imported as
1224+ -- top-level exports.
1225+ = ImportViaParent rendered p :| [ImportTopLevel rendered | not isDatacon]
1226+ | otherwise
1227+ = ImportTopLevel rendered :| []
1228+
1229+ renderImportStyle :: ImportStyle -> T. Text
1230+ renderImportStyle (ImportTopLevel x) = x
1231+ renderImportStyle (ImportViaParent x p) = p <> " (" <> x <> " )"
0 commit comments