Skip to content

Commit 2c8cf8f

Browse files
author
Jana Chadt
committed
Add plugin for formatting cabal files using cabal-fmt
Introduce configuration for a cabal formatting provider.
1 parent 78381b8 commit 2c8cf8f

20 files changed

+545
-12
lines changed

cabal.project

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ packages:
2222
./plugins/hls-pragmas-plugin
2323
./plugins/hls-module-name-plugin
2424
./plugins/hls-ormolu-plugin
25+
./plugins/hls-cabal-fmt-plugin
2526
tests: true
2627

2728
package *

exe/Plugins.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ import Ide.Plugin.StylishHaskell as StylishHaskell
7878
#if brittany
7979
import Ide.Plugin.Brittany as Brittany
8080
#endif
81-
81+
#if cabalfmt
82+
import Ide.Plugin.CabalFmt as CabalFmt
83+
#endif
8284
-- ---------------------------------------------------------------------
8385

8486
-- | The plugins configured for use in this instance of the language
@@ -117,6 +119,9 @@ idePlugins includeExamples = pluginDescToIdePlugins allPlugins
117119
#if brittany
118120
Brittany.descriptor "brittany" :
119121
#endif
122+
#if cabalfmt
123+
CabalFmt.descriptor "cabal-fmt" :
124+
#endif
120125
#if class
121126
Class.descriptor "class" :
122127
#endif

ghcide/src/Development/IDE/LSP/Notifications.hs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
{-# LANGUAGE RankNTypes #-}
88

99
module Development.IDE.LSP.Notifications
10-
( whenUriFile
10+
( whenUriHaskellFile
1111
, descriptor
1212
) where
1313

@@ -38,16 +38,21 @@ import Development.IDE.Core.RuleTypes (GetClientSettings (..))
3838
import Development.IDE.Types.Shake (toKey)
3939
import Ide.Plugin.Config (CheckParents (CheckOnClose))
4040
import Ide.Types
41+
import System.FilePath (takeExtension)
4142

42-
whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
43-
whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath'
43+
whenUriHaskellFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
44+
whenUriHaskellFile uri act = whenJust maybeHaskellFile $ act . toNormalizedFilePath'
45+
where
46+
maybeHaskellFile = do
47+
fp <- LSP.uriToFilePath uri
48+
if takeExtension fp `elem` [".hs", ".lhs"] then Just fp else Nothing
4449

4550
descriptor :: PluginId -> PluginDescriptor IdeState
4651
descriptor plId = (defaultPluginDescriptor plId) { pluginNotificationHandlers = mconcat
4752
[ mkPluginNotificationHandler LSP.STextDocumentDidOpen $
4853
\ide _ (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> liftIO $ do
4954
updatePositionMapping ide (VersionedTextDocumentIdentifier _uri (Just _version)) (List [])
50-
whenUriFile _uri $ \file -> do
55+
whenUriHaskellFile _uri $ \file -> do
5156
-- We don't know if the file actually exists, or if the contents match those on disk
5257
-- For example, vscode restores previously unsaved contents on open
5358
addFileOfInterest ide file Modified{firstOpen=True}
@@ -57,21 +62,21 @@ descriptor plId = (defaultPluginDescriptor plId) { pluginNotificationHandlers =
5762
, mkPluginNotificationHandler LSP.STextDocumentDidChange $
5863
\ide _ (DidChangeTextDocumentParams identifier@VersionedTextDocumentIdentifier{_uri} changes) -> liftIO $ do
5964
updatePositionMapping ide identifier changes
60-
whenUriFile _uri $ \file -> do
65+
whenUriHaskellFile _uri $ \file -> do
6166
addFileOfInterest ide file Modified{firstOpen=False}
6267
setFileModified ide False file
6368
logDebug (ideLogger ide) $ "Modified text document: " <> getUri _uri
6469

6570
, mkPluginNotificationHandler LSP.STextDocumentDidSave $
6671
\ide _ (DidSaveTextDocumentParams TextDocumentIdentifier{_uri} _) -> liftIO $ do
67-
whenUriFile _uri $ \file -> do
72+
whenUriHaskellFile _uri $ \file -> do
6873
addFileOfInterest ide file OnDisk
6974
setFileModified ide True file
7075
logDebug (ideLogger ide) $ "Saved text document: " <> getUri _uri
7176

7277
, mkPluginNotificationHandler LSP.STextDocumentDidClose $
7378
\ide _ (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> liftIO $ do
74-
whenUriFile _uri $ \file -> do
79+
whenUriHaskellFile _uri $ \file -> do
7580
deleteFileOfInterest ide file
7681
-- Refresh all the files that depended on this
7782
checkParents <- optCheckParents =<< getIdeOptionsIO (shakeExtras ide)
@@ -120,3 +125,4 @@ descriptor plId = (defaultPluginDescriptor plId) { pluginNotificationHandlers =
120125
liftIO $ logDebug (ideLogger ide) "Warning: Client does not support watched files. Falling back to OS polling"
121126
]
122127
}
128+

haskell-language-server.cabal

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ flag brittany
183183
default: True
184184
manual: True
185185

186+
flag cabalfmt
187+
description: Enable cabal-fmt plugin
188+
default: True
189+
manual: True
190+
186191
common example-plugins
187192
hs-source-dirs: plugins/default/src
188193
other-modules: Ide.Plugin.Example,
@@ -270,6 +275,11 @@ common brittany
270275
build-depends: hls-brittany-plugin ^>= 1.0.0.1
271276
cpp-options: -Dbrittany
272277

278+
common cabalfmt
279+
if (flag(cabalfmt) || flag(all-formatters))
280+
build-depends: hls-cabal-fmt-plugin ^>= 0.1.0.0
281+
cpp-options: -Dcabalfmt
282+
273283
executable haskell-language-server
274284
import: common-deps
275285
-- plugins
@@ -290,6 +300,7 @@ executable haskell-language-server
290300
, ormolu
291301
, stylishHaskell
292302
, brittany
303+
, cabalfmt
293304

294305
main-is: Main.hs
295306
hs-source-dirs: exe

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ data Config =
5656
, liquidOn :: !Bool
5757
, formatOnImportOn :: !Bool
5858
, formattingProvider :: !T.Text
59+
, cabalFormattingProvider :: !T.Text
5960
, maxCompletions :: !Int
6061
, plugins :: !(Map.Map T.Text PluginConfig)
6162
} deriving (Show,Eq)
@@ -73,6 +74,7 @@ instance Default Config where
7374
, formattingProvider = "ormolu"
7475
-- , formattingProvider = "floskell"
7576
-- , formattingProvider = "stylish-haskell"
77+
, cabalFormattingProvider = "cabal-fmt"
7678
, maxCompletions = 40
7779
, plugins = Map.empty
7880
}
@@ -94,6 +96,7 @@ parseConfig defValue = A.withObject "Config" $ \v -> do
9496
<*> o .:? "liquidOn" .!= liquidOn defValue
9597
<*> o .:? "formatOnImportOn" .!= formatOnImportOn defValue
9698
<*> o .:? "formattingProvider" .!= formattingProvider defValue
99+
<*> o .:? "cabalFormattingProvider" .!= cabalFormattingProvider defValue
97100
<*> o .:? "maxCompletions" .!= maxCompletions defValue
98101
<*> o .:? "plugin" .!= plugins defValue
99102

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,12 @@ instance PluginMethod TextDocumentCompletion where
248248
consumeCompletionResponse n (InR (CompletionList isCompleteResponse (List xx)))
249249

250250
instance PluginMethod TextDocumentFormatting where
251-
pluginEnabled _ pid conf = (PluginId $ formattingProvider conf) == pid
252-
combineResponses _ _ _ _ (x :| _) = x
251+
pluginEnabled STextDocumentFormatting pid conf =
252+
PluginId (formattingProvider conf) == pid || PluginId (cabalFormattingProvider conf) == pid
253+
combineResponses _ _ _ _ x = sconcat x
253254

254255
instance PluginMethod TextDocumentRangeFormatting where
255-
pluginEnabled _ pid conf = (PluginId $ formattingProvider conf) == pid
256+
pluginEnabled _ pid conf = PluginId (formattingProvider conf) == pid
256257
combineResponses _ _ _ _ (x :| _) = x
257258

258259
-- ---------------------------------------------------------------------

hls-test-utils/src/Test/Hls.hs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ module Test.Hls
1414
goldenGitDiff,
1515
goldenWithHaskellDoc,
1616
goldenWithHaskellDocFormatter,
17+
goldenWithCabalDocFormatter,
1718
def,
1819
runSessionWithServer,
1920
runSessionWithServerFormatter,
21+
runSessionWithCabalServerFormatter,
2022
runSessionWithServer',
2123
waitForProgressDone,
2224
PluginDescriptor,
@@ -43,7 +45,7 @@ import qualified Development.IDE.Main as Ghcide
4345
import qualified Development.IDE.Plugin.HLS.GhcIde as Ghcide
4446
import Development.IDE.Types.Options
4547
import GHC.IO.Handle
46-
import Ide.Plugin.Config (Config, formattingProvider)
48+
import Ide.Plugin.Config (Config (cabalFormattingProvider), formattingProvider)
4749
import Ide.PluginUtils (pluginDescToIdePlugins)
4850
import Ide.Types
4951
import Language.LSP.Test
@@ -121,6 +123,34 @@ runSessionWithServerFormatter plugin formatter =
121123
def
122124
fullCaps
123125

126+
goldenWithCabalDocFormatter
127+
:: PluginDescriptor IdeState
128+
-> String
129+
-> TestName
130+
-> FilePath
131+
-> FilePath
132+
-> FilePath
133+
-> FilePath
134+
-> (TextDocumentIdentifier -> Session ())
135+
-> TestTree
136+
goldenWithCabalDocFormatter plugin formatter title testDataDir path desc ext act =
137+
goldenGitDiff title (testDataDir </> path <.> desc <.> ext)
138+
$ runSessionWithServerFormatter plugin formatter testDataDir
139+
$ TL.encodeUtf8 . TL.fromStrict
140+
<$> do
141+
doc <- openDoc (path <.> ext) "cabal"
142+
act doc
143+
documentContents doc
144+
145+
runSessionWithCabalServerFormatter :: PluginDescriptor IdeState -> String -> FilePath -> Session a -> IO a
146+
runSessionWithCabalServerFormatter plugin formatter =
147+
runSessionWithServer'
148+
[plugin]
149+
def {cabalFormattingProvider = T.pack formatter}
150+
def
151+
fullCaps
152+
153+
124154
-- | Run an action, with stderr silenced
125155
silenceStderr :: IO a -> IO a
126156
silenceStderr action = withTempFile $ \temp ->

0 commit comments

Comments
 (0)