@@ -24,7 +24,7 @@ import Data.Generics
24
24
import Data.Hashable
25
25
import Data.HashSet (HashSet )
26
26
import qualified Data.HashSet as HS
27
- import Data.List.Extra
27
+ import Data.List.Extra hiding ( length )
28
28
import qualified Data.Map as M
29
29
import Data.Maybe
30
30
import Data.Mod.Word
@@ -42,7 +42,6 @@ import Development.IDE.GHC.ExactPrint
42
42
import Development.IDE.Spans.AtPoint
43
43
import Development.IDE.Types.Location
44
44
import HieDb.Query
45
- import Ide.Plugin.Config
46
45
import Ide.Plugin.Properties
47
46
import Ide.PluginUtils
48
47
import Ide.Types
@@ -65,16 +64,28 @@ descriptor pluginId = (defaultPluginDescriptor pluginId)
65
64
renameProvider :: PluginMethodHandler IdeState TextDocumentRename
66
65
renameProvider state pluginId (RenameParams (TextDocumentIdentifier uri) pos _prog newNameText) =
67
66
pluginResponse $ do
68
- nfp <- safeUriToNfp uri
69
- oldName <- getNameAtPos state nfp pos
70
- refLocs <- refsAtName state nfp oldName
67
+ nfp <- handleUriToNfp uri
68
+ directOldNames <- getNamesAtPos state nfp pos
69
+ directRefs <- concat <$> mapM (refsAtName state nfp) directOldNames
70
+
71
+ {- References in HieDB are not necessarily transitive. With `NamedFieldPuns`, we can have
72
+ indirect references through punned names. To find the transitive closure, we do a pass of
73
+ the direct references to find the references for any punned names.
74
+ See the `IndirectPuns` test for an example. -}
75
+ indirectOldNames <- concat . filter ((> 1 ) . Prelude. length ) <$>
76
+ mapM (uncurry (getNamesAtPos state) . locToFilePos) directRefs
77
+ let oldNames = indirectOldNames ++ directOldNames
78
+ refs <- HS. fromList . concat <$> mapM (refsAtName state nfp) oldNames
79
+
80
+ -- Validate rename
71
81
crossModuleEnabled <- lift $ usePropertyLsp # crossModule pluginId properties
72
- unless crossModuleEnabled $ failWhenImportOrExport state nfp refLocs oldName
73
- when (isBuiltInSyntax oldName) $
74
- throwE (" Invalid rename of built-in syntax: \" " ++ showName oldName ++ " \" " )
82
+ unless crossModuleEnabled $ failWhenImportOrExport state nfp refs oldNames
83
+ when (any isBuiltInSyntax oldNames) $ throwE " Invalid rename of built-in syntax"
84
+
85
+ -- Perform rename
75
86
let newName = mkTcOcc $ T. unpack newNameText
76
- filesRefs = collectWith locToUri refLocs
77
- getFileEdit = flip $ getSrcEdit state . renameRefs newName
87
+ filesRefs = collectWith locToUri refs
88
+ getFileEdit = flip $ getSrcEdit state . replaceRefs newName
78
89
fileEdits <- mapM (uncurry getFileEdit) filesRefs
79
90
pure $ foldl' (<>) mempty fileEdits
80
91
@@ -84,16 +95,16 @@ failWhenImportOrExport ::
84
95
IdeState ->
85
96
NormalizedFilePath ->
86
97
HashSet Location ->
87
- Name ->
98
+ [ Name ] ->
88
99
ExceptT String m ()
89
- failWhenImportOrExport state nfp refLocs name = do
100
+ failWhenImportOrExport state nfp refLocs names = do
90
101
pm <- handleMaybeM (" No parsed module for: " ++ show nfp) $ liftIO $ runAction
91
102
" Rename.GetParsedModule"
92
103
state
93
104
(use GetParsedModule nfp)
94
105
let hsMod = unLoc $ pm_parsed_source pm
95
106
case (unLoc <$> hsmodName hsMod, hsmodExports hsMod) of
96
- (mbModName, _) | not $ nameIsLocalOrFrom (replaceModName name mbModName) name
107
+ (mbModName, _) | not $ any ( \ n -> nameIsLocalOrFrom (replaceModName n mbModName) n) names
97
108
-> throwE " Renaming of an imported name is unsupported"
98
109
(_, Just (L _ exports)) | any ((`HS.member` refLocs) . unsafeSrcSpanToLoc . getLoc) exports
99
110
-> throwE " Renaming of an exported name is unsupported"
@@ -112,7 +123,7 @@ getSrcEdit ::
112
123
ExceptT String m WorkspaceEdit
113
124
getSrcEdit state updatePs uri = do
114
125
ccs <- lift getClientCapabilities
115
- nfp <- safeUriToNfp uri
126
+ nfp <- handleUriToNfp uri
116
127
annAst <- handleMaybeM (" No parsed source for: " ++ show nfp) $ liftIO $ runAction
117
128
" Rename.GetAnnotatedParsedSource"
118
129
state
@@ -128,13 +139,13 @@ getSrcEdit state updatePs uri = do
128
139
pure $ diffText ccs (uri, src) res IncludeDeletions
129
140
130
141
-- | Replace names at every given `Location` (in a given `ParsedSource`) with a given new name.
131
- renameRefs ::
142
+ replaceRefs ::
132
143
OccName ->
133
144
HashSet Location ->
134
145
ParsedSource ->
135
146
ParsedSource
136
147
#if MIN_VERSION_ghc(9,2,1)
137
- renameRefs newName refs = everywhere $
148
+ replaceRefs newName refs = everywhere $
138
149
-- there has to be a better way...
139
150
mkT (replaceLoc @ AnnListItem ) `extT`
140
151
-- replaceLoc @AnnList `extT` -- not needed
@@ -149,14 +160,13 @@ renameRefs newName refs = everywhere $
149
160
| isRef (locA srcSpan) = L srcSpan $ replace oldRdrName
150
161
replaceLoc lOldRdrName = lOldRdrName
151
162
#else
152
- renameRefs newName refs = everywhere $ mkT replaceLoc
163
+ replaceRefs newName refs = everywhere $ mkT replaceLoc
153
164
where
154
165
replaceLoc :: Located RdrName -> Located RdrName
155
166
replaceLoc (L srcSpan oldRdrName)
156
167
| isRef srcSpan = L srcSpan $ replace oldRdrName
157
168
replaceLoc lOldRdrName = lOldRdrName
158
169
#endif
159
-
160
170
replace :: RdrName -> RdrName
161
171
replace (Qual modName _) = Qual modName newName
162
172
replace _ = Unqual newName
@@ -173,10 +183,10 @@ refsAtName ::
173
183
IdeState ->
174
184
NormalizedFilePath ->
175
185
Name ->
176
- ExceptT String m ( HashSet Location )
186
+ ExceptT String m [ Location ]
177
187
refsAtName state nfp name = do
178
188
ShakeExtras {withHieDb} <- liftIO $ runAction " Rename.HieDb" state getShakeExtras
179
- ast <- safeGetHieAst state nfp
189
+ ast <- handleGetHieAst state nfp
180
190
dbRefs <- case nameModule_maybe name of
181
191
Nothing -> pure []
182
192
Just mod -> liftIO $ mapMaybe rowToLoc <$> withHieDb (\ hieDb ->
@@ -188,32 +198,32 @@ refsAtName state nfp name = do
188
198
(Just $ moduleUnit mod )
189
199
[fromNormalizedFilePath nfp]
190
200
)
191
- pure $ HS. fromList $ getNameLocs name ast ++ dbRefs
201
+ pure $ nameLocs name ast ++ dbRefs
192
202
193
- getNameLocs :: Name -> (HieAstResult , PositionMapping ) -> [Location ]
194
- getNameLocs name (HAR _ _ rm _ _, pm) =
203
+ nameLocs :: Name -> (HieAstResult , PositionMapping ) -> [Location ]
204
+ nameLocs name (HAR _ _ rm _ _, pm) =
195
205
mapMaybe (toCurrentLocation pm . realSrcSpanToLocation . fst )
196
206
(concat $ M. lookup (Right name) rm)
197
207
198
208
---------------------------------------------------------------------------------------------------
199
209
-- Util
200
210
201
- getNameAtPos :: IdeState -> NormalizedFilePath -> Position -> ExceptT String ( LspT Config IO ) Name
202
- getNameAtPos state nfp pos = do
203
- (HAR {hieAst}, pm) <- safeGetHieAst state nfp
204
- handleMaybe ( " No name at " ++ showPos pos) $ listToMaybe $ getNamesAtPoint hieAst pos pm
211
+ getNamesAtPos :: MonadIO m => IdeState -> NormalizedFilePath -> Position -> ExceptT String m [ Name ]
212
+ getNamesAtPos state nfp pos = do
213
+ (HAR {hieAst}, pm) <- handleGetHieAst state nfp
214
+ pure $ getNamesAtPoint hieAst pos pm
205
215
206
- safeGetHieAst ::
216
+ handleGetHieAst ::
207
217
MonadIO m =>
208
218
IdeState ->
209
219
NormalizedFilePath ->
210
220
ExceptT String m (HieAstResult , PositionMapping )
211
- safeGetHieAst state nfp = handleMaybeM
221
+ handleGetHieAst state nfp = handleMaybeM
212
222
(" No AST for file: " ++ show nfp)
213
223
(liftIO $ runAction " Rename.GetHieAst" state $ useWithStale GetHieAst nfp)
214
224
215
- safeUriToNfp :: (Monad m ) => Uri -> ExceptT String m NormalizedFilePath
216
- safeUriToNfp uri = handleMaybe
225
+ handleUriToNfp :: (Monad m ) => Uri -> ExceptT String m NormalizedFilePath
226
+ handleUriToNfp uri = handleMaybe
217
227
(" No filepath for uri: " ++ show uri)
218
228
(toNormalizedFilePath <$> uriToFilePath uri)
219
229
@@ -230,15 +240,17 @@ nfpToUri = filePathToUri . fromNormalizedFilePath
230
240
showName :: Name -> String
231
241
showName = occNameString . getOccName
232
242
233
- showPos :: Position -> String
234
- showPos Position {_line, _character} = " line: " ++ show _line ++ " - character: " ++ show _character
235
-
236
243
unsafeSrcSpanToLoc :: SrcSpan -> Location
237
244
unsafeSrcSpanToLoc srcSpan =
238
245
case srcSpanToLocation srcSpan of
239
246
Nothing -> error " Invalid conversion from UnhelpfulSpan to Location"
240
247
Just location -> location
241
248
249
+ locToFilePos :: Location -> (NormalizedFilePath , Position )
250
+ locToFilePos (Location uri (Range pos _)) = (nfp, pos)
251
+ where
252
+ Just nfp = (uriToNormalizedFilePath . toNormalizedUri) uri
253
+
242
254
replaceModName :: Name -> Maybe ModuleName -> Module
243
255
replaceModName name mbModName =
244
256
mkModule (moduleUnit $ nameModule name) (fromMaybe (mkModuleName " Main" ) mbModName)
0 commit comments