@@ -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 )
@@ -622,9 +624,13 @@ suggestExtendImport exportsMap contents Diagnostic{_range=_range,..}
622624 in x{_end = (_end x){_character = succ (_character (_end x))}}
623625 _ -> error " bug in srcspan parser" ,
624626 importLine <- textInRange range c,
625- Just ident <- lookupExportMap binding mod ,
626- Just result <- addBindingToImportList ident importLine
627- = [(" Add " <> renderIdentInfo ident <> " to the import list of " <> mod , [TextEdit range result])]
627+ Just ident <- lookupExportMap binding mod
628+ = [ ( " Add " <> rendered <> " to the import list of " <> mod
629+ , [TextEdit range result]
630+ )
631+ | importStyle <- NE. toList $ importStyles ident
632+ , let rendered = renderImportStyle importStyle
633+ , result <- maybeToList $ addBindingToImportList importStyle importLine]
628634 | otherwise = []
629635 lookupExportMap binding mod
630636 | Just match <- Map. lookup binding (getExportsMap exportsMap)
@@ -933,13 +939,15 @@ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules =
933939 , suggestion <- renderNewImport identInfo m
934940 ]
935941 where
942+ renderNewImport :: IdentInfo -> T. Text -> [T. Text ]
936943 renderNewImport identInfo m
937944 | Just q <- qual
938945 , asQ <- if q == m then " " else " as " <> q
939946 = [" import qualified " <> m <> asQ]
940947 | otherwise
941- = [" import " <> m <> " (" <> renderIdentInfo identInfo <> " )"
942- ," import " <> m ]
948+ = [" import " <> m <> " (" <> renderImportStyle importStyle <> " )"
949+ | importStyle <- NE. toList $ importStyles identInfo] ++
950+ [" import " <> m ]
943951
944952canUseIdent :: NotInScope -> IdentInfo -> Bool
945953canUseIdent NotInScopeDataConstructor {} = isDatacon
@@ -1080,15 +1088,18 @@ rangesForBinding' _ _ = []
10801088-- import (qualified) A (..) ..
10811089-- Places the new binding first, preserving whitespace.
10821090-- Copes with multi-line import lists
1083- addBindingToImportList :: IdentInfo -> T. Text -> Maybe T. Text
1084- addBindingToImportList IdentInfo {parent = _parent, .. } importLine =
1091+ addBindingToImportList :: ImportStyle -> T. Text -> Maybe T. Text
1092+ addBindingToImportList importStyle importLine =
10851093 case T. breakOn " (" importLine of
10861094 (pre, T. uncons -> Just (_, rest)) ->
1087- case _parent of
1088- -- the binding is not a constructor, add it to the head of import list
1089- Nothing -> Just $ T. concat [pre, " (" , rendered, addCommaIfNeeds rest]
1090- Just parent -> case T. breakOn parent rest of
1091- -- the binding is a constructor, and current import list contains its parent
1095+ case importStyle of
1096+ ImportTopLevel rendered ->
1097+ -- the binding has no parent, add it to the head of import list
1098+ Just $ T. concat [pre, " (" , rendered, addCommaIfNeeds rest]
1099+ ImportViaParent rendered parent -> case T. breakOn parent rest of
1100+ -- the binding has a parent, and the current import list contains the
1101+ -- parent
1102+ --
10921103 -- `rest'` could be 1. `,...)`
10931104 -- or 2. `(),...)`
10941105 -- or 3. `(ConsA),...)`
@@ -1180,7 +1191,43 @@ matchRegExMultipleImports message = do
11801191 imps <- regExImports imports
11811192 return (binding, imps)
11821193
1183- renderIdentInfo :: IdentInfo -> T. Text
1184- renderIdentInfo IdentInfo {parent, rendered}
1185- | Just p <- parent = p <> " (" <> rendered <> " )"
1186- | otherwise = rendered
1194+ -- | Possible import styles for an 'IdentInfo'.
1195+ --
1196+ -- The first 'Text' parameter corresponds to the 'rendered' field of the
1197+ -- 'IdentInfo'.
1198+ data ImportStyle
1199+ = ImportTopLevel T. Text
1200+ -- ^ Import a top-level export from a module, e.g., a function, a type, a
1201+ -- class.
1202+ --
1203+ -- > import M (?)
1204+ --
1205+ -- Some exports that have a parent, like a type-class method or an
1206+ -- associated type/data family, can still be imported as a top-level
1207+ -- import.
1208+ --
1209+ -- Note that this is not the case for constructors, they must always be
1210+ -- imported as part of their parent data type.
1211+
1212+ | ImportViaParent T. Text T. Text
1213+ -- ^ Import an export (first parameter) through its parent (second
1214+ -- parameter).
1215+ --
1216+ -- import M (P(?))
1217+ --
1218+ -- @P@ and @?@ can be a data type and a constructor, a class and a method,
1219+ -- a class and an associated type/data family, etc.
1220+
1221+ importStyles :: IdentInfo -> NonEmpty ImportStyle
1222+ importStyles IdentInfo {parent, rendered, isDatacon}
1223+ | Just p <- parent
1224+ -- Constructors always have to be imported via their parent data type, but
1225+ -- methods and associated type/data families can also be imported as
1226+ -- top-level exports.
1227+ = ImportViaParent rendered p :| [ImportTopLevel rendered | not isDatacon]
1228+ | otherwise
1229+ = ImportTopLevel rendered :| []
1230+
1231+ renderImportStyle :: ImportStyle -> T. Text
1232+ renderImportStyle (ImportTopLevel x) = x
1233+ renderImportStyle (ImportViaParent x p) = p <> " (" <> x <> " )"
0 commit comments