Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
29 changes: 20 additions & 9 deletions plugins/default/src/Ide/Plugin/Pragmas.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

-- | Provides code actions to add missing pragmas (whenever GHC suggests to)
Expand Down Expand Up @@ -67,27 +68,37 @@ codeActionProvider _ state _plId docId _ (J.CodeActionContext (J.List diags) _mo
let mFile = docId ^. J.uri & uriToFilePath <&> toNormalizedFilePath'
pm <- fmap join $ runAction "addPragma" state $ getParsedModule `traverse` mFile
let dflags = ms_hspp_opts . pm_mod_summary <$> pm
-- Filter diagnostics that are from ghcmod
ghcDiags = filter (\d -> d ^. J.source == Just "typecheck") diags
-- Filter diagnostics that are from GHC
ghcDiags = filter isGhcDiag diags
-- Get all potential Pragmas for all diagnostics.
pragmas = concatMap (\d -> genPragma dflags (d ^. J.message)) ghcDiags
cmds <- mapM mkCodeAction pragmas
return $ Right $ List cmds
where
isGhcDiag diag
| Just source <- diag ^. J.source
= source `elem` ["parser", "typecheck"]
| otherwise
= False

mkCodeAction pragmaName = do
let
codeAction = J.CACodeAction $ J.CodeAction title (Just J.CodeActionQuickFix) (Just (J.List [])) (Just edit) Nothing
title = "Add \"" <> pragmaName <> "\""
edit = mkPragmaEdit (docId ^. J.uri) pragmaName
return codeAction

genPragma mDynflags target
| Just dynFlags <- mDynflags,
-- GHC does not export 'OnOff', so we have to view it as string
disabled <- [ e | Just e <- T.stripPrefix "Off " . T.pack . prettyPrint <$> extensions dynFlags]
= [ r | r <- findPragma target, r `notElem` disabled]
| otherwise = []

genPragma mDynflags target =
[ r | r <- findPragma target, r `notElem` disabled]
where
disabled
| Just dynFlags <- mDynflags
-- GHC does not export 'OnOff', so we have to view it as string
= [ e | Just e <- T.stripPrefix "Off " . T.pack . prettyPrint <$> extensions dynFlags]
| otherwise
-- When the module failed to parse, we don't have access to its
-- dynFlags. In that case, simply don't disable any pragmas.
= []

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

Expand Down
25 changes: 25 additions & 0 deletions test/functional/FunctionalCodeAction.hs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ missingPragmaTests = testGroup "missing pragma warning code actions" [
contents <- documentContents doc

let expected = [ "{-# LANGUAGE TypeSynonymInstances #-}"
, "module NeedsPragmas where"
, ""
, "import GHC.Generics"
, ""
Expand All @@ -443,6 +444,30 @@ missingPragmaTests = testGroup "missing pragma warning code actions" [
]

liftIO $ (T.lines contents) @?= expected

, testCase "Adds TypeApplications pragma" $ do
runSession hlsCommand fullCaps "test/testdata/addPragmas" $ do
doc <- openDoc "TypeApplications.hs" "haskell"

_ <- waitForDiagnosticsFrom doc
cas <- map fromAction <$> getAllCodeActions doc

liftIO $ "Add \"TypeApplications\"" `elem` map (^. L.title) cas @? "Contains TypeApplications code action"

executeCodeAction $ head cas

contents <- documentContents doc

let expected =
[ "{-# LANGUAGE TypeApplications #-}"
, "{-# LANGUAGE ScopedTypeVariables #-}"
, "module TypeApplications where"
, ""
, "foo :: forall a. a -> a"
, "foo = id @a"
]

liftIO $ (T.lines contents) @?= expected
]

unusedTermTests :: TestTree
Expand Down
1 change: 1 addition & 0 deletions test/testdata/addPragmas/NeedsPragmas.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module NeedsPragmas where

import GHC.Generics

Expand Down
5 changes: 5 additions & 0 deletions test/testdata/addPragmas/TypeApplications.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{-# LANGUAGE ScopedTypeVariables #-}
module TypeApplications where

foo :: forall a. a -> a
foo = id @a
1 change: 1 addition & 0 deletions test/testdata/addPragmas/hie.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ cradle:
direct:
arguments:
- "NeedsPragmas"
- "TypeApplications"
5 changes: 3 additions & 2 deletions test/testdata/addPragmas/test.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ category: Web
build-type: Simple
cabal-version: >=1.10

executable p
main-is: NeedsPragmas.hs
library
exposed-modules: NeedsPragmas
TypeApplications
hs-source-dirs: .
build-depends: base >= 4.7 && < 5
default-language: Haskell2010
Expand Down