@@ -16,6 +16,7 @@ import Development.IDE.GHC.Compat
1616import Development.IDE.Core.Rules
1717import Development.IDE.Core.RuleTypes
1818import Development.IDE.Core.Shake
19+ import Development.IDE.GHC.Error
1920import Development.IDE.LSP.Server
2021import Development.IDE.Types.Location
2122import qualified Data.HashMap.Strict as Map
@@ -85,8 +86,22 @@ executeAddSignatureCommand _lsp _ideState ExecuteCommandParams{..}
8586 | otherwise
8687 = return (Null , Nothing )
8788
88- suggestAction :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
89- suggestAction contents diag@ Diagnostic {_range= _range@ Range {.. },.. }
89+ suggestAction :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
90+ suggestAction text diag = concat
91+ [ suggestAddExtension diag
92+ , suggestExtendImport text diag
93+ , suggestFillHole diag
94+ , suggestFillTypeWildcard diag
95+ , suggestFixConstructorImport text diag
96+ , suggestModuleTypo diag
97+ , suggestRemoveRedundantImport text diag
98+ , suggestReplaceIdentifier text diag
99+ , suggestSignature True diag
100+ ]
101+
102+
103+ suggestRemoveRedundantImport :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
104+ suggestRemoveRedundantImport contents Diagnostic {_range= _range@ Range {.. },.. }
90105-- The qualified import of ‘many’ from module ‘Control.Applicative’ is redundant
91106 | Just [_, bindings] <- matchRegex _message " The( qualified)? import of ‘([^’]*)’ from module [^ ]* is redundant"
92107 , Just c <- contents
@@ -100,7 +115,10 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
100115-- To import instances alone, use: import Data.List()
101116 | _message =~ (" The( qualified)? import of [^ ]* is redundant" :: String )
102117 = [(" Remove import" , [TextEdit (extendToWholeLineIfPossible contents _range) " " ])]
118+ | otherwise = []
103119
120+ suggestReplaceIdentifier :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
121+ suggestReplaceIdentifier contents Diagnostic {_range= _range@ Range {.. },.. }
104122-- File.hs:52:41: error:
105123-- * Variable not in scope:
106124-- suggestAcion :: Maybe T.Text -> Range -> Range
@@ -114,15 +132,21 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
114132-- Module ‘Data.Text’ does not export ‘isPrfixOf’.
115133 | renameSuggestions@ (_: _) <- extractRenamableTerms _message
116134 = [ (" Replace with ‘" <> name <> " ’" , [mkRenameEdit contents _range name]) | name <- renameSuggestions ]
135+ | otherwise = []
117136
137+ suggestFillTypeWildcard :: Diagnostic -> [(T. Text , [TextEdit ])]
138+ suggestFillTypeWildcard Diagnostic {_range= _range@ Range {.. },.. }
118139-- Foo.hs:3:8: error:
119140-- * Found type wildcard `_' standing for `p -> p1 -> p'
120141
121142 | " Found type wildcard" `T.isInfixOf` _message
122143 , " standing for " `T.isInfixOf` _message
123144 , typeSignature <- extractWildCardTypeSignature _message
124145 = [(" Use type signature: ‘" <> typeSignature <> " ’" , [TextEdit _range typeSignature])]
146+ | otherwise = []
125147
148+ suggestAddExtension :: Diagnostic -> [(T. Text , [TextEdit ])]
149+ suggestAddExtension Diagnostic {_range= _range@ Range {.. },.. }
126150-- File.hs:22:8: error:
127151-- Illegal lambda-case (use -XLambdaCase)
128152-- File.hs:22:6: error:
@@ -145,7 +169,10 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
145169-- In the instance declaration for `Unit (m a)'
146170 | exts@ (_: _) <- filter (`Set.member` ghcExtensions) $ T. split (not . isAlpha) $ T. replace " -X" " " _message
147171 = [(" Add " <> x <> " extension" , [TextEdit (Range (Position 0 0 ) (Position 0 0 )) $ " {-# LANGUAGE " <> x <> " #-}\n " ]) | x <- exts]
172+ | otherwise = []
148173
174+ suggestModuleTypo :: Diagnostic -> [(T. Text , [TextEdit ])]
175+ suggestModuleTypo Diagnostic {_range= _range@ Range {.. },.. }
149176-- src/Development/IDE/Core/Compile.hs:58:1: error:
150177-- Could not find module ‘Data.Cha’
151178-- Perhaps you meant Data.Char (from base-4.12.0.0)
@@ -154,7 +181,10 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
154181 findSuggestedModules = map (head . T. words ) . drop 2 . T. lines
155182 proposeModule mod = (" replace with " <> mod , [TextEdit _range mod ])
156183 in map proposeModule $ nubOrd $ findSuggestedModules _message
184+ | otherwise = []
157185
186+ suggestFillHole :: Diagnostic -> [(T. Text , [TextEdit ])]
187+ suggestFillHole Diagnostic {_range= _range@ Range {.. },.. }
158188-- ...Development/IDE/LSP/CodeAction.hs:103:9: warning:
159189-- * Found hole: _ :: Int -> String
160190-- * In the expression: _
@@ -187,9 +217,36 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
187217 extractFitNames = map (T. strip . head . T. splitOn " :: " )
188218 in map proposeHoleFit $ nubOrd $ findSuggestedHoleFits _message
189219
190- | tlb @ [_] <- suggestSignature True diag = tlb
220+ | otherwise = []
191221
192- suggestAction _ _ = []
222+ suggestExtendImport :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
223+ suggestExtendImport contents Diagnostic {_range= _range,.. }
224+ | Just [binding, mod , srcspan] <-
225+ matchRegex _message
226+ " Perhaps you want to add ‘([^’]*)’ to the import list in the import of ‘([^’]*)’ *\\ ((.*)\\ ).$"
227+ , Just c <- contents
228+ = let range = case [ x | (x," " ) <- readSrcSpan (T. unpack srcspan)] of
229+ [s] -> let x = srcSpanToRange s
230+ in x{_end = (_end x){_character = succ (_character (_end x))}}
231+ _ -> error " bug in srcspan parser"
232+ importLine = textInRange range c
233+ in [(" Add " <> binding <> " to the import list of " <> mod
234+ , [TextEdit range (addBindingToImportList binding importLine)])]
235+ | otherwise = []
236+
237+ suggestFixConstructorImport :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
238+ suggestFixConstructorImport _ Diagnostic {_range= _range,.. }
239+ -- ‘Success’ is a data constructor of ‘Result’
240+ -- To import it use
241+ -- import Data.Aeson.Types( Result( Success ) )
242+ -- or
243+ -- import Data.Aeson.Types( Result(..) ) (lsp-ui)
244+ | Just [constructor, typ] <-
245+ matchRegex _message
246+ " ‘([^’]*)’ is a data constructor of ‘([^’]*)’ To import it use"
247+ = let fixedImport = typ <> " (" <> constructor <> " )"
248+ in [(" Fix import of " <> fixedImport, [TextEdit _range fixedImport])]
249+ | otherwise = []
193250
194251suggestSignature :: Bool -> Diagnostic -> [(T. Text , [TextEdit ])]
195252suggestSignature isQuickFix Diagnostic {_range= _range@ Range {.. },.. }
@@ -282,6 +339,7 @@ splitTextAtPosition (Position row col) x
282339 = (T. intercalate " \n " $ preRow ++ [preCol], T. intercalate " \n " $ postCol : postRow)
283340 | otherwise = (x, T. empty)
284341
342+ -- | Returns [start .. end[
285343textInRange :: Range -> T. Text -> T. Text
286344textInRange (Range (Position startRow startCol) (Position endRow endCol)) text =
287345 case compare startRow endRow of
@@ -338,11 +396,27 @@ dropBindingsFromImportLine bindings_ importLine =
338396 joinCloseParens (x : rest) = x : joinCloseParens rest
339397 joinCloseParens [] = []
340398
399+ -- | Extends an import list with a new binding.
400+ -- Assumes an import statement of the form:
401+ -- import (qualified) A (..) ..
402+ -- Places the new binding first, preserving whitespace.
403+ -- Copes with multi-line import lists
404+ addBindingToImportList :: T. Text -> T. Text -> T. Text
405+ addBindingToImportList binding importLine = case T. breakOn " (" importLine of
406+ (pre, T. uncons -> Just (_, rest)) ->
407+ case T. uncons (T. dropWhile isSpace rest) of
408+ Just (' )' , _) -> T. concat [pre, " (" , binding, rest]
409+ _ -> T. concat [pre, " (" , binding, " , " , rest]
410+ _ ->
411+ error
412+ $ " importLine does not have the expected structure: "
413+ <> T. unpack importLine
414+
341415-- | Returns Just (the submatches) for the first capture, or Nothing.
342416matchRegex :: T. Text -> T. Text -> Maybe [T. Text ]
343- matchRegex message regex = case message =~~ regex of
344- Just (_ :: T. Text , _ :: T. Text , _ :: T. Text , bindings ) -> Just bindings
345- Nothing -> Nothing
417+ matchRegex message regex = case T. unwords ( T. words message) =~~ regex of
418+ Just (_ :: T. Text , _ :: T. Text , _ :: T. Text , bindings ) -> Just bindings
419+ Nothing -> Nothing
346420
347421setHandlersCodeAction :: PartialHandlers
348422setHandlersCodeAction = PartialHandlers $ \ WithMessage {.. } x -> return x{
0 commit comments