Skip to content

Commit 1b30c71

Browse files
Jana ChadtVeryMilkyJoe
Jana Chadt
authored andcommitted
Add plugin for formatting cabal files using cabal-fmt
Introduce configuration for a cabal formatting provider.
1 parent 897109e commit 1b30c71

20 files changed

+544
-12
lines changed

cabal.project

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ packages:
2323
./plugins/hls-module-name-plugin
2424
./plugins/hls-ormolu-plugin
2525
./plugins/hls-call-hierarchy-plugin
26+
./plugins/hls-cabal-fmt-plugin
2627
tests: true
2728

2829
package *

exe/Plugins.hs

+6-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ import Ide.Plugin.StylishHaskell as StylishHaskell
8282
#if brittany
8383
import Ide.Plugin.Brittany as Brittany
8484
#endif
85-
85+
#if cabalfmt
86+
import Ide.Plugin.CabalFmt as CabalFmt
87+
#endif
8688
-- ---------------------------------------------------------------------
8789

8890
-- | The plugins configured for use in this instance of the language
@@ -124,6 +126,9 @@ idePlugins includeExamples = pluginDescToIdePlugins allPlugins
124126
#if callHierarchy
125127
CallHierarchy.descriptor "callHierarchy":
126128
#endif
129+
#if cabalfmt
130+
CabalFmt.descriptor "cabal-fmt" :
131+
#endif
127132
#if class
128133
Class.descriptor "class" :
129134
#endif

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

+13-7
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

+11
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ flag brittany
188188
default: True
189189
manual: True
190190

191+
flag cabalfmt
192+
description: Enable cabal-fmt plugin
193+
default: True
194+
manual: True
195+
191196
common example-plugins
192197
hs-source-dirs: plugins/default/src
193198
other-modules: Ide.Plugin.Example,
@@ -280,6 +285,11 @@ common brittany
280285
build-depends: hls-brittany-plugin ^>= 1.0.0.1
281286
cpp-options: -Dbrittany
282287

288+
common cabalfmt
289+
if (flag(cabalfmt) || flag(all-formatters))
290+
build-depends: hls-cabal-fmt-plugin ^>= 0.1.0.0
291+
cpp-options: -Dcabalfmt
292+
283293
executable haskell-language-server
284294
import: common-deps
285295
-- plugins
@@ -301,6 +311,7 @@ executable haskell-language-server
301311
, ormolu
302312
, stylishHaskell
303313
, brittany
314+
, cabalfmt
304315

305316
main-is: Main.hs
306317
hs-source-dirs: exe

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

+3
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

+4-3
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
instance PluginMethod TextDocumentPrepareCallHierarchy where

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

+31-1
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)