Skip to content

Commit 72d46e9

Browse files
Jana Chadtfendor
Jana Chadt
authored andcommitted
Add plugin for formatting cabal files using cabal-fmt
1 parent 2b94f85 commit 72d46e9

26 files changed

+814
-20
lines changed

.github/workflows/test.yml

+5
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ jobs:
250250
name: Test hls-explicit-fixity-plugin test suite
251251
run: cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS"
252252

253+
## version needs to be limited since the tests depend on cabal-fmt which only builds using specific ghc versions
254+
- if: matrix.test && matrix.ghc == '8.10.7'
255+
name: Test hls-cabal-fmt-plugin test suite
256+
run: cabal test hls-cabal-fmt-plugin --test-options="$TEST_OPTS" || cabal test hls-cabal-fmt-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-cabal-fmt-plugin --test-options="$TEST_OPTS"
257+
253258
test_post_job:
254259
if: always()
255260
runs-on: ubuntu-latest

CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# Plugins
1010
/plugins/hls-alternate-number-format-plugin @drsooch
1111
/plugins/hls-brittany-plugin @fendor
12+
/plugins/hls-cabal-fmt-plugin @VeryMilkyJoe @fendor
1213
/plugins/hls-call-hierarchy-plugin @July541
1314
/plugins/hls-class-plugin @Ailrun
1415
/plugins/hls-eval-plugin

cabal.project

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ packages:
88
./ghcide/test
99
./hls-plugin-api
1010
./hls-test-utils
11+
./plugins/hls-cabal-fmt-plugin
1112
./plugins/hls-tactics-plugin
1213
./plugins/hls-brittany-plugin
1314
./plugins/hls-stylish-haskell-plugin

docs/features.md

+7
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ Format your code with various Haskell code formatters.
107107
| Ormolu | `hls-ormolu-plugin` |
108108
| Stylish Haskell | `hls-stylish-haskell-plugin` |
109109

110+
Format your cabal files with a cabal code formatter.
111+
112+
| Formatter | Provided by |
113+
|-----------------|------------------------------|
114+
| cabal-fmt | `hls-cabal-fmt-plugin` |
115+
116+
110117
## Document symbols
111118

112119
Provided by: `ghcide`

docs/support/plugin-support.md

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ For example, a plugin to provide a formatter which has itself been abandoned has
4646
| `hls-pragmas-plugin` | 1 | |
4747
| `hls-refactor-plugin` | 1 | 9.4 |
4848
| `hls-alternate-number-plugin` | 2 | |
49+
| `hls-cabal-fmt-plugin` | 2 | |
4950
| `hls-class-plugin` | 2 | |
5051
| `hls-change-type-signature-plugin` | 2 | |
5152
| `hls-eval-plugin` | 2 | 9.4 |

exe/Plugins.hs

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE ExistentialQuantification #-}
3+
{-# LANGUAGE OverloadedStrings #-}
4+
module Plugins where
5+
6+
import Development.IDE.Types.Logger (Pretty (pretty), Recorder,
7+
WithPriority, cmapWithPrio)
8+
import Ide.PluginUtils (pluginDescToIdePlugins)
9+
import Ide.Types (IdePlugins)
10+
11+
-- fixed plugins
12+
import Development.IDE (IdeState)
13+
import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde
14+
import qualified Ide.Plugin.Example as Example
15+
import qualified Ide.Plugin.Example2 as Example2
16+
import qualified Ide.Plugin.ExampleCabal as ExampleCabal
17+
18+
-- haskell-language-server optional plugins
19+
#if qualifyImportedNames
20+
import qualified Ide.Plugin.QualifyImportedNames as QualifyImportedNames
21+
#endif
22+
23+
#if callHierarchy
24+
import qualified Ide.Plugin.CallHierarchy as CallHierarchy
25+
#endif
26+
27+
#if class
28+
import qualified Ide.Plugin.Class as Class
29+
#endif
30+
31+
#if haddockComments
32+
import qualified Ide.Plugin.HaddockComments as HaddockComments
33+
#endif
34+
35+
#if eval
36+
import qualified Ide.Plugin.Eval as Eval
37+
#endif
38+
39+
#if importLens
40+
import qualified Ide.Plugin.ExplicitImports as ExplicitImports
41+
#endif
42+
43+
#if refineImports
44+
import qualified Ide.Plugin.RefineImports as RefineImports
45+
#endif
46+
47+
#if rename
48+
import qualified Ide.Plugin.Rename as Rename
49+
#endif
50+
51+
#if retrie
52+
import qualified Ide.Plugin.Retrie as Retrie
53+
#endif
54+
55+
#if tactic
56+
import qualified Ide.Plugin.Tactic as Tactic
57+
#endif
58+
59+
#if hlint
60+
import qualified Ide.Plugin.Hlint as Hlint
61+
#endif
62+
63+
#if moduleName
64+
import qualified Ide.Plugin.ModuleName as ModuleName
65+
#endif
66+
67+
#if pragmas
68+
import qualified Ide.Plugin.Pragmas as Pragmas
69+
#endif
70+
71+
#if splice
72+
import qualified Ide.Plugin.Splice as Splice
73+
#endif
74+
75+
#if alternateNumberFormat
76+
import qualified Ide.Plugin.AlternateNumberFormat as AlternateNumberFormat
77+
#endif
78+
79+
#if selectionRange
80+
import Ide.Plugin.SelectionRange as SelectionRange
81+
#endif
82+
83+
#if changeTypeSignature
84+
import Ide.Plugin.ChangeTypeSignature as ChangeTypeSignature
85+
#endif
86+
87+
#if gadt
88+
import Ide.Plugin.GADT as GADT
89+
#endif
90+
-- formatters
91+
92+
#if floskell
93+
import qualified Ide.Plugin.Floskell as Floskell
94+
#endif
95+
96+
#if fourmolu
97+
import qualified Ide.Plugin.Fourmolu as Fourmolu
98+
#endif
99+
100+
#if ormolu
101+
import qualified Ide.Plugin.Ormolu as Ormolu
102+
#endif
103+
104+
#if stylishHaskell
105+
import qualified Ide.Plugin.StylishHaskell as StylishHaskell
106+
#endif
107+
108+
#if brittany
109+
import qualified Ide.Plugin.Brittany as Brittany
110+
#endif
111+
112+
#if cabalfmt
113+
import qualified Ide.Plugin.CabalFmt as CabalFmt
114+
#endif
115+
116+
data Log = forall a. (Pretty a) => Log a
117+
118+
instance Pretty Log where
119+
pretty (Log a) = pretty a
120+
121+
-- ---------------------------------------------------------------------
122+
123+
-- | The plugins configured for use in this instance of the language
124+
-- server.
125+
-- These can be freely added or removed to tailor the available
126+
-- features of the server.
127+
128+
idePlugins :: Recorder (WithPriority Log) -> Bool -> IdePlugins IdeState
129+
idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins
130+
where
131+
pluginRecorder :: forall log. (Pretty log) => Recorder (WithPriority log)
132+
pluginRecorder = cmapWithPrio Log recorder
133+
allPlugins = if includeExamples
134+
then basePlugins ++ examplePlugins
135+
else basePlugins
136+
basePlugins =
137+
#if pragmas
138+
Pragmas.descriptor "pragmas" :
139+
#endif
140+
#if floskell
141+
Floskell.descriptor "floskell" :
142+
#endif
143+
#if fourmolu
144+
Fourmolu.descriptor pluginRecorder "fourmolu" :
145+
#endif
146+
#if tactic
147+
Tactic.descriptor pluginRecorder "tactics" :
148+
#endif
149+
#if ormolu
150+
Ormolu.descriptor "ormolu" :
151+
#endif
152+
#if stylishHaskell
153+
StylishHaskell.descriptor "stylish-haskell" :
154+
#endif
155+
#if rename
156+
Rename.descriptor "rename" :
157+
#endif
158+
#if retrie
159+
Retrie.descriptor "retrie" :
160+
#endif
161+
#if brittany
162+
Brittany.descriptor "brittany" :
163+
#endif
164+
#if cabalfmt
165+
CabalFmt.descriptor pluginRecorder "cabal-fmt" :
166+
#endif
167+
#if callHierarchy
168+
CallHierarchy.descriptor :
169+
#endif
170+
#if class
171+
Class.descriptor pluginRecorder "class" :
172+
#endif
173+
#if haddockComments
174+
HaddockComments.descriptor "haddockComments" :
175+
#endif
176+
#if eval
177+
Eval.descriptor pluginRecorder "eval" :
178+
#endif
179+
#if importLens
180+
ExplicitImports.descriptor pluginRecorder "importLens" :
181+
#endif
182+
#if qualifyImportedNames
183+
QualifyImportedNames.descriptor "qualifyImportedNames" :
184+
#endif
185+
#if refineImports
186+
RefineImports.descriptor pluginRecorder "refineImports" :
187+
#endif
188+
#if moduleName
189+
ModuleName.descriptor "moduleName" :
190+
#endif
191+
#if hlint
192+
Hlint.descriptor pluginRecorder "hlint" :
193+
#endif
194+
#if splice
195+
Splice.descriptor "splice" :
196+
#endif
197+
#if alternateNumberFormat
198+
AlternateNumberFormat.descriptor pluginRecorder :
199+
#endif
200+
#if selectionRange
201+
SelectionRange.descriptor "selectionRange" :
202+
#endif
203+
#if changeTypeSignature
204+
ChangeTypeSignature.descriptor :
205+
#endif
206+
#if gadt
207+
GADT.descriptor "gadt" :
208+
#endif
209+
-- The ghcide descriptors should come last so that the notification handlers
210+
-- (which restart the Shake build) run after everything else
211+
GhcIde.descriptors pluginRecorder
212+
examplePlugins =
213+
[Example.descriptor pluginRecorder "eg"
214+
,Example2.descriptor pluginRecorder "eg2"
215+
,ExampleCabal.descriptor pluginRecorder "ec"
216+
]

haskell-language-server.cabal

+10
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,16 @@ flag dynamic
200200
default: True
201201
manual: True
202202

203+
flag cabalfmt
204+
description: Enable cabal-fmt plugin
205+
default: True
206+
manual: True
207+
208+
common cabalfmt
209+
if flag(cabalfmt)
210+
build-depends: hls-cabal-fmt-plugin ^>= 0.1.0.0
211+
cpp-options: -Dcabalfmt
212+
203213
common class
204214
if flag(class)
205215
build-depends: hls-class-plugin ^>= 1.1

hls-plugin-api/src/Ide/Plugin/Config.hs

+8-5
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ data CheckParents
4747
-- will be surprises relating to config options being ignored, initially though.
4848
data Config =
4949
Config
50-
{ checkParents :: CheckParents
51-
, checkProject :: !Bool
52-
, formattingProvider :: !T.Text
53-
, maxCompletions :: !Int
54-
, plugins :: !(Map.Map T.Text PluginConfig)
50+
{ checkParents :: CheckParents
51+
, checkProject :: !Bool
52+
, formattingProvider :: !T.Text
53+
, cabalFormattingProvider :: !T.Text
54+
, maxCompletions :: !Int
55+
, plugins :: !(Map.Map T.Text PluginConfig)
5556
} deriving (Show,Eq)
5657

5758
instance Default Config where
@@ -62,6 +63,7 @@ instance Default Config where
6263
, formattingProvider = "ormolu"
6364
-- , formattingProvider = "floskell"
6465
-- , formattingProvider = "stylish-haskell"
66+
, cabalFormattingProvider = "cabal-fmt"
6567
, maxCompletions = 40
6668
, plugins = Map.empty
6769
}
@@ -78,6 +80,7 @@ parseConfig defValue = A.withObject "Config" $ \v -> do
7880
<$> (o .:? "checkParents" <|> v .:? "checkParents") .!= checkParents defValue
7981
<*> (o .:? "checkProject" <|> v .:? "checkProject") .!= checkProject defValue
8082
<*> o .:? "formattingProvider" .!= formattingProvider defValue
83+
<*> o .:? "cabalFormattingProvider" .!= cabalFormattingProvider defValue
8184
<*> o .:? "maxCompletions" .!= maxCompletions defValue
8285
<*> o .:? "plugin" .!= plugins defValue
8386

hls-plugin-api/src/Ide/Types.hs

+3-2
Original file line numberDiff line numberDiff line change
@@ -403,14 +403,15 @@ instance PluginMethod Request TextDocumentCompletion where
403403

404404
instance PluginMethod Request TextDocumentFormatting where
405405
pluginEnabled STextDocumentFormatting msgParams pluginDesc conf =
406-
pluginResponsible uri pluginDesc && PluginId (formattingProvider conf) == pid
406+
pluginResponsible uri pluginDesc
407+
&& (PluginId (formattingProvider conf) == pid || PluginId (cabalFormattingProvider conf) == pid)
407408
where
408409
uri = msgParams ^. J.textDocument . J.uri
409410
pid = pluginId pluginDesc
410411

411412
instance PluginMethod Request TextDocumentRangeFormatting where
412413
pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc
413-
&& PluginId (formattingProvider conf) == pid
414+
&& (PluginId (formattingProvider conf) == pid || PluginId (cabalFormattingProvider conf) == pid)
414415
where
415416
uri = msgParams ^. J.textDocument . J.uri
416417
pid = pluginId pluginDesc

0 commit comments

Comments
 (0)