Skip to content

Commit e5fd0f2

Browse files
committed
Add support for multiple cabal formatters
In addition, allow different cabal file formatter provider to specify an explicit file path, instead of searching only on $PATH.
1 parent 8d1bcfe commit e5fd0f2

File tree

11 files changed

+138
-21
lines changed

11 files changed

+138
-21
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ instance ToJSON Config where
178178
object [ "checkParents" .= checkParents
179179
, "checkProject" .= checkProject
180180
, "formattingProvider" .= formattingProvider
181+
, "cabalFormattingProvider" .= cabalFormattingProvider
181182
, "maxCompletions" .= maxCompletions
182183
, "plugin" .= Map.mapKeysMonotonic (\(PluginId p) -> p) plugins
183184
]
@@ -189,7 +190,7 @@ instance Default Config where
189190
, formattingProvider = "ormolu"
190191
-- , formattingProvider = "floskell"
191192
-- , formattingProvider = "stylish-haskell"
192-
, cabalFormattingProvider = "gild"
193+
, cabalFormattingProvider = "cabal-gild"
193194
-- , cabalFormattingProvider = "cabal-fmt"
194195
-- this string value needs to kept in sync with the value provided in HlsPlugins
195196
, maxCompletions = 40

plugins/hls-cabal-fmt-plugin/src/Ide/Plugin/CabalFmt.hs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{-# LANGUAGE LambdaCase #-}
22
{-# LANGUAGE OverloadedStrings #-}
3+
{-# LANGUAGE OverloadedLabels #-}
4+
{-# LANGUAGE DataKinds #-}
35

46
module Ide.Plugin.CabalFmt where
57

@@ -19,12 +21,13 @@ import System.Exit
1921
import System.FilePath
2022
import System.Process.ListLike
2123
import qualified System.Process.Text as Process
24+
import Ide.Plugin.Properties
2225

2326
data Log
2427
= LogProcessInvocationFailure Int
2528
| LogReadCreateProcessInfo T.Text [String]
2629
| LogInvalidInvocationInfo
27-
| LogCabalFmtNotFound
30+
| LogFormatterBinNotFound FilePath
2831
deriving (Show)
2932

3033
instance Pretty Log where
@@ -35,29 +38,39 @@ instance Pretty Log where
3538
["Invocation of cabal-fmt with arguments" <+> pretty args]
3639
++ ["failed with standard error:" <+> pretty stdErrorOut | not (T.null stdErrorOut)]
3740
LogInvalidInvocationInfo -> "Invocation of cabal-fmt with range was called but is not supported."
38-
LogCabalFmtNotFound -> "Couldn't find executable 'cabal-fmt'"
41+
LogFormatterBinNotFound fp -> "Couldn't find formatter executable 'cabal-fmt' at:" <+> pretty fp
3942

4043
descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState
4144
descriptor recorder plId =
4245
(defaultCabalPluginDescriptor plId "Provides formatting of cabal files with cabal-fmt")
43-
{ pluginHandlers = mkFormattingHandlers (provider recorder)
46+
{ pluginHandlers = mkFormattingHandlers (provider recorder plId)
47+
, pluginConfigDescriptor = defaultConfigDescriptor{configCustomConfig = mkCustomConfig properties}
4448
}
4549

50+
properties :: Properties '[ 'PropertyKey "path" 'TString]
51+
properties =
52+
emptyProperties
53+
& defineStringProperty
54+
#path
55+
"Set path to 'cabal-fmt' executable"
56+
"cabal-fmt"
57+
4658
-- | Formatter provider of cabal fmt.
4759
-- Formats the given source in either a given Range or the whole Document.
4860
-- If the provider fails an error is returned that can be displayed to the user.
49-
provider :: Recorder (WithPriority Log) -> FormattingHandler IdeState
50-
provider recorder _ _ (FormatRange _) _ _ _ = do
61+
provider :: Recorder (WithPriority Log) -> PluginId -> FormattingHandler IdeState
62+
provider recorder _ _ _ (FormatRange _) _ _ _ = do
5163
logWith recorder Info LogInvalidInvocationInfo
5264
throwError $ PluginInvalidParams "You cannot format a text-range using cabal-fmt."
53-
provider recorder _ide _ FormatText contents nfp opts = do
65+
provider recorder plId ideState _ FormatText contents nfp opts = do
5466
let cabalFmtArgs = [ "--indent", show tabularSize]
55-
x <- liftIO $ findExecutable "cabal-fmt"
67+
cabalFmtExePath <- fmap T.unpack $ liftIO $ runAction "cabal-gild" ideState $ usePropertyAction #path plId properties
68+
x <- liftIO $ findExecutable cabalFmtExePath
5669
case x of
5770
Just _ -> do
5871
(exitCode, out, err) <-
5972
liftIO $ Process.readCreateProcessWithExitCode
60-
( proc "cabal-fmt" cabalFmtArgs
73+
( proc cabalFmtExePath cabalFmtArgs
6174
)
6275
{ cwd = Just $ takeDirectory fp
6376
}
@@ -71,8 +84,8 @@ provider recorder _ide _ FormatText contents nfp opts = do
7184
let fmtDiff = makeDiffTextEdit contents out
7285
pure $ InL fmtDiff
7386
Nothing -> do
74-
log Error LogCabalFmtNotFound
75-
throwError (PluginInternalError "No installation of cabal-fmt could be found. Please install it into your global environment.")
87+
log Error $ LogFormatterBinNotFound cabalFmtExePath
88+
throwError (PluginInternalError "No installation of cabal-gild could be found. Please install it globally, or provide the full path to the executable")
7689
where
7790
fp = fromNormalizedFilePath nfp
7891
tabularSize = opts ^. L.tabSize

plugins/hls-cabal-gild-plugin/src/Ide/Plugin/CabalGild.hs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{-# LANGUAGE LambdaCase #-}
22
{-# LANGUAGE OverloadedStrings #-}
3+
{-# LANGUAGE OverloadedLabels #-}
4+
{-# LANGUAGE DataKinds #-}
35

46
module Ide.Plugin.CabalGild where
57

@@ -17,12 +19,13 @@ import System.Exit
1719
import System.FilePath
1820
import System.Process.ListLike
1921
import qualified System.Process.Text as Process
22+
import Ide.Plugin.Properties
2023

2124
data Log
2225
= LogProcessInvocationFailure Int T.Text
2326
| LogReadCreateProcessInfo [String]
2427
| LogInvalidInvocationInfo
25-
| LogFormatterBinNotFound
28+
| LogFormatterBinNotFound FilePath
2629
deriving (Show)
2730

2831
instance Pretty Log where
@@ -35,30 +38,41 @@ instance Pretty Log where
3538
LogReadCreateProcessInfo args ->
3639
"Formatter invocation: cabal-gild " <+> pretty args
3740
LogInvalidInvocationInfo -> "Invocation of cabal-gild with range was called but is not supported."
38-
LogFormatterBinNotFound -> "Couldn't find executable 'cabal-gild'"
41+
LogFormatterBinNotFound fp -> "Couldn't find formatter executable 'cabal-gild' at:" <+> pretty fp
3942

4043
descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState
4144
descriptor recorder plId =
4245
(defaultCabalPluginDescriptor plId "Provides formatting of cabal files with cabal-gild")
43-
{ pluginHandlers = mkFormattingHandlers (provider recorder)
46+
{ pluginHandlers = mkFormattingHandlers (provider recorder plId)
47+
, pluginConfigDescriptor = defaultConfigDescriptor{configCustomConfig = mkCustomConfig properties}
4448
}
4549

50+
properties :: Properties '[ 'PropertyKey "path" 'TString]
51+
properties =
52+
emptyProperties
53+
& defineStringProperty
54+
#path
55+
"Set path to 'cabal-gild' executable"
56+
"cabal-gild"
57+
4658
-- | Formatter provider of cabal gild.
4759
-- Formats the given source in either a given Range or the whole Document.
4860
-- If the provider fails an error is returned that can be displayed to the user.
49-
provider :: Recorder (WithPriority Log) -> FormattingHandler IdeState
50-
provider recorder _ _ (FormatRange _) _ _ _ = do
61+
provider :: Recorder (WithPriority Log) -> PluginId -> FormattingHandler IdeState
62+
provider recorder _ _ _ (FormatRange _) _ _ _ = do
5163
logWith recorder Info LogInvalidInvocationInfo
5264
throwError $ PluginInvalidParams "You cannot format a text-range using cabal-gild."
53-
provider recorder _ide _ FormatText contents nfp _ = do
65+
provider recorder plId ideState _ FormatText contents nfp _ = do
5466
let cabalGildArgs = ["--stdin=" <> fp, "--input=-"] -- < Read from stdin
55-
x <- liftIO $ findExecutable "cabal-gild"
67+
68+
cabalGildExePath <- fmap T.unpack $ liftIO $ runAction "cabal-gild" ideState $ usePropertyAction #path plId properties
69+
x <- liftIO $ findExecutable cabalGildExePath
5670
case x of
5771
Just _ -> do
5872
log Debug $ LogReadCreateProcessInfo cabalGildArgs
5973
(exitCode, out, err) <-
6074
liftIO $ Process.readCreateProcessWithExitCode
61-
( proc "cabal-gild" cabalGildArgs
75+
( proc cabalGildExePath cabalGildArgs
6276
)
6377
{ cwd = Just $ takeDirectory fp
6478
}
@@ -71,8 +85,8 @@ provider recorder _ide _ FormatText contents nfp _ = do
7185
let fmtDiff = makeDiffTextEdit contents out
7286
pure $ InL fmtDiff
7387
Nothing -> do
74-
log Error LogFormatterBinNotFound
75-
throwError (PluginInternalError "No installation of cabal-gild could be found. Please install it into your global environment.")
88+
log Error $ LogFormatterBinNotFound cabalGildExePath
89+
throwError (PluginInternalError "No installation of cabal-gild could be found. Please install it globally, or provide the full path to the executable.")
7690
where
7791
fp = fromNormalizedFilePath nfp
7892
log = logWith recorder

test/testdata/schema/ghc92/default-config.golden.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
"completionOn": true,
1313
"diagnosticsOn": true
1414
},
15+
"cabal-fmt": {
16+
"config": {
17+
"path": "cabal-fmt"
18+
}
19+
},
20+
"cabal-gild": {
21+
"config": {
22+
"path": "cabal-gild"
23+
}
24+
},
1525
"callHierarchy": {
1626
"globalOn": true
1727
},

test/testdata/schema/ghc92/vscode-extension-schema.golden.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
"scope": "resource",
66
"type": "boolean"
77
},
8+
"haskell.plugin.cabal-fmt.config.path": {
9+
"default": "cabal-fmt",
10+
"markdownDescription": "Set path to 'cabal-fmt' executable",
11+
"scope": "resource",
12+
"type": "string"
13+
},
14+
"haskell.plugin.cabal-gild.config.path": {
15+
"default": "cabal-gild",
16+
"markdownDescription": "Set path to 'cabal-gild' executable",
17+
"scope": "resource",
18+
"type": "string"
19+
},
820
"haskell.plugin.cabal.codeActionsOn": {
921
"default": true,
1022
"description": "Enables cabal code actions",

test/testdata/schema/ghc94/default-config.golden.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
"completionOn": true,
1313
"diagnosticsOn": true
1414
},
15+
"cabal-fmt": {
16+
"config": {
17+
"path": "cabal-fmt"
18+
}
19+
},
20+
"cabal-gild": {
21+
"config": {
22+
"path": "cabal-gild"
23+
}
24+
},
1525
"callHierarchy": {
1626
"globalOn": true
1727
},

test/testdata/schema/ghc94/vscode-extension-schema.golden.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
"scope": "resource",
66
"type": "boolean"
77
},
8+
"haskell.plugin.cabal-fmt.config.path": {
9+
"default": "cabal-fmt",
10+
"markdownDescription": "Set path to 'cabal-fmt' executable",
11+
"scope": "resource",
12+
"type": "string"
13+
},
14+
"haskell.plugin.cabal-gild.config.path": {
15+
"default": "cabal-gild",
16+
"markdownDescription": "Set path to 'cabal-gild' executable",
17+
"scope": "resource",
18+
"type": "string"
19+
},
820
"haskell.plugin.cabal.codeActionsOn": {
921
"default": true,
1022
"description": "Enables cabal code actions",

test/testdata/schema/ghc96/default-config.golden.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"cabalFormattingProvider": "cabal-gild",
23
"checkParents": "CheckOnSave",
34
"checkProject": true,
45
"formattingProvider": "ormolu",
@@ -12,6 +13,16 @@
1213
"completionOn": true,
1314
"diagnosticsOn": true
1415
},
16+
"cabal-fmt": {
17+
"config": {
18+
"path": "cabal-fmt"
19+
}
20+
},
21+
"cabal-gild": {
22+
"config": {
23+
"path": "cabal-gild"
24+
}
25+
},
1526
"callHierarchy": {
1627
"globalOn": true
1728
},

test/testdata/schema/ghc96/vscode-extension-schema.golden.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
"scope": "resource",
66
"type": "boolean"
77
},
8+
"haskell.plugin.cabal-fmt.config.path": {
9+
"default": "cabal-fmt",
10+
"markdownDescription": "Set path to 'cabal-fmt' executable",
11+
"scope": "resource",
12+
"type": "string"
13+
},
14+
"haskell.plugin.cabal-gild.config.path": {
15+
"default": "cabal-gild",
16+
"markdownDescription": "Set path to 'cabal-gild' executable",
17+
"scope": "resource",
18+
"type": "string"
19+
},
820
"haskell.plugin.cabal.codeActionsOn": {
921
"default": true,
1022
"description": "Enables cabal code actions",

test/testdata/schema/ghc98/default-config.golden.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
"completionOn": true,
1313
"diagnosticsOn": true
1414
},
15+
"cabal-fmt": {
16+
"config": {
17+
"path": "cabal-fmt"
18+
}
19+
},
20+
"cabal-gild": {
21+
"config": {
22+
"path": "cabal-gild"
23+
}
24+
},
1525
"callHierarchy": {
1626
"globalOn": true
1727
},

test/testdata/schema/ghc98/vscode-extension-schema.golden.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
"scope": "resource",
66
"type": "boolean"
77
},
8+
"haskell.plugin.cabal-fmt.config.path": {
9+
"default": "cabal-fmt",
10+
"markdownDescription": "Set path to 'cabal-fmt' executable",
11+
"scope": "resource",
12+
"type": "string"
13+
},
14+
"haskell.plugin.cabal-gild.config.path": {
15+
"default": "cabal-gild",
16+
"markdownDescription": "Set path to 'cabal-gild' executable",
17+
"scope": "resource",
18+
"type": "string"
19+
},
820
"haskell.plugin.cabal.codeActionsOn": {
921
"default": true,
1022
"description": "Enables cabal code actions",

0 commit comments

Comments
 (0)