Skip to content

Commit e390da5

Browse files
authored
Merge pull request #408 from pepeiborra/retrie-command
Retrie - calculate imports in the command handler
2 parents 8a71972 + e233ad5 commit e390da5

File tree

1 file changed

+102
-86
lines changed

1 file changed

+102
-86
lines changed

plugins/default/src/Ide/Plugin/Retrie.hs

+102-86
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ import Control.Monad.IO.Class (MonadIO (liftIO))
2525
import Control.Monad.Trans.Class (MonadTrans (lift))
2626
import Control.Monad.Trans.Except (ExceptT (..), runExceptT,
2727
throwE)
28-
import Data.Aeson (ToJSON (toJSON), Value (Null))
29-
import Data.Aeson.Types (FromJSON)
28+
import Data.Aeson (genericParseJSON, FromJSON(..), ToJSON (..), Value (Null))
3029
import Data.Bifunctor (Bifunctor (first), second)
3130
import Data.Coerce
3231
import Data.Either (partitionEithers)
@@ -35,7 +34,7 @@ import qualified Data.HashMap.Strict as HM
3534
import qualified Data.HashSet as Set
3635
import Data.IORef.Extra (atomicModifyIORef'_, newIORef,
3736
readIORef)
38-
import Data.List.Extra (nubOrdOn)
37+
import Data.List.Extra (find, nubOrdOn)
3938
import Data.String (IsString (fromString))
4039
import qualified Data.Text as T
4140
import qualified Data.Text.IO as T
@@ -86,6 +85,9 @@ import Retrie.SYB (listify)
8685
import Retrie.Util (Verbosity (Loud))
8786
import StringBuffer (stringToStringBuffer)
8887
import System.Directory (makeAbsolute)
88+
import Control.Monad.Trans.Maybe
89+
import Development.IDE.Core.PositionMapping
90+
import qualified Data.Aeson as Aeson
8991

9092
descriptor :: PluginId -> PluginDescriptor
9193
descriptor plId =
@@ -104,59 +106,110 @@ retrieCommand =
104106
-- | Parameters for the runRetrie PluginCommand.
105107
data RunRetrieParams = RunRetrieParams
106108
{ description :: T.Text,
107-
-- | rewrites for Retrie
108-
rewrites :: [Either ImportSpec RewriteSpec],
109-
-- | Originating file
110-
originatingFile :: String,
109+
rewrites :: [RewriteSpec],
110+
originatingFile :: NormalizedUriJSON,
111111
restrictToOriginatingFile :: Bool
112112
}
113113
deriving (Eq, Show, Generic, FromJSON, ToJSON)
114114

115+
newtype NormalizedUriJSON = NormalizedUriJSON NormalizedUri
116+
deriving (Eq, Show)
117+
118+
instance FromJSON NormalizedUriJSON where
119+
parseJSON = fmap NormalizedUriJSON . genericParseJSON Aeson.defaultOptions
120+
121+
instance ToJSON NormalizedUriJSON where
122+
toJSON (NormalizedUriJSON x) = Aeson.genericToJSON Aeson.defaultOptions x
123+
115124
runRetrieCmd ::
116125
LspFuncs a ->
117126
IdeState ->
118127
RunRetrieParams ->
119128
IO (Either ResponseError Value, Maybe (ServerMethod, ApplyWorkspaceEditParams))
120-
runRetrieCmd lsp state RunRetrieParams {..} =
129+
runRetrieCmd lsp state RunRetrieParams{originatingFile = NormalizedUriJSON nuri, ..} =
121130
withIndefiniteProgress lsp description Cancellable $ do
122-
session <-
123-
runAction "Retrie.GhcSessionDeps" state $
124-
use_ GhcSessionDeps $
125-
toNormalizedFilePath originatingFile
126-
(errors, edits) <-
127-
callRetrie
128-
state
129-
(hscEnv session)
130-
rewrites
131-
(toNormalizedFilePath originatingFile)
132-
restrictToOriginatingFile
133-
unless (null errors) $
134-
sendFunc lsp $
135-
NotShowMessage $
136-
NotificationMessage "2.0" WindowShowMessage $
137-
ShowMessageParams MtWarning $
138-
T.unlines $
139-
"## Found errors during rewrite:" :
140-
["-" <> T.pack (show e) | e <- errors]
131+
res <- runMaybeT $ do
132+
nfp <- MaybeT $ return $ uriToNormalizedFilePath nuri
133+
(session, _) <- MaybeT $
134+
runAction "Retrie.GhcSessionDeps" state $
135+
useWithStale GhcSessionDeps $
136+
nfp
137+
(ms, binds, _, _, _) <- MaybeT $ runAction "Retrie.getBinds" state $ getBinds nfp
138+
let importRewrites = concatMap (extractImports ms binds) rewrites
139+
(errors, edits) <- lift $
140+
callRetrie
141+
state
142+
(hscEnv session)
143+
(map Right rewrites <> map Left importRewrites)
144+
nfp
145+
restrictToOriginatingFile
146+
unless (null errors) $
147+
lift $ sendFunc lsp $
148+
NotShowMessage $
149+
NotificationMessage "2.0" WindowShowMessage $
150+
ShowMessageParams MtWarning $
151+
T.unlines $
152+
"## Found errors during rewrite:" :
153+
["-" <> T.pack (show e) | e <- errors]
154+
return (WorkspaceApplyEdit, ApplyWorkspaceEditParams edits)
141155
return
142-
(Right Null, Just (WorkspaceApplyEdit, ApplyWorkspaceEditParams edits))
156+
(Right Null, res)
157+
158+
extractImports :: ModSummary -> [HsBindLR GhcRn GhcRn] -> RewriteSpec -> [ImportSpec]
159+
extractImports ModSummary{ms_mod} topLevelBinds (Unfold thing)
160+
| Just FunBind {fun_matches}
161+
<- find (\case FunBind{fun_id = L _ n} -> prettyPrint n == thing ; _ -> False) topLevelBinds
162+
, names <- listify p fun_matches
163+
=
164+
[ AddImport {..}
165+
| name <- names,
166+
Just ideclNameString <-
167+
[moduleNameString . GHC.moduleName <$> nameModule_maybe name],
168+
let ideclSource = False,
169+
let r = nameRdrName name,
170+
let ideclQualifiedBool = isQual r,
171+
let ideclAsString = moduleNameString . fst <$> isQual_maybe r,
172+
let ideclThing = Just (IEVar $ occNameString $ rdrNameOcc r)
173+
]
174+
where
175+
p name = nameModule_maybe name /= Just ms_mod
176+
-- TODO handle imports for all rewrites
177+
extractImports _ _ _ = []
143178

144179
-------------------------------------------------------------------------------
145180

146181
provider :: CodeActionProvider
147182
provider _a state plId (TextDocumentIdentifier uri) range ca = response $ do
148183
let (J.CodeActionContext _diags _monly) = ca
149-
fp <- handleMaybe "uri" $ uriToFilePath' uri
150-
let nfp = toNormalizedFilePath' fp
184+
nuri = toNormalizedUri uri
185+
nuriJson = NormalizedUriJSON nuri
186+
nfp <- handleMaybe "uri" $ uriToNormalizedFilePath nuri
151187

152-
tm <-
153-
handleMaybeM "no typechecked module" $
154-
useRule "retrie.typecheckModule" state TypeCheck nfp
188+
(ModSummary{ms_mod}, topLevelBinds, posMapping, hs_ruleds, hs_tyclds)
189+
<- handleMaybeM "typecheck" $ runAction "retrie" state $ getBinds nfp
155190

156-
ModSummary {ms_mod} <-
157-
handleMaybeM "no mod summary" $
158-
useRule "retrie.typecheckModule" state GetModSummary nfp
191+
pos <- handleMaybe "pos" $ _start <$> fromCurrentRange posMapping range
192+
let rewrites =
193+
concatMap (suggestBindRewrites nuriJson pos ms_mod) topLevelBinds
194+
++ concatMap (suggestRuleRewrites nuriJson pos ms_mod) hs_ruleds
195+
++ [ r
196+
| TyClGroup {group_tyclds} <- hs_tyclds,
197+
L l g <- group_tyclds,
198+
r <- suggestTypeRewrites nuriJson ms_mod g,
199+
pos `isInsideSrcSpan` l
200+
201+
]
202+
203+
commands <- lift $
204+
forM rewrites $ \(title, kind, params) -> do
205+
c <- mkLspCommand plId (coerce retrieCommandName) title (Just [toJSON params])
206+
return $ CodeAction title (Just kind) Nothing Nothing (Just c)
207+
208+
return $ J.List [CACodeAction c | c <- commands]
159209

210+
getBinds :: NormalizedFilePath -> Action (Maybe (ModSummary, [HsBindLR GhcRn GhcRn], PositionMapping, [LRuleDecls GhcRn], [TyClGroup GhcRn]))
211+
getBinds nfp = runMaybeT $ do
212+
(tm, posMapping) <- MaybeT $ useWithStale TypeCheck nfp
160213
-- we use the typechecked source instead of the parsed source
161214
-- to be able to extract module names from the Ids,
162215
-- so that we can include adding the required imports in the retrie command
@@ -173,60 +226,29 @@ provider _a state plId (TextDocumentIdentifier uri) range ca = response $ do
173226
_
174227
) = rn
175228

176-
pos = _start range
177229
topLevelBinds =
178230
[ decl
179231
| (_, bagBinds) <- binds,
180232
L _ decl <- GHC.bagToList bagBinds
181233
]
182-
183-
rewrites =
184-
concatMap (suggestBindRewrites fp pos ms_mod) topLevelBinds
185-
++ concatMap (suggestRuleRewrites fp pos ms_mod) hs_ruleds
186-
++ [ r
187-
| TyClGroup {group_tyclds} <- hs_tyclds,
188-
L _ g <- group_tyclds,
189-
r <- suggestTypeRewrites fp pos ms_mod g
190-
]
191-
192-
commands <- lift $
193-
forM rewrites $ \(title, kind, params) -> do
194-
c <- mkLspCommand plId (coerce retrieCommandName) title (Just [toJSON params])
195-
return $ CodeAction title (Just kind) Nothing Nothing (Just c)
196-
197-
return $ J.List [CACodeAction c | c <- commands]
234+
return (tmrModSummary tm, topLevelBinds, posMapping, hs_ruleds, hs_tyclds)
198235

199236
suggestBindRewrites ::
200-
String ->
237+
NormalizedUriJSON ->
201238
Position ->
202239
GHC.Module ->
203240
HsBindLR GhcRn GhcRn ->
204241
[(T.Text, CodeActionKind, RunRetrieParams)]
205-
suggestBindRewrites originatingFile pos ms_mod (FunBind {fun_id = L l' rdrName, fun_matches})
242+
suggestBindRewrites originatingFile pos ms_mod (FunBind {fun_id = L l' rdrName})
206243
| pos `isInsideSrcSpan` l' =
207244
let pprName = prettyPrint rdrName
208245
pprNameText = T.pack pprName
209-
names = listify p fun_matches
210-
p name = nameModule_maybe name /= Just ms_mod
211-
imports =
212-
[ AddImport {..}
213-
| name <- names,
214-
Just ideclNameString <-
215-
[moduleNameString . GHC.moduleName <$> nameModule_maybe name],
216-
let ideclSource = False,
217-
let r = nameRdrName name,
218-
let ideclQualifiedBool = isQual r,
219-
let ideclAsString = moduleNameString . fst <$> isQual_maybe r,
220-
let ideclThing = Just (IEVar $ occNameString $ rdrNameOcc r)
221-
]
222246
unfoldRewrite restrictToOriginatingFile =
223-
let rewrites =
224-
[Right $ Unfold (qualify ms_mod pprName)]
225-
++ map Left imports
247+
let rewrites = [Unfold (qualify ms_mod pprName)]
226248
description = "Unfold " <> pprNameText <> describeRestriction restrictToOriginatingFile
227249
in (description, CodeActionRefactorInline, RunRetrieParams {..})
228250
foldRewrite restrictToOriginatingFile =
229-
let rewrites = [Right $ Fold (qualify ms_mod pprName)]
251+
let rewrites = [Fold (qualify ms_mod pprName)]
230252
description = "Fold " <> pprNameText <> describeRestriction restrictToOriginatingFile
231253
in (description, CodeActionRefactorExtract, RunRetrieParams {..})
232254
in [unfoldRewrite False, unfoldRewrite True, foldRewrite False, foldRewrite True]
@@ -237,32 +259,28 @@ describeRestriction :: IsString p => Bool -> p
237259
describeRestriction restrictToOriginatingFile =
238260
if restrictToOriginatingFile then " in current file" else ""
239261

240-
-- TODO add imports to the rewrite
241262
suggestTypeRewrites ::
242263
(Outputable (IdP pass)) =>
243-
String ->
244-
Position ->
264+
NormalizedUriJSON ->
245265
GHC.Module ->
246266
TyClDecl pass ->
247267
[(T.Text, CodeActionKind, RunRetrieParams)]
248-
suggestTypeRewrites originatingFile pos ms_mod (SynDecl {tcdLName = L l rdrName})
249-
| pos `isInsideSrcSpan` l =
268+
suggestTypeRewrites originatingFile ms_mod (SynDecl {tcdLName = L _ rdrName}) =
250269
let pprName = prettyPrint rdrName
251270
pprNameText = T.pack pprName
252271
unfoldRewrite restrictToOriginatingFile =
253-
let rewrites = [Right $ TypeForward (qualify ms_mod pprName)]
272+
let rewrites = [TypeForward (qualify ms_mod pprName)]
254273
description = "Unfold " <> pprNameText <> describeRestriction restrictToOriginatingFile
255274
in (description, CodeActionRefactorInline, RunRetrieParams {..})
256275
foldRewrite restrictToOriginatingFile =
257-
let rewrites = [Right $ TypeBackward (qualify ms_mod pprName)]
276+
let rewrites = [TypeBackward (qualify ms_mod pprName)]
258277
description = "Fold " <> pprNameText <> describeRestriction restrictToOriginatingFile
259278
in (description, CodeActionRefactorExtract, RunRetrieParams {..})
260279
in [unfoldRewrite False, unfoldRewrite True, foldRewrite False, foldRewrite True]
261-
suggestTypeRewrites _ _ _ _ = []
280+
suggestTypeRewrites _ _ _ = []
262281

263-
-- TODO add imports to the rewrite
264282
suggestRuleRewrites ::
265-
FilePath ->
283+
NormalizedUriJSON ->
266284
Position ->
267285
GHC.Module ->
268286
LRuleDecls pass ->
@@ -285,8 +303,7 @@ suggestRuleRewrites originatingFile pos ms_mod (L _ (HsRules {rds_rules})) =
285303
]
286304
where
287305
forwardRewrite ruleName restrictToOriginatingFile =
288-
let rewrites =
289-
[Right $ RuleForward (qualify ms_mod ruleName)]
306+
let rewrites = [RuleForward (qualify ms_mod ruleName)]
290307
description = "Apply rule " <> T.pack ruleName <> " forward" <>
291308
describeRestriction restrictToOriginatingFile
292309

@@ -295,8 +312,7 @@ suggestRuleRewrites originatingFile pos ms_mod (L _ (HsRules {rds_rules})) =
295312
RunRetrieParams {..}
296313
)
297314
backwardsRewrite ruleName restrictToOriginatingFile =
298-
let rewrites =
299-
[Right $ RuleBackward (qualify ms_mod ruleName)]
315+
let rewrites = [RuleBackward (qualify ms_mod ruleName)]
300316
description = "Apply rule " <> T.pack ruleName <> " backwards"
301317
in ( description,
302318
CodeActionRefactor,

0 commit comments

Comments
 (0)