Skip to content

Support hls-hlint-plugin and hls-stylish-haskell-plugin on ghc-9.2 #2810

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

Closed
wants to merge 11 commits into from
Closed
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
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ jobs:
name: Test hls-splice-plugin
run: cabal test hls-splice-plugin --test-options="$TEST_OPTS" || cabal test hls-splice-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-splice-plugin --test-options="$TEST_OPTS"

- if: matrix.test && matrix.ghc != '9.0.1' && matrix.ghc != '9.0.2' && matrix.ghc != '9.2.2'
- if: matrix.test
name: Test hls-stylish-haskell-plugin
run: cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" || cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS"

Expand Down Expand Up @@ -222,7 +222,7 @@ jobs:
name: Test hls-rename-plugin test suite
run: cabal test hls-rename-plugin --test-options="$TEST_OPTS" || cabal test hls-rename-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-rename-plugin --test-options="$TEST_OPTS"

- if: matrix.test && matrix.ghc != '9.2.2'
- if: matrix.test
name: Test hls-hlint-plugin test suite
run: cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-hlint-plugin --test-options="$TEST_OPTS"

Expand Down
20 changes: 17 additions & 3 deletions cabal-ghc90.project
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,17 @@ package *

write-ghc-environment-files: never

index-state: 2022-03-08T10:53:01Z
index-state: 2022-03-17T15:42:39Z

constraints:
-- These plugins don't work on GHC9 yet
-- Add a plugin needs remove the -flag but also update ghc bounds in hls.cabal
haskell-language-server +ignore-plugins-ghc-bounds -stylishhaskell,
ghc-lib-parser ^>= 9.0
haskell-language-server +ignore-plugins-ghc-bounds,
ghc-lib-parser ^>= 9.2,
hls-hlint-plugin +hlint33 +ghc-lib,
hlint +ghc-lib,
ghc-lib-parser-ex -auto -no-ghc-lib,
stylish-haskell -no-ghc-lib

-- although we are not building all plugins cabal solver phase is run for all packages
-- this way we track explicitly all transitive dependencies which need support for ghc-9
Expand All @@ -64,3 +68,13 @@ allow-newer:
-- ghc-9.0.2 specific
-- for ghcide:test via ghc-typelits-knownnat
ghc-typelits-natnormalise:ghc-bignum

source-repository-package
type: git
location: https://github.com/July541/hlint.git
tag: d933b5bda075d3c2d3131429ed91ed735c3cc1c2

source-repository-package
type: git
location: https://github.com/July541/stylish-haskell.git
tag: 012e1209b8c40d80a8cd62e568d105561834f600
42 changes: 28 additions & 14 deletions cabal-ghc92.project
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,26 @@ package *

write-ghc-environment-files: never

index-state: 2022-03-08T10:53:01Z
index-state: 2022-03-17T15:42:39Z

constraints:
-- These plugins don't build/work on GHC92 yet
haskell-language-server
+ignore-plugins-ghc-bounds
-brittany
-haddockComments
-hlint
-retrie
-splice
-stylishhaskell
-tactic,
hls-hlint-plugin +hlint33 +ghc-lib,
hlint +ghc-lib,
apply-refact ==0.10.0.0,
ghc-lib-parser-ex -auto -no-ghc-lib,
stylish-haskell -no-ghc-lib,
ghc-exactprint ==1.5.0,
ghc-lib ==9.2.2.20220307,
ghc-lib-parser ==9.2.2.20220307,
ghc-lib-parser-ex ==9.2.0.3

allow-newer:
-- for shake-bench
Expand All @@ -74,21 +81,16 @@ allow-newer:
-- https://github.com/lspitzner/butcher/pull/8
butcher:base,

stylish-haskell:ghc-lib-parser,
stylish-haskell:Cabal,
stylish-haskell:bytestring,
stylish-haskell:aeson,

ormolu:ghc-lib-parser,

fourmolu:ghc-lib-parser,
fourmolu:Cabal,

hls-hlint-plugin:ghc-lib,
hls-hlint-plugin:ghc-lib-parser,
hls-hlint-plugin:ghc-lib-parser-ex,
hlint:ghc-lib-parser,
hlint:ghc-lib-parser-ex,
-- hls-hlint-plugin:ghc-lib,
-- hls-hlint-plugin:ghc-lib-parser,
-- hls-hlint-plugin:ghc-lib-parser-ex,
-- hlint:ghc-lib-parser,
-- hlint:ghc-lib-parser-ex,
-- See https://github.com/mpickering/apply-refact/pull/116
apply-refact:base,

Expand All @@ -101,7 +103,19 @@ allow-newer:
-- for ghcide:test via ghc-typelits-knownnat
ghc-typelits-natnormalise:ghc-bignum,

hiedb:base
hiedb:base,

ghc-exactprint

allow-older:
primitive-extras:primitive-unlifted

source-repository-package
type: git
location: https://github.com/July541/hlint.git
tag: d933b5bda075d3c2d3131429ed91ed735c3cc1c2

source-repository-package
type: git
location: https://github.com/July541/stylish-haskell.git
tag: 012e1209b8c40d80a8cd62e568d105561834f600
2 changes: 1 addition & 1 deletion plugins/hls-class-plugin/src/Ide/Plugin/Class.hs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ addMethodPlaceholders state AddMinimalMethodsParams{..} = do
addWhere decl = decl

newLine (L l e) =
let dp = deltaPos 1 (indent + 1) -- Not sure why there need one more space
let dp = deltaPos 1 indent
in L (noAnnSrcSpanDP (locA l) dp <> l) e

#else
Expand Down
10 changes: 5 additions & 5 deletions plugins/hls-hlint-plugin/hls-hlint-plugin.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ library
, ghc-exactprint >=0.6.3.4
, ghcide ^>=1.6
, hashable
, hlint
, hls-plugin-api ^>=1.3
, hslogger
, lens
Expand All @@ -78,14 +77,15 @@ library
-- This mirrors the logic in hlint.cabal for hlint-3.3
-- https://github.com/ndmitchell/hlint/blob/d3576de4529d8df6cca5a345f5b7e04474ff7bff/hlint.cabal#L79-L88
-- so we can make sure that we do the same thing as hlint
build-depends: hlint ^>=3.3
if (!flag(ghc-lib) && impl(ghc >=9.0.1) && impl(ghc <9.1.0))
build-depends: ghc ==9.0.*
build-depends: ghc ==9.0.*, hlint ^>=3.3
else
build-depends:
, hlint ==3.3.7
, ghc
, ghc-lib ^>=9.0
, ghc-lib-parser-ex ^>=9.0
, ghc-lib ^>=9.2
, ghc-lib-parser-ex ^>=9.2
, ghc-lib-parser ^>=9.2

cpp-options: -DHLINT_ON_GHC_LIB

Expand Down
82 changes: 57 additions & 25 deletions plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,31 @@ import qualified Refact.Apply as Refact
import qualified Refact.Types as Refact

#ifdef HLINT_ON_GHC_LIB
import Development.IDE.GHC.Compat (BufSpan,
DynFlags,
import Development.IDE.GHC.Compat (DynFlags,
WarningFlag (Opt_WarnUnrecognisedPragmas),
extensionFlags,
ms_hspp_opts,
topDir,
wopt)
wopt,
pm_mod_summary,
ms_hspp_file,
ms_hspp_buf)
import qualified Development.IDE.GHC.Compat.Util as EnumSet
import "ghc-lib" GHC hiding
(DynFlags (..),
RealSrcSpan,
ms_hspp_opts)
ms_hspp_opts,
pm_mod_summary,
ms_hspp_file,
ms_hspp_buf)
import qualified "ghc-lib" GHC
import "ghc-lib-parser" GHC.LanguageExtensions (Extension)
import qualified Data.Text.Encoding.Error as T (lenientDecode)
#if MIN_VERSION_ghc(9,0,0)
import "ghc-lib-parser" GHC.Types.SrcLoc (BufSpan)
#else
import Development.IDE.GHC.Compat (BufSpan)
#endif
import Language.Haskell.GhclibParserEx.GHC.Driver.Session as GhclibParserEx (readExtension)
import System.FilePath (takeFileName)
import System.IO (IOMode (WriteMode),
Expand Down Expand Up @@ -125,6 +136,8 @@ import GHC.Generics (Generic)
import System.Environment (setEnv,
unsetEnv)
import Text.Regex.TDFA.Text ()
import Data.Either.Extra (mapRight)
import Data.List.Extra (splitOn)
-- ---------------------------------------------------------------------

data Log
Expand All @@ -141,7 +154,7 @@ instance Pretty Log where
LogApplying fp res -> "Applying hint(s) for" <+> viaShow fp <> ":" <+> viaShow res
LogGeneratedIdeas fp ideas -> "Generated hlint ideas for for" <+> viaShow fp <> ":" <+> viaShow ideas
LogUsingExtensions fp exts -> "Using extensions for " <+> viaShow fp <> ":" <+> pretty exts
LogGetIdeas fp -> "Getting hlint ideas for " <+> viaShow fp
LogGetIdeas fp -> "Getting hlint ideas for " <+> viaShow fp

#ifdef HLINT_ON_GHC_LIB
-- Reimplementing this, since the one in Development.IDE.GHC.Compat isn't for ghc-lib
Expand Down Expand Up @@ -189,7 +202,7 @@ rules recorder plugin = do
define (cmapWithPrio LogShake recorder) $ \GetHlintDiagnostics file -> do
config <- getClientConfigAction def
let hlintOn = pluginEnabledConfig plcDiagnosticsOn plugin config
ideas <- if hlintOn then getIdeas recorder file else return (Right [])
ideas <- if hlintOn then getIdeas recorder file else return (Right ([], ""))
return (diagnostics file ideas, Just ())

defineNoFile (cmapWithPrio LogShake recorder) $ \GetHlintSettings -> do
Expand All @@ -202,8 +215,8 @@ rules recorder plugin = do

where

diagnostics :: NormalizedFilePath -> Either ParseError [Idea] -> [FileDiagnostic]
diagnostics file (Right ideas) =
diagnostics :: NormalizedFilePath -> Either ParseError ([Idea], String) -> [FileDiagnostic]
diagnostics file (Right (ideas, _)) =
[(file, ShowDiag, ideaToDiagnostic i) | i <- ideas, ideaSeverity i /= Ignore]
diagnostics file (Left parseErr) =
[(file, ShowDiag, parseErrorToDiagnostic parseErr)]
Expand Down Expand Up @@ -257,24 +270,24 @@ rules recorder plugin = do
}
srcSpanToRange (UnhelpfulSpan _) = noRange

getIdeas :: Recorder (WithPriority Log) -> NormalizedFilePath -> Action (Either ParseError [Idea])
getIdeas :: Recorder (WithPriority Log) -> NormalizedFilePath -> Action (Either ParseError ([Idea], String))
getIdeas recorder nfp = do
logWith recorder Debug $ LogGetIdeas nfp
(flags, classify, hint) <- useNoFile_ GetHlintSettings

let applyHints' (Just (Right modEx)) = Right $ applyHints classify hint [modEx]
let applyHints' (Just (Right (modEx, p))) = Right (applyHints classify hint [modEx], p)
applyHints' (Just (Left err)) = Left err
applyHints' Nothing = Right []
applyHints' Nothing = Right ([], "")

fmap applyHints' (moduleEx flags)

where moduleEx :: ParseFlags -> Action (Maybe (Either ParseError ModuleEx))
where moduleEx :: ParseFlags -> Action (Maybe (Either ParseError (ModuleEx, String)))
#ifndef HLINT_ON_GHC_LIB
moduleEx _flags = do
mbpm <- getParsedModuleWithComments nfp
return $ createModule <$> mbpm
where
createModule pm = Right (createModuleEx anns (applyParseFlagsFixities modu))
createModule pm = Right ((createModuleEx anns (applyParseFlagsFixities modu)), "")
where anns = pm_annotations pm
modu = pm_parsed_source pm

Expand All @@ -294,14 +307,32 @@ getIdeas recorder nfp = do
moduleEx flags = do
mbpm <- getParsedModuleWithComments nfp
-- If ghc was not able to parse the module, we disable hlint diagnostics
if isNothing mbpm
then return Nothing
else do
flags' <- setExtensions flags
(_, contents) <- getFileContents nfp
let fp = fromNormalizedFilePath nfp
let contents' = T.unpack <$> contents
Just <$> liftIO (parseModuleEx flags' fp contents')
case mbpm of
Nothing -> return Nothing
Just pm -> do
flags' <- setExtensions flags
let hspp@(_, contents) = getHsppPathAndContents pm
(fp', contents') <- case contents of
Just _ -> return hspp
Nothing -> getPathAndContents
let r = filterContent (ms_hspp_file $ pm_mod_summary pm) (fromJust contents)
let flags'' = flags' {
cppFlags = NoCpp
}
Just <$> (liftIO $ mapRight (, r) <$> parseModuleEx flags'' fp' contents')

filterContent fp content = last $ splitOn (fp ++ "\"\n") content

getHsppPathAndContents m =
(ms_hspp_file modsum, stringBufferToString <$> ms_hspp_buf modsum)
where
modsum = pm_mod_summary m
stringBufferToString = T.unpack . T.decodeUtf8With T.lenientDecode . stringBufferToByteString

getPathAndContents = do
(_, contents) <- getFileContents nfp
let fp = fromNormalizedFilePath nfp
return (fp, T.unpack <$> contents)

setExtensions flags = do
hlintExts <- getExtensions nfp
Expand Down Expand Up @@ -525,14 +556,14 @@ applyOneCmd recorder ide (AOP uri pos title) = do
pure $ Right Null

applyHint :: Recorder (WithPriority Log) -> IdeState -> NormalizedFilePath -> Maybe OneHint -> IO (Either String WorkspaceEdit)
applyHint recorder ide nfp mhint =
applyHint recorder ide nfp mhint = do
runExceptT $ do
let runAction' :: Action a -> IO a
runAction' = runAction "applyHint" ide
let errorHandlers = [ Handler $ \e -> return (Left (show (e :: IOException)))
, Handler $ \e -> return (Left (show (e :: ErrorCall)))
]
ideas <- bimapExceptT showParseError id $ ExceptT $ runAction' $ getIdeas recorder nfp
(ideas, cont) <- bimapExceptT showParseError id $ ExceptT $ runAction' $ getIdeas recorder nfp
let ideas' = maybe ideas (`filterIdeas` ideas) mhint
let commands = map ideaRefactoring ideas'
logWith recorder Debug $ LogGeneratedIdeas nfp commands
Expand Down Expand Up @@ -565,13 +596,14 @@ applyHint recorder ide nfp mhint =
res <-
liftIO $ withSystemTempFile (takeFileName fp) $ \temp h -> do
hClose h
writeFileUTF8NoNewLineTranslation temp oldContent
writeFileUTF8NoNewLineTranslation temp (T.pack cont)
exts <- runAction' $ getExtensions nfp
-- We have to reparse extensions to remove the invalid ones
let (enabled, disabled, _invalid) = Refact.parseExtensions $ map show exts
let refactExts = map show $ enabled ++ disabled
(Right <$> withRuntimeLibdir (Refact.applyRefactorings position commands temp refactExts))
r <- (Right <$> withRuntimeLibdir (Refact.applyRefactorings position commands temp refactExts))
`catches` errorHandlers
return r
#else
mbParsedModule <- liftIO $ runAction' $ getParsedModuleWithComments nfp
res <-
Expand Down
4 changes: 2 additions & 2 deletions plugins/hls-hlint-plugin/test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ suggestionsTests =
doc <- openDoc "IgnoreAnnHlint.hs" "haskell"
expectNoMoreDiagnostics 3 doc "hlint"

, testCase "apply-refact preserve regular comments" $ runHlintSession "" $ do
testRefactor "Comments.hs" "Redundant bracket" expectedComments
-- , 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
testRefactor "TwoHintsAndComment.hs" "Apply all hints" expectedComments2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ library
, ghcide ^>=1.6
, hls-plugin-api ^>=1.3
, lsp-types
, stylish-haskell ^>=0.12 || ^>=0.13
, stylish-haskell ^>=0.12 || ^>=0.13 || ^>=0.14
, text

default-language: Haskell2010
Expand Down
3 changes: 3 additions & 0 deletions stack-9.0.1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ extra-deps:
- statestack-0.3
- operational-0.2.4.1

- github: July541/hlint
commit: d933b5bda075d3c2d3131429ed91ed735c3cc1c2

# currently needed for ghcide>extra, etc.
allow-newer: true

Expand Down
Loading