Description
(This issue is WIP -- I'm going to record all the things I found confusing about writing a plugin and then suggest changes, but welcome comments as I go.)
I'm currently following the plugin writing documentation / tutorial, and encountered the following things. I'm using the plugin name ArgSwap
because that's the plugin I was writing.
Creating a new plugin and linking it
I had to edit a load of files and do a load of work just to include my plugin in my build:
- create
plugins/hls-argswap-plugin
- create
plugins/hls-argswap-plugin/hls-argswap-plugin.cabal
(for which I copied the cabal file ofhls-module-name-plugin
- create
plugins/hls-argswap-plusin/src/Ide/Plugin/ArgSwap.hs
I then had to "link" it:
- add my plugin to
cabal.project
by adding./plugins/hls-argswap-plugin
topackages
- create a new flag in
haskell-language-server.cabal
diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal
index c925b916..5d54dd67 100644
--- a/haskell-language-server.cabal
+++ b/haskell-language-server.cabal
@@ -164,6 +164,11 @@ flag overloadedRecordDot
default: True
manual: True
+flag argSwap
+ description: Enable the args swapping plugin
+ default: True
+ manual: True
+
-- formatters
flag floskell
@@ -306,6 +311,11 @@ common overloadedRecordDot
build-depends: hls-overloaded-record-dot-plugin == 2.4.0.0
cpp-options: -Dhls_overloaded_record_dot
+common argSwap
+ if flag(argSwap)
+ build-depends: hls-argswap-plugin == 2.4.0.0
+ cpp-options: -Dhls_argswap
+
-- formatters
common floskell
@@ -365,6 +375,7 @@ library
, stylishHaskell
, refactor
, overloadedRecordDot
+ , argSwap
exposed-modules:
Ide.Arguments
- Use that flag to enable the plugin in
src/HlsPlugins.hs
diff --git a/src/HlsPlugins.hs b/src/HlsPlugins.hs
index 4d371859..cb4b12b2 100644
--- a/src/HlsPlugins.hs
+++ b/src/HlsPlugins.hs
@@ -94,6 +94,10 @@ import qualified Ide.Plugin.ExplicitFields as ExplicitFields
import qualified Ide.Plugin.OverloadedRecordDot as OverloadedRecordDot
#endif
+#if hls_argswap
+import qualified Ide.Plugin.ArgSwap as ArgSwap
+#endif
+
-- formatters
#if hls_floskell
@@ -223,6 +227,9 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins
#endif
#if hls_overloaded_record_dot
let pId = "overloaded-record-dot" in OverloadedRecordDot.descriptor (pluginRecorder pId) pId :
+#endif
+#if hls_argswap
+ let pId = "arg-swap" in ArgSwap.descriptor (pluginRecorder pId) pId :
#endif
GhcIde.descriptors (pluginRecorder "ghcide")
- add the plugin to
stack.yaml
andstack-lts21.yaml
underpackages:
Not knowing what is imported whence
Example plugins I looked at used some implicit imports. HLS wouldn't work for me on the HLS codebase, so I had to make a lot of guesses as to what came whence.
Some outdated types (and missing links)
Near the beginning of the tutorial, there is a description of the PluginDescriptor
datatype as being defined in Ide.Plugin
as:
data PluginDescriptor =
PluginDescriptor { pluginId :: !PluginId
, pluginRules :: !(Rules ())
, pluginCommands :: ![PluginCommand]
, pluginCodeActionProvider :: !(Maybe CodeActionProvider)
, pluginCodeLensProvider :: !(Maybe CodeLensProvider)
, pluginHoverProvider :: !(Maybe HoverProvider)
, pluginSymbolsProvider :: !(Maybe SymbolsProvider)
, pluginFormattingProvider :: !(Maybe (FormattingProvider IO))
, pluginCompletionProvider :: !(Maybe CompletionProvider)
, pluginRenameProvider :: !(Maybe RenameProvider)
}
In fact (at least in 2.4.0) it is defined in Ide.Types
as:
data PluginDescriptor (ideState :: Type) =
PluginDescriptor { pluginId :: !PluginId
-- ^ Unique identifier of the plugin.
, pluginPriority :: Natural
-- ^ Plugin handlers are called in priority order, higher priority first
, pluginRules :: !(Rules ())
, pluginCommands :: ![PluginCommand ideState]
, pluginHandlers :: PluginHandlers ideState
, pluginConfigDescriptor :: ConfigDescriptor
, pluginNotificationHandlers :: PluginNotificationHandlers ideState
, pluginModifyDynflags :: DynFlagsModifications
, pluginCli :: Maybe (ParserInfo (IdeCommand ideState))
, pluginFileType :: [T.Text]
-- ^ File extension of the files the plugin is responsible for.
-- The plugin is only allowed to handle files with these extensions.
-- When writing handlers, etc. for this plugin it can be assumed that all handled files are of this type.
-- The file extension must have a leading '.'.
}
Keeping documentation up to date is hard, but one of the things which would be really helpful (especially as HLS just won't run on the HLS codebase for me) is links to the definitions.
Alternatively, I'd love it if HLS was packaged up on Hackage and therefore Hoogleable, because at the moment building local hoogle (via stack hoogle
) exhausted my machine's memory (and I have 32GB of RAM on this laptop).