Skip to content

Commit 9c494ca

Browse files
VeryMilkyJoefendor
authored andcommitted
Generalise file extension handling for plugins
NotificationHandler now distinguishes between different file extensions RequestHandler distinguishes between different file extensions
1 parent 140f904 commit 9c494ca

File tree

5 files changed

+173
-50
lines changed

5 files changed

+173
-50
lines changed

ghcide/src/Development/IDE/Plugin/HLS.hs

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ asGhcIdePlugin :: Recorder (WithPriority Log) -> IdePlugins IdeState -> Plugin C
5858
asGhcIdePlugin recorder (IdePlugins ls) =
5959
mkPlugin rulesPlugins HLS.pluginRules <>
6060
mkPlugin executeCommandPlugins HLS.pluginCommands <>
61-
mkPlugin extensiblePlugins HLS.pluginHandlers <>
62-
mkPlugin (extensibleNotificationPlugins recorder) HLS.pluginNotificationHandlers <>
61+
mkPlugin extensiblePlugins id <>
62+
mkPlugin (extensibleNotificationPlugins recorder) id <>
6363
mkPlugin dynFlagsPlugins HLS.pluginModifyDynflags
6464
where
6565

@@ -153,55 +153,80 @@ executeCommandHandlers ecs = requestHandler SWorkspaceExecuteCommand execCmd
153153

154154
-- ---------------------------------------------------------------------
155155

156-
extensiblePlugins :: [(PluginId, PluginHandlers IdeState)] -> Plugin Config
156+
extensiblePlugins :: [(PluginId, PluginDescriptor IdeState)] -> Plugin Config
157157
extensiblePlugins xs = mempty { P.pluginHandlers = handlers }
158158
where
159+
getPluginDescriptor pid = lookup pid xs
159160
IdeHandlers handlers' = foldMap bakePluginId xs
160-
bakePluginId :: (PluginId, PluginHandlers IdeState) -> IdeHandlers
161-
bakePluginId (pid,PluginHandlers hs) = IdeHandlers $ DMap.map
161+
bakePluginId :: (PluginId, PluginDescriptor IdeState) -> IdeHandlers
162+
bakePluginId (pid,pluginDesc) = IdeHandlers $ DMap.map
162163
(\(PluginHandler f) -> IdeHandler [(pid,f pid)])
163164
hs
165+
where
166+
PluginHandlers hs = HLS.pluginHandlers pluginDesc
164167
handlers = mconcat $ do
165168
(IdeMethod m :=> IdeHandler fs') <- DMap.assocs handlers'
166169
pure $ requestHandler m $ \ide params -> do
167170
config <- Ide.PluginUtils.getClientConfig
168-
let fs = filter (\(pid,_) -> pluginEnabled m pid config) fs'
169-
case nonEmpty fs of
170-
Nothing -> pure $ Left $ ResponseError InvalidRequest
171-
("No plugin enabled for " <> T.pack (show m) <> ", available: " <> T.pack (show $ map fst fs))
172-
Nothing
173-
Just fs -> do
174-
let msg e pid = "Exception in plugin " <> T.pack (show pid) <> "while processing " <> T.pack (show m) <> ": " <> T.pack (show e)
175-
es <- runConcurrently msg (show m) fs ide params
176-
let (errs,succs) = partitionEithers $ toList es
177-
case nonEmpty succs of
178-
Nothing -> pure $ Left $ combineErrors errs
179-
Just xs -> do
180-
caps <- LSP.getClientCapabilities
181-
pure $ Right $ combineResponses m config caps params xs
171+
let pluginInfo = map (\(pid,_) -> (pid, getPluginDescriptor pid)) fs'
172+
cleanPluginInfo <- collectPluginDescriptors pluginInfo []
173+
case cleanPluginInfo of
174+
Left err -> pure $ Left err
175+
Right pluginInfos -> do
176+
let fs = map snd $ filter (\((_, desc), _) -> pluginEnabled m params desc config) (zip pluginInfos fs')
177+
case nonEmpty fs of
178+
Nothing -> pure $ Left $ ResponseError InvalidRequest
179+
("No plugin enabled for " <> T.pack (show m) <> ", available: " <> T.pack (show $ map fst fs))
180+
Nothing
181+
Just fs -> do
182+
let msg e pid = "Exception in plugin " <> T.pack (show pid) <> "while processing " <> T.pack (show m) <> ": " <> T.pack (show e)
183+
es <- runConcurrently msg (show m) fs ide params
184+
let (errs,succs) = partitionEithers $ toList es
185+
case nonEmpty succs of
186+
Nothing -> pure $ Left $ combineErrors errs
187+
Just xs -> do
188+
caps <- LSP.getClientCapabilities
189+
pure $ Right $ combineResponses m config caps params xs
190+
191+
collectPluginDescriptors :: [(PluginId, Maybe (PluginDescriptor c))] -> [(PluginId, PluginDescriptor c)] -> LSP.LspM Config (Either ResponseError [(PluginId, PluginDescriptor c)])
192+
collectPluginDescriptors ((pid, Nothing):_) _ = pure $ Left $ ResponseError InvalidRequest
193+
("No plugindescriptor found for " <> pidT <> ", available: ")
194+
Nothing
195+
where
196+
PluginId pidT = pid
197+
collectPluginDescriptors ((pid, Just desc):xs) ys = collectPluginDescriptors xs (ys ++ [(pid, desc)])
198+
collectPluginDescriptors [] ys = pure $ Right ys
199+
182200
-- ---------------------------------------------------------------------
183201

184-
extensibleNotificationPlugins :: Recorder (WithPriority Log) -> [(PluginId, PluginNotificationHandlers IdeState)] -> Plugin Config
202+
extensibleNotificationPlugins :: Recorder (WithPriority Log) -> [(PluginId, PluginDescriptor IdeState)] -> Plugin Config
185203
extensibleNotificationPlugins recorder xs = mempty { P.pluginHandlers = handlers }
186204
where
205+
getPluginDescriptor pid = lookup pid xs
187206
IdeNotificationHandlers handlers' = foldMap bakePluginId xs
188-
bakePluginId :: (PluginId, PluginNotificationHandlers IdeState) -> IdeNotificationHandlers
189-
bakePluginId (pid,PluginNotificationHandlers hs) = IdeNotificationHandlers $ DMap.map
207+
bakePluginId :: (PluginId, PluginDescriptor IdeState) -> IdeNotificationHandlers
208+
bakePluginId (pid,pluginDesc) = IdeNotificationHandlers $ DMap.map
190209
(\(PluginNotificationHandler f) -> IdeNotificationHandler [(pid,f pid)])
191210
hs
211+
where PluginNotificationHandlers hs = HLS.pluginNotificationHandlers pluginDesc
192212
handlers = mconcat $ do
193213
(IdeNotification m :=> IdeNotificationHandler fs') <- DMap.assocs handlers'
194214
pure $ notificationHandler m $ \ide vfs params -> do
195215
config <- Ide.PluginUtils.getClientConfig
196-
let fs = filter (\(pid,_) -> plcGlobalOn $ configForPlugin config pid) fs'
197-
case nonEmpty fs of
198-
Nothing -> do
199-
logWith recorder Info LogNoEnabledPlugins
200-
pure ()
201-
Just fs -> do
202-
-- We run the notifications in order, so the core ghcide provider
203-
-- (which restarts the shake process) hopefully comes last
204-
mapM_ (\(pid,f) -> otTracedProvider pid (fromString $ show m) $ f ide vfs params) fs
216+
let pluginInfo = map (\(pid,_) -> (pid, getPluginDescriptor pid)) fs'
217+
cleanPluginInfo <- collectPluginDescriptors pluginInfo []
218+
case cleanPluginInfo of
219+
Left _ -> pure ()
220+
Right pluginInfos -> do
221+
let fs = map snd $ filter (\((_, desc), _) -> pluginEnabled2 m params desc config) (zip pluginInfos fs')
222+
case nonEmpty fs of
223+
Nothing -> do
224+
logWith recorder Info LogNoEnabledPlugins
225+
pure ()
226+
Just fs -> do
227+
-- We run the notifications in order, so the core ghcide provider
228+
-- (which restarts the shake process) hopefully comes last
229+
mapM_ (\(pid,f) -> otTracedProvider pid (fromString $ show m) $ f ide vfs params) fs
205230

206231
-- ---------------------------------------------------------------------
207232

haskell-language-server.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ flag dynamic
226226
common example-plugins
227227
hs-source-dirs: plugins/default/src
228228
other-modules: Ide.Plugin.Example,
229-
Ide.Plugin.Example2
229+
Ide.Plugin.Example2,
230+
Ide.Plugin.ExampleCabal
230231

231232
common class
232233
if flag(class)

hls-plugin-api/hls-plugin-api.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ library
4343
, Diff ^>=0.4.0
4444
, dlist
4545
, extra
46+
, filepath
4647
, ghc
4748
, hashable
4849
, hls-graph ^>= 1.7

hls-plugin-api/src/Ide/Types.hs

Lines changed: 112 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import Language.LSP.Types.Lens as J (HasChildren (children),
6969
import Language.LSP.VFS
7070
import OpenTelemetry.Eventlog
7171
import Options.Applicative (ParserInfo)
72+
import System.FilePath
7273
import System.IO.Unsafe
7374
import Text.Regex.TDFA.Text ()
7475

@@ -117,6 +118,7 @@ data PluginDescriptor ideState =
117118
, pluginNotificationHandlers :: PluginNotificationHandlers ideState
118119
, pluginModifyDynflags :: DynFlagsModifications
119120
, pluginCli :: Maybe (ParserInfo (IdeCommand ideState))
121+
, pluginFileType :: [T.Text]
120122
}
121123

122124
-- | An existential wrapper of 'Properties'
@@ -162,7 +164,7 @@ defaultConfigDescriptor = ConfigDescriptor True False (mkCustomConfig emptyPrope
162164
class HasTracing (MessageParams m) => PluginMethod m where
163165

164166
-- | Parse the configuration to check if this plugin is enabled
165-
pluginEnabled :: SMethod m -> PluginId -> Config -> Bool
167+
pluginEnabled :: SMethod m -> MessageParams m -> PluginDescriptor c -> Config -> Bool
166168

167169
-- | How to combine responses from different plugins
168170
combineResponses
@@ -177,11 +179,14 @@ class HasTracing (MessageParams m) => PluginMethod m where
177179
combineResponses _method _config _caps _params = sconcat
178180

179181
instance PluginMethod TextDocumentCodeAction where
180-
pluginEnabled _ = pluginEnabledConfig plcCodeActionsOn
182+
pluginEnabled _ msgParams pluginDesc
183+
| pluginResponsible uri pluginDesc = pluginEnabledConfig plcCodeActionsOn (pluginId pluginDesc)
184+
| otherwise = const False
185+
where
186+
uri = msgParams ^. J.textDocument . J.uri
181187
combineResponses _method _config (ClientCapabilities _ textDocCaps _ _ _) (CodeActionParams _ _ _ _ context) resps =
182188
fmap compat $ List $ filter wasRequested $ (\(List x) -> x) $ sconcat resps
183189
where
184-
185190
compat :: (Command |? CodeAction) -> (Command |? CodeAction)
186191
compat x@(InL _) = x
187192
compat x@(InR action)
@@ -205,12 +210,31 @@ instance PluginMethod TextDocumentCodeAction where
205210
, Just caKind <- ca ^. kind = any (\k -> k `codeActionKindSubsumes` caKind) allowed
206211
| otherwise = False
207212

213+
pluginResponsible :: Uri -> PluginDescriptor c -> Bool
214+
pluginResponsible uri pluginDesc
215+
| Just fp <- mfp
216+
, T.pack (takeExtension fp) `elem` pluginFileType pluginDesc = True
217+
| otherwise = False
218+
where
219+
mfp = uriToFilePath uri
220+
208221
instance PluginMethod TextDocumentCodeLens where
209-
pluginEnabled _ = pluginEnabledConfig plcCodeLensOn
222+
pluginEnabled _ msgParams pluginDesc config =
223+
pluginResponsible uri pluginDesc
224+
&& pluginEnabledConfig plcCodeLensOn (pluginId pluginDesc) config
225+
where
226+
uri = msgParams ^. J.textDocument . J.uri
227+
210228
instance PluginMethod TextDocumentRename where
211-
pluginEnabled _ = pluginEnabledConfig plcRenameOn
229+
pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc
230+
&& pluginEnabledConfig plcRenameOn (pluginId pluginDesc) config
231+
where
232+
uri = msgParams ^. J.textDocument . J.uri
212233
instance PluginMethod TextDocumentHover where
213-
pluginEnabled _ = pluginEnabledConfig plcHoverOn
234+
pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc
235+
&& pluginEnabledConfig plcHoverOn (pluginId pluginDesc) config
236+
where
237+
uri = msgParams ^. J.textDocument . J.uri
214238
combineResponses _ _ _ _ (catMaybes . toList -> hs) = h
215239
where
216240
r = listToMaybe $ mapMaybe (^. range) hs
@@ -219,7 +243,10 @@ instance PluginMethod TextDocumentHover where
219243
hh -> Just $ Hover hh r
220244

221245
instance PluginMethod TextDocumentDocumentSymbol where
222-
pluginEnabled _ = pluginEnabledConfig plcSymbolsOn
246+
pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc
247+
&& pluginEnabledConfig plcSymbolsOn (pluginId pluginDesc) config
248+
where
249+
uri = msgParams ^. J.textDocument . J.uri
223250
combineResponses _ _ (ClientCapabilities _ tdc _ _ _) params xs = res
224251
where
225252
uri' = params ^. textDocument . uri
@@ -241,7 +268,10 @@ instance PluginMethod TextDocumentDocumentSymbol where
241268
in [si] <> children'
242269

243270
instance PluginMethod TextDocumentCompletion where
244-
pluginEnabled _ = pluginEnabledConfig plcCompletionOn
271+
pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc
272+
&& pluginEnabledConfig plcCompletionOn (pluginId pluginDesc) config
273+
where
274+
uri = msgParams ^. J.textDocument . J.uri
245275
combineResponses _ conf _ _ (toList -> xs) = snd $ consumeCompletionResponse limit $ combine xs
246276
where
247277
limit = maxCompletions conf
@@ -270,32 +300,82 @@ instance PluginMethod TextDocumentCompletion where
270300
consumeCompletionResponse n (InR (CompletionList isCompleteResponse (List xx)))
271301

272302
instance PluginMethod TextDocumentFormatting where
273-
pluginEnabled _ pid conf = (PluginId $ formattingProvider conf) == pid
274-
combineResponses _ _ _ _ (x :| _) = x
303+
pluginEnabled STextDocumentFormatting msgParams pluginDesc conf =
304+
pluginResponsible uri pluginDesc && PluginId (formattingProvider conf) == pid
305+
where
306+
uri = msgParams ^. J.textDocument . J.uri
307+
pid = pluginId pluginDesc
308+
combineResponses _ _ _ _ x = sconcat x
309+
275310

276311
instance PluginMethod TextDocumentRangeFormatting where
277-
pluginEnabled _ pid conf = (PluginId $ formattingProvider conf) == pid
312+
pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc
313+
&& PluginId (formattingProvider conf) == pid
314+
where
315+
uri = msgParams ^. J.textDocument . J.uri
316+
pid = pluginId pluginDesc
278317
combineResponses _ _ _ _ (x :| _) = x
279318

280319
instance PluginMethod TextDocumentPrepareCallHierarchy where
281-
pluginEnabled _ = pluginEnabledConfig plcCallHierarchyOn
320+
pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc
321+
&& pluginEnabledConfig plcCallHierarchyOn pid conf
322+
where
323+
uri = msgParams ^. J.textDocument . J.uri
324+
pid = pluginId pluginDesc
282325

283326
instance PluginMethod TextDocumentSelectionRange where
284-
pluginEnabled _ = pluginEnabledConfig plcSelectionRangeOn
327+
pluginEnabled _ _ pluginDesc conf = pluginEnabledConfig plcSelectionRangeOn pid conf
328+
where
329+
pid = pluginId pluginDesc
285330
combineResponses _ _ _ _ (x :| _) = x
286331

287332
instance PluginMethod CallHierarchyIncomingCalls where
288-
pluginEnabled _ = pluginEnabledConfig plcCallHierarchyOn
333+
pluginEnabled _ _ pluginDesc conf = pluginEnabledConfig plcCallHierarchyOn pid conf
334+
where
335+
pid = pluginId pluginDesc
289336

290337
instance PluginMethod CallHierarchyOutgoingCalls where
291-
pluginEnabled _ = pluginEnabledConfig plcCallHierarchyOn
338+
pluginEnabled _ _ pluginDesc conf = pluginEnabledConfig plcCallHierarchyOn pid conf
339+
where
340+
pid = pluginId pluginDesc
292341

293342
instance PluginMethod CustomMethod where
294-
pluginEnabled _ _ _ = True
343+
pluginEnabled _ _ _ _ = True
295344
combineResponses _ _ _ _ (x :| _) = x
296345

297346
-- ---------------------------------------------------------------------
298347

348+
class HasTracing (MessageParams m) => PluginNotificationMethod (m :: Method FromClient Notification) where
349+
pluginEnabled2 :: SMethod m -> MessageParams m -> PluginDescriptor c -> Config -> Bool
350+
351+
default pluginEnabled2 :: (HasTextDocument (MessageParams m) doc, HasUri doc Uri)
352+
=> SMethod m -> MessageParams m -> PluginDescriptor c -> Config -> Bool
353+
pluginEnabled2 _ params desc conf = pluginResponsible uri desc && plcGlobalOn (configForPlugin conf (pluginId desc))
354+
where
355+
uri = params ^. J.textDocument . J.uri
356+
357+
instance PluginNotificationMethod TextDocumentDidOpen where
358+
359+
instance PluginNotificationMethod TextDocumentDidChange where
360+
361+
instance PluginNotificationMethod TextDocumentDidSave where
362+
363+
instance PluginNotificationMethod TextDocumentDidClose where
364+
365+
instance PluginNotificationMethod WorkspaceDidChangeWatchedFiles where
366+
pluginEnabled2 _ _ desc conf = plcGlobalOn $ configForPlugin conf (pluginId desc)
367+
368+
instance PluginNotificationMethod WorkspaceDidChangeWorkspaceFolders where
369+
pluginEnabled2 _ _ desc conf = plcGlobalOn $ configForPlugin conf (pluginId desc)
370+
371+
instance PluginNotificationMethod WorkspaceDidChangeConfiguration where
372+
pluginEnabled2 _ _ desc conf = plcGlobalOn $ configForPlugin conf (pluginId desc)
373+
374+
instance PluginNotificationMethod Initialized where
375+
pluginEnabled2 _ _ desc conf = plcGlobalOn $ configForPlugin conf (pluginId desc)
376+
377+
-- ---------------------------------------------------------------------
378+
299379
-- | Methods which have a PluginMethod instance
300380
data IdeMethod (m :: Method FromClient Request) = PluginMethod m => IdeMethod (SMethod m)
301381
instance GEq IdeMethod where
@@ -304,7 +384,7 @@ instance GCompare IdeMethod where
304384
gcompare (IdeMethod a) (IdeMethod b) = gcompare a b
305385

306386
-- | Methods which have a PluginMethod instance
307-
data IdeNotification (m :: Method FromClient Notification) = HasTracing (MessageParams m) => IdeNotification (SMethod m)
387+
data IdeNotification (m :: Method FromClient Notification) = PluginNotificationMethod m => IdeNotification (SMethod m)
308388
instance GEq IdeNotification where
309389
geq (IdeNotification a) (IdeNotification b) = geq a b
310390
instance GCompare IdeNotification where
@@ -353,7 +433,7 @@ mkPluginHandler m f = PluginHandlers $ DMap.singleton (IdeMethod m) (PluginHandl
353433

354434
-- | Make a handler for plugins with no extra data
355435
mkPluginNotificationHandler
356-
:: HasTracing (MessageParams m)
436+
:: PluginNotificationMethod m
357437
=> SClientMethod (m :: Method FromClient Notification)
358438
-> PluginNotificationMethodHandler ideState m
359439
-> PluginNotificationHandlers ideState
@@ -373,6 +453,20 @@ defaultPluginDescriptor plId =
373453
mempty
374454
mempty
375455
Nothing
456+
[".hs", ".lhs"]
457+
458+
defaultCabalPluginDescriptor :: PluginId -> PluginDescriptor ideState
459+
defaultCabalPluginDescriptor plId =
460+
PluginDescriptor
461+
plId
462+
mempty
463+
mempty
464+
mempty
465+
defaultConfigDescriptor
466+
mempty
467+
mempty
468+
Nothing
469+
[".cabal"]
376470

377471
newtype CommandId = CommandId T.Text
378472
deriving (Show, Read, Eq, Ord)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module Ide.Plugin.ExampleCabal where
2+

0 commit comments

Comments
 (0)