@@ -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)
@@ -924,13 +930,15 @@ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules =
924
930
, suggestion <- renderNewImport identInfo m
925
931
]
926
932
where
933
+ renderNewImport :: IdentInfo -> T. Text -> [T. Text ]
927
934
renderNewImport identInfo m
928
935
| Just q <- qual
929
936
, asQ <- if q == m then " " else " as " <> q
930
937
= [" import qualified " <> m <> asQ]
931
938
| otherwise
932
- = [" import " <> m <> " (" <> renderIdentInfo identInfo <> " )"
933
- ," import " <> m ]
939
+ = [" import " <> m <> " (" <> renderImportStyle importStyle <> " )"
940
+ | importStyle <- NE. toList $ importStyles identInfo] ++
941
+ [" import " <> m ]
934
942
935
943
canUseIdent :: NotInScope -> IdentInfo -> Bool
936
944
canUseIdent NotInScopeDataConstructor {} = isDatacon
@@ -1071,15 +1079,18 @@ rangesForBinding' _ _ = []
1071
1079
-- import (qualified) A (..) ..
1072
1080
-- Places the new binding first, preserving whitespace.
1073
1081
-- Copes with multi-line import lists
1074
- addBindingToImportList :: IdentInfo -> T. Text -> Maybe T. Text
1075
- addBindingToImportList IdentInfo {parent = _parent, .. } importLine =
1082
+ addBindingToImportList :: ImportStyle -> T. Text -> Maybe T. Text
1083
+ addBindingToImportList importStyle importLine =
1076
1084
case T. breakOn " (" importLine of
1077
1085
(pre, T. uncons -> Just (_, rest)) ->
1078
- case _parent of
1079
- -- the binding is not a constructor, add it to the head of import list
1080
- Nothing -> Just $ T. concat [pre, " (" , rendered, addCommaIfNeeds rest]
1081
- Just parent -> case T. breakOn parent rest of
1082
- -- the binding is a constructor, and current import list contains its parent
1086
+ case importStyle of
1087
+ ImportTopLevel rendered ->
1088
+ -- the binding has no parent, add it to the head of import list
1089
+ Just $ T. concat [pre, " (" , rendered, addCommaIfNeeds rest]
1090
+ ImportViaParent rendered parent -> case T. breakOn parent rest of
1091
+ -- the binding has a parent, and the current import list contains the
1092
+ -- parent
1093
+ --
1083
1094
-- `rest'` could be 1. `,...)`
1084
1095
-- or 2. `(),...)`
1085
1096
-- or 3. `(ConsA),...)`
@@ -1171,7 +1182,43 @@ matchRegExMultipleImports message = do
1171
1182
imps <- regExImports imports
1172
1183
return (binding, imps)
1173
1184
1174
- renderIdentInfo :: IdentInfo -> T. Text
1175
- renderIdentInfo IdentInfo {parent, rendered}
1176
- | Just p <- parent = p <> " (" <> rendered <> " )"
1177
- | otherwise = rendered
1185
+ -- | Possible import styles for an 'IdentInfo'.
1186
+ --
1187
+ -- The first 'Text' parameter corresponds to the 'rendered' field of the
1188
+ -- 'IdentInfo'.
1189
+ data ImportStyle
1190
+ = ImportTopLevel T. Text
1191
+ -- ^ Import a top-level export from a module, e.g., a function, a type, a
1192
+ -- class.
1193
+ --
1194
+ -- > import M (?)
1195
+ --
1196
+ -- Some exports that have a parent, like a type-class method or an
1197
+ -- associated type/data family, can still be imported as a top-level
1198
+ -- import.
1199
+ --
1200
+ -- Note that this is not the case for constructors, they must always be
1201
+ -- imported as part of their parent data type.
1202
+
1203
+ | ImportViaParent T. Text T. Text
1204
+ -- ^ Import an export (first parameter) through its parent (second
1205
+ -- parameter).
1206
+ --
1207
+ -- import M (P(?))
1208
+ --
1209
+ -- @P@ and @?@ can be a data type and a constructor, a class and a method,
1210
+ -- a class and an associated type/data family, etc.
1211
+
1212
+ importStyles :: IdentInfo -> NonEmpty ImportStyle
1213
+ importStyles IdentInfo {parent, rendered, isDatacon}
1214
+ | Just p <- parent
1215
+ -- Constructors always have to be imported via their parent data type, but
1216
+ -- methods and associated type/data families can also be imported as
1217
+ -- top-level exports.
1218
+ = ImportViaParent rendered p :| [ImportTopLevel rendered | not isDatacon]
1219
+ | otherwise
1220
+ = ImportTopLevel rendered :| []
1221
+
1222
+ renderImportStyle :: ImportStyle -> T. Text
1223
+ renderImportStyle (ImportTopLevel x) = x
1224
+ renderImportStyle (ImportViaParent x p) = p <> " (" <> x <> " )"
0 commit comments