@@ -43,7 +43,8 @@ import Development.IDE.GHC.Compat
43
43
import Development.IDE.GHC.Error
44
44
import Development.IDE.GHC.ExactPrint
45
45
import Development.IDE.GHC.Util (prettyPrint ,
46
- printRdrName )
46
+ printRdrName ,
47
+ unsafePrintSDoc )
47
48
import Development.IDE.Plugin.CodeAction.Args
48
49
import Development.IDE.Plugin.CodeAction.ExactPrint
49
50
import Development.IDE.Plugin.CodeAction.PositionIndexed
@@ -71,7 +72,8 @@ import Outputable (Outputable,
71
72
import RdrName (GlobalRdrElt (.. ),
72
73
lookupGlobalRdrEnv )
73
74
import Safe (atMay )
74
- import SrcLoc (realSrcSpanStart )
75
+ import SrcLoc (realSrcSpanEnd ,
76
+ realSrcSpanStart )
75
77
import TcRnTypes (ImportAvails (.. ),
76
78
TcGblEnv (.. ))
77
79
import Text.Regex.TDFA (mrAfter ,
@@ -179,8 +181,8 @@ findDeclContainingLoc loc = find (\(L l _) -> loc `isInsideSrcSpan` l)
179
181
-- imported from ‘Data.ByteString’ at B.hs:6:1-22
180
182
-- imported from ‘Data.ByteString.Lazy’ at B.hs:8:1-27
181
183
-- imported from ‘Data.Text’ at B.hs:7:1-16
182
- suggestHideShadow :: ParsedSource -> Maybe TcModuleResult -> Maybe HieAstResult -> Diagnostic -> [(T. Text , [Rewrite ])]
183
- suggestHideShadow pm @ (L _ HsModule {hsmodImports}) mTcM mHar Diagnostic {_message, _range}
184
+ suggestHideShadow :: ParsedSource -> Maybe TcModuleResult -> Maybe HieAstResult -> Diagnostic -> [(T. Text , [Either TextEdit Rewrite ])]
185
+ suggestHideShadow ps @ (L _ HsModule {hsmodImports}) mTcM mHar Diagnostic {_message, _range}
184
186
| Just [identifier, modName, s] <-
185
187
matchRegexUnifySpaces
186
188
_message
@@ -205,8 +207,8 @@ suggestHideShadow pm@(L _ HsModule {hsmodImports}) mTcM mHar Diagnostic {_messag
205
207
mDecl <- findImportDeclByModuleName hsmodImports $ T. unpack modName,
206
208
title <- " Hide " <> identifier <> " from " <> modName =
207
209
if modName == " Prelude" && null mDecl
208
- then [( title, maybeToList $ hideImplicitPreludeSymbol ( T. unpack identifier) pm)]
209
- else maybeToList $ (title,) . pure . hideSymbol (T. unpack identifier) <$> mDecl
210
+ then maybeToList $ ( \ (_, te) -> ( title, [ Left te])) <$> newImportToEdit (hideImplicitPreludeSymbol identifier) ps
211
+ else maybeToList $ (title,) . pure . pure . hideSymbol (T. unpack identifier) <$> mDecl
210
212
| otherwise = []
211
213
212
214
findImportDeclByModuleName :: [LImportDecl GhcPs ] -> String -> Maybe (LImportDecl GhcPs )
@@ -808,7 +810,7 @@ suggestImportDisambiguation ::
808
810
Maybe T. Text ->
809
811
ParsedSource ->
810
812
Diagnostic ->
811
- [(T. Text , [Rewrite ])]
813
+ [(T. Text , [Either TextEdit Rewrite ])]
812
814
suggestImportDisambiguation df (Just txt) ps@ (L _ HsModule {hsmodImports}) diag@ Diagnostic {.. }
813
815
| Just [ambiguous] <-
814
816
matchRegexUnifySpaces
@@ -897,23 +899,23 @@ disambiguateSymbol ::
897
899
Diagnostic ->
898
900
T. Text ->
899
901
HidingMode ->
900
- [Rewrite ]
902
+ [Either TextEdit Rewrite ]
901
903
disambiguateSymbol pm Diagnostic {.. } (T. unpack -> symbol) = \ case
902
904
(HideOthers hiddens0) ->
903
- [ hideSymbol symbol idecl
905
+ [ Right $ hideSymbol symbol idecl
904
906
| ExistingImp idecls <- hiddens0
905
907
, idecl <- NE. toList idecls
906
908
]
907
909
++ mconcat
908
910
[ if null imps
909
- then maybeToList $ hideImplicitPreludeSymbol symbol pm
910
- else hideSymbol symbol <$> imps
911
+ then maybeToList $ Left . snd <$> newImportToEdit ( hideImplicitPreludeSymbol $ T. pack symbol) pm
912
+ else Right . hideSymbol symbol <$> imps
911
913
| ImplicitPrelude imps <- hiddens0
912
914
]
913
915
(ToQualified parensed qualMod) ->
914
916
let occSym = mkVarOcc symbol
915
917
rdr = Qual qualMod occSym
916
- in [ if parensed
918
+ in Right <$> [ if parensed
917
919
then Rewrite (rangeToSrcSpan " <dummy>" _range) $ \ df ->
918
920
liftParseAST @ (HsExpr GhcPs ) df $
919
921
prettyPrint $
@@ -1136,7 +1138,7 @@ removeRedundantConstraints mContents Diagnostic{..}
1136
1138
1137
1139
-------------------------------------------------------------------------------------------------
1138
1140
1139
- suggestNewOrExtendImportForClassMethod :: ExportsMap -> ParsedSource -> Diagnostic -> [(T. Text , [Rewrite ])]
1141
+ suggestNewOrExtendImportForClassMethod :: ExportsMap -> ParsedSource -> Diagnostic -> [(T. Text , [Either TextEdit Rewrite ])]
1140
1142
suggestNewOrExtendImportForClassMethod packageExportsMap ps Diagnostic {_message}
1141
1143
| Just [methodName, className] <-
1142
1144
matchRegexUnifySpaces
@@ -1155,22 +1157,24 @@ suggestNewOrExtendImportForClassMethod packageExportsMap ps Diagnostic {_message
1155
1157
-- extend
1156
1158
Just decl ->
1157
1159
[ ( " Add " <> renderImportStyle style <> " to the import list of " <> moduleNameText,
1158
- [uncurry extendImport (unImportStyle style) decl]
1160
+ [Right $ uncurry extendImport (unImportStyle style) decl]
1159
1161
)
1160
1162
| style <- importStyle
1161
1163
]
1162
1164
-- new
1163
- _ ->
1164
- [ ( " Import " <> moduleNameText <> " with " <> rendered,
1165
- maybeToList $ newUnqualImport (T. unpack moduleNameText) (T. unpack rendered) False ps
1166
- )
1165
+ _
1166
+ | Just (range, indent) <- newImportInsertRange ps
1167
+ ->
1168
+ (\ (unNewImport -> x) -> (x, [Left $ TextEdit range (x <> " \n " <> T. replicate indent " " )])) <$>
1169
+ [ newUnqualImport moduleNameText rendered False
1167
1170
| style <- importStyle,
1168
1171
let rendered = renderImportStyle style
1169
1172
]
1170
- <> maybeToList ((" Import " <> moduleNameText,) <$> fmap pure (newImportAll (T. unpack moduleNameText) ps))
1173
+ <> [newImportAll moduleNameText]
1174
+ | otherwise -> []
1171
1175
1172
- suggestNewImport :: ExportsMap -> ParsedModule -> Diagnostic -> [(T. Text , TextEdit )]
1173
- suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule {.. }} Diagnostic {_message}
1176
+ suggestNewImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T. Text , TextEdit )]
1177
+ suggestNewImport packageExportsMap ps @ ( L _ HsModule {.. }) Diagnostic {_message}
1174
1178
| msg <- unifySpaces _message
1175
1179
, Just thingMissing <- extractNotInScopeName msg
1176
1180
, qual <- extractQualifiedModuleName msg
@@ -1179,23 +1183,16 @@ suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule
1179
1183
>>= (findImportDeclByModuleName hsmodImports . T. unpack)
1180
1184
>>= ideclAs . unLoc
1181
1185
<&> T. pack . moduleNameString . unLoc
1182
- , Just insertLine <- case hsmodImports of
1183
- [] -> case srcSpanStart $ getLoc (head hsmodDecls) of
1184
- RealSrcLoc s -> Just $ srcLocLine s - 1
1185
- _ -> Nothing
1186
- _ -> case srcSpanEnd $ getLoc (last hsmodImports) of
1187
- RealSrcLoc s -> Just $ srcLocLine s
1188
- _ -> Nothing
1189
- , insertPos <- Position insertLine 0
1186
+ , Just (range, indent) <- newImportInsertRange ps
1190
1187
, extendImportSuggestions <- matchRegexUnifySpaces msg
1191
1188
" Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’"
1192
- = [(imp, TextEdit ( Range insertPos insertPos) (imp <> " \n " ))
1193
- | imp <- sort $ constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions
1189
+ = sortOn fst [(imp, TextEdit range (imp <> " \n " <> T. replicate indent " " ))
1190
+ | (unNewImport -> imp) <- constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions
1194
1191
]
1195
1192
suggestNewImport _ _ _ = []
1196
1193
1197
1194
constructNewImportSuggestions
1198
- :: ExportsMap -> (Maybe T. Text , NotInScope ) -> Maybe [T. Text ] -> [T. Text ]
1195
+ :: ExportsMap -> (Maybe T. Text , NotInScope ) -> Maybe [T. Text ] -> [NewImport ]
1199
1196
constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrd
1200
1197
[ suggestion
1201
1198
| Just name <- [T. stripPrefix (maybe " " (<> " ." ) qual) $ notInScope thingMissing]
@@ -1205,18 +1202,74 @@ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules =
1205
1202
, suggestion <- renderNewImport identInfo
1206
1203
]
1207
1204
where
1208
- renderNewImport :: IdentInfo -> [T. Text ]
1205
+ renderNewImport :: IdentInfo -> [NewImport ]
1209
1206
renderNewImport identInfo
1210
1207
| Just q <- qual
1211
- , asQ <- if q == m then " " else " as " <> q
1212
- = [" import qualified " <> m <> asQ]
1208
+ = [newQualImport m q]
1213
1209
| otherwise
1214
- = [" import " <> m <> " ( " <> renderImportStyle importStyle <> " ) "
1210
+ = [newUnqualImport m ( renderImportStyle importStyle) False
1215
1211
| importStyle <- NE. toList $ importStyles identInfo] ++
1216
- [" import " <> m ]
1212
+ [newImportAll m ]
1217
1213
where
1218
1214
m = moduleNameText identInfo
1219
1215
1216
+ newtype NewImport = NewImport { unNewImport :: T. Text}
1217
+ deriving (Show , Eq , Ord )
1218
+
1219
+ newImportToEdit :: NewImport -> ParsedSource -> Maybe (T. Text , TextEdit )
1220
+ newImportToEdit (unNewImport -> imp) ps
1221
+ | Just (range, indent) <- newImportInsertRange ps
1222
+ = Just (imp, TextEdit range (imp <> " \n " <> T. replicate indent " " ))
1223
+ | otherwise = Nothing
1224
+
1225
+ newImportInsertRange :: ParsedSource -> Maybe (Range , Int )
1226
+ newImportInsertRange (L _ HsModule {.. })
1227
+ | Just (uncurry Position -> insertPos, col) <- case hsmodImports of
1228
+ [] -> case getLoc (head hsmodDecls) of
1229
+ RealSrcSpan s -> let col = srcLocCol (realSrcSpanStart s) - 1
1230
+ in Just ((srcLocLine (realSrcSpanStart s) - 1 , col), col)
1231
+ _ -> Nothing
1232
+ _ -> case getLoc (last hsmodImports) of
1233
+ RealSrcSpan s -> let col = srcLocCol (realSrcSpanStart s) - 1
1234
+ in Just ((srcLocLine $ realSrcSpanEnd s,col), col)
1235
+ _ -> Nothing
1236
+ = Just (Range insertPos insertPos, col)
1237
+ | otherwise = Nothing
1238
+
1239
+ -- | Construct an import declaration with at most one symbol
1240
+ newImport
1241
+ :: T. Text -- ^ module name
1242
+ -> Maybe T. Text -- ^ the symbol
1243
+ -> Maybe T. Text -- ^ qualified name
1244
+ -> Bool -- ^ the symbol is to be imported or hidden
1245
+ -> NewImport
1246
+ newImport modName mSymbol mQual hiding = NewImport impStmt
1247
+ where
1248
+ symImp
1249
+ | Just symbol <- mSymbol
1250
+ , symOcc <- mkVarOcc $ T. unpack symbol =
1251
+ " (" <> T. pack (unsafePrintSDoc (parenSymOcc symOcc $ ppr symOcc)) <> " )"
1252
+ | otherwise = " "
1253
+ impStmt =
1254
+ " import "
1255
+ <> maybe " " (const " qualified " ) mQual
1256
+ <> modName
1257
+ <> (if hiding then " hiding" else " " )
1258
+ <> symImp
1259
+ <> maybe " " (\ qual -> if modName == qual then " " else " as " <> qual) mQual
1260
+
1261
+ newQualImport :: T. Text -> T. Text -> NewImport
1262
+ newQualImport modName qual = newImport modName Nothing (Just qual) False
1263
+
1264
+ newUnqualImport :: T. Text -> T. Text -> Bool -> NewImport
1265
+ newUnqualImport modName symbol = newImport modName (Just symbol) Nothing
1266
+
1267
+ newImportAll :: T. Text -> NewImport
1268
+ newImportAll modName = newImport modName Nothing Nothing False
1269
+
1270
+ hideImplicitPreludeSymbol :: T. Text -> NewImport
1271
+ hideImplicitPreludeSymbol symbol = newUnqualImport " Prelude" symbol True
1272
+
1220
1273
canUseIdent :: NotInScope -> IdentInfo -> Bool
1221
1274
canUseIdent NotInScopeDataConstructor {} = isDatacon
1222
1275
canUseIdent _ = const True
0 commit comments