@@ -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 )
@@ -620,9 +622,13 @@ suggestExtendImport exportsMap contents Diagnostic{_range=_range,..}
620
622
in x{_end = (_end x){_character = succ (_character (_end x))}}
621
623
_ -> error " bug in srcspan parser" ,
622
624
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]
626
632
| otherwise = []
627
633
lookupExportMap binding mod
628
634
| Just match <- Map. lookup binding (getExportsMap exportsMap)
@@ -931,13 +937,15 @@ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules =
931
937
, suggestion <- renderNewImport identInfo m
932
938
]
933
939
where
940
+ renderNewImport :: IdentInfo -> T. Text -> [T. Text ]
934
941
renderNewImport identInfo m
935
942
| Just q <- qual
936
943
, asQ <- if q == m then " " else " as " <> q
937
944
= [" import qualified " <> m <> asQ]
938
945
| otherwise
939
- = [" import " <> m <> " (" <> renderIdentInfo identInfo <> " )"
940
- ," import " <> m ]
946
+ = [" import " <> m <> " (" <> renderImportStyle importStyle <> " )"
947
+ | importStyle <- NE. toList $ importStyles identInfo] ++
948
+ [" import " <> m ]
941
949
942
950
canUseIdent :: NotInScope -> IdentInfo -> Bool
943
951
canUseIdent NotInScopeDataConstructor {} = isDatacon
@@ -1078,15 +1086,18 @@ rangesForBinding' _ _ = []
1078
1086
-- import (qualified) A (..) ..
1079
1087
-- Places the new binding first, preserving whitespace.
1080
1088
-- 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 =
1083
1091
case T. breakOn " (" importLine of
1084
1092
(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
+ --
1090
1101
-- `rest'` could be 1. `,...)`
1091
1102
-- or 2. `(),...)`
1092
1103
-- or 3. `(ConsA),...)`
@@ -1178,7 +1189,43 @@ matchRegExMultipleImports message = do
1178
1189
imps <- regExImports imports
1179
1190
return (binding, imps)
1180
1191
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