on master help sign in
More code actions for hie-core (#1948)

* Push the suggestion work further up * Make LspFuncs an argument to the handlers * Actually pass around the contents of the buffer to suggestAction * Make suggestAction do sensible figuring out if you remove the next line too * Better indentation * Code action to add GHC extensions as required * Deal with extra arguments to LSP handler

commit42221e66d53c54b956ab52ce247dbf12ca75ce94 authorNeil Mitchell <35463327+neil-da@users.noreply.github.com> dateMon 1 Jul 2019, 08:30 (7 years ago) parentff492c3
6 files changed+70−22
src/Development/IDE/LSP/CodeAction.hs+57−9 src/Development/IDE/LSP/Definition.hs+1−1 src/Development/IDE/LSP/Hover.hs+1−1 src/Development/IDE/LSP/LanguageServer.hs+5−5 src/Development/IDE/LSP/Notifications.hs+4−4 src/Development/IDE/LSP/Server.hs+2−2
src/Development/IDE/LSP/CodeAction.hs+57−9
@@ -10,40 +10,88 @@ module Development.IDE.LSP.CodeAction
1010 ) where
1111
1212import Language.Haskell.LSP.Types
13−
13+import GHC.LanguageExtensions.Type
1414import Development.IDE.Core.Rules
1515import Development.IDE.LSP.Server
1616import qualified Data.HashMap.Strict as Map
17+import qualified Data.HashSet as Set
1718import qualified Language.Haskell.LSP.Core as LSP
18−import qualified Language.Haskell.LSP.Types as LSP
19+import Language.Haskell.LSP.VFS
1920import Language.Haskell.LSP.Messages
20−
21+import qualified Data.Rope.UTF16 as Rope
22+import Data.Char
2123import qualified Data.Text as T
2224
2325-- | Generate code actions.
2426codeAction
25− :: IdeState
27+ :: LSP.LspFuncs ()
28+ -> IdeState
2629 -> CodeActionParams
2730 -> IO (List CAResult)
28−codeAction _ CodeActionParams{_textDocument=TextDocumentIdentifier uri,_context=CodeActionContext{_diagnostics=List xs}} = do
31+codeAction lsp _ CodeActionParams{_textDocument=TextDocumentIdentifier uri,_context=CodeActionContext{_diagnostics=List xs}} = do
2932 -- disable logging as its quite verbose
3033 -- logInfo (ideLogger ide) $ T.pack $ "Code action req: " ++ show arg
34+ contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri
35+ let text = Rope.toText . (_text :: VirtualFile -> Rope.Rope) <$> contents
3136 pure $ List
3237 [ CACodeAction $ CodeAction title (Just CodeActionQuickFix) (Just $ List [x]) (Just edit) Nothing
33− | x <- xs, (title, edit) <- suggestAction uri x]
38+ | x <- xs, (title, tedit) <- suggestAction text x
39+ , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing
40+ ]
41+
3442
43+suggestAction :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
44+suggestAction contents Diagnostic{_range=_range@Range{..},..}
3545
36−suggestAction :: Uri -> Diagnostic -> [(T.Text, LSP.WorkspaceEdit)]
37−suggestAction uri Diagnostic{..}
3846-- File.hs:16:1: warning:
3947-- The import of `Data.List' is redundant
4048-- except perhaps to import instances from `Data.List'
4149-- To import instances alone, use: import Data.List()
4250 | "The import of " `T.isInfixOf` _message
4351 , " is redundant" `T.isInfixOf` _message
44− = [("Remove import", WorkspaceEdit (Just $ Map.singleton uri $ List [TextEdit _range ""]) Nothing)]
52+ , let newlineAfter = maybe False (T.isPrefixOf "\n" . T.dropWhile (\x -> isSpace x && x /= '\n') . snd . textAtPosition _end) contents
53+ , let extend = newlineAfter && _character _start == 0 -- takes up an entire line, so remove the whole line
54+ = [("Remove import", [TextEdit (if extend then Range _start (Position (_line _end + 1) 0) else _range) ""])]
55+
56+-- File.hs:22:8: error:
57+-- Illegal lambda-case (use -XLambdaCase)
58+-- File.hs:22:6: error:
59+-- Illegal view pattern: x -> foo
60+-- Use ViewPatterns to enable view patterns
61+-- File.hs:26:8: error:
62+-- Illegal `..' in record pattern
63+-- Use RecordWildCards to permit this
64+-- File.hs:53:28: error:
65+-- Illegal tuple section: use TupleSections
66+-- File.hs:238:29: error:
67+-- * Can't make a derived instance of `Data FSATrace':
68+-- You need DeriveDataTypeable to derive an instance for this class
69+-- * In the data declaration for `FSATrace'
70+-- C:\Neil\shake\src\Development\Shake\Command.hs:515:31: error:
71+-- * Illegal equational constraint a ~ ()
72+-- (Use GADTs or TypeFamilies to permit this)
73+-- * In the context: a ~ ()
74+-- While checking an instance declaration
75+-- In the instance declaration for `Unit (m a)'
76+ | exts@(_:_) <- filter (`Set.member` ghcExtensions) $ T.split (not . isAlpha) $ T.replace "-X" "" _message
77+ = [("Add " <> x <> " extension", [TextEdit (Range (Position 0 0) (Position 0 0)) $ "{-# LANGUAGE " <> x <> " #-}\n"]) | x <- exts]
78+
4579suggestAction _ _ = []
4680
81+
82+-- | All the GHC extensions
83+ghcExtensions :: Set.HashSet T.Text
84+ghcExtensions = Set.fromList $ map (T.pack . show) [Cpp .. StarIsType] -- use enumerate from GHC 8.8 and beyond
85+
86+
87+textAtPosition :: Position -> T.Text -> (T.Text, T.Text)
88+textAtPosition (Position row col) x
89+ | (preRow, mid:postRow) <- splitAt row $ T.splitOn "\n" x
90+ , (preCol, postCol) <- T.splitAt col mid
91+ = (T.intercalate "\n" $ preRow ++ [preCol], T.intercalate "\n" $ postCol : postRow)
92+ | otherwise = (x, T.empty)
93+
94+
4795setHandlersCodeAction :: PartialHandlers
4896setHandlersCodeAction = PartialHandlers $ \WithMessage{..} x -> return x{
4997 LSP.codeActionHandler = withResponse RspCodeAction codeAction
src/Development/IDE/LSP/Definition.hs+1−1
@@ -40,5 +40,5 @@ gotoDefinition ide (TextDocumentPositionParams (TextDocumentIdentifier uri) pos)
4040
4141setHandlersDefinition :: PartialHandlers
4242setHandlersDefinition = PartialHandlers $ \WithMessage{..} x -> return x{
43− LSP.definitionHandler = withResponse RspDefinition gotoDefinition
43+ LSP.definitionHandler = withResponse RspDefinition $ const gotoDefinition
4444 }
src/Development/IDE/LSP/Hover.hs+1−1
@@ -44,5 +44,5 @@ onHover ide (TextDocumentPositionParams (TextDocumentIdentifier uri) pos) = do
4444
4545setHandlersHover :: PartialHandlers
4646setHandlersHover = PartialHandlers $ \WithMessage{..} x -> return x{
47− LSP.hoverHandler = withResponse RspHover onHover
47+ LSP.hoverHandler = withResponse RspHover $ const onHover
4848 }
src/Development/IDE/LSP/LanguageServer.hs+5−5
@@ -66,7 +66,7 @@ runLanguageServer options userHandlers getIdeState = do
6666 clientMsgBarrier <- newBarrier
6767
6868 let withResponse wrap f = Just $ \r -> writeChan clientMsgChan $ Response r wrap f
69− let withNotification old f = Just $ \r -> writeChan clientMsgChan $ Notification r (\ide x -> f ide x >> whenJust old ($ r))
69+ let withNotification old f = Just $ \r -> writeChan clientMsgChan $ Notification r (\lsp ide x -> f lsp ide x >> whenJust old ($ r))
7070 let PartialHandlers parts =
7171 setHandlersIgnore <> -- least important
7272 setHandlersDefinition <> setHandlersHover <> setHandlersCodeAction <> -- useful features someone may override
@@ -94,14 +94,14 @@ runLanguageServer options userHandlers getIdeState = do
9494 msg <- readChan clientMsgChan
9595 case msg of
9696 Notification x@NotificationMessage{_params} act -> do
97− catch (act ide _params) $ \(e :: SomeException) ->
97+ catch (act lspFuncs ide _params) $ \(e :: SomeException) ->
9898 logError (ideLogger ide) $ T.pack $
9999 "Unexpected exception on notification, please report!\n" ++
100100 "Message: " ++ show x ++ "\n" ++
101101 "Exception: " ++ show e
102102 Response x@RequestMessage{_id, _params} wrap act ->
103103 catch (do
104− res <- act ide _params
104+ res <- act lspFuncs ide _params
105105 sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) (Just res) Nothing
106106 ) $ \(e :: SomeException) -> do
107107 logError (ideLogger ide) $ T.pack $
@@ -126,8 +126,8 @@ setHandlersIgnore = PartialHandlers $ \_ x -> return x
126126-- | A message that we need to deal with - the pieces are split up with existentials to gain additional type safety
127127-- and defer precise processing until later (allows us to keep at a higher level of abstraction slightly longer)
128128data Message
129− = forall m req resp . (Show m, Show req) => Response (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (IdeState -> req -> IO resp)
130− | forall m req . (Show m, Show req) => Notification (NotificationMessage m req) (IdeState -> req -> IO ())
129+ = forall m req resp . (Show m, Show req) => Response (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (LSP.LspFuncs () -> IdeState -> req -> IO resp)
130+ | forall m req . (Show m, Show req) => Notification (NotificationMessage m req) (LSP.LspFuncs () -> IdeState -> req -> IO ())
131131
132132
133133modifyOptions :: LSP.Options -> LSP.Options
src/Development/IDE/LSP/Notifications.hs+4−4
@@ -32,24 +32,24 @@ whenUriFile ide uri act = case LSP.uriToFilePath uri of
3232setHandlersNotifications :: PartialHandlers
3333setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
3434 {LSP.didOpenTextDocumentNotificationHandler = withNotification (LSP.didOpenTextDocumentNotificationHandler x) $
35− \ide (DidOpenTextDocumentParams TextDocumentItem{_uri}) -> do
35+ \_ ide (DidOpenTextDocumentParams TextDocumentItem{_uri}) -> do
3636 setSomethingModified ide
3737 whenUriFile ide _uri $ \file ->
3838 modifyFilesOfInterest ide (S.insert file)
3939 logInfo (ideLogger ide) $ "Opened text document: " <> getUri _uri
4040
4141 ,LSP.didChangeTextDocumentNotificationHandler = withNotification (LSP.didChangeTextDocumentNotificationHandler x) $
42− \ide (DidChangeTextDocumentParams VersionedTextDocumentIdentifier{_uri} _) -> do
42+ \_ ide (DidChangeTextDocumentParams VersionedTextDocumentIdentifier{_uri} _) -> do
4343 setSomethingModified ide
4444 logInfo (ideLogger ide) $ "Modified text document: " <> getUri _uri
4545
4646 ,LSP.didSaveTextDocumentNotificationHandler = withNotification (LSP.didSaveTextDocumentNotificationHandler x) $
47− \ide (DidSaveTextDocumentParams TextDocumentIdentifier{_uri}) -> do
47+ \_ ide (DidSaveTextDocumentParams TextDocumentIdentifier{_uri}) -> do
4848 setSomethingModified ide
4949 logInfo (ideLogger ide) $ "Saved text document: " <> getUri _uri
5050
5151 ,LSP.didCloseTextDocumentNotificationHandler = withNotification (LSP.didCloseTextDocumentNotificationHandler x) $
52− \ide (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> do
52+ \_ ide (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> do
5353 setSomethingModified ide
5454 whenUriFile ide _uri $ \file ->
5555 modifyFilesOfInterest ide (S.delete file)
src/Development/IDE/LSP/Server.hs+2−2
@@ -20,11 +20,11 @@ import Development.IDE.Core.Service
2020data WithMessage = WithMessage
2121 {withResponse :: forall m req resp . (Show m, Show req) =>
2222 (ResponseMessage resp -> LSP.FromServerMessage) -> -- how to wrap a response
23− (IdeState -> req -> IO resp) -> -- actual work
23+ (LSP.LspFuncs () -> IdeState -> req -> IO resp) -> -- actual work
2424 Maybe (LSP.Handler (RequestMessage m req resp))
2525 ,withNotification :: forall m req . (Show m, Show req) =>
2626 Maybe (LSP.Handler (NotificationMessage m req)) -> -- old notification handler
27− (IdeState -> req -> IO ()) -> -- actual work
27+ (LSP.LspFuncs () -> IdeState -> req -> IO ()) -> -- actual work
2828 Maybe (LSP.Handler (NotificationMessage m req))
2929 }
3030