Skip to content

Commit a33c2b4

Browse files
committed
More plugin refactoring, add codelens support
Starting to look pretty solid.
1 parent bcc4b96 commit a33c2b4

File tree

10 files changed

+142
-150
lines changed

10 files changed

+142
-150
lines changed

exe/Main.hs

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -69,38 +69,15 @@ import Ide.Plugin.Pragmas as Pragmas
6969

7070
-- ---------------------------------------------------------------------
7171

72-
-- | TODO: these should come out of something like asGhcIdePlugin
73-
commandIds :: T.Text -> [T.Text]
74-
commandIds pid = "typesignature.add" : allLspCmdIds pid [("pragmas", Pragmas.commands)]
75-
76-
-- | The plugins configured for use in this instance of the language
77-
-- server.
78-
-- These can be freely added or removed to tailor the available
79-
-- features of the server.
80-
_idePlugins :: Bool -> Plugin Config
81-
_idePlugins includeExample
82-
= Completions.plugin <>
83-
CodeAction.plugin <>
84-
formatterPlugins [("ormolu", Ormolu.provider)
85-
,("floskell", Floskell.provider)] <>
86-
codeActionPlugins [("eg", Example.codeAction)
87-
,("eg2", Example2.codeAction)
88-
,("pragmas", Pragmas.codeAction)] <>
89-
executeCommandPlugins [("pragmas", Pragmas.commands)] <>
90-
hoverPlugins [("eg", Example.hover)
91-
,("eg2", Example2.hover)] <>
92-
if includeExample then Example.plugin <> Example2.plugin
93-
else mempty
94-
95-
9672
-- | The plugins configured for use in this instance of the language
9773
-- server.
9874
-- These can be freely added or removed to tailor the available
9975
-- features of the server.
100-
idePlugins :: Bool -> Plugin Config
101-
idePlugins includeExamples
102-
= asGhcIdePlugin $ pluginDescToIdePlugins allPlugins
76+
idePlugins :: T.Text -> Bool -> (Plugin Config, [T.Text])
77+
idePlugins pid includeExamples
78+
= (asGhcIdePlugin ps, allLspCmdIds' pid ps)
10379
where
80+
ps = pluginDescToIdePlugins allPlugins
10481
allPlugins = if includeExamples
10582
then basePlugins ++ examplePlugins
10683
else basePlugins
@@ -164,9 +141,12 @@ main = do
164141
dir <- IO.getCurrentDirectory
165142

166143
pid <- getPid
167-
-- let plugins = idePlugins argsExamplePlugin
168-
let plugins = idePlugins True
169-
options = def { LSP.executeCommandCommands = Just (commandIds pid)
144+
let
145+
-- (ps, commandIds) = idePlugins pid argsExamplePlugin
146+
(ps, commandIds) = idePlugins pid True
147+
plugins = Completions.plugin <> CodeAction.plugin <>
148+
ps
149+
options = def { LSP.executeCommandCommands = Just commandIds
170150
, LSP.completionTriggerCharacters = Just "."
171151
}
172152

src/Ide/Logger.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
{- | Provides an implementation of the ghcide @Logger@ which uses
2+
@System.Log.Logger@ under the hood.
3+
-}
14
module Ide.Logger
25
(
36
hlsLogger
@@ -13,7 +16,7 @@ import qualified Development.IDE.Types.Logger as L
1316
import System.Log.Logger
1417

1518
-- ---------------------------------------------------------------------
16-
-- data Logger = Logger {logPriority :: Priority -> T.Text -> IO ()}
19+
1720
hlsLogger :: L.Logger
1821
hlsLogger = L.Logger $ \pri txt ->
1922
case pri of

src/Ide/Plugin.hs

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@ module Ide.Plugin
1111
(
1212
asGhcIdePlugin
1313
, pluginDescToIdePlugins
14-
, formatterPlugins
15-
, hoverPlugins
16-
, codeActionPlugins
17-
, executeCommandPlugins
1814
, mkLspCommand
1915
, allLspCmdIds
16+
, allLspCmdIds'
2017
, getPid
2118
) where
2219

@@ -29,9 +26,11 @@ import qualified Data.Map as Map
2926
import Data.Maybe
3027
import qualified Data.Text as T
3128
import Development.IDE.Core.Rules
29+
import Development.IDE.Core.Shake
3230
import Development.IDE.LSP.Server
3331
import Development.IDE.Plugin hiding (pluginCommands, pluginRules)
3432
import Development.IDE.Types.Diagnostics as D
33+
import Development.IDE.Types.Logger
3534
import Development.Shake hiding ( Diagnostic, command )
3635
import GHC.Generics
3736
import Ide.Compat
@@ -56,11 +55,12 @@ asGhcIdePlugin mp =
5655
mkPlugin rulesPlugins (Just . pluginRules) <>
5756
mkPlugin executeCommandPlugins (Just . pluginCommands) <>
5857
mkPlugin codeActionPlugins pluginCodeActionProvider <>
59-
-- diagnostics from pluginDiagnosticProvider
58+
mkPlugin codeLensPlugins pluginCodeLensProvider <>
59+
-- Note: diagnostics are provided via Rules from pluginDiagnosticProvider
6060
mkPlugin hoverPlugins pluginHoverProvider <>
61-
-- symbols via pluginSymbolProvider
61+
-- TODO: symbols via pluginSymbolProvider
6262
mkPlugin formatterPlugins pluginFormattingProvider
63-
-- completions
63+
-- TODO: completions
6464
where
6565
justs (p, Just x) = [(p, x)]
6666
justs (_, Nothing) = []
@@ -75,6 +75,17 @@ asGhcIdePlugin mp =
7575
pluginDescToIdePlugins :: [PluginDescriptor] -> IdePlugins
7676
pluginDescToIdePlugins plugins = IdePlugins $ Map.fromList $ map (\p -> (pluginId p, p)) plugins
7777

78+
allLspCmdIds' :: T.Text -> IdePlugins -> [T.Text]
79+
allLspCmdIds' pid mp = mkPlugin (allLspCmdIds pid) (Just . pluginCommands)
80+
where
81+
justs (p, Just x) = [(p, x)]
82+
justs (_, Nothing) = []
83+
84+
ls = Map.toList (ipMap mp)
85+
86+
mkPlugin maker selector
87+
= maker $ concatMap (\(pid, p) -> justs (pid, selector p)) ls
88+
7889
-- ---------------------------------------------------------------------
7990

8091
rulesPlugins :: [(PluginId, Rules ())] -> Plugin Config
@@ -140,6 +151,46 @@ data FallbackCodeActionParams =
140151

141152
-- -----------------------------------------------------------
142153

154+
codeLensPlugins :: [(PluginId, CodeLensProvider)] -> Plugin Config
155+
codeLensPlugins cas = Plugin mempty codeLensRules (codeLensHandlers cas)
156+
157+
codeLensRules :: Rules ()
158+
codeLensRules = mempty
159+
160+
codeLensHandlers :: [(PluginId, CodeLensProvider)] -> PartialHandlers Config
161+
codeLensHandlers cas = PartialHandlers $ \WithMessage{..} x -> return x
162+
{ LSP.codeLensHandler
163+
= withResponse RspCodeLens (makeCodeLens cas)
164+
}
165+
166+
makeCodeLens :: [(PluginId, CodeLensProvider)]
167+
-> LSP.LspFuncs Config
168+
-> IdeState
169+
-> CodeLensParams
170+
-> IO (Either ResponseError (List CodeLens))
171+
makeCodeLens cas _lf ideState params = do
172+
logInfo (ideLogger ideState) "Plugin.makeCodeLens (ideLogger)" -- AZ
173+
let
174+
makeLens (pid, provider) = do
175+
r <- provider ideState pid params
176+
return (pid, r)
177+
breakdown :: [(PluginId, Either ResponseError a)] -> ([(PluginId, ResponseError)], [(PluginId, a)])
178+
breakdown ls = (concatMap doOneLeft ls, concatMap doOneRight ls)
179+
where
180+
doOneLeft (pid, Left err) = [(pid,err)]
181+
doOneLeft (_, Right _) = []
182+
183+
doOneRight (pid, Right a) = [(pid,a)]
184+
doOneRight (_, Left _) = []
185+
186+
r <- mapM makeLens cas
187+
case breakdown r of
188+
([],[]) -> return $ Right $ List []
189+
(es,[]) -> return $ Left $ ResponseError InternalError (T.pack $ "codeLens failed:" ++ show es) Nothing
190+
(_,rs) -> return $ Right $ List (concatMap (\(_,List cs) -> cs) rs)
191+
192+
-- -----------------------------------------------------------
193+
143194
executeCommandPlugins :: [(PluginId, [PluginCommand])] -> Plugin Config
144195
executeCommandPlugins ecs = Plugin mempty mempty (executeCommandHandlers ecs)
145196

@@ -275,6 +326,9 @@ makeExecuteCommands ecs _lf _params = do
275326
276327
execCmd (params ^. J.command) (params ^. J.arguments)
277328
-}
329+
330+
-- -----------------------------------------------------------
331+
278332
-- | Runs a plugin command given a PluginId, CommandId and
279333
-- arguments in the form of a JSON object.
280334
runPluginCommand :: Map.Map PluginId [PluginCommand] -> PluginId -> CommandId -> J.Value
@@ -290,14 +344,7 @@ runPluginCommand m p@(PluginId p') com@(CommandId com') arg =
290344
Just (PluginCommand _ _ f) -> case J.fromJSON arg of
291345
J.Error err -> return (Left $
292346
ResponseError InvalidParams ("error while parsing args for " <> com' <> " in plugin " <> p' <> ": " <> T.pack err) Nothing, Nothing)
293-
J.Success a -> do
294-
res <- f a
295-
return res
296-
-- case res of
297-
-- Left e -> return (Left e, Nothing)
298-
-- -- Right r -> return (Right $ J.toJSON r, Nothing)
299-
-- Right r -> return r
300-
-- -- return (Right J.Null, Just(WorkspaceApplyEdit, _ r))
347+
J.Success a -> f a
301348

302349
-- -----------------------------------------------------------
303350

src/Ide/Plugin/Example.hs

Lines changed: 43 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
{-# LANGUAGE ViewPatterns #-}
2+
{-# LANGUAGE DeriveAnyClass #-}
23
{-# LANGUAGE DeriveGeneric #-}
34
{-# LANGUAGE DuplicateRecordFields #-}
45
{-# LANGUAGE FlexibleContexts #-}
56
{-# LANGUAGE FlexibleInstances #-}
67
{-# LANGUAGE OverloadedStrings #-}
7-
{-# LANGUAGE RecordWildCards #-}
88
{-# LANGUAGE TupleSections #-}
99
{-# LANGUAGE TypeFamilies #-}
1010

1111
module Ide.Plugin.Example
1212
(
1313
descriptor
14-
, plugin
15-
, hover
16-
, codeAction
1714
) where
1815

1916
import Control.DeepSeq ( NFData )
2017
import Control.Monad.Trans.Maybe
21-
import Data.Aeson.Types (toJSON, fromJSON, Value(..), Result(..))
18+
import Data.Aeson
2219
import Data.Binary
2320
import Data.Functor
2421
import qualified Data.HashMap.Strict as Map
@@ -31,16 +28,13 @@ import Development.IDE.Core.RuleTypes
3128
import Development.IDE.Core.Rules
3229
import Development.IDE.Core.Service
3330
import Development.IDE.Core.Shake
34-
import Development.IDE.LSP.Server
35-
import Development.IDE.Plugin
3631
import Development.IDE.Types.Diagnostics as D
3732
import Development.IDE.Types.Location
3833
import Development.IDE.Types.Logger
3934
import Development.Shake hiding ( Diagnostic )
4035
import GHC.Generics
36+
import Ide.Plugin
4137
import Ide.Types
42-
import qualified Language.Haskell.LSP.Core as LSP
43-
import Language.Haskell.LSP.Messages
4438
import Language.Haskell.LSP.Types
4539
import Text.Regex.TDFA.Text()
4640

@@ -50,8 +44,9 @@ descriptor :: PluginId -> PluginDescriptor
5044
descriptor plId = PluginDescriptor
5145
{ pluginId = plId
5246
, pluginRules = exampleRules
53-
, pluginCommands = []
47+
, pluginCommands = [PluginCommand "codelens.todo" "example adding" addTodoCmd]
5448
, pluginCodeActionProvider = Just codeAction
49+
, pluginCodeLensProvider = Just codeLens
5550
, pluginDiagnosticProvider = Nothing
5651
, pluginHoverProvider = Just hover
5752
, pluginSymbolProvider = Nothing
@@ -61,23 +56,15 @@ descriptor plId = PluginDescriptor
6156

6257
-- ---------------------------------------------------------------------
6358

64-
plugin :: Plugin c
65-
plugin = Plugin mempty exampleRules handlersExample
66-
-- <> codeActionPlugin codeAction
67-
<> Plugin mempty mempty handlersCodeLens
68-
6959
hover :: IdeState -> TextDocumentPositionParams -> IO (Either ResponseError (Maybe Hover))
7060
hover = request "Hover" blah (Right Nothing) foundHover
7161

7262
blah :: NormalizedFilePath -> Position -> Action (Maybe (Maybe Range, [T.Text]))
7363
blah _ (Position line col)
7464
= return $ Just (Just (Range (Position line col) (Position (line+1) 0)), ["example hover 1\n"])
7565

76-
handlersExample :: PartialHandlers c
77-
handlersExample = mempty
78-
-- handlersExample = PartialHandlers $ \WithMessage{..} x ->
79-
-- return x{LSP.hoverHandler = withResponse RspHover $ const hover}
80-
66+
-- ---------------------------------------------------------------------
67+
-- Generating Diagnostics via rules
8168
-- ---------------------------------------------------------------------
8269

8370
data Example = Example
@@ -116,6 +103,8 @@ mkDiag file diagSource sev loc msg = (file, D.ShowDiag,)
116103
}
117104

118105
-- ---------------------------------------------------------------------
106+
-- code actions
107+
-- ---------------------------------------------------------------------
119108

120109
-- | Generate code actions.
121110
codeAction
@@ -136,49 +125,50 @@ codeAction _state _pid (TextDocumentIdentifier uri) _range CodeActionContext{_di
136125

137126
-- ---------------------------------------------------------------------
138127

139-
-- | Generate code lenses.
140-
handlersCodeLens :: PartialHandlers c
141-
handlersCodeLens = PartialHandlers $ \WithMessage{..} x -> return x{
142-
LSP.codeLensHandler = withResponse RspCodeLens codeLens,
143-
LSP.executeCommandHandler = withResponseAndRequest RspExecuteCommand ReqApplyWorkspaceEdit executeAddSignatureCommand
144-
}
145-
146128
codeLens
147-
:: LSP.LspFuncs c
148-
-> IdeState
129+
:: IdeState
130+
-> PluginId
149131
-> CodeLensParams
150132
-> IO (Either ResponseError (List CodeLens))
151-
codeLens _lsp ideState CodeLensParams{_textDocument=TextDocumentIdentifier uri} = do
133+
codeLens ideState plId CodeLensParams{_textDocument=TextDocumentIdentifier uri} = do
134+
logInfo (ideLogger ideState) "Example.codeLens entered (ideLogger)" -- AZ
152135
case uriToFilePath' uri of
153136
Just (toNormalizedFilePath -> filePath) -> do
154137
_ <- runAction ideState $ runMaybeT $ useE TypeCheck filePath
155138
_diag <- getDiagnostics ideState
156139
_hDiag <- getHiddenDiagnostics ideState
157140
let
158141
title = "Add TODO Item via Code Lens"
159-
tedit = [TextEdit (Range (Position 3 0) (Position 3 0))
160-
"-- TODO added by Example Plugin via code lens action\n"]
161-
edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing
162-
range = (Range (Position 3 0) (Position 4 0))
163-
pure $ Right $ List
164-
-- [ CodeLens range (Just (Command title "codelens.do" (Just $ List [toJSON edit]))) Nothing
165-
[ CodeLens range (Just (Command title "codelens.todo" (Just $ List [toJSON edit]))) Nothing
166-
]
142+
-- tedit = [TextEdit (Range (Position 3 0) (Position 3 0))
143+
-- "-- TODO added by Example Plugin via code lens action\n"]
144+
-- edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing
145+
range = Range (Position 3 0) (Position 4 0)
146+
let cmdParams = AddTodoParams uri "do abc"
147+
cmd <- mkLspCommand plId "codelens.todo" title (Just [(toJSON cmdParams)])
148+
pure $ Right $ List [ CodeLens range (Just cmd) Nothing ]
167149
Nothing -> pure $ Right $ List []
168150

169-
-- | Execute the "codelens.todo" command.
170-
executeAddSignatureCommand
171-
:: LSP.LspFuncs c
172-
-> IdeState
173-
-> ExecuteCommandParams
174-
-> IO (Either ResponseError Value, Maybe (ServerMethod, ApplyWorkspaceEditParams))
175-
executeAddSignatureCommand _lsp _ideState ExecuteCommandParams{..}
176-
| _command == "codelens.todo"
177-
, Just (List [edit]) <- _arguments
178-
, Success wedit <- fromJSON edit
179-
= return (Right Null, Just (WorkspaceApplyEdit, ApplyWorkspaceEditParams wedit))
180-
| otherwise
181-
= return (Right Null, Nothing)
151+
-- ---------------------------------------------------------------------
152+
-- | Parameters for the addTodo PluginCommand.
153+
data AddTodoParams = AddTodoParams
154+
{ file :: Uri -- ^ Uri of the file to add the pragma to
155+
, todoText :: T.Text
156+
}
157+
deriving (Show, Eq, Generic, ToJSON, FromJSON)
158+
159+
addTodoCmd :: AddTodoParams -> IO (Either ResponseError Value,
160+
Maybe (ServerMethod, ApplyWorkspaceEditParams))
161+
addTodoCmd (AddTodoParams uri todoText) = do
162+
let
163+
pos = Position 0 0
164+
textEdits = List
165+
[TextEdit (Range pos pos)
166+
("-- TODO:" <> todoText)
167+
]
168+
res = WorkspaceEdit
169+
(Just $ Map.singleton uri textEdits)
170+
Nothing
171+
return (Right Null, Just (WorkspaceApplyEdit, ApplyWorkspaceEditParams res))
182172

183173
-- ---------------------------------------------------------------------
184174

@@ -203,7 +193,8 @@ request label getResults notFound found ide (TextDocumentPositionParams (TextDoc
203193
Nothing -> pure Nothing
204194
pure $ maybe notFound found mbResult
205195

206-
logAndRunRequest :: T.Text -> (NormalizedFilePath -> Position -> Action b) -> IdeState -> Position -> String -> IO b
196+
logAndRunRequest :: T.Text -> (NormalizedFilePath -> Position -> Action b)
197+
-> IdeState -> Position -> String -> IO b
207198
logAndRunRequest label getResults ide pos path = do
208199
let filePath = toNormalizedFilePath path
209200
logInfo (ideLogger ide) $

0 commit comments

Comments
 (0)