Skip to content

Support optional plugins #3193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Dec 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions bench/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS -Wno-orphans #-}

Expand All @@ -53,7 +54,7 @@ import Data.Default
import Data.Foldable (find)
import qualified Data.Map.Strict as Map
import Data.Maybe
import Data.Text (pack, unpack)
import Data.Text (unpack)
import Data.Yaml (FromJSON (..), ToJSON (toJSON),
decodeFileThrow)
import Development.Benchmark.Rules hiding (parallelism)
Expand All @@ -74,7 +75,7 @@ import GHC.Exts (toList)
import GHC.Generics (Generic)
import HlsPlugins (idePlugins)
import qualified Ide.Plugin.Config as Plugin
import Ide.Types
import Ide.Types hiding (Config)
import Numeric.Natural (Natural)
import System.Console.GetOpt
import System.Directory
Expand Down Expand Up @@ -175,13 +176,13 @@ createBuildSystem config = do
disableAllPluginsBut :: (PluginId -> Bool) -> Plugin.Config
disableAllPluginsBut pred = def {Plugin.plugins = pluginsMap} where
pluginsMap = Map.fromList
[ (p, def { Plugin.plcGlobalOn = globalOn})
| PluginDescriptor{pluginId = plugin@(PluginId p)} <- plugins
[ (plugin, def { Plugin.plcGlobalOn = globalOn})
| PluginDescriptor{pluginId = plugin} <- plugins
, let globalOn =
-- ghcide-core is required, nothing works without it
plugin == PluginId (pack "ghcide-core")
plugin == "ghcide-core"
-- document symbols is required by the benchmark suite
|| plugin == PluginId (pack "ghcide-hover-and-symbols")
|| plugin == "ghcide-hover-and-symbols"
|| pred plugin
]
IdePlugins plugins = idePlugins mempty
Expand Down
3 changes: 2 additions & 1 deletion exe/Wrapper.hs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import Development.IDE.Types.Logger (Logger (Logger),
makeDefaultStderrRecorder)
import GHC.Stack.Types (emptyCallStack)
import Ide.Plugin.Config (Config)
import Ide.Types (IdePlugins (IdePlugins))
import Language.LSP.Server (LspM)
import qualified Language.LSP.Server as LSP
import Language.LSP.Types (MessageActionItem (MessageActionItem),
Expand Down Expand Up @@ -276,7 +277,7 @@ launchErrorLSP errorMsg = do

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

let defaultArguments = Main.defaultArguments (cmapWithPrio pretty recorder) logger
let defaultArguments = Main.defaultArguments (cmapWithPrio pretty recorder) logger (IdePlugins [])

inH <- Main.argsHandleIn defaultArguments

Expand Down
4 changes: 2 additions & 2 deletions ghcide/exe/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ main = withTelemetryLogger $ \telemetryLogger -> do

let arguments =
if argsTesting
then IDEMain.testing (cmapWithPrio LogIDEMain recorder) logger
else IDEMain.defaultArguments (cmapWithPrio LogIDEMain recorder) logger
then IDEMain.testing (cmapWithPrio LogIDEMain recorder) logger hlsPlugins
else IDEMain.defaultArguments (cmapWithPrio LogIDEMain recorder) logger hlsPlugins

IDEMain.defaultMain (cmapWithPrio LogIDEMain recorder) arguments
{ IDEMain.argsProjectRoot = Just argsCwd
Expand Down
5 changes: 3 additions & 2 deletions ghcide/src/Development/IDE.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import Development.IDE.Core.IdeConfiguration as X (IdeConfiguration (.
isWorkspaceFile)
import Development.IDE.Core.OfInterest as X (getFilesOfInterestUntracked)
import Development.IDE.Core.Rules as X (getClientConfigAction,
getParsedModule)
getParsedModule,
usePropertyAction)
import Development.IDE.Core.RuleTypes as X
import Development.IDE.Core.Service as X (runAction)
import Development.IDE.Core.Shake as X (FastResult (..),
Expand All @@ -31,7 +32,7 @@ import Development.IDE.Core.Shake as X (FastResult (..),
defineEarlyCutoff,
defineNoDiagnostics,
getClientConfig,
getPluginConfig,
getPluginConfigAction,
ideLogger,
runIdeAction,
shakeExtras, use,
Expand Down
19 changes: 4 additions & 15 deletions ghcide/src/Development/IDE/Core/Rules.hs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ import Ide.Plugin.Properties (HasProperty,
useProperty)
import Ide.PluginUtils (configForPlugin)
import Ide.Types (DynFlagsModifications (dynFlagsModifyGlobal, dynFlagsModifyParser),
PluginId)
PluginId, PluginDescriptor (pluginId), IdePlugins (IdePlugins))
import Control.Concurrent.STM.Stats (atomically)
import Language.LSP.Server (LspT)
import System.Info.Extra (isWindows)
Expand All @@ -154,7 +154,7 @@ import qualified Development.IDE.Core.Shake as Shake
import qualified Development.IDE.Types.Logger as Logger
import qualified Development.IDE.Types.Shake as Shake
import Development.IDE.GHC.CoreFile
import Data.Time.Clock.POSIX (posixSecondsToUTCTime, utcTimeToPOSIXSeconds)
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
import Control.Monad.IO.Unlift
#if MIN_VERSION_ghc(9,3,0)
import GHC.Unit.Module.Graph
Expand Down Expand Up @@ -341,7 +341,7 @@ getParsedModuleWithCommentsRule recorder =
getModifyDynFlags :: (DynFlagsModifications -> a) -> Action a
getModifyDynFlags f = do
opts <- getIdeOptions
cfg <- getClientConfigAction def
cfg <- getClientConfigAction
pure $ f $ optModifyDynFlags opts cfg


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

-- | Returns the client configuration stored in the IdeState.
-- You can use this function to access it from shake Rules
getClientConfigAction :: Config -- ^ default value
-> Action Config
getClientConfigAction defValue = do
mbVal <- unhashed <$> useNoFile_ GetClientSettings
case A.parse (parseConfig defValue) <$> mbVal of
Just (Success c) -> return c
_ -> return defValue

usePropertyAction ::
(HasProperty s k t r) =>
KeyNameProxy s ->
PluginId ->
Properties r ->
Action (ToHsType t)
usePropertyAction kn plId p = do
config <- getClientConfigAction def
let pluginConfig = configForPlugin config plId
pluginConfig <- getPluginConfigAction plId
pure $ useProperty kn p $ plcConfig pluginConfig

-- ---------------------------------------------------------------------
Expand Down
37 changes: 28 additions & 9 deletions ghcide/src/Development/IDE/Core/Shake.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ module Development.IDE.Core.Shake(
getIdeOptionsIO,
GlobalIdeOptions(..),
HLS.getClientConfig,
getPluginConfig,
getPluginConfigAction,
knownTargets,
setPriority,
ideLogger,
Expand All @@ -77,7 +77,7 @@ module Development.IDE.Core.Shake(
garbageCollectDirtyKeys,
garbageCollectDirtyKeysOlderThan,
Log(..),
VFSModified(..)
VFSModified(..), getClientConfigAction
) where

import Control.Concurrent.Async
Expand All @@ -90,15 +90,16 @@ import Control.Monad.Extra
import Control.Monad.IO.Class
import Control.Monad.Reader
import Control.Monad.Trans.Maybe
import Data.Aeson (toJSON)
import Data.Aeson (Result (Success),
toJSON)
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Char8 as BS8
import Data.Coerce (coerce)
import Data.Default
import Data.Dynamic
import Data.EnumMap.Strict (EnumMap)
import qualified Data.EnumMap.Strict as EM
import Data.Foldable (for_, toList)
import Data.Foldable (find, for_, toList)
import Data.Functor ((<&>))
import Data.Functor.Identity
import Data.Hashable
Expand Down Expand Up @@ -134,6 +135,7 @@ import Development.IDE.GHC.Compat (NameCache,
#if !MIN_VERSION_ghc(9,3,0)
import Development.IDE.GHC.Compat (upNameCache)
#endif
import qualified Data.Aeson.Types as A
import Development.IDE.GHC.Orphans ()
import Development.IDE.Graph hiding (ShakeValue)
import qualified Development.IDE.Graph as Shake
Expand Down Expand Up @@ -161,7 +163,9 @@ import GHC.Stack (HasCallStack)
import HieDb.Types
import Ide.Plugin.Config
import qualified Ide.PluginUtils as HLS
import Ide.Types (IdePlugins, PluginId)
import Ide.Types (IdePlugins (IdePlugins),
PluginDescriptor (pluginId),
PluginId)
import Language.LSP.Diagnostics
import qualified Language.LSP.Server as LSP
import Language.LSP.Types
Expand Down Expand Up @@ -311,10 +315,25 @@ getShakeExtrasRules = do
Just x <- getShakeExtraRules @ShakeExtras
return x

getPluginConfig
:: LSP.MonadLsp Config m => PluginId -> m PluginConfig
getPluginConfig plugin = do
config <- HLS.getClientConfig
-- | Returns the client configuration, creating a build dependency.
-- You should always use this function when accessing client configuration
-- from build rules.
getClientConfigAction :: Action Config
getClientConfigAction = do
ShakeExtras{lspEnv, idePlugins} <- getShakeExtras
currentConfig <- (`LSP.runLspT` LSP.getConfig) `traverse` lspEnv
mbVal <- unhashed <$> useNoFile_ GetClientSettings
let defValue = fromMaybe def currentConfig
case A.parse (parseConfig idePlugins defValue) <$> mbVal of
Just (Success c) -> return c
_ -> return defValue

getPluginConfigAction :: PluginId -> Action PluginConfig
getPluginConfigAction plId = do
config <- getClientConfigAction
ShakeExtras{idePlugins = IdePlugins plugins} <- getShakeExtras
let plugin = fromMaybe (error $ "Plugin not found: " <> show plId) $
find (\p -> pluginId p == plId) plugins
return $ HLS.configForPlugin config plugin

-- | Register a function that will be called to get the "stale" result of a rule, possibly from disk
Expand Down
39 changes: 13 additions & 26 deletions ghcide/src/Development/IDE/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,22 @@ module Development.IDE.Main
,Log(..)
) where
import Control.Concurrent.Extra (withNumCapabilities)
import Control.Concurrent.STM.Stats (atomically,
dumpSTMStats)
import Control.Concurrent.STM.Stats (dumpSTMStats)
import Control.Exception.Safe (SomeException,
catchAny,
displayException)
import Control.Monad.Extra (concatMapM, unless,
when)
import qualified Data.Aeson.Encode.Pretty as A
import Data.Coerce (coerce)
import Data.Default (Default (def))
import Data.Foldable (traverse_)
import Data.Hashable (hashed)
import qualified Data.HashMap.Strict as HashMap
import Data.List.Extra (intercalate,
isPrefixOf, nub,
nubOrd, partition)
isPrefixOf, nubOrd,
partition)
import Data.Maybe (catMaybes, isJust)
import qualified Data.Text as T
import Data.Text.Lazy.Encoding (decodeUtf8)
import qualified Data.Text.Lazy.IO as LT
import Data.Typeable (typeOf)
import Development.IDE (Action,
GhcVersion (..),
Priority (Debug, Error),
Expand All @@ -47,20 +42,16 @@ import Development.IDE.Core.IdeConfiguration (IdeConfiguration (..)
import Development.IDE.Core.OfInterest (FileOfInterestStatus (OnDisk),
kick,
setFilesOfInterest)
import Development.IDE.Core.Rules (GhcSessionIO (GhcSessionIO),
mainRule)
import Development.IDE.Core.Rules (mainRule)
import qualified Development.IDE.Core.Rules as Rules
import Development.IDE.Core.RuleTypes (GenerateCore (GenerateCore),
GetHieAst (GetHieAst),
GhcSession (GhcSession),
GhcSessionDeps (GhcSessionDeps),
TypeCheck (TypeCheck))
import Development.IDE.Core.Service (initialise,
runAction)
import qualified Development.IDE.Core.Service as Service
import Development.IDE.Core.Shake (IdeState (shakeExtras),
IndexQueue,
ShakeExtras (state),
shakeSessionInit,
uses)
import qualified Development.IDE.Core.Shake as Shake
Expand Down Expand Up @@ -102,8 +93,7 @@ import Development.IDE.Types.Options (IdeGhcSession,
defaultIdeOptions,
optModifyDynFlags,
optTesting)
import Development.IDE.Types.Shake (WithHieDb,
fromKeyType)
import Development.IDE.Types.Shake (WithHieDb)
import GHC.Conc (getNumProcessors)
import GHC.IO.Encoding (setLocaleEncoding)
import GHC.IO.Handle (hDuplicate)
Expand All @@ -113,8 +103,6 @@ import Ide.Plugin.Config (CheckParents (NeverCh
Config, checkParents,
checkProject,
getConfigFromNotification)
import Ide.Plugin.ConfigUtils (pluginsToDefaultConfig,
pluginsToVSCodeExtensionSchema)
import Ide.PluginUtils (allLspCmdIds',
getProcessID,
idePluginsToPluginDesc,
Expand All @@ -125,10 +113,8 @@ import Ide.Types (IdeCommand (IdeComman
PluginId (PluginId),
ipMap, pluginId)
import qualified Language.LSP.Server as LSP
import qualified "list-t" ListT
import Numeric.Natural (Natural)
import Options.Applicative hiding (action)
import qualified StmContainers.Map as STM
import qualified System.Directory.Extra as IO
import System.Exit (ExitCode (ExitFailure),
exitWith)
Expand Down Expand Up @@ -239,14 +225,14 @@ data Arguments = Arguments
, argsMonitoring :: IO Monitoring
}

defaultArguments :: Recorder (WithPriority Log) -> Logger -> Arguments
defaultArguments recorder logger = Arguments
defaultArguments :: Recorder (WithPriority Log) -> Logger -> IdePlugins IdeState -> Arguments
defaultArguments recorder logger plugins = Arguments
{ argsProjectRoot = Nothing
, argCommand = LSP
, argsLogger = pure logger
, argsRules = mainRule (cmapWithPrio LogRules recorder) def >> action kick
, argsGhcidePlugin = mempty
, argsHlsPlugins = pluginDescToIdePlugins (GhcIde.descriptors (cmapWithPrio LogGhcIde recorder))
, argsHlsPlugins = pluginDescToIdePlugins (GhcIde.descriptors (cmapWithPrio LogGhcIde recorder)) <> plugins
, argsSessionLoadingOptions = def
, argsIdeOptions = \config ghcSession -> (defaultIdeOptions ghcSession)
{ optCheckProject = pure $ checkProject config
Expand Down Expand Up @@ -276,10 +262,11 @@ defaultArguments recorder logger = Arguments
}


testing :: Recorder (WithPriority Log) -> Logger -> Arguments
testing recorder logger =
testing :: Recorder (WithPriority Log) -> Logger -> IdePlugins IdeState -> Arguments
testing recorder logger plugins =
let
arguments@Arguments{ argsHlsPlugins, argsIdeOptions } = defaultArguments recorder logger
arguments@Arguments{ argsHlsPlugins, argsIdeOptions } =
defaultArguments recorder logger plugins
hlsPlugins = pluginDescToIdePlugins $
idePluginsToPluginDesc argsHlsPlugins
++ [Test.blockCommandDescriptor "block-command", Test.plugin]
Expand Down Expand Up @@ -310,7 +297,7 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re
hlsCommands = allLspCmdIds' pid argsHlsPlugins
plugins = hlsPlugin <> argsGhcidePlugin
options = argsLspOptions { LSP.executeCommandCommands = LSP.executeCommandCommands argsLspOptions <> Just hlsCommands }
argsOnConfigChange = getConfigFromNotification
argsOnConfigChange = getConfigFromNotification argsHlsPlugins
rules = argsRules >> pluginRules plugins

debouncer <- argsDebouncer
Expand Down
Loading