hls-plugin-api-1.3.0.0: src/Ide/Plugin/Config.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
module Ide.Plugin.Config
( getConfigFromNotification
, Config(..)
, parseConfig
, PluginConfig(..)
, CheckParents(..)
) where
import Control.Applicative
import Data.Aeson hiding (Error)
import qualified Data.Aeson as A
import qualified Data.Aeson.Types as A
import Data.Default
import qualified Data.Map as Map
import qualified Data.Text as T
import GHC.Generics (Generic)
-- ---------------------------------------------------------------------
-- | Given a DidChangeConfigurationNotification message, this function returns the parsed
-- Config object if possible.
getConfigFromNotification :: Config -> A.Value -> Either T.Text Config
getConfigFromNotification defaultValue p =
case A.parse (parseConfig defaultValue) p of
A.Success c -> Right c
A.Error err -> Left $ T.pack err
-- ---------------------------------------------------------------------
data CheckParents
-- Note that ordering of constructors is meaningful and must be monotonically
-- increasing in the scenarios where parents are checked
= NeverCheck
| CheckOnSave
| AlwaysCheck
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (FromJSON, ToJSON)
-- | We (initially anyway) mirror the hie configuration, so that existing
-- clients can simply switch executable and not have any nasty surprises. There
-- will be surprises relating to config options being ignored, initially though.
data Config =
Config
{ checkParents :: CheckParents
, checkProject :: !Bool
, formattingProvider :: !T.Text
, maxCompletions :: !Int
, plugins :: !(Map.Map T.Text PluginConfig)
} deriving (Show,Eq)
instance Default Config where
def = Config
{ checkParents = CheckOnSave
, checkProject = True
-- , formattingProvider = "brittany"
, formattingProvider = "ormolu"
-- , formattingProvider = "floskell"
-- , formattingProvider = "stylish-haskell"
, maxCompletions = 40
, plugins = Map.empty
}
-- TODO: Add API for plugins to expose their own LSP config options
parseConfig :: Config -> Value -> A.Parser Config
parseConfig defValue = A.withObject "Config" $ \v -> do
-- Officially, we use "haskell" as the section name but for
-- backwards compatibility we also accept "languageServerHaskell"
c <- v .: "haskell" <|> v .:? "languageServerHaskell"
case c of
Nothing -> return defValue
Just s -> flip (A.withObject "Config.settings") s $ \o -> Config
<$> (o .:? "checkParents" <|> v .:? "checkParents") .!= checkParents defValue
<*> (o .:? "checkProject" <|> v .:? "checkProject") .!= checkProject defValue
<*> o .:? "formattingProvider" .!= formattingProvider defValue
<*> o .:? "maxCompletions" .!= maxCompletions defValue
<*> o .:? "plugin" .!= plugins defValue
instance A.ToJSON Config where
toJSON Config{..} =
object [ "haskell" .= r ]
where
r = object [ "checkParents" .= checkParents
, "checkProject" .= checkProject
, "formattingProvider" .= formattingProvider
, "maxCompletions" .= maxCompletions
, "plugin" .= plugins
]
-- ---------------------------------------------------------------------
-- | A PluginConfig is a generic configuration for a given HLS plugin. It
-- provides a "big switch" to turn it on or off as a whole, as well as small
-- switches per feature, and a slot for custom config.
-- This provides a regular naming scheme for all plugin config.
data PluginConfig =
PluginConfig
{ plcGlobalOn :: !Bool
, plcCallHierarchyOn :: !Bool
, plcCodeActionsOn :: !Bool
, plcCodeLensOn :: !Bool
, plcDiagnosticsOn :: !Bool
, plcHoverOn :: !Bool
, plcSymbolsOn :: !Bool
, plcCompletionOn :: !Bool
, plcRenameOn :: !Bool
, plcSelectionRangeOn :: !Bool
, plcConfig :: !A.Object
} deriving (Show,Eq)
instance Default PluginConfig where
def = PluginConfig
{ plcGlobalOn = True
, plcCallHierarchyOn = True
, plcCodeActionsOn = True
, plcCodeLensOn = True
, plcDiagnosticsOn = True
, plcHoverOn = True
, plcSymbolsOn = True
, plcCompletionOn = True
, plcRenameOn = True
, plcSelectionRangeOn = True
, plcConfig = mempty
}
instance A.ToJSON PluginConfig where
toJSON (PluginConfig g ch ca cl d h s c rn sr cfg) = r
where
r = object [ "globalOn" .= g
, "callHierarchyOn" .= ch
, "codeActionsOn" .= ca
, "codeLensOn" .= cl
, "diagnosticsOn" .= d
, "hoverOn" .= h
, "symbolsOn" .= s
, "completionOn" .= c
, "renameOn" .= rn
, "selectionRangeOn" .= sr
, "config" .= cfg
]
instance A.FromJSON PluginConfig where
parseJSON = A.withObject "PluginConfig" $ \o -> PluginConfig
<$> o .:? "globalOn" .!= plcGlobalOn def
<*> o .:? "callHierarchyOn" .!= plcCallHierarchyOn def
<*> o .:? "codeActionsOn" .!= plcCodeActionsOn def
<*> o .:? "codeLensOn" .!= plcCodeLensOn def
<*> o .:? "diagnosticsOn" .!= plcDiagnosticsOn def -- AZ
<*> o .:? "hoverOn" .!= plcHoverOn def
<*> o .:? "symbolsOn" .!= plcSymbolsOn def
<*> o .:? "completionOn" .!= plcCompletionOn def
<*> o .:? "renameOn" .!= plcRenameOn def
<*> o .:? "selectionRangeOn" .!= plcSelectionRangeOn def
<*> o .:? "config" .!= plcConfig def
-- ---------------------------------------------------------------------