hls-plugin-api-1.1.0.0: src/Ide/Plugin/ConfigUtils.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ViewPatterns #-}
module Ide.Plugin.ConfigUtils where
import qualified Data.Aeson as A
import qualified Data.Aeson.Types as A
import Data.Default (def)
import qualified Data.Dependent.Map as DMap
import qualified Data.Dependent.Sum as DSum
import qualified Data.HashMap.Lazy as HMap
import Ide.Plugin.Config
import Ide.Plugin.Properties (toDefaultJSON, toVSCodeExtensionSchema)
import Ide.Types
import Language.LSP.Types
-- Attention:
-- 'diagnosticsOn' will never be added into the default config or the schema,
-- since diagnostics emit in arbitrary shake rules -- we don't know
-- whether a plugin is capable of producing diagnostics.
-- | Generates a defalut 'Config', but remains only effective items
pluginsToDefaultConfig :: IdePlugins a -> A.Value
pluginsToDefaultConfig IdePlugins {..} =
A.Object $
HMap.adjust
( \(unsafeValueToObject -> o) ->
A.Object $ HMap.insert "plugin" elems o -- inplace the "plugin" section with our 'elems', leaving others unchanged
)
"haskell"
(unsafeValueToObject (A.toJSON defaultConfig))
where
defaultConfig@Config {} = def
unsafeValueToObject (A.Object o) = o
unsafeValueToObject _ = error "impossible"
elems = A.object $ mconcat $ singlePlugin <$> map snd ipMap
-- Splice genericDefaultConfig and dedicatedDefaultConfig
-- Example:
--
-- {
-- "plugin-id": {
-- "globalOn": true,
-- "codeActionsOn": true,
-- "codeLensOn": true,
-- "config": {
-- "property1": "foo"
-- }
-- }
-- }
singlePlugin PluginDescriptor {..} =
let x = genericDefaultConfig <> dedicatedDefaultConfig
in [pId A..= A.object x | not $ null x]
where
(PluginHandlers (DMap.toList -> handlers)) = pluginHandlers
customConfigToDedicatedDefaultConfig (CustomConfig p) = toDefaultJSON p
-- Example:
--
-- {
-- "globalOn": true,
-- "codeActionsOn": true,
-- "codeLensOn": true
-- }
--
-- we don't generate the config section if the plugin doesn't register any of the following six methods,
-- which avoids producing trivial configuration for formatters:
--
-- "stylish-haskell": {
-- "globalOn": true
-- }
genericDefaultConfig =
let x = mconcat (handlersToGenericDefaultConfig <$> handlers)
in ["globalOn" A..= True | not $ null x] <> x
-- Example:
--
-- {
-- "config": {
-- "property1": "foo"
-- }
--}
dedicatedDefaultConfig =
let x = customConfigToDedicatedDefaultConfig pluginCustomConfig
in ["config" A..= A.object x | not $ null x]
(PluginId pId) = pluginId
-- This function captures ide methods registered by the plugin, and then converts it to kv pairs
handlersToGenericDefaultConfig :: DSum.DSum IdeMethod f -> [A.Pair]
handlersToGenericDefaultConfig (IdeMethod m DSum.:=> _) = case m of
STextDocumentCodeAction -> ["codeActionsOn" A..= True]
STextDocumentCodeLens -> ["codeLensOn" A..= True]
STextDocumentRename -> ["renameOn" A..= True]
STextDocumentHover -> ["hoverOn" A..= True]
STextDocumentDocumentSymbol -> ["symbolsOn" A..= True]
STextDocumentCompletion -> ["completionOn" A..= True]
_ -> []
-- | Generates json schema used in haskell vscode extension
-- Similar to 'pluginsToDefaultConfig' but simpler, since schema has a flatten structure
pluginsToVSCodeExtensionSchema :: IdePlugins a -> A.Value
pluginsToVSCodeExtensionSchema IdePlugins {..} = A.object $ mconcat $ singlePlugin <$> map snd ipMap
where
singlePlugin PluginDescriptor {..} = genericSchema <> dedicatedSchema
where
(PluginHandlers (DMap.toList -> handlers)) = pluginHandlers
customConfigToDedicatedSchema (CustomConfig p) = toVSCodeExtensionSchema (withIdPrefix "config.") p
(PluginId pId) = pluginId
genericSchema = withIdPrefix "globalOn" A..= schemaEntry "plugin" : mconcat (handlersToGenericSchema <$> handlers)
dedicatedSchema = customConfigToDedicatedSchema pluginCustomConfig
handlersToGenericSchema (IdeMethod m DSum.:=> _) = case m of
STextDocumentCodeAction -> [withIdPrefix "codeActionsOn" A..= schemaEntry "code actions"]
STextDocumentCodeLens -> [withIdPrefix "codeLensOn" A..= schemaEntry "code lenses"]
STextDocumentRename -> [withIdPrefix "renameOn" A..= schemaEntry "rename"]
STextDocumentHover -> [withIdPrefix "hoverOn" A..= schemaEntry "hover"]
STextDocumentDocumentSymbol -> [withIdPrefix "symbolsOn" A..= schemaEntry "symbols"]
STextDocumentCompletion -> [withIdPrefix "completionOn" A..= schemaEntry "completions"]
_ -> []
schemaEntry desc =
A.object
[ "scope" A..= A.String "resource",
"type" A..= A.String "boolean",
"default" A..= True,
"description" A..= A.String ("Enables " <> pId <> " " <> desc)
]
withIdPrefix x = "haskell.plugin." <> pId <> "." <> x