Skip to content

Commit 7c15374

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

26 files changed

+948
-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/supported-versions.md

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Supported GHC versions
2+
3+
## Current GHC version support status
4+
5+
The current support for different GHC versions is given in the following table.
6+
7+
Last supporting HLS version:
8+
- "next": this GHC version is supported in master, and will be in the next released version of HLS.
9+
- "latest": this GHC version is one of the actively supported versions (see below) and is supported in the latest released version of HLS.
10+
- specific version number: this GHC version is no longer one of the actively supported versions, and the last version of HLS which supports it is listed.
11+
12+
Support status (see the support policy below for more details):
13+
- "supported": this version of GHC is currently actively supported
14+
- "deprecated": this version of GHC was supported in the past, but is now deprecated
15+
- "will be deprecated ...": this version of GHC has special deprecation conditions that deviate from the support policy
16+
- "partial": not all features and plugins work, see the plugin support table and any linked issues for more details
17+
18+
| GHC version | Last supporting HLS version | Support status |
19+
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
20+
| 9.2.3 | next | supported, ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) |
21+
| 9.2.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported, ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) |
22+
| 9.2.1 | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | deprecated |
23+
| 9.0.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported |
24+
| 9.0.1 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated |
25+
| 8.10.7 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported |
26+
| 8.10.6 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated |
27+
| 8.10.5 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/tag/1.5.1) | deprecated |
28+
| 8.10.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated |
29+
| 8.10.3 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated |
30+
| 8.10.2 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated |
31+
| 8.10.1 | [0.9.0](https://github.com/haskell/haskell-language-server/releases/tag/0.9.0) | deprecated |
32+
| 8.8.4 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | will be deprecated after LTS and HLS full support for ghc-9.2 |
33+
| 8.8.3 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/1.5.1) | deprecated |
34+
| 8.8.2 | [1.2.0](https://github.com/haskell/haskell-language-server/releases/tag/1.2.0) | deprecated |
35+
| 8.6.5 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | will be deprecated after LTS and HLS full suppot for ghc-9.2 |
36+
| 8.6.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated |
37+
38+
39+
GHC versions not in the list have never been supported by HLS. LTS stands for [Stackage](https://www.stackage.org/) Long Term Support.
40+
41+
The policy for when we deprecate support for versions of GHC is given below. The table reflects that, but we may decide to deviate from it for good reasons.
42+
43+
Additionally, some plugins do not have support for some GHC versions, as shown in the following table.
44+
As such, the functionality provided by those plugins is not available in HLS when using a GHC version which they do not support.
45+
Sometimes a plugin will be supported in the pre-built binaries but not in a HLS binary installed from Hackage.
46+
47+
### Plugins support by GHC version
48+
49+
| Plugin | Unsupported GHC versions |
50+
|-------------------------------------|--------------------------|
51+
| `hls-alternate-number-plugin` | |
52+
| `hls-brittany-plugin` | 9.2 |
53+
| `hls-cabal-fmt-plugin` | |
54+
| `hls-call-hierarchy-plugin` | |
55+
| `hls-class-plugin` | |
56+
| `hls-eval-plugin` | |
57+
| `hls-explicit-imports-plugin` | |
58+
| `hls-floskell-plugin` | |
59+
| `hls-fourmolu-plugin` | |
60+
| `hls-haddock-comments-plugin` | 9.2 |
61+
| `hls-hlint-plugin` | |
62+
| `hls-module-name-plugin` | |
63+
| `hls-ormolu-plugin` | |
64+
| `hls-pragmas-plugin` | |
65+
| `hls-qualify-imported-names-plugin` | |
66+
| `hls-refine-imports-plugin` | |
67+
| `hls-rename-plugin` | |
68+
| `hls-retrie-plugin` | 9.2 |
69+
| `hls-splice-plugin` | 9.2 |
70+
| `hls-stylish-haskell-plugin` | |
71+
| `hls-tactics-plugin` | 9.2 |
72+
| `hls-selection-range-plugin` | |
73+
| `hls-gadt-plugin` | |
74+
75+
### Using deprecated GHC versions
76+
77+
Users who want to use a GHC version which is not supported by the latest HLS can still use older versions of HLS (consult the version support table above to identify the appropriate HLS version).
78+
In the future, we may extend the existing discovery mechanisms (`haskell-language-server-wrapper`, automatic download in vscode extension) to find and download older HLS binaries in this case.
79+
80+
Users of a deprecated minor version (where the major version is still supported) can try building the latest HLS from source, which will likely still work, since the GHC API tends to remain compatible across minor versions.
81+
82+
### Using GHC versions not yet supported in a HLS release
83+
84+
Some users may wish to use a version of GHC that is not yet supported by a released version of HLS.
85+
In particular, this means that pre-built binaries will not be available for that GHC version.
86+
87+
The easiest thing to do in this case is to build HLS from source yourself.
88+
This can be done easily with `ghcup`, see the examples for `ghcup compile` on the [installation page](./installation.md).
89+
90+
Generally, if a version of GHC is supported by HLS on master _or_ is a new minor version of a GHC version that is supported by HLS on master, then compiling from source is likely to work.
91+
Major versions of GHC which are not supported by HLS on master are extremely unlikely to work.
92+
93+
## GHC version deprecation policy
94+
95+
### Major versions
96+
97+
A major GHC version is a "legacy" version if it is 3 or more major versions behind the latest GHC version that is
98+
99+
1. Fully supported by HLS
100+
2. Used in the a Stackage LTS
101+
102+
For example, if 9.2 is the latest major version fully supported by HLS and used in a Stackage LTS, then the 8.8 major version and older will be legacy.
103+
104+
HLS will support all non-legacy major versions of GHC.
105+
106+
### Minor versions
107+
108+
For the latest supported major GHC version we will support at least 2 minor versions.
109+
110+
For the rest of the supported major GHC versions, we will support at least the latest minor version in Stackage LTS (so 1 minor version).
111+
112+
### Announcements
113+
114+
We will warn users about the upcoming deprecation of a GHC version in the notes of the release *prior* to the deprecation itself.
115+
116+
### Why deprecate older versions of GHC?
117+
118+
`haskell-language-server`(HLS) is highly tied to the GHC API. This imposes a high maintenance cost:
119+
120+
- The codebase is littered with conditional logic,
121+
- We own auxiliary packages to support older versions of GHC.
122+
- CI has to cover all the supported versions.
123+
124+
So we need to limit the GHC support to save maintainers and contributors time and reduce CI resources.
125+
126+
At same time we aim to support the right balance of GHC versions to minimize impact to final users.
127+
128+
### What factors do we take into account when deprecating a version?
129+
130+
To establish and apply the policy we take into account:
131+
132+
- Completeness: support includes all plugins and features
133+
- The most recent [stackage](https://www.stackage.org/) LTS snapshot
134+
- The GHC versions used in the most popular [linux distributions](https://repology.org/project/ghc/versions)
135+
- The reliability of different ghc versions on the major operating systems (Linux, Windows, MacOS)

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

0 commit comments

Comments
 (0)