3
3
4
4
{-# LANGUAGE CPP #-}
5
5
{-# LANGUAGE DuplicateRecordFields #-}
6
- {-# LANGUAGE GADTs #-}
7
- {-# LANGUAGE RankNTypes #-}
8
6
#include "ghc-api-version.h"
9
7
10
8
-- | Go to the definition of a variable.
9
+
11
10
module Development.IDE.Plugin.CodeAction
12
11
( descriptor
13
12
@@ -20,7 +19,6 @@ import Control.Applicative ((<|>))
20
19
import Control.Arrow (second ,
21
20
(>>>) )
22
21
import Control.Concurrent.Extra (readVar )
23
- import Control.Lens (alaf )
24
22
import Control.Monad (guard , join )
25
23
import Control.Monad.IO.Class
26
24
import Data.Char
@@ -34,7 +32,6 @@ import Data.List.NonEmpty (NonEmpty ((:
34
32
import qualified Data.List.NonEmpty as NE
35
33
import qualified Data.Map as M
36
34
import Data.Maybe
37
- import Data.Monoid (Ap (.. ))
38
35
import qualified Data.Rope.UTF16 as Rope
39
36
import qualified Data.Set as S
40
37
import qualified Data.Text as T
@@ -47,13 +44,12 @@ import Development.IDE.GHC.Error
47
44
import Development.IDE.GHC.ExactPrint
48
45
import Development.IDE.GHC.Util (prettyPrint ,
49
46
printRdrName )
47
+ import Development.IDE.Plugin.CodeAction.Args
50
48
import Development.IDE.Plugin.CodeAction.ExactPrint
51
49
import Development.IDE.Plugin.CodeAction.PositionIndexed
52
50
import Development.IDE.Plugin.TypeLenses (GetGlobalBindingTypeSigs (GetGlobalBindingTypeSigs ),
53
- GlobalBindingTypeSigsResult ,
54
51
suggestSignature )
55
52
import Development.IDE.Spans.Common
56
- import Development.IDE.Spans.LocalBindings (Bindings )
57
53
import Development.IDE.Types.Exports
58
54
import Development.IDE.Types.HscEnvEq
59
55
import Development.IDE.Types.Location
@@ -116,68 +112,44 @@ codeAction state _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range Cod
116
112
exportsMap = localExports <> pkgExports
117
113
df = ms_hspp_opts . pm_mod_summary <$> parsedModule
118
114
actions =
119
- [ mkCA title [x] edit
120
- | x <- xs, (title, tedit) <- suggestAction exportsMap ideOptions parsedModule text df annotatedPS tcM har bindings gblSigs x
115
+ [ mkCA title kind isPreferred [x] edit
116
+ | x <- xs, (title, kind, isPreferred, tedit) <- suggestAction $ CodeActionArgs exportsMap ideOptions parsedModule text df annotatedPS tcM har bindings gblSigs x
121
117
, let edit = WorkspaceEdit (Just $ Map. singleton uri $ List tedit) Nothing
122
118
]
123
119
actions' = caRemoveRedundantImports parsedModule text diag xs uri
124
120
<> actions
125
121
<> caRemoveInvalidExports parsedModule text diag xs uri
126
122
pure $ Right $ List actions'
127
123
128
- mkCA :: T. Text -> [Diagnostic ] -> WorkspaceEdit -> (Command |? CodeAction )
129
- mkCA title diags edit =
130
- InR $ CodeAction title (Just CodeActionQuickFix ) (Just $ List diags) Nothing Nothing (Just edit) Nothing
131
-
132
- rewrite ::
133
- Maybe DynFlags ->
134
- Maybe (Annotated ParsedSource ) ->
135
- (DynFlags -> ParsedSource -> [(T. Text , [Rewrite ])]) ->
136
- [(T. Text , [TextEdit ])]
137
- rewrite (Just df) (Just ps) f
138
- | Right edit <- (traverse . traverse )
139
- (alaf Ap foldMap (rewriteToEdit df (annsA ps)))
140
- (f df $ astA ps) = edit
141
- rewrite _ _ _ = []
142
-
143
- suggestAction
144
- :: ExportsMap
145
- -> IdeOptions
146
- -> Maybe ParsedModule
147
- -> Maybe T. Text
148
- -> Maybe DynFlags
149
- -> Maybe (Annotated ParsedSource )
150
- -> Maybe TcModuleResult
151
- -> Maybe HieAstResult
152
- -> Maybe Bindings
153
- -> Maybe GlobalBindingTypeSigsResult
154
- -> Diagnostic
155
- -> [(T. Text , [TextEdit ])]
156
- suggestAction packageExports ideOptions parsedModule text df annSource tcM har bindings gblSigs diag =
157
- concat
158
- -- Order these suggestions by priority
159
- [ suggestSignature True gblSigs tcM bindings diag
160
- , rewrite df annSource $ \ _ ps -> suggestExtendImport packageExports ps diag
161
- , rewrite df annSource $ \ df ps ->
162
- suggestImportDisambiguation df text ps diag
163
- , rewrite df annSource $ \ _ ps -> suggestNewOrExtendImportForClassMethod packageExports ps diag
164
- , suggestFillTypeWildcard diag
165
- , suggestFixConstructorImport text diag
166
- , suggestModuleTypo diag
167
- , suggestReplaceIdentifier text diag
168
- , removeRedundantConstraints text diag
169
- , suggestAddTypeAnnotationToSatisfyContraints text diag
170
- , rewrite df annSource $ \ df ps -> suggestConstraint df ps diag
171
- , rewrite df annSource $ \ _ ps -> suggestImplicitParameter ps diag
172
- , rewrite df annSource $ \ _ ps -> suggestHideShadow ps tcM har diag
173
- ] ++ concat
174
- [ suggestNewDefinition ideOptions pm text diag
175
- ++ suggestNewImport packageExports pm diag
176
- ++ suggestDeleteUnusedBinding pm text diag
177
- ++ suggestExportUnusedTopBinding text pm diag
178
- | Just pm <- [parsedModule]
179
- ] ++
180
- suggestFillHole diag -- Lowest priority
124
+ mkCA :: T. Text -> Maybe CodeActionKind -> Maybe Bool -> [Diagnostic ] -> WorkspaceEdit -> (Command |? CodeAction )
125
+ mkCA title kind isPreferred diags edit =
126
+ InR $ CodeAction title kind (Just $ List diags) isPreferred Nothing (Just edit) Nothing
127
+
128
+ suggestAction :: CodeActionArgs -> GhcideCodeActions
129
+ suggestAction caa =
130
+ concat -- Order these suggestions by priority
131
+ [ wrap $ suggestSignature True
132
+ , wrap suggestExtendImport
133
+ , wrap suggestImportDisambiguation
134
+ , wrap suggestNewOrExtendImportForClassMethod
135
+ , wrap suggestFillTypeWildcard
136
+ , wrap suggestFixConstructorImport
137
+ , wrap suggestModuleTypo
138
+ , wrap suggestReplaceIdentifier
139
+ , wrap removeRedundantConstraints
140
+ , wrap suggestAddTypeAnnotationToSatisfyContraints
141
+ , wrap suggestConstraint
142
+ , wrap suggestImplicitParameter
143
+ , wrap suggestHideShadow
144
+ , wrap suggestNewDefinition
145
+ , wrap suggestNewImport
146
+ , wrap suggestDeleteUnusedBinding
147
+ , wrap suggestExportUnusedTopBinding
148
+ , wrap suggestFillHole -- Lowest priority
149
+ ]
150
+ where
151
+ wrap :: ToCodeAction a => a -> GhcideCodeActions
152
+ wrap = toCodeAction caa
181
153
182
154
findSigOfDecl :: (IdP p -> Bool ) -> [LHsDecl p ] -> Maybe (Sig p )
183
155
findSigOfDecl pred decls =
@@ -304,7 +276,7 @@ caRemoveRedundantImports m contents digs ctxDigs uri
304
276
= caRemoveCtx ++ [caRemoveAll]
305
277
| otherwise = []
306
278
where
307
- removeSingle title tedit diagnostic = mkCA title [diagnostic] WorkspaceEdit {.. } where
279
+ removeSingle title tedit diagnostic = mkCA title ( Just CodeActionQuickFix ) Nothing [diagnostic] WorkspaceEdit {.. } where
308
280
_changes = Just $ Map. singleton uri $ List tedit
309
281
_documentChanges = Nothing
310
282
removeAll tedit = InR $ CodeAction {.. } where
@@ -504,7 +476,7 @@ data ExportsAs = ExportName | ExportPattern | ExportAll
504
476
getLocatedRange :: Located a -> Maybe Range
505
477
getLocatedRange = srcSpanToRange . getLoc
506
478
507
- suggestExportUnusedTopBinding :: Maybe T. Text -> ParsedModule -> Diagnostic -> [(T. Text , [ TextEdit ] )]
479
+ suggestExportUnusedTopBinding :: Maybe T. Text -> ParsedModule -> Diagnostic -> [(T. Text , TextEdit )]
508
480
suggestExportUnusedTopBinding srcOpt ParsedModule {pm_parsed_source = L _ HsModule {.. }} Diagnostic {.. }
509
481
-- Foo.hs:4:1: warning: [-Wunused-top-binds] Defined but not used: ‘f’
510
482
-- Foo.hs:5:1: warning: [-Wunused-top-binds] Defined but not used: type constructor or class ‘F’
@@ -522,7 +494,7 @@ suggestExportUnusedTopBinding srcOpt ParsedModule{pm_parsed_source = L _ HsModul
522
494
, Just needComma <- needsComma source <$> hsmodExports
523
495
, let exportName = (if needComma then " ," else " " ) <> printExport exportType name
524
496
insertPos = pos {_character = pred $ _character pos}
525
- = [(" Export ‘" <> name <> " ’" , [ TextEdit (Range insertPos insertPos) exportName] )]
497
+ = [(" Export ‘" <> name <> " ’" , TextEdit (Range insertPos insertPos) exportName)]
526
498
| otherwise = []
527
499
where
528
500
-- we get the last export and the closing bracket and check for comma in that range
@@ -669,30 +641,30 @@ newDefinitionAction IdeOptions{..} parsedModule Range{_start} name typ
669
641
ParsedModule {pm_parsed_source = L _ HsModule {hsmodDecls}} = parsedModule
670
642
671
643
672
- suggestFillTypeWildcard :: Diagnostic -> [(T. Text , [ TextEdit ] )]
644
+ suggestFillTypeWildcard :: Diagnostic -> [(T. Text , TextEdit )]
673
645
suggestFillTypeWildcard Diagnostic {_range= _range,.. }
674
646
-- Foo.hs:3:8: error:
675
647
-- * Found type wildcard `_' standing for `p -> p1 -> p'
676
648
677
649
| " Found type wildcard" `T.isInfixOf` _message
678
650
, " standing for " `T.isInfixOf` _message
679
651
, typeSignature <- extractWildCardTypeSignature _message
680
- = [(" Use type signature: ‘" <> typeSignature <> " ’" , [ TextEdit _range typeSignature] )]
652
+ = [(" Use type signature: ‘" <> typeSignature <> " ’" , TextEdit _range typeSignature)]
681
653
| otherwise = []
682
654
683
- suggestModuleTypo :: Diagnostic -> [(T. Text , [ TextEdit ] )]
655
+ suggestModuleTypo :: Diagnostic -> [(T. Text , TextEdit )]
684
656
suggestModuleTypo Diagnostic {_range= _range,.. }
685
657
-- src/Development/IDE/Core/Compile.hs:58:1: error:
686
658
-- Could not find module ‘Data.Cha’
687
659
-- Perhaps you meant Data.Char (from base-4.12.0.0)
688
660
| " Could not find module" `T.isInfixOf` _message
689
661
, " Perhaps you meant" `T.isInfixOf` _message = let
690
662
findSuggestedModules = map (head . T. words ) . drop 2 . T. lines
691
- proposeModule mod = (" replace with " <> mod , [ TextEdit _range mod ] )
663
+ proposeModule mod = (" replace with " <> mod , TextEdit _range mod )
692
664
in map proposeModule $ nubOrd $ findSuggestedModules _message
693
665
| otherwise = []
694
666
695
- suggestFillHole :: Diagnostic -> [(T. Text , [ TextEdit ] )]
667
+ suggestFillHole :: Diagnostic -> [(T. Text , TextEdit )]
696
668
suggestFillHole Diagnostic {_range= _range,.. }
697
669
| Just holeName <- extractHoleName _message
698
670
, (holeFits, refFits) <- processHoleSuggestions (T. lines _message)
@@ -703,7 +675,7 @@ suggestFillHole Diagnostic{_range=_range,..}
703
675
extractHoleName = fmap head . flip matchRegexUnifySpaces " Found hole: ([^ ]*)"
704
676
proposeHoleFit holeName parenthise name =
705
677
( " replace " <> holeName <> " with " <> name
706
- , [ TextEdit _range $ if parenthise then parens name else name] )
678
+ , TextEdit _range $ if parenthise then parens name else name)
707
679
parens x = " (" <> x <> " )"
708
680
709
681
processHoleSuggestions :: [T. Text ] -> ([T. Text ], [T. Text ])
@@ -766,7 +738,7 @@ getIndentedGroupsBy pred inp = case dropWhile (not.pred) inp of
766
738
indentation :: T. Text -> Int
767
739
indentation = T. length . T. takeWhile isSpace
768
740
769
- suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T. Text , [ Rewrite ] )]
741
+ suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T. Text , Rewrite )]
770
742
suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic {_range= _range,.. }
771
743
| Just [binding, mod , srcspan] <-
772
744
matchRegexUnifySpaces _message
@@ -785,7 +757,7 @@ suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_
785
757
Just decl <- findImportDeclByRange decls range,
786
758
Just ident <- lookupExportMap binding mod
787
759
= [ ( " Add " <> renderImportStyle importStyle <> " to the import list of " <> mod
788
- , [ uncurry extendImport (unImportStyle importStyle) decl]
760
+ , uncurry extendImport (unImportStyle importStyle) decl
789
761
)
790
762
| importStyle <- NE. toList $ importStyles ident
791
763
]
@@ -955,8 +927,8 @@ disambiguateSymbol pm Diagnostic {..} (T.unpack -> symbol) = \case
955
927
findImportDeclByRange :: [LImportDecl GhcPs ] -> Range -> Maybe (LImportDecl GhcPs )
956
928
findImportDeclByRange xs range = find (\ (L l _)-> srcSpanToRange l == Just range) xs
957
929
958
- suggestFixConstructorImport :: Maybe T. Text -> Diagnostic -> [(T. Text , [ TextEdit ] )]
959
- suggestFixConstructorImport _ Diagnostic {_range= _range,.. }
930
+ suggestFixConstructorImport :: Diagnostic -> [(T. Text , TextEdit )]
931
+ suggestFixConstructorImport Diagnostic {_range= _range,.. }
960
932
-- ‘Success’ is a data constructor of ‘Result’
961
933
-- To import it use
962
934
-- import Data.Aeson.Types( Result( Success ) )
@@ -966,16 +938,16 @@ suggestFixConstructorImport _ Diagnostic{_range=_range,..}
966
938
matchRegexUnifySpaces _message
967
939
" ‘([^’]*)’ is a data constructor of ‘([^’]*)’ To import it use"
968
940
= let fixedImport = typ <> " (" <> constructor <> " )"
969
- in [(" Fix import of " <> fixedImport, [ TextEdit _range fixedImport] )]
941
+ in [(" Fix import of " <> fixedImport, TextEdit _range fixedImport)]
970
942
| otherwise = []
971
943
-- | Suggests a constraint for a declaration for which a constraint is missing.
972
- suggestConstraint :: DynFlags -> ParsedSource -> Diagnostic -> [(T. Text , [ Rewrite ] )]
944
+ suggestConstraint :: DynFlags -> ParsedSource -> Diagnostic -> [(T. Text , Rewrite )]
973
945
suggestConstraint df parsedModule diag@ Diagnostic {.. }
974
946
| Just missingConstraint <- findMissingConstraint _message
975
947
= let codeAction = if _message =~ (" the type signature for:" :: String )
976
948
then suggestFunctionConstraint df parsedModule
977
949
else suggestInstanceConstraint df parsedModule
978
- in map (second ( : [] )) $ codeAction diag missingConstraint
950
+ in codeAction diag missingConstraint
979
951
| otherwise = []
980
952
where
981
953
findMissingConstraint :: T. Text -> Maybe T. Text
@@ -1031,14 +1003,14 @@ suggestInstanceConstraint df (L _ HsModule {hsmodDecls}) Diagnostic {..} missing
1031
1003
suggestImplicitParameter ::
1032
1004
ParsedSource ->
1033
1005
Diagnostic ->
1034
- [(T. Text , [ Rewrite ] )]
1006
+ [(T. Text , Rewrite )]
1035
1007
suggestImplicitParameter (L _ HsModule {hsmodDecls}) Diagnostic {_message, _range}
1036
1008
| Just [implicitT] <- matchRegexUnifySpaces _message " Unbound implicit parameter \\ (([^:]+::.+)\\ ) arising" ,
1037
1009
Just (L _ (ValD _ FunBind {fun_id = L _ funId})) <- findDeclContainingLoc (_start _range) hsmodDecls,
1038
1010
Just (TypeSig _ _ HsWC {hswc_body = HsIB {hsib_body}}) <- findSigOfDecl (== funId) hsmodDecls
1039
1011
=
1040
1012
[( " Add " <> implicitT <> " to the context of " <> T. pack (printRdrName funId)
1041
- , [ appendConstraint (T. unpack implicitT) hsib_body] )]
1013
+ , appendConstraint (T. unpack implicitT) hsib_body)]
1042
1014
| otherwise = []
1043
1015
1044
1016
findTypeSignatureName :: T. Text -> Maybe T. Text
@@ -1086,7 +1058,7 @@ suggestFunctionConstraint df (L _ HsModule {hsmodDecls}) Diagnostic {..} missing
1086
1058
<> " ` to the context of the type signature for `" <> typeSignatureName <> " `"
1087
1059
1088
1060
-- | Suggests the removal of a redundant constraint for a type signature.
1089
- removeRedundantConstraints :: Maybe T. Text -> Diagnostic -> [(T. Text , [ TextEdit ] )]
1061
+ removeRedundantConstraints :: Maybe T. Text -> Diagnostic -> [(T. Text , TextEdit )]
1090
1062
removeRedundantConstraints mContents Diagnostic {.. }
1091
1063
-- • Redundant constraint: Eq a
1092
1064
-- • In the type signature for:
@@ -1108,7 +1080,7 @@ removeRedundantConstraints mContents Diagnostic{..}
1108
1080
endOfConstraint = Position typeSignatureLine $
1109
1081
typeSignatureFirstChar + T. length (constraints <> " => " )
1110
1082
range = Range startOfConstraint endOfConstraint
1111
- in [(actionTitle redundantConstraintList typeSignatureName, [ TextEdit range newConstraints] )]
1083
+ in [(actionTitle redundantConstraintList typeSignatureName, TextEdit range newConstraints)]
1112
1084
| otherwise = []
1113
1085
where
1114
1086
parseConstraints :: T. Text -> [T. Text ]
@@ -1197,7 +1169,7 @@ suggestNewOrExtendImportForClassMethod packageExportsMap ps Diagnostic {_message
1197
1169
]
1198
1170
<> maybeToList ((" Import " <> moduleNameText,) <$> fmap pure (newImportAll (T. unpack moduleNameText) ps))
1199
1171
1200
- suggestNewImport :: ExportsMap -> ParsedModule -> Diagnostic -> [(T. Text , [ TextEdit ] )]
1172
+ suggestNewImport :: ExportsMap -> ParsedModule -> Diagnostic -> [(T. Text , TextEdit )]
1201
1173
suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule {.. }} Diagnostic {_message}
1202
1174
| msg <- unifySpaces _message
1203
1175
, Just thingMissing <- extractNotInScopeName msg
@@ -1217,7 +1189,7 @@ suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule
1217
1189
, insertPos <- Position insertLine 0
1218
1190
, extendImportSuggestions <- matchRegexUnifySpaces msg
1219
1191
" Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’"
1220
- = [(imp, [ TextEdit (Range insertPos insertPos) (imp <> " \n " )] )
1192
+ = [(imp, TextEdit (Range insertPos insertPos) (imp <> " \n " ))
1221
1193
| imp <- sort $ constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions
1222
1194
]
1223
1195
suggestNewImport _ _ _ = []
0 commit comments