Skip to content

Commit 7020532

Browse files
VeryMilkyJoeJana Chadtfendor
authored
Add formatting plugin for cabal files which uses cabal-fmt (#2047)
* Add plugin for formatting cabal files using cabal-fmt * Add 'isolateTests' cabal flag to make plugin install cabal-fmt For CI, we want to run the tests with a specific cabal-fmt version, installed automatically by cabal. However, locally, we might want to test with a locally installed cabal-fmt version. This flag allows developers to either let cabal install the build-tool-depends or install a fitting version locally. * Ignore failing cabal-fmt test on windows * Add log message for missing cabal-fmt executable Co-authored-by: Jana Chadt <jana.chadt@tuwien.ac.at> Co-authored-by: Fendor <power.walross@gmail.com>
1 parent 85f7881 commit 7020532

26 files changed

+641
-20
lines changed

Diff for: .github/workflows/test.yml

+5
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,11 @@ jobs:
254254
name: Test hls-explicit-record-fields-plugin test suite
255255
run: cabal test hls-explicit-record-fields-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-record-fields-plugin --test-options="$TEST_OPTS"
256256

257+
## version needs to be limited since the tests depend on cabal-fmt which only builds using specific ghc versions
258+
- if: matrix.test && matrix.ghc == '8.10.7'
259+
name: Test hls-cabal-fmt-plugin test suite
260+
run: cabal test hls-cabal-fmt-plugin --flag=isolateTests --test-options="$TEST_OPTS" || cabal test hls-cabal-fmt-plugin --flag=isolateTests --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 --flag=isolateTests --test-options="$TEST_OPTS"
261+
257262
test_post_job:
258263
if: always()
259264
runs-on: ubuntu-latest

Diff for: 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

Diff for: 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

Diff for: 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`

Diff for: 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 |

Diff for: haskell-language-server.cabal

+10
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,16 @@ flag dynamic
205205
default: True
206206
manual: True
207207

208+
flag cabalfmt
209+
description: Enable cabal-fmt plugin
210+
default: True
211+
manual: True
212+
213+
common cabalfmt
214+
if flag(cabalfmt)
215+
build-depends: hls-cabal-fmt-plugin ^>= 0.1.0.0
216+
cpp-options: -Dhls_cabalfmt
217+
208218
common class
209219
if flag(class)
210220
build-depends: hls-class-plugin ^>= 1.1

Diff for: 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

Diff for: 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

Diff for: hls-test-utils/src/Test/Hls.hs

+48-12
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ module Test.Hls
1717
goldenGitDiff,
1818
goldenWithHaskellDoc,
1919
goldenWithHaskellDocFormatter,
20+
goldenWithCabalDocFormatter,
2021
def,
2122
runSessionWithServer,
2223
runSessionWithServerFormatter,
24+
runSessionWithCabalServerFormatter,
2325
runSessionWithServer',
2426
waitForProgressDone,
2527
waitForAllProgressDone,
@@ -70,6 +72,7 @@ import Development.IDE.Types.Options
7072
import GHC.IO.Handle
7173
import GHC.Stack (emptyCallStack)
7274
import Ide.Plugin.Config (Config, PluginConfig,
75+
cabalFormattingProvider,
7376
formattingProvider, plugins)
7477
import Ide.PluginUtils (idePluginsToPluginDesc,
7578
pluginDescToIdePlugins)
@@ -130,15 +133,30 @@ goldenWithHaskellDoc plugin title testDataDir path desc ext act =
130133
act doc
131134
documentContents doc
132135

136+
137+
runSessionWithServer :: PluginDescriptor IdeState -> FilePath -> Session a -> IO a
138+
runSessionWithServer plugin = runSessionWithServer' [plugin] def def fullCaps
139+
140+
runSessionWithServerFormatter :: PluginDescriptor IdeState -> String -> PluginConfig -> FilePath -> Session a -> IO a
141+
runSessionWithServerFormatter plugin formatter conf =
142+
runSessionWithServer'
143+
[plugin]
144+
def
145+
{ formattingProvider = T.pack formatter
146+
, plugins = M.singleton (T.pack formatter) conf
147+
}
148+
def
149+
fullCaps
150+
133151
goldenWithHaskellDocFormatter
134-
:: PluginDescriptor IdeState
135-
-> String
152+
:: PluginDescriptor IdeState -- ^ Formatter plugin to be used
153+
-> String -- ^ Name of the formatter to be used
136154
-> PluginConfig
137-
-> TestName
138-
-> FilePath
139-
-> FilePath
140-
-> FilePath
141-
-> FilePath
155+
-> TestName -- ^ Title of the test
156+
-> FilePath -- ^ Directory of the test data to be used
157+
-> FilePath -- ^ Path to the testdata to be used within the directory
158+
-> FilePath -- ^ Additional suffix to be appended to the output file
159+
-> FilePath -- ^ Extension of the output file
142160
-> (TextDocumentIdentifier -> Session ())
143161
-> TestTree
144162
goldenWithHaskellDocFormatter plugin formatter conf title testDataDir path desc ext act =
@@ -151,15 +169,33 @@ goldenWithHaskellDocFormatter plugin formatter conf title testDataDir path desc
151169
act doc
152170
documentContents doc
153171

154-
runSessionWithServer :: PluginDescriptor IdeState -> FilePath -> Session a -> IO a
155-
runSessionWithServer plugin = runSessionWithServer' [plugin] def def fullCaps
172+
goldenWithCabalDocFormatter
173+
:: PluginDescriptor IdeState -- ^ Formatter plugin to be used
174+
-> String -- ^ Name of the formatter to be used
175+
-> PluginConfig
176+
-> TestName -- ^ Title of the test
177+
-> FilePath -- ^ Directory of the test data to be used
178+
-> FilePath -- ^ Path to the testdata to be used within the directory
179+
-> FilePath -- ^ Additional suffix to be appended to the output file
180+
-> FilePath -- ^ Extension of the output file
181+
-> (TextDocumentIdentifier -> Session ())
182+
-> TestTree
183+
goldenWithCabalDocFormatter plugin formatter conf title testDataDir path desc ext act =
184+
goldenGitDiff title (testDataDir </> path <.> desc <.> ext)
185+
$ runSessionWithCabalServerFormatter plugin formatter conf testDataDir
186+
$ TL.encodeUtf8 . TL.fromStrict
187+
<$> do
188+
doc <- openDoc (path <.> ext) "cabal"
189+
void waitForBuildQueue
190+
act doc
191+
documentContents doc
156192

157-
runSessionWithServerFormatter :: PluginDescriptor IdeState -> String -> PluginConfig -> FilePath -> Session a -> IO a
158-
runSessionWithServerFormatter plugin formatter conf =
193+
runSessionWithCabalServerFormatter :: PluginDescriptor IdeState -> String -> PluginConfig -> FilePath -> Session a -> IO a
194+
runSessionWithCabalServerFormatter plugin formatter conf =
159195
runSessionWithServer'
160196
[plugin]
161197
def
162-
{ formattingProvider = T.pack formatter
198+
{ cabalFormattingProvider = T.pack formatter
163199
, plugins = M.singleton (T.pack formatter) conf
164200
}
165201
def

0 commit comments

Comments
 (0)