@@ -75,35 +75,6 @@ import Language.LSP.Types (CodeAction (..),
75
75
import qualified Language.LSP.Types.Lens as L
76
76
77
77
78
- -- `Outputable` instance of `HsRecFields` does smart things to print
79
- -- the records that originally had wildcards with dots, even after they
80
- -- are removed by the renamer pass. Here `rec_dotdot` is set to
81
- -- `Nothing` so that fields are printed without such post-processing.
82
- preprocessRecord :: HsRecFields (GhcPass c ) arg -> HsRecFields (GhcPass c ) arg
83
- preprocessRecord flds = flds { rec_dotdot = Nothing , rec_flds = rec_flds' }
84
- where
85
- no_pun_count = maybe (length (rec_flds flds)) unLoc (rec_dotdot flds)
86
- -- Field binds of the explicit form (e.g. `{ a = a' }`) should be
87
- -- left as is, hence the split.
88
- (no_puns, puns) = splitAt no_pun_count (rec_flds flds)
89
- -- `hsRecPun` is set to `True` in order to pretty-print the fields as field
90
- -- puns (since there is similar mechanism in the `Outputable` instance as
91
- -- explained above).
92
- puns' = map (mapLoc (\ fld -> fld { hfbPun = True })) puns
93
- rec_flds' = no_puns <> puns'
94
-
95
- showRecordPat :: Outputable (Pat (GhcPass c )) => Pat (GhcPass c ) -> Maybe Text
96
- showRecordPat pat@ (ConPat _ _ (RecCon flds)) =
97
- Just $ printOutputable $
98
- pat { pat_args = RecCon (preprocessRecord flds) }
99
- showRecordPat _ = Nothing
100
-
101
- showRecordCon :: Outputable (HsExpr (GhcPass c )) => HsExpr (GhcPass c ) -> Maybe Text
102
- showRecordCon expr@ (RecordCon _ _ flds) =
103
- Just $ printOutputable $
104
- expr { rcon_flds = preprocessRecord flds }
105
- showRecordCon _ = Nothing
106
-
107
78
data Log = LogShake Shake. Log
108
79
109
80
instance Pretty Log where
@@ -116,6 +87,71 @@ descriptor recorder plId = (defaultPluginDescriptor plId)
116
87
, pluginRules = collectRecordsRule recorder
117
88
}
118
89
90
+ codeActionProvider :: PluginMethodHandler IdeState 'TextDocumentCodeAction
91
+ codeActionProvider ideState pId (CodeActionParams _ _ docId range _) = pluginResponse $ do
92
+ nfp <- getNormalizedFilePath (docId ^. L. uri)
93
+ pragma <- getFirstPragma pId ideState nfp
94
+ CRR renderedRecs (map unExt -> exts) <- collectRecordsInRange range ideState nfp
95
+ let actions = map (mkCodeAction nfp exts pragma) renderedRecs
96
+ pure $ List actions
97
+
98
+ where
99
+ mkCodeAction :: NormalizedFilePath -> [Extension ] -> NextPragmaInfo -> RenderedRecordInfo -> Command |? CodeAction
100
+ mkCodeAction nfp exts pragma rec = InR CodeAction
101
+ { _title = mkCodeActionTitle exts
102
+ , _kind = Just CodeActionRefactorRewrite
103
+ , _diagnostics = Nothing
104
+ , _isPreferred = Nothing
105
+ , _disabled = Nothing
106
+ , _edit = Just $ mkWorkspaceEdit nfp edits
107
+ , _command = Nothing
108
+ , _xdata = Nothing
109
+ }
110
+ where
111
+ edits = catMaybes [ mkTextEdit rec , pragmaEdit ]
112
+
113
+ mkTextEdit :: RenderedRecordInfo -> Maybe TextEdit
114
+ mkTextEdit (RenderedRecordInfo ss r) = TextEdit <$> srcSpanToRange ss <*> pure r
115
+
116
+ -- NOTE(ozkutuk): `RecordPuns` extension is renamed to `NamedFieldPuns`
117
+ -- in GHC 9.4, but we still want to insert `NamedFieldPuns` in pre-9.4
118
+ -- GHC as well, hence the replacement.
119
+ -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6156
120
+ pragmaEdit :: Maybe TextEdit
121
+ pragmaEdit = if NamedFieldPuns `elem` exts
122
+ then Nothing
123
+ else Just $ patchExtName $ insertNewPragma pragma NamedFieldPuns
124
+ where
125
+ patchExtName = L. newText %~ T. replace " Record" " NamedField"
126
+
127
+ mkWorkspaceEdit :: NormalizedFilePath -> [TextEdit ] -> WorkspaceEdit
128
+ mkWorkspaceEdit nfp edits = WorkspaceEdit changes Nothing Nothing
129
+ where
130
+ changes = Just $ HashMap. singleton (fromNormalizedUri (normalizedFilePathToUri nfp)) (List edits)
131
+
132
+ mkCodeActionTitle :: [Extension ] -> Text
133
+ mkCodeActionTitle exts =
134
+ if NamedFieldPuns `elem` exts
135
+ then title
136
+ else title <> " (needs extension: NamedFieldPuns)"
137
+ where
138
+ title = " Expand record wildcard"
139
+
140
+ collectRecordsRule :: Recorder (WithPriority Log ) -> Rules ()
141
+ collectRecordsRule recorder = define (cmapWithPrio LogShake recorder) $ \ CollectRecords nfp -> do
142
+ tmr <- use TypeCheck nfp
143
+ let exts = getEnabledExtensions <$> tmr
144
+ recs = getRecords <$> tmr
145
+ renderedRecs = mapMaybe renderRecordInfo <$> recs
146
+ pure ([] , CRR <$> renderedRecs <*> exts)
147
+
148
+ getEnabledExtensions :: TcModuleResult -> [GhcExtension ]
149
+ getEnabledExtensions = map GhcExtension . toList . extensionFlags . ms_hspp_opts . pm_mod_summary . tmrParsed
150
+
151
+ getRecords :: TcModuleResult -> [RecordInfo ]
152
+ getRecords (tmrRenamed -> (hs_valds -> valBinds,_,_,_)) =
153
+ collectRecords valBinds
154
+
119
155
data CollectRecords = CollectRecords
120
156
deriving (Eq , Show , Generic )
121
157
@@ -156,7 +192,35 @@ renderRecordInfo :: RecordInfo -> Maybe RenderedRecordInfo
156
192
renderRecordInfo (RecordInfoPat ss pat) = RenderedRecordInfo ss <$> showRecordPat pat
157
193
renderRecordInfo (RecordInfoCon ss expr) = RenderedRecordInfo ss <$> showRecordCon expr
158
194
159
- -- collectRecords :: GenericQ [LPat (GhcPass 'Renamed)]
195
+ -- `Outputable` instance of `HsRecFields` does smart things to print
196
+ -- the records that originally had wildcards with dots, even after they
197
+ -- are removed by the renamer pass. Here `rec_dotdot` is set to
198
+ -- `Nothing` so that fields are printed without such post-processing.
199
+ preprocessRecord :: HsRecFields (GhcPass c ) arg -> HsRecFields (GhcPass c ) arg
200
+ preprocessRecord flds = flds { rec_dotdot = Nothing , rec_flds = rec_flds' }
201
+ where
202
+ no_pun_count = maybe (length (rec_flds flds)) unLoc (rec_dotdot flds)
203
+ -- Field binds of the explicit form (e.g. `{ a = a' }`) should be
204
+ -- left as is, hence the split.
205
+ (no_puns, puns) = splitAt no_pun_count (rec_flds flds)
206
+ -- `hsRecPun` is set to `True` in order to pretty-print the fields as field
207
+ -- puns (since there is similar mechanism in the `Outputable` instance as
208
+ -- explained above).
209
+ puns' = map (mapLoc (\ fld -> fld { hfbPun = True })) puns
210
+ rec_flds' = no_puns <> puns'
211
+
212
+ showRecordPat :: Outputable (Pat (GhcPass c )) => Pat (GhcPass c ) -> Maybe Text
213
+ showRecordPat pat@ (ConPat _ _ (RecCon flds)) =
214
+ Just $ printOutputable $
215
+ pat { pat_args = RecCon (preprocessRecord flds) }
216
+ showRecordPat _ = Nothing
217
+
218
+ showRecordCon :: Outputable (HsExpr (GhcPass c )) => HsExpr (GhcPass c ) -> Maybe Text
219
+ showRecordCon expr@ (RecordCon _ _ flds) =
220
+ Just $ printOutputable $
221
+ expr { rcon_flds = preprocessRecord flds }
222
+ showRecordCon _ = Nothing
223
+
160
224
collectRecords :: GenericQ [RecordInfo ]
161
225
collectRecords = everything (<>) (maybeToList . (Nothing `mkQ` getRecPatterns `extQ` getRecCons))
162
226
@@ -176,21 +240,6 @@ getRecPatterns conPat@(unLoc -> ConPat _ _ (RecCon flds))
176
240
mkRecInfo pat = RecordInfoPat (getLoc pat) (unLoc pat)
177
241
getRecPatterns _ = Nothing
178
242
179
- getEnabledExtensions :: TcModuleResult -> [GhcExtension ]
180
- getEnabledExtensions = map GhcExtension . toList . extensionFlags . ms_hspp_opts . pm_mod_summary . tmrParsed
181
-
182
- getRecords :: TcModuleResult -> [RecordInfo ]
183
- getRecords (tmrRenamed -> (hs_valds -> valBinds,_,_,_)) =
184
- collectRecords valBinds
185
-
186
- collectRecordsRule :: Recorder (WithPriority Log ) -> Rules ()
187
- collectRecordsRule recorder = define (cmapWithPrio LogShake recorder) $ \ CollectRecords nfp -> do
188
- tmr <- use TypeCheck nfp
189
- let exts = getEnabledExtensions <$> tmr
190
- recs = getRecords <$> tmr
191
- renderedRecs = mapMaybe renderRecordInfo <$> recs
192
- pure ([] , CRR <$> renderedRecs <*> exts)
193
-
194
243
collectRecords' :: MonadIO m => IdeState -> NormalizedFilePath -> ExceptT String m CollectRecordsResult
195
244
collectRecords' ideState =
196
245
handleMaybeM " Unable to TypeCheck"
@@ -207,57 +256,6 @@ collectRecordsInRange range ideState nfp = do
207
256
inRange :: RenderedRecordInfo -> Bool
208
257
inRange (RenderedRecordInfo ss _) = maybe False (subRange range) (srcSpanToRange ss)
209
258
210
-
211
- codeActionProvider :: PluginMethodHandler IdeState 'TextDocumentCodeAction
212
- codeActionProvider ideState pId (CodeActionParams _ _ docId range _) = pluginResponse $ do
213
- nfp <- getNormalizedFilePath (docId ^. L. uri)
214
- pragma <- getFirstPragma pId ideState nfp
215
- CRR renderedRecs (map unExt -> exts) <- collectRecordsInRange range ideState nfp
216
- let actions = map (mkCodeAction nfp exts pragma) renderedRecs
217
- pure $ List actions
218
-
219
- where
220
- mkCodeAction :: NormalizedFilePath -> [Extension ] -> NextPragmaInfo -> RenderedRecordInfo -> Command |? CodeAction
221
- mkCodeAction nfp exts pragma rec = InR CodeAction
222
- { _title = mkCodeActionTitle exts
223
- , _kind = Just CodeActionRefactorRewrite
224
- , _diagnostics = Nothing
225
- , _isPreferred = Nothing
226
- , _disabled = Nothing
227
- , _edit = Just $ mkWorkspaceEdit nfp edits
228
- , _command = Nothing
229
- , _xdata = Nothing
230
- }
231
- where
232
- edits = catMaybes [ mkTextEdit rec , pragmaEdit ]
233
-
234
- mkTextEdit :: RenderedRecordInfo -> Maybe TextEdit
235
- mkTextEdit (RenderedRecordInfo ss r) = TextEdit <$> srcSpanToRange ss <*> pure r
236
-
237
- -- NOTE(ozkutuk): `RecordPuns` extension is renamed to `NamedFieldPuns`
238
- -- in GHC 9.4, but we still want to insert `NamedFieldPuns` in pre-9.4
239
- -- GHC as well, hence the replacement.
240
- -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6156
241
- pragmaEdit :: Maybe TextEdit
242
- pragmaEdit = if NamedFieldPuns `elem` exts
243
- then Nothing
244
- else Just $ patchExtName $ insertNewPragma pragma NamedFieldPuns
245
- where
246
- patchExtName = L. newText %~ T. replace " Record" " NamedField"
247
-
248
- mkWorkspaceEdit :: NormalizedFilePath -> [TextEdit ] -> WorkspaceEdit
249
- mkWorkspaceEdit nfp edits = WorkspaceEdit changes Nothing Nothing
250
- where
251
- changes = Just $ HashMap. singleton (fromNormalizedUri (normalizedFilePathToUri nfp)) (List edits)
252
-
253
- mkCodeActionTitle :: [Extension ] -> Text
254
- mkCodeActionTitle exts =
255
- if NamedFieldPuns `elem` exts
256
- then title
257
- else title <> " (needs extension: NamedFieldPuns)"
258
- where
259
- title = " Expand record wildcard"
260
-
261
259
-- Copied from hls-alternate-number-format-plugin
262
260
getFirstPragma :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m NextPragmaInfo
263
261
getFirstPragma (PluginId pId) state nfp = handleMaybeM " Could not get NextPragmaInfo" $ do
0 commit comments