@@ -48,6 +48,8 @@ import Data.Aeson.Types (toJSON, fromJSON, Value(..), Result(..))
48
48
import Data.Char
49
49
import Data.Maybe
50
50
import Data.List.Extra
51
+ import Data.List.NonEmpty (NonEmpty ((:|) ))
52
+ import qualified Data.List.NonEmpty as NE
51
53
import qualified Data.Text as T
52
54
import Text.Regex.TDFA (mrAfter , (=~) , (=~~) )
53
55
import Outputable (ppr , showSDocUnsafe )
@@ -622,9 +624,13 @@ suggestExtendImport exportsMap contents Diagnostic{_range=_range,..}
622
624
in x{_end = (_end x){_character = succ (_character (_end x))}}
623
625
_ -> error " bug in srcspan parser" ,
624
626
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]
628
634
| otherwise = []
629
635
lookupExportMap binding mod
630
636
| Just match <- Map. lookup binding (getExportsMap exportsMap)
@@ -933,13 +939,15 @@ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules =
933
939
, suggestion <- renderNewImport identInfo m
934
940
]
935
941
where
942
+ renderNewImport :: IdentInfo -> T. Text -> [T. Text ]
936
943
renderNewImport identInfo m
937
944
| Just q <- qual
938
945
, asQ <- if q == m then " " else " as " <> q
939
946
= [" import qualified " <> m <> asQ]
940
947
| otherwise
941
- = [" import " <> m <> " (" <> renderIdentInfo identInfo <> " )"
942
- ," import " <> m ]
948
+ = [" import " <> m <> " (" <> renderImportStyle importStyle <> " )"
949
+ | importStyle <- NE. toList $ importStyles identInfo] ++
950
+ [" import " <> m ]
943
951
944
952
canUseIdent :: NotInScope -> IdentInfo -> Bool
945
953
canUseIdent NotInScopeDataConstructor {} = isDatacon
@@ -1080,15 +1088,18 @@ rangesForBinding' _ _ = []
1080
1088
-- import (qualified) A (..) ..
1081
1089
-- Places the new binding first, preserving whitespace.
1082
1090
-- 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 =
1085
1093
case T. breakOn " (" importLine of
1086
1094
(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
+ --
1092
1103
-- `rest'` could be 1. `,...)`
1093
1104
-- or 2. `(),...)`
1094
1105
-- or 3. `(ConsA),...)`
@@ -1180,7 +1191,43 @@ matchRegExMultipleImports message = do
1180
1191
imps <- regExImports imports
1181
1192
return (binding, imps)
1182
1193
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