@@ -16,10 +16,12 @@ import Control.Monad (join)
1616import Development.IDE.GHC.Compat
1717import Development.IDE.Core.Rules
1818import Development.IDE.Core.RuleTypes
19+import Development.IDE.Core.Service
1920import Development.IDE.Core.Shake
2021import Development.IDE.GHC.Error
2122import Development.IDE.LSP.Server
2223import Development.IDE.Types.Location
24+import Development.IDE.Types.Options
2325import qualified Data.HashMap.Strict as Map
2426import qualified Data.HashSet as Set
2527import qualified Language.Haskell.LSP.Core as LSP
@@ -47,10 +49,12 @@ codeAction lsp state CodeActionParams{_textDocument=TextDocumentIdentifier uri,_
4749 -- logInfo (ideLogger ide) $ T.pack $ "Code action req: " ++ show arg
4850 contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri
4951 let text = Rope.toText . (_text :: VirtualFile -> Rope.Rope) <$> contents
50− parsedModule <- (runAction state . getParsedModule . toNormalizedFilePath) `traverse` uriToFilePath uri
52+ (ideOptions, parsedModule) <- runAction state $
53+ (,) <$> getIdeOptions
54+ <*> (getParsedModule . toNormalizedFilePath) `traverse` uriToFilePath uri
5155 pure $ List
5256 [ CACodeAction $ CodeAction title (Just CodeActionQuickFix) (Just $ List [x]) (Just edit) Nothing
53− | x <- xs, (title, tedit) <- suggestAction ( join parsedModule ) text x
57+ | x <- xs, (title, tedit) <- suggestAction ideOptions ( join parsedModule ) text x
5458 , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing
5559 ]
5660
@@ -89,8 +93,8 @@ executeAddSignatureCommand _lsp _ideState ExecuteCommandParams{..}
8993 | otherwise
9094 = return (Null, Nothing)
9195
92−suggestAction :: Maybe ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
93−suggestAction parsedModule text diag = concat
96+suggestAction :: IdeOptions -> Maybe ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
97+suggestAction ideOptions parsedModule text diag = concat
9498 [ suggestAddExtension diag
9599 , suggestExtendImport text diag
96100 , suggestFillHole diag
@@ -100,7 +104,9 @@ suggestAction parsedModule text diag = concat
100104 , suggestReplaceIdentifier text diag
101105 , suggestSignature True diag
102106 ] ++ concat
103− [ suggestRemoveRedundantImport pm text diag | Just pm <- [parsedModule]]
107+ [ suggestNewDefinition ideOptions pm text diag
108+ ++ suggestRemoveRedundantImport pm text diag
109+ | Just pm <- [parsedModule]]
104110
105111
106112suggestRemoveRedundantImport :: ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
@@ -138,6 +144,36 @@ suggestReplaceIdentifier contents Diagnostic{_range=_range@Range{..},..}
138144 = [ ("Replace with ‘" <> name <> "’", [mkRenameEdit contents _range name]) | name <- renameSuggestions ]
139145 | otherwise = []
140146
147+suggestNewDefinition :: IdeOptions -> ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
148+suggestNewDefinition ideOptions parsedModule contents Diagnostic{_message, _range}
149+-- * Variable not in scope:
150+-- suggestAcion :: Maybe T.Text -> Range -> Range
151+ | Just [name, typ] <- matchRegex message "Variable not in scope: ([^ ]+) :: ([^*•]+)"
152+ = newDefinitionAction ideOptions parsedModule _range name typ
153+ | Just [name, typ] <- matchRegex message "Found hole: _([^ ]+) :: ([^*•]+) Or perhaps"
154+ , [(label, newDefinitionEdits)] <- newDefinitionAction ideOptions parsedModule _range name typ
155+ = [(label, mkRenameEdit contents _range name : newDefinitionEdits)]
156+ | otherwise = []
157+ where
158+ message = unifySpaces _message
159+
160+newDefinitionAction :: IdeOptions -> ParsedModule -> Range -> T.Text -> T.Text -> [(T.Text, [TextEdit])]
161+newDefinitionAction IdeOptions{..} parsedModule Range{_start} name typ
162+ | Range _ lastLineP : _ <-
163+ [ srcSpanToRange l
164+ | (L l _) <- hsmodDecls
165+ , _start `isInsideSrcSpan` l]
166+ , nextLineP <- Position{ _line = _line lastLineP + 1, _character = 0}
167+ = [ ("Define " <> sig
168+ , [TextEdit (Range nextLineP nextLineP) (T.unlines ["", sig, name <> " = error \"not implemented\""])]
169+ )]
170+ | otherwise = []
171+ where
172+ colon = if optNewColonConvention then " : " else " :: "
173+ sig = name <> colon <> T.dropWhileEnd isSpace typ
174+ ParsedModule{pm_parsed_source = L _ HsModule{hsmodDecls}} = parsedModule
175+
176+
141177suggestFillTypeWildcard :: Diagnostic -> [(T.Text, [TextEdit])]
142178suggestFillTypeWildcard Diagnostic{_range=_range@Range{..},..}
143179-- Foo.hs:3:8: error:
@@ -255,8 +291,6 @@ suggestFixConstructorImport _ Diagnostic{_range=_range,..}
255291suggestSignature :: Bool -> Diagnostic -> [(T.Text, [TextEdit])]
256292suggestSignature isQuickFix Diagnostic{_range=_range@Range{..},..}
257293 | "Top-level binding with no type signature" `T.isInfixOf` _message = let
258− filterNewlines = T.concat . T.lines
259− unifySpaces = T.unwords . T.words
260294 signature = T.strip $ unifySpaces $ last $ T.splitOn "type signature: " $ filterNewlines _message
261295 startOfLine = Position (_line _start) 0
262296 beforeLine = Range startOfLine startOfLine
@@ -265,8 +299,6 @@ suggestSignature isQuickFix Diagnostic{_range=_range@Range{..},..}
265299 in [(title, [action])]
266300suggestSignature isQuickFix Diagnostic{_range=_range@Range{..},..}
267301 | "Polymorphic local binding with no type signature" `T.isInfixOf` _message = let
268− filterNewlines = T.concat . T.lines
269− unifySpaces = T.unwords . T.words
270302 signature = removeInitialForAll
271303 $ T.takeWhile (\x -> x/='*' && x/='•')
272304 $ T.strip $ unifySpaces $ last $ T.splitOn "type signature: " $ filterNewlines _message
@@ -403,7 +435,7 @@ addBindingToImportList binding importLine = case T.breakOn "(" importLine of
403435
404436-- | Returns Just (the submatches) for the first capture, or Nothing.
405437matchRegex :: T.Text -> T.Text -> Maybe [T.Text]
406−matchRegex message regex = case T.unwords (T.words message) =~~ regex of
438+matchRegex message regex = case unifySpaces message =~~ regex of
407439 Just (_ :: T.Text, _ :: T.Text, _ :: T.Text, bindings) -> Just bindings
408440 Nothing -> Nothing
409441
@@ -418,6 +450,12 @@ setHandlersCodeLens = PartialHandlers $ \WithMessage{..} x -> return x{
418450 LSP.executeCommandHandler = withResponseAndRequest RspExecuteCommand ReqApplyWorkspaceEdit executeAddSignatureCommand
419451 }
420452
453+filterNewlines :: T.Text -> T.Text
454+filterNewlines = T.concat . T.lines
455+
456+unifySpaces :: T.Text -> T.Text
457+unifySpaces = T.unwords . T.words
458+
421459--------------------------------------------------------------------------------
422460
423461type PositionIndexedString = [(Position, Char)]