forked from haskell/haskell-language-server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathConfigUtils.hs
137 lines (130 loc) · 6.67 KB
/
ConfigUtils.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ViewPatterns #-}
module Ide.Plugin.ConfigUtils where
import Control.Lens (at, (&), (?~))
import qualified Data.Aeson as A
import Data.Aeson.Lens (_Object)
import qualified Data.Aeson.Types as A
import Data.Default
import qualified Data.Dependent.Map as DMap
import qualified Data.Dependent.Sum as DSum
import Data.List.Extra (nubOrd)
import Data.String (IsString (fromString))
import qualified Data.Text as T
import Ide.Plugin.Config
import Ide.Plugin.Properties (toDefaultJSON,
toVSCodeExtensionSchema)
import Ide.Types
import Language.LSP.Protocol.Message
-- 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 default 'Config', but remains only effective items
pluginsToDefaultConfig :: IdePlugins a -> A.Value
pluginsToDefaultConfig IdePlugins {..} =
-- Use '_Object' and 'at' to get at the "plugin" key
-- and actually set it.
A.toJSON defaultConfig & _Object . at "plugin" ?~ elems
where
defaultConfig@Config {} = def
elems = A.object $ mconcat $ singlePlugin <$> ipMap
-- Splice genericDefaultConfig and dedicatedDefaultConfig
-- Example:
--
-- {
-- "plugin-id": {
-- "globalOn": true,
-- "codeActionsOn": true,
-- "codeLensOn": true,
-- "config": {
-- "property1": "foo"
-- }
-- }
-- }
singlePlugin PluginDescriptor {pluginConfigDescriptor = ConfigDescriptor {..}, ..} =
let x = genericDefaultConfig <> dedicatedDefaultConfig
in [fromString (T.unpack pId) A..= A.object x | not $ null x]
where
(PluginHandlers (DMap.toList -> handlers)) = pluginHandlers
customConfigToDedicatedDefaultConfig (CustomConfig p) = toDefaultJSON p
-- Example:
--
-- {
-- "codeActionsOn": true,
-- "codeLensOn": true
-- }
--
genericDefaultConfig =
let x = ["diagnosticsOn" A..= True | configHasDiagnostics]
<> nubOrd (mconcat
(handlersToGenericDefaultConfig configInitialGenericConfig <$> handlers))
in case x of
-- if the plugin has only one capability, we produce globalOn instead of the specific one;
-- otherwise we don't produce globalOn at all
[_] -> ["globalOn" A..= plcGlobalOn configInitialGenericConfig]
_ -> x
-- Example:
--
-- {
-- "config": {
-- "property1": "foo"
-- }
--}
dedicatedDefaultConfig =
let x = customConfigToDedicatedDefaultConfig configCustomConfig
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 :: PluginConfig -> DSum.DSum IdeMethod f -> [A.Pair]
handlersToGenericDefaultConfig PluginConfig{..} (IdeMethod m DSum.:=> _) = case m of
SMethod_TextDocumentCodeAction -> ["codeActionsOn" A..= plcCodeActionsOn]
SMethod_TextDocumentCodeLens -> ["codeLensOn" A..= plcCodeLensOn]
SMethod_TextDocumentRename -> ["renameOn" A..= plcRenameOn]
SMethod_TextDocumentHover -> ["hoverOn" A..= plcHoverOn]
SMethod_TextDocumentDocumentSymbol -> ["symbolsOn" A..= plcSymbolsOn]
SMethod_TextDocumentCompletion -> ["completionOn" A..= plcCompletionOn]
SMethod_TextDocumentPrepareCallHierarchy -> ["callHierarchyOn" A..= plcCallHierarchyOn]
SMethod_TextDocumentSemanticTokensFull -> ["semanticTokensOn" A..= plcSemanticTokensOn]
_ -> []
-- | 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 <$> ipMap
where
singlePlugin PluginDescriptor {pluginConfigDescriptor = ConfigDescriptor {..}, ..} = genericSchema <> dedicatedSchema
where
(PluginHandlers (DMap.toList -> handlers)) = pluginHandlers
customConfigToDedicatedSchema (CustomConfig p) = toVSCodeExtensionSchema (withIdPrefix "config.") p
(PluginId pId) = pluginId
genericSchema =
let x =
[toKey' "diagnosticsOn" A..= schemaEntry "diagnostics" | configHasDiagnostics]
<> nubOrd (mconcat (handlersToGenericSchema <$> handlers))
in case x of
-- If the plugin has only one capability, we produce globalOn instead of the specific one;
-- otherwise we don't produce globalOn at all
[_] -> [toKey' "globalOn" A..= schemaEntry "plugin"]
_ -> x
dedicatedSchema = customConfigToDedicatedSchema configCustomConfig
handlersToGenericSchema (IdeMethod m DSum.:=> _) = case m of
SMethod_TextDocumentCodeAction -> [toKey' "codeActionsOn" A..= schemaEntry "code actions"]
SMethod_TextDocumentCodeLens -> [toKey' "codeLensOn" A..= schemaEntry "code lenses"]
SMethod_TextDocumentRename -> [toKey' "renameOn" A..= schemaEntry "rename"]
SMethod_TextDocumentHover -> [toKey' "hoverOn" A..= schemaEntry "hover"]
SMethod_TextDocumentDocumentSymbol -> [toKey' "symbolsOn" A..= schemaEntry "symbols"]
SMethod_TextDocumentCompletion -> [toKey' "completionOn" A..= schemaEntry "completions"]
SMethod_TextDocumentPrepareCallHierarchy -> [toKey' "callHierarchyOn" A..= schemaEntry "call hierarchy"]
SMethod_TextDocumentSemanticTokensFull -> [toKey' "semanticTokensOn" A..= schemaEntry "semantic tokens"]
_ -> []
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
toKey' = fromString . T.unpack . withIdPrefix