Skip to content

Commit 6f5a735

Browse files
authored
Support optional plugins (#3193)
* Support initial generic config for plugins This enables opt-in plugins, i.e. plugins that are not enabled by default * Support initial generic config for plugins This enables opt-in plugins, i.e. plugins that are not enabled by default * Address review comments and remove optionality in configInitialGenericConfig I can't remember why this was optional in the first place, which probably means no good reason for it to be * use defConfig instead of def * test * descriptor is the golden source for plugin default configs Read plugin defaults from plugin descriptor instead of copying into initial config This is less fragile as now the initial generic config specified in the plugin descriptor is enforced by construction. The downside is that now the descriptor is needed to parse the config, which means a few places where a plugin id was enough now require a plugin descriptor, which is much harder to get. We paper over this by moving these call sites to the Action monad. * redundant import * simplify * Build fixes * Build fixes * pedantic fixes
1 parent f629652 commit 6f5a735

File tree

41 files changed

+413
-339
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+413
-339
lines changed

bench/Main.hs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
-}
4343
{-# LANGUAGE DeriveAnyClass #-}
4444
{-# LANGUAGE DerivingStrategies #-}
45+
{-# LANGUAGE OverloadedStrings #-}
4546
{-# LANGUAGE TypeFamilies #-}
4647
{-# OPTIONS -Wno-orphans #-}
4748

@@ -53,7 +54,7 @@ import Data.Default
5354
import Data.Foldable (find)
5455
import qualified Data.Map.Strict as Map
5556
import Data.Maybe
56-
import Data.Text (pack, unpack)
57+
import Data.Text (unpack)
5758
import Data.Yaml (FromJSON (..), ToJSON (toJSON),
5859
decodeFileThrow)
5960
import Development.Benchmark.Rules hiding (parallelism)
@@ -74,7 +75,7 @@ import GHC.Exts (toList)
7475
import GHC.Generics (Generic)
7576
import HlsPlugins (idePlugins)
7677
import qualified Ide.Plugin.Config as Plugin
77-
import Ide.Types
78+
import Ide.Types hiding (Config)
7879
import Numeric.Natural (Natural)
7980
import System.Console.GetOpt
8081
import System.Directory
@@ -175,13 +176,13 @@ createBuildSystem config = do
175176
disableAllPluginsBut :: (PluginId -> Bool) -> Plugin.Config
176177
disableAllPluginsBut pred = def {Plugin.plugins = pluginsMap} where
177178
pluginsMap = Map.fromList
178-
[ (p, def { Plugin.plcGlobalOn = globalOn})
179-
| PluginDescriptor{pluginId = plugin@(PluginId p)} <- plugins
179+
[ (plugin, def { Plugin.plcGlobalOn = globalOn})
180+
| PluginDescriptor{pluginId = plugin} <- plugins
180181
, let globalOn =
181182
-- ghcide-core is required, nothing works without it
182-
plugin == PluginId (pack "ghcide-core")
183+
plugin == "ghcide-core"
183184
-- document symbols is required by the benchmark suite
184-
|| plugin == PluginId (pack "ghcide-hover-and-symbols")
185+
|| plugin == "ghcide-hover-and-symbols"
185186
|| pred plugin
186187
]
187188
IdePlugins plugins = idePlugins mempty

exe/Wrapper.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import Development.IDE.Types.Logger (Logger (Logger),
5454
makeDefaultStderrRecorder)
5555
import GHC.Stack.Types (emptyCallStack)
5656
import Ide.Plugin.Config (Config)
57+
import Ide.Types (IdePlugins (IdePlugins))
5758
import Language.LSP.Server (LspM)
5859
import qualified Language.LSP.Server as LSP
5960
import Language.LSP.Types (MessageActionItem (MessageActionItem),
@@ -276,7 +277,7 @@ launchErrorLSP errorMsg = do
276277

277278
let logger = Logger $ \p m -> logger_ recorder (WithPriority p emptyCallStack (pretty m))
278279

279-
let defaultArguments = Main.defaultArguments (cmapWithPrio pretty recorder) logger
280+
let defaultArguments = Main.defaultArguments (cmapWithPrio pretty recorder) logger (IdePlugins [])
280281

281282
inH <- Main.argsHandleIn defaultArguments
282283

ghcide/exe/Main.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ main = withTelemetryLogger $ \telemetryLogger -> do
121121

122122
let arguments =
123123
if argsTesting
124-
then IDEMain.testing (cmapWithPrio LogIDEMain recorder) logger
125-
else IDEMain.defaultArguments (cmapWithPrio LogIDEMain recorder) logger
124+
then IDEMain.testing (cmapWithPrio LogIDEMain recorder) logger hlsPlugins
125+
else IDEMain.defaultArguments (cmapWithPrio LogIDEMain recorder) logger hlsPlugins
126126

127127
IDEMain.defaultMain (cmapWithPrio LogIDEMain recorder) arguments
128128
{ IDEMain.argsProjectRoot = Just argsCwd

ghcide/src/Development/IDE.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import Development.IDE.Core.IdeConfiguration as X (IdeConfiguration (.
1717
isWorkspaceFile)
1818
import Development.IDE.Core.OfInterest as X (getFilesOfInterestUntracked)
1919
import Development.IDE.Core.Rules as X (getClientConfigAction,
20-
getParsedModule)
20+
getParsedModule,
21+
usePropertyAction)
2122
import Development.IDE.Core.RuleTypes as X
2223
import Development.IDE.Core.Service as X (runAction)
2324
import Development.IDE.Core.Shake as X (FastResult (..),
@@ -31,7 +32,7 @@ import Development.IDE.Core.Shake as X (FastResult (..),
3132
defineEarlyCutoff,
3233
defineNoDiagnostics,
3334
getClientConfig,
34-
getPluginConfig,
35+
getPluginConfigAction,
3536
ideLogger,
3637
runIdeAction,
3738
shakeExtras, use,

ghcide/src/Development/IDE/Core/Rules.hs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ import Ide.Plugin.Properties (HasProperty,
144144
useProperty)
145145
import Ide.PluginUtils (configForPlugin)
146146
import Ide.Types (DynFlagsModifications (dynFlagsModifyGlobal, dynFlagsModifyParser),
147-
PluginId)
147+
PluginId, PluginDescriptor (pluginId), IdePlugins (IdePlugins))
148148
import Control.Concurrent.STM.Stats (atomically)
149149
import Language.LSP.Server (LspT)
150150
import System.Info.Extra (isWindows)
@@ -154,7 +154,7 @@ import qualified Development.IDE.Core.Shake as Shake
154154
import qualified Development.IDE.Types.Logger as Logger
155155
import qualified Development.IDE.Types.Shake as Shake
156156
import Development.IDE.GHC.CoreFile
157-
import Data.Time.Clock.POSIX (posixSecondsToUTCTime, utcTimeToPOSIXSeconds)
157+
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
158158
import Control.Monad.IO.Unlift
159159
#if MIN_VERSION_ghc(9,3,0)
160160
import GHC.Unit.Module.Graph
@@ -341,7 +341,7 @@ getParsedModuleWithCommentsRule recorder =
341341
getModifyDynFlags :: (DynFlagsModifications -> a) -> Action a
342342
getModifyDynFlags f = do
343343
opts <- getIdeOptions
344-
cfg <- getClientConfigAction def
344+
cfg <- getClientConfigAction
345345
pure $ f $ optModifyDynFlags opts cfg
346346

347347

@@ -1057,25 +1057,14 @@ getClientSettingsRule recorder = defineEarlyCutOffNoFile (cmapWithPrio LogShake
10571057
settings <- clientSettings <$> getIdeConfiguration
10581058
return (LBS.toStrict $ B.encode $ hash settings, settings)
10591059

1060-
-- | Returns the client configuration stored in the IdeState.
1061-
-- You can use this function to access it from shake Rules
1062-
getClientConfigAction :: Config -- ^ default value
1063-
-> Action Config
1064-
getClientConfigAction defValue = do
1065-
mbVal <- unhashed <$> useNoFile_ GetClientSettings
1066-
case A.parse (parseConfig defValue) <$> mbVal of
1067-
Just (Success c) -> return c
1068-
_ -> return defValue
1069-
10701060
usePropertyAction ::
10711061
(HasProperty s k t r) =>
10721062
KeyNameProxy s ->
10731063
PluginId ->
10741064
Properties r ->
10751065
Action (ToHsType t)
10761066
usePropertyAction kn plId p = do
1077-
config <- getClientConfigAction def
1078-
let pluginConfig = configForPlugin config plId
1067+
pluginConfig <- getPluginConfigAction plId
10791068
pure $ useProperty kn p $ plcConfig pluginConfig
10801069

10811070
-- ---------------------------------------------------------------------

ghcide/src/Development/IDE/Core/Shake.hs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ module Development.IDE.Core.Shake(
5353
getIdeOptionsIO,
5454
GlobalIdeOptions(..),
5555
HLS.getClientConfig,
56-
getPluginConfig,
56+
getPluginConfigAction,
5757
knownTargets,
5858
setPriority,
5959
ideLogger,
@@ -77,7 +77,7 @@ module Development.IDE.Core.Shake(
7777
garbageCollectDirtyKeys,
7878
garbageCollectDirtyKeysOlderThan,
7979
Log(..),
80-
VFSModified(..)
80+
VFSModified(..), getClientConfigAction
8181
) where
8282

8383
import Control.Concurrent.Async
@@ -90,15 +90,16 @@ import Control.Monad.Extra
9090
import Control.Monad.IO.Class
9191
import Control.Monad.Reader
9292
import Control.Monad.Trans.Maybe
93-
import Data.Aeson (toJSON)
93+
import Data.Aeson (Result (Success),
94+
toJSON)
9495
import qualified Data.ByteString.Char8 as BS
9596
import qualified Data.ByteString.Char8 as BS8
9697
import Data.Coerce (coerce)
9798
import Data.Default
9899
import Data.Dynamic
99100
import Data.EnumMap.Strict (EnumMap)
100101
import qualified Data.EnumMap.Strict as EM
101-
import Data.Foldable (for_, toList)
102+
import Data.Foldable (find, for_, toList)
102103
import Data.Functor ((<&>))
103104
import Data.Functor.Identity
104105
import Data.Hashable
@@ -134,6 +135,7 @@ import Development.IDE.GHC.Compat (NameCache,
134135
#if !MIN_VERSION_ghc(9,3,0)
135136
import Development.IDE.GHC.Compat (upNameCache)
136137
#endif
138+
import qualified Data.Aeson.Types as A
137139
import Development.IDE.GHC.Orphans ()
138140
import Development.IDE.Graph hiding (ShakeValue)
139141
import qualified Development.IDE.Graph as Shake
@@ -161,7 +163,9 @@ import GHC.Stack (HasCallStack)
161163
import HieDb.Types
162164
import Ide.Plugin.Config
163165
import qualified Ide.PluginUtils as HLS
164-
import Ide.Types (IdePlugins, PluginId)
166+
import Ide.Types (IdePlugins (IdePlugins),
167+
PluginDescriptor (pluginId),
168+
PluginId)
165169
import Language.LSP.Diagnostics
166170
import qualified Language.LSP.Server as LSP
167171
import Language.LSP.Types
@@ -311,10 +315,25 @@ getShakeExtrasRules = do
311315
Just x <- getShakeExtraRules @ShakeExtras
312316
return x
313317

314-
getPluginConfig
315-
:: LSP.MonadLsp Config m => PluginId -> m PluginConfig
316-
getPluginConfig plugin = do
317-
config <- HLS.getClientConfig
318+
-- | Returns the client configuration, creating a build dependency.
319+
-- You should always use this function when accessing client configuration
320+
-- from build rules.
321+
getClientConfigAction :: Action Config
322+
getClientConfigAction = do
323+
ShakeExtras{lspEnv, idePlugins} <- getShakeExtras
324+
currentConfig <- (`LSP.runLspT` LSP.getConfig) `traverse` lspEnv
325+
mbVal <- unhashed <$> useNoFile_ GetClientSettings
326+
let defValue = fromMaybe def currentConfig
327+
case A.parse (parseConfig idePlugins defValue) <$> mbVal of
328+
Just (Success c) -> return c
329+
_ -> return defValue
330+
331+
getPluginConfigAction :: PluginId -> Action PluginConfig
332+
getPluginConfigAction plId = do
333+
config <- getClientConfigAction
334+
ShakeExtras{idePlugins = IdePlugins plugins} <- getShakeExtras
335+
let plugin = fromMaybe (error $ "Plugin not found: " <> show plId) $
336+
find (\p -> pluginId p == plId) plugins
318337
return $ HLS.configForPlugin config plugin
319338

320339
-- | Register a function that will be called to get the "stale" result of a rule, possibly from disk

ghcide/src/Development/IDE/Main.hs

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,22 @@ module Development.IDE.Main
1313
,Log(..)
1414
) where
1515
import Control.Concurrent.Extra (withNumCapabilities)
16-
import Control.Concurrent.STM.Stats (atomically,
17-
dumpSTMStats)
16+
import Control.Concurrent.STM.Stats (dumpSTMStats)
1817
import Control.Exception.Safe (SomeException,
1918
catchAny,
2019
displayException)
2120
import Control.Monad.Extra (concatMapM, unless,
2221
when)
23-
import qualified Data.Aeson.Encode.Pretty as A
2422
import Data.Coerce (coerce)
2523
import Data.Default (Default (def))
2624
import Data.Foldable (traverse_)
2725
import Data.Hashable (hashed)
2826
import qualified Data.HashMap.Strict as HashMap
2927
import Data.List.Extra (intercalate,
30-
isPrefixOf, nub,
31-
nubOrd, partition)
28+
isPrefixOf, nubOrd,
29+
partition)
3230
import Data.Maybe (catMaybes, isJust)
3331
import qualified Data.Text as T
34-
import Data.Text.Lazy.Encoding (decodeUtf8)
35-
import qualified Data.Text.Lazy.IO as LT
36-
import Data.Typeable (typeOf)
3732
import Development.IDE (Action,
3833
GhcVersion (..),
3934
Priority (Debug, Error),
@@ -47,20 +42,16 @@ import Development.IDE.Core.IdeConfiguration (IdeConfiguration (..)
4742
import Development.IDE.Core.OfInterest (FileOfInterestStatus (OnDisk),
4843
kick,
4944
setFilesOfInterest)
50-
import Development.IDE.Core.Rules (GhcSessionIO (GhcSessionIO),
51-
mainRule)
45+
import Development.IDE.Core.Rules (mainRule)
5246
import qualified Development.IDE.Core.Rules as Rules
5347
import Development.IDE.Core.RuleTypes (GenerateCore (GenerateCore),
5448
GetHieAst (GetHieAst),
55-
GhcSession (GhcSession),
56-
GhcSessionDeps (GhcSessionDeps),
5749
TypeCheck (TypeCheck))
5850
import Development.IDE.Core.Service (initialise,
5951
runAction)
6052
import qualified Development.IDE.Core.Service as Service
6153
import Development.IDE.Core.Shake (IdeState (shakeExtras),
6254
IndexQueue,
63-
ShakeExtras (state),
6455
shakeSessionInit,
6556
uses)
6657
import qualified Development.IDE.Core.Shake as Shake
@@ -102,8 +93,7 @@ import Development.IDE.Types.Options (IdeGhcSession,
10293
defaultIdeOptions,
10394
optModifyDynFlags,
10495
optTesting)
105-
import Development.IDE.Types.Shake (WithHieDb,
106-
fromKeyType)
96+
import Development.IDE.Types.Shake (WithHieDb)
10797
import GHC.Conc (getNumProcessors)
10898
import GHC.IO.Encoding (setLocaleEncoding)
10999
import GHC.IO.Handle (hDuplicate)
@@ -113,8 +103,6 @@ import Ide.Plugin.Config (CheckParents (NeverCh
113103
Config, checkParents,
114104
checkProject,
115105
getConfigFromNotification)
116-
import Ide.Plugin.ConfigUtils (pluginsToDefaultConfig,
117-
pluginsToVSCodeExtensionSchema)
118106
import Ide.PluginUtils (allLspCmdIds',
119107
getProcessID,
120108
idePluginsToPluginDesc,
@@ -125,10 +113,8 @@ import Ide.Types (IdeCommand (IdeComman
125113
PluginId (PluginId),
126114
ipMap, pluginId)
127115
import qualified Language.LSP.Server as LSP
128-
import qualified "list-t" ListT
129116
import Numeric.Natural (Natural)
130117
import Options.Applicative hiding (action)
131-
import qualified StmContainers.Map as STM
132118
import qualified System.Directory.Extra as IO
133119
import System.Exit (ExitCode (ExitFailure),
134120
exitWith)
@@ -239,14 +225,14 @@ data Arguments = Arguments
239225
, argsMonitoring :: IO Monitoring
240226
}
241227

242-
defaultArguments :: Recorder (WithPriority Log) -> Logger -> Arguments
243-
defaultArguments recorder logger = Arguments
228+
defaultArguments :: Recorder (WithPriority Log) -> Logger -> IdePlugins IdeState -> Arguments
229+
defaultArguments recorder logger plugins = Arguments
244230
{ argsProjectRoot = Nothing
245231
, argCommand = LSP
246232
, argsLogger = pure logger
247233
, argsRules = mainRule (cmapWithPrio LogRules recorder) def >> action kick
248234
, argsGhcidePlugin = mempty
249-
, argsHlsPlugins = pluginDescToIdePlugins (GhcIde.descriptors (cmapWithPrio LogGhcIde recorder))
235+
, argsHlsPlugins = pluginDescToIdePlugins (GhcIde.descriptors (cmapWithPrio LogGhcIde recorder)) <> plugins
250236
, argsSessionLoadingOptions = def
251237
, argsIdeOptions = \config ghcSession -> (defaultIdeOptions ghcSession)
252238
{ optCheckProject = pure $ checkProject config
@@ -276,10 +262,11 @@ defaultArguments recorder logger = Arguments
276262
}
277263

278264

279-
testing :: Recorder (WithPriority Log) -> Logger -> Arguments
280-
testing recorder logger =
265+
testing :: Recorder (WithPriority Log) -> Logger -> IdePlugins IdeState -> Arguments
266+
testing recorder logger plugins =
281267
let
282-
arguments@Arguments{ argsHlsPlugins, argsIdeOptions } = defaultArguments recorder logger
268+
arguments@Arguments{ argsHlsPlugins, argsIdeOptions } =
269+
defaultArguments recorder logger plugins
283270
hlsPlugins = pluginDescToIdePlugins $
284271
idePluginsToPluginDesc argsHlsPlugins
285272
++ [Test.blockCommandDescriptor "block-command", Test.plugin]
@@ -310,7 +297,7 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re
310297
hlsCommands = allLspCmdIds' pid argsHlsPlugins
311298
plugins = hlsPlugin <> argsGhcidePlugin
312299
options = argsLspOptions { LSP.executeCommandCommands = LSP.executeCommandCommands argsLspOptions <> Just hlsCommands }
313-
argsOnConfigChange = getConfigFromNotification
300+
argsOnConfigChange = getConfigFromNotification argsHlsPlugins
314301
rules = argsRules >> pluginRules plugins
315302

316303
debouncer <- argsDebouncer

0 commit comments

Comments
 (0)