Skip to content

Support hlint on 9.10 apart from apply-refact #4616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/support/plugin-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ For example, a plugin to provide a formatter which has itself been abandoned has
| `hls-explicit-record-fields-plugin` | 2 | |
| `hls-fourmolu-plugin` | 2 | |
| `hls-gadt-plugin` | 2 | |
| `hls-hlint-plugin` | 2 | 9.10.1 |
| `hls-hlint-plugin` | 2 | |
| `hls-module-name-plugin` | 2 | |
| `hls-notes-plugin` | 2 | |
| `hls-qualify-imported-names-plugin` | 2 | |
Expand Down
15 changes: 10 additions & 5 deletions haskell-language-server.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -703,14 +703,14 @@ flag hlint
manual: True

common hlint
if flag(hlint) && ((impl(ghc < 9.10) || impl(ghc > 9.11)) || flag(ignore-plugins-ghc-bounds))
if flag(hlint)
build-depends: haskell-language-server:hls-hlint-plugin
cpp-options: -Dhls_hlint

library hls-hlint-plugin
import: defaults, pedantic, warnings
-- https://github.com/ndmitchell/hlint/pull/1594
if !(flag(hlint)) || ((impl(ghc >= 9.10) && impl(ghc < 9.11)) && !flag(ignore-plugins-ghc-bounds))
if !flag(hlint)
buildable: False
exposed-modules: Ide.Plugin.Hlint
hs-source-dirs: plugins/hls-hlint-plugin/src
Expand All @@ -735,10 +735,14 @@ library hls-hlint-plugin
, transformers
, unordered-containers
, ghc-lib-parser-ex
, apply-refact
--
, lsp-types

-- apply-refact doesn't work on 9.10, or even have a buildable
-- configuration
if impl(ghc >= 9.11) || impl(ghc < 9.10)
cpp-options: -DAPPLY_REFACT
build-depends: apply-refact

if flag(ghc-lib)
cpp-options: -DGHC_LIB
build-depends:
Expand All @@ -753,14 +757,15 @@ library hls-hlint-plugin

test-suite hls-hlint-plugin-tests
import: defaults, pedantic, test-defaults, warnings
if !flag(hlint) || ((impl(ghc >= 9.10) && impl(ghc < 9.11)) && !flag(ignore-plugins-ghc-bounds))
if !flag(hlint)
buildable: False
type: exitcode-stdio-1.0
hs-source-dirs: plugins/hls-hlint-plugin/test
main-is: Main.hs
-- Work around https://gitlab.haskell.org/ghc/ghc/-/issues/24648
if os(darwin)
ghc-options: -optl-Wl,-ld_classic

build-depends:
aeson
, containers
Expand Down
35 changes: 27 additions & 8 deletions plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StrictData #-}
Expand Down Expand Up @@ -54,8 +53,15 @@ import Development.IDE.Core.FileStore (getVersione
import Development.IDE.Core.Rules (defineNoFile,
getParsedModuleWithComments)
import Development.IDE.Core.Shake (getDiagnostics)

#if APPLY_REFACT
import qualified Refact.Apply as Refact
import qualified Refact.Types as Refact
#if !MIN_VERSION_apply_refact(0,12,0)
import System.Environment (setEnv,
unsetEnv)
#endif
#endif

import Development.IDE.GHC.Compat (DynFlags,
WarningFlag (Opt_WarnUnrecognisedPragmas),
Expand Down Expand Up @@ -105,6 +111,7 @@ import Language.LSP.Protocol.Types hiding
(Null)
import qualified Language.LSP.Protocol.Types as LSP

import Development.IDE.Core.PluginUtils as PluginUtils
import qualified Development.IDE.Core.Shake as Shake
import Development.IDE.Spans.Pragmas (LineSplitTextEdits (LineSplitTextEdits),
NextPragmaInfo (NextPragmaInfo),
Expand All @@ -114,19 +121,16 @@ import Development.IDE.Spans.Pragmas (LineSplitTe
lineSplitTextEdits,
nextPragmaLine)
import GHC.Generics (Generic)
#if !MIN_VERSION_apply_refact(0,12,0)
import System.Environment (setEnv,
unsetEnv)
#endif
import Development.IDE.Core.PluginUtils as PluginUtils
import Text.Regex.TDFA.Text ()

-- ---------------------------------------------------------------------

data Log
= LogShake Shake.Log
| LogApplying NormalizedFilePath (Either String WorkspaceEdit)
#if APPLY_REFACT
| LogGeneratedIdeas NormalizedFilePath [[Refact.Refactoring Refact.SrcSpan]]
#endif
| LogGetIdeas NormalizedFilePath
| LogUsingExtensions NormalizedFilePath [String] -- Extension is only imported conditionally, so we just stringify them
| forall a. (Pretty a) => LogResolve a
Expand All @@ -135,7 +139,9 @@ instance Pretty Log where
pretty = \case
LogShake log -> pretty log
LogApplying fp res -> "Applying hint(s) for" <+> viaShow fp <> ":" <+> viaShow res
#if APPLY_REFACT
LogGeneratedIdeas fp ideas -> "Generated hlint ideas for for" <+> viaShow fp <> ":" <+> viaShow ideas
#endif
LogUsingExtensions fp exts -> "Using extensions for " <+> viaShow fp <> ":" <> line <> indent 4 (pretty exts)
LogGetIdeas fp -> "Getting hlint ideas for " <+> viaShow fp
LogResolve msg -> pretty msg
Expand Down Expand Up @@ -413,12 +419,19 @@ resolveProvider recorder ideState _plId ca uri resolveValue = do
edit <- ExceptT $ liftIO $ ignoreHint recorder ideState file verTxtDocId hintTitle
pure $ ca & LSP.edit ?~ edit

applyRefactAvailable :: Bool
#if APPLY_REFACT
applyRefactAvailable = True
#else
applyRefactAvailable = False
#endif

-- | Convert a hlint diagnostic into an apply and an ignore code action
-- if applicable
diagnosticToCodeActions :: VersionedTextDocumentIdentifier -> LSP.Diagnostic -> [LSP.CodeAction]
diagnosticToCodeActions verTxtDocId diagnostic
| LSP.Diagnostic{ _source = Just "hlint", _code = Just (InR code), _range = LSP.Range start _ } <- diagnostic
, let isHintApplicable = "refact:" `T.isPrefixOf` code
, let isHintApplicable = "refact:" `T.isPrefixOf` code && applyRefactAvailable
, let hint = T.replace "refact:" "" code
, let suppressHintTitle = "Ignore hint \"" <> hint <> "\" in this module"
, let suppressHintArguments = IgnoreHint verTxtDocId hint
Expand Down Expand Up @@ -506,6 +519,11 @@ data OneHint =
} deriving (Generic, Eq, Show, ToJSON, FromJSON)

applyHint :: Recorder (WithPriority Log) -> IdeState -> NormalizedFilePath -> Maybe OneHint -> VersionedTextDocumentIdentifier -> IO (Either PluginError WorkspaceEdit)
#if !APPLY_REFACT
applyHint _ _ _ _ _ =
-- https://github.com/ndmitchell/hlint/pull/1594#issuecomment-2338898673
evaluate $ error "Cannot apply refactoring: apply-refact does not work on GHC 9.10"
#else
applyHint recorder ide nfp mhint verTxtDocId =
runExceptT $ do
let runAction' :: Action a -> IO a
Expand Down Expand Up @@ -607,7 +625,7 @@ applyRefactorings ::
-- with the @LANGUAGE@ pragmas, pragmas win.
[String] ->
IO String
applyRefactorings =
applyRefactorings =
#if MIN_VERSION_apply_refact(0,12,0)
Refact.applyRefactorings
#else
Expand All @@ -624,3 +642,4 @@ applyRefactorings =
withRuntimeLibdir libdir = bracket_ (setEnv key libdir) (unsetEnv key)
where key = "GHC_EXACTPRINT_GHC_LIBDIR"
#endif
#endif
18 changes: 9 additions & 9 deletions plugins/hls-hlint-plugin/test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ getApplyHintText :: T.Text -> T.Text
getApplyHintText name = "Apply hint \"" <> name <> "\""

resolveTests :: TestTree
resolveTests = testGroup "hlint resolve tests"
resolveTests = knownBrokenForGhcVersions [GHC910] "apply-refact doesn't work on 9.10" $ testGroup "hlint resolve tests"
[
ignoreHintGoldenResolveTest
"Resolve version of: Ignore hint in this module inserts -Wno-unrecognised-pragmas and hlint ignore pragma if warn unrecognized pragmas is off"
Expand Down Expand Up @@ -76,7 +76,7 @@ ignoreHintTests = testGroup "hlint ignore hint tests"
]

applyHintTests :: TestTree
applyHintTests = testGroup "hlint apply hint tests"
applyHintTests = knownBrokenForGhcVersions [GHC910] "apply-refact doesn't work on 9.10" $ testGroup "hlint apply hint tests"
[
applyHintGoldenTest
"[#2612] Apply hint works when operator fixities go right-to-left"
Expand All @@ -88,7 +88,7 @@ applyHintTests = testGroup "hlint apply hint tests"
suggestionsTests :: TestTree
suggestionsTests =
testGroup "hlint suggestions" [
testCase "provides 3.8 code actions including apply all" $ runHlintSession "" $ do
knownBrokenForGhcVersions [GHC910] "apply-refact doesn't work on 9.10" $ testCase "provides 3.8 code actions including apply all" $ runHlintSession "" $ do
doc <- openDoc "Base.hs" "haskell"
diags@(reduceDiag:_) <- hlintCaptureKick

Expand Down Expand Up @@ -120,7 +120,7 @@ suggestionsTests =
contents <- skipManyTill anyMessage $ getDocumentEdit doc
liftIO $ contents @?= "main = undefined\nfoo x = x\n"

, testCase "falls back to pre 3.8 code actions" $
, knownBrokenForGhcVersions [GHC910] "apply-refact doesn't work on 9.10" $ testCase "falls back to pre 3.8 code actions" $
runSessionWithTestConfig def
{ testConfigCaps = noLiteralCaps
, testDirLocation = Left testDir
Expand Down Expand Up @@ -179,15 +179,15 @@ suggestionsTests =
doc <- openDoc "CppHeader.hs" "haskell"
testHlintDiagnostics doc

, testCase "[#590] apply-refact works with -XLambdaCase argument" $ runHlintSession "lambdacase" $ do
, knownBrokenForGhcVersions [GHC910] "apply-refact doesn't work on 9.10" $ testCase "[#590] apply-refact works with -XLambdaCase argument" $ runHlintSession "lambdacase" $ do
testRefactor "LambdaCase.hs" "Redundant bracket"
expectedLambdaCase

, testCase "[#1242] apply-refact works with -XTypeApplications argument" $ runHlintSession "typeapps" $ do
, knownBrokenForGhcVersions [GHC910] "apply-refact doesn't work on 9.10" $ testCase "[#1242] apply-refact works with -XTypeApplications argument" $ runHlintSession "typeapps" $ do
testRefactor "TypeApplication.hs" "Redundant bracket"
expectedTypeApp

, testCase "apply hints works with LambdaCase via language pragma" $ runHlintSession "" $ do
, knownBrokenForGhcVersions [GHC910] "apply-refact doesn't work on 9.10" $ testCase "apply hints works with LambdaCase via language pragma" $ runHlintSession "" $ do
testRefactor "LambdaCase.hs" "Redundant bracket"
("{-# LANGUAGE LambdaCase #-}" : expectedLambdaCase)

Expand All @@ -213,10 +213,10 @@ suggestionsTests =
doc <- openDoc "IgnoreAnnHlint.hs" "haskell"
testNoHlintDiagnostics doc

, testCase "apply-refact preserve regular comments" $ runHlintSession "" $ do
, knownBrokenForGhcVersions [GHC910] "apply-refact doesn't work on 9.10" $ testCase "apply-refact preserve regular comments" $ runHlintSession "" $ do
testRefactor "Comments.hs" "Redundant bracket" expectedComments

, testCase "[#2290] apply all hints works with a trailing comment" $ runHlintSession "" $ do
, knownBrokenForGhcVersions [GHC910] "apply-refact doesn't work on 9.10" $ testCase "[#2290] apply all hints works with a trailing comment" $ runHlintSession "" $ do
testRefactor "TwoHintsAndComment.hs" "Apply all hints" expectedComments2

, testCase "applyAll is shown only when there is at least one diagnostic in range" $ runHlintSession "" $ do
Expand Down
7 changes: 7 additions & 0 deletions test/testdata/schema/ghc910/default-config.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@
},
"globalOn": true
},
"hlint": {
"codeActionsOn": true,
"config": {
"flags": []
},
"diagnosticsOn": true
},
"importLens": {
"codeActionsOn": true,
"codeLensOn": true,
Expand Down
18 changes: 18 additions & 0 deletions test/testdata/schema/ghc910/vscode-extension-schema.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,24 @@
"scope": "resource",
"type": "boolean"
},
"haskell.plugin.hlint.codeActionsOn": {
"default": true,
"description": "Enables hlint code actions",
"scope": "resource",
"type": "boolean"
},
"haskell.plugin.hlint.config.flags": {
"default": [],
"markdownDescription": "Flags used by hlint",
"scope": "resource",
"type": "array"
},
"haskell.plugin.hlint.diagnosticsOn": {
"default": true,
"description": "Enables hlint diagnostics",
"scope": "resource",
"type": "boolean"
},
"haskell.plugin.importLens.codeActionsOn": {
"default": true,
"description": "Enables importLens code actions",
Expand Down
Loading