@@ -13,13 +13,16 @@ import Control.Exception (SomeException)
13
13
import Control.Lens ((^.) )
14
14
import Control.Monad
15
15
import qualified Data.Aeson as J
16
+ import Data.Bifunctor (first )
16
17
import Data.Dependent.Map (DMap )
17
18
import qualified Data.Dependent.Map as DMap
18
19
import Data.Dependent.Sum
19
20
import Data.Either
20
21
import qualified Data.List as List
21
22
import Data.List.NonEmpty (NonEmpty , nonEmpty , toList )
23
+ import qualified Data.List.NonEmpty as NE
22
24
import qualified Data.Map as Map
25
+ import Data.Some
23
26
import Data.String
24
27
import Data.Text (Text )
25
28
import qualified Data.Text as T
@@ -38,6 +41,7 @@ import Language.LSP.Types
38
41
import qualified Language.LSP.Types as J
39
42
import qualified Language.LSP.Types.Lens as LSP
40
43
import Language.LSP.VFS
44
+ import Prettyprinter.Render.String (renderString )
41
45
import Text.Regex.TDFA.Text ()
42
46
import UnliftIO (MonadUnliftIO )
43
47
import UnliftIO.Async (forConcurrently )
@@ -46,12 +50,18 @@ import UnliftIO.Exception (catchAny)
46
50
-- ---------------------------------------------------------------------
47
51
--
48
52
49
- data Log = LogPluginError ResponseError
50
- deriving Show
51
-
53
+ data Log
54
+ = LogPluginError PluginId ResponseError
55
+ | LogNoPluginForMethod (Some SMethod )
56
+ | LogInvalidCommandIdentifier
52
57
instance Pretty Log where
53
58
pretty = \ case
54
- LogPluginError err -> prettyResponseError err
59
+ LogPluginError (PluginId pId) err -> pretty pId <> " :" <+> prettyResponseError err
60
+ LogNoPluginForMethod (Some method) ->
61
+ " No plugin enabled for " <> pretty (show method)
62
+ LogInvalidCommandIdentifier -> " Invalid command identifier"
63
+
64
+ instance Show Log where show = renderString . layoutCompact . pretty
55
65
56
66
-- various error message specific builders
57
67
prettyResponseError :: ResponseError -> Doc a
@@ -77,10 +87,10 @@ failedToParseArgs :: CommandId -- ^ command that failed to parse
77
87
failedToParseArgs (CommandId com) (PluginId pid) err arg = " Error while parsing args for " <> com <> " in plugin " <> pid <> " : " <> T. pack err <> " \n arg = " <> T. pack (show arg)
78
88
79
89
-- | Build a ResponseError and log it before returning to the caller
80
- logAndReturnError :: Recorder (WithPriority Log ) -> ErrorCode -> Text -> LSP. LspT Config IO (Either ResponseError a )
81
- logAndReturnError recorder errCode msg = do
90
+ logAndReturnError :: Recorder (WithPriority Log ) -> PluginId -> ErrorCode -> Text -> LSP. LspT Config IO (Either ResponseError a )
91
+ logAndReturnError recorder p errCode msg = do
82
92
let err = ResponseError errCode msg Nothing
83
- logWith recorder Warning $ LogPluginError err
93
+ logWith recorder Warning $ LogPluginError p err
84
94
pure $ Left err
85
95
86
96
-- | Map a set of plugins to the underlying ghcide engine.
@@ -164,15 +174,17 @@ executeCommandHandlers recorder ecs = requestHandler SWorkspaceExecuteCommand ex
164
174
Just (plugin, cmd) -> runPluginCommand ide plugin cmd cmdParams
165
175
166
176
-- Couldn't parse the command identifier
167
- _ -> logAndReturnError recorder InvalidParams " Invalid command Identifier"
177
+ _ -> do
178
+ logWith recorder Warning LogInvalidCommandIdentifier
179
+ return $ Left $ ResponseError InvalidParams " Invalid command identifier" Nothing
168
180
169
181
runPluginCommand ide p com arg =
170
182
case Map. lookup p pluginMap of
171
- Nothing -> logAndReturnError recorder InvalidRequest (pluginDoesntExist p)
183
+ Nothing -> logAndReturnError recorder p InvalidRequest (pluginDoesntExist p)
172
184
Just xs -> case List. find ((com == ) . commandId) xs of
173
- Nothing -> logAndReturnError recorder InvalidRequest (commandDoesntExist com p xs)
185
+ Nothing -> logAndReturnError recorder p InvalidRequest (commandDoesntExist com p xs)
174
186
Just (PluginCommand _ _ f) -> case J. fromJSON arg of
175
- J. Error err -> logAndReturnError recorder InvalidParams (failedToParseArgs com p err arg)
187
+ J. Error err -> logAndReturnError recorder p InvalidParams (failedToParseArgs com p err arg)
176
188
J. Success a -> f ide a
177
189
178
190
-- ---------------------------------------------------------------------
@@ -195,15 +207,21 @@ extensiblePlugins recorder xs = mempty { P.pluginHandlers = handlers }
195
207
let fs = filter (\ (_, desc, _) -> pluginEnabled m params desc config) fs'
196
208
-- Clients generally don't display ResponseErrors so instead we log any that we come across
197
209
case nonEmpty fs of
198
- Nothing -> logAndReturnError recorder InvalidRequest (pluginNotEnabled m fs')
210
+ Nothing -> do
211
+ logWith recorder Warning (LogNoPluginForMethod $ Some m)
212
+ let err = ResponseError InvalidRequest msg Nothing
213
+ msg = pluginNotEnabled m fs'
214
+ return $ Left err
199
215
Just fs -> do
200
216
let msg e pid = " Exception in plugin " <> T. pack (show pid) <> " while processing " <> T. pack (show m) <> " : " <> T. pack (show e)
201
217
handlers = fmap (\ (plid,_,handler) -> (plid,handler)) fs
202
218
es <- runConcurrently msg (show m) handlers ide params
203
- let (errs,succs) = partitionEithers $ toList es
204
- unless (null errs) $ forM_ errs $ \ err -> logWith recorder Warning $ LogPluginError err
219
+
220
+ let (errs,succs) = partitionEithers $ toList $ join $ NE. zipWith (\ (pId,_) -> fmap (first (pId,))) handlers es
221
+ unless (null errs) $ forM_ errs $ \ (pId, err) ->
222
+ logWith recorder Warning $ LogPluginError pId err
205
223
case nonEmpty succs of
206
- Nothing -> pure $ Left $ combineErrors errs
224
+ Nothing -> pure $ Left $ combineErrors $ map snd errs
207
225
Just xs -> do
208
226
caps <- LSP. getClientCapabilities
209
227
pure $ Right $ combineResponses m config caps params xs
@@ -226,7 +244,8 @@ extensibleNotificationPlugins recorder xs = mempty { P.pluginHandlers = handlers
226
244
-- Only run plugins that are allowed to run on this request
227
245
let fs = filter (\ (_, desc, _) -> pluginEnabled m params desc config) fs'
228
246
case nonEmpty fs of
229
- Nothing -> void $ logAndReturnError recorder InvalidRequest (pluginNotEnabled m fs')
247
+ Nothing -> do
248
+ logWith recorder Warning (LogNoPluginForMethod $ Some m)
230
249
Just fs -> do
231
250
-- We run the notifications in order, so the core ghcide provider
232
251
-- (which restarts the shake process) hopefully comes last
@@ -242,8 +261,8 @@ runConcurrently
242
261
-- ^ Enabled plugin actions that we are allowed to run
243
262
-> a
244
263
-> b
245
- -> m (NonEmpty (Either ResponseError d ))
246
- runConcurrently msg method fs a b = fmap join $ forConcurrently fs $ \ (pid,f) -> otTracedProvider pid (fromString method) $ do
264
+ -> m (NonEmpty ( NonEmpty (Either ResponseError d ) ))
265
+ runConcurrently msg method fs a b = forConcurrently fs $ \ (pid,f) -> otTracedProvider pid (fromString method) $ do
247
266
f a b
248
267
`catchAny` (\ e -> pure $ pure $ Left $ ResponseError InternalError (msg e pid) Nothing )
249
268
0 commit comments