From 6187959f6ff55f935ed15a7f913be2ee923adaec Mon Sep 17 00:00:00 2001 From: Junyoung/Clare Jang Date: Fri, 14 May 2021 17:36:40 -0400 Subject: [PATCH] Extract pragmas plugin --- cabal.project | 1 + haskell-language-server.cabal | 4 +- nix/default.nix | 1 + plugins/hls-pragmas-plugin/LICENSE | 201 +++++++++++++++ .../hls-pragmas-plugin.cabal | 50 ++++ .../src/Ide/Plugin/Pragmas.hs | 41 +-- plugins/hls-pragmas-plugin/test/Main.hs | 197 ++++++++++++++ .../test/testdata/AfterShebang.expected.hs | 13 + .../test/testdata}/AfterShebang.hs | 0 .../testdata/AppendToExisting.expected.hs | 12 + .../test/testdata}/AppendToExisting.hs | 0 .../testdata/BeforeDocComment.expected.hs | 15 ++ .../test/testdata}/BeforeDocComment.hs | 0 .../test/testdata/Completion.hs | 9 + .../testdata/MissingSignatures.expected.hs | 3 + .../test/testdata/MissingSignatures.hs | 2 + .../test/testdata/NamedFieldPuns.expected.hs | 10 + .../test/testdata}/NamedFieldPuns.hs | 0 .../test/testdata/NeedsPragmas.expected.hs | 17 ++ .../test/testdata}/NeedsPragmas.hs | 0 .../testdata/TypeApplications.expected.hs | 6 + .../test/testdata}/TypeApplications.hs | 0 .../test/testdata/UnusedImports.expected.hs | 7 + .../test/testdata/UnusedImports.hs | 6 + .../test/testdata}/hie.yaml | 0 stack-8.10.2.yaml | 1 + stack-8.10.3.yaml | 1 + stack-8.10.4.yaml | 1 + stack-8.6.4.yaml | 1 + stack-8.6.5.yaml | 1 + stack-8.8.2.yaml | 1 + stack-8.8.3.yaml | 1 + stack-8.8.4.yaml | 1 + stack.yaml | 1 + test/functional/Completion.hs | 102 -------- test/functional/FunctionalCodeAction.hs | 242 ------------------ 36 files changed, 581 insertions(+), 367 deletions(-) create mode 100644 plugins/hls-pragmas-plugin/LICENSE create mode 100644 plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal rename plugins/{default => hls-pragmas-plugin}/src/Ide/Plugin/Pragmas.hs (82%) create mode 100644 plugins/hls-pragmas-plugin/test/Main.hs create mode 100644 plugins/hls-pragmas-plugin/test/testdata/AfterShebang.expected.hs rename {test/testdata/addPragmas => plugins/hls-pragmas-plugin/test/testdata}/AfterShebang.hs (100%) create mode 100644 plugins/hls-pragmas-plugin/test/testdata/AppendToExisting.expected.hs rename {test/testdata/addPragmas => plugins/hls-pragmas-plugin/test/testdata}/AppendToExisting.hs (100%) create mode 100644 plugins/hls-pragmas-plugin/test/testdata/BeforeDocComment.expected.hs rename {test/testdata/addPragmas => plugins/hls-pragmas-plugin/test/testdata}/BeforeDocComment.hs (100%) create mode 100644 plugins/hls-pragmas-plugin/test/testdata/Completion.hs create mode 100644 plugins/hls-pragmas-plugin/test/testdata/MissingSignatures.expected.hs create mode 100644 plugins/hls-pragmas-plugin/test/testdata/MissingSignatures.hs create mode 100644 plugins/hls-pragmas-plugin/test/testdata/NamedFieldPuns.expected.hs rename {test/testdata/addPragmas => plugins/hls-pragmas-plugin/test/testdata}/NamedFieldPuns.hs (100%) create mode 100644 plugins/hls-pragmas-plugin/test/testdata/NeedsPragmas.expected.hs rename {test/testdata/addPragmas => plugins/hls-pragmas-plugin/test/testdata}/NeedsPragmas.hs (100%) create mode 100644 plugins/hls-pragmas-plugin/test/testdata/TypeApplications.expected.hs rename {test/testdata/addPragmas => plugins/hls-pragmas-plugin/test/testdata}/TypeApplications.hs (100%) create mode 100644 plugins/hls-pragmas-plugin/test/testdata/UnusedImports.expected.hs create mode 100644 plugins/hls-pragmas-plugin/test/testdata/UnusedImports.hs rename {test/testdata/addPragmas => plugins/hls-pragmas-plugin/test/testdata}/hie.yaml (100%) diff --git a/cabal.project b/cabal.project index b6666ff273..9859c33873 100644 --- a/cabal.project +++ b/cabal.project @@ -19,6 +19,7 @@ packages: ./plugins/hls-haddock-comments-plugin ./plugins/hls-splice-plugin ./plugins/hls-floskell-plugin + ./plugins/hls-pragmas-plugin tests: true package * diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 8f82ad05a0..744c7ab43b 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -237,9 +237,7 @@ common moduleName common pragmas if flag(pragmas) || flag(all-plugins) - hs-source-dirs: plugins/default/src - build-depends: fuzzy - other-modules: Ide.Plugin.Pragmas + build-depends: hls-pragmas-plugin ^>= 1.0.0.0 cpp-options: -Dpragmas common splice diff --git a/nix/default.nix b/nix/default.nix index 9e2f0a92c1..28bb55598c 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -43,6 +43,7 @@ let hls-splice-plugin = gitignoreSource ../plugins/hls-splice-plugin; hls-tactics-plugin = gitignoreSource ../plugins/hls-tactics-plugin; hls-floskell-plugin = gitignoreSource ../plugins/hls-floskell-plugin; + hls-pragmas-plugin = gitignoreSource ../plugins/hls-pragmas-plugin; }; gitignoreSource = (import sources.gitignore { inherit (pkgs) lib; }).gitignoreSource; extended = haskellPackages: diff --git a/plugins/hls-pragmas-plugin/LICENSE b/plugins/hls-pragmas-plugin/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/plugins/hls-pragmas-plugin/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal new file mode 100644 index 0000000000..8d7688a9a8 --- /dev/null +++ b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal @@ -0,0 +1,50 @@ +cabal-version: 2.4 +name: hls-pragmas-plugin +version: 1.0.0.0 +synopsis: Pragmas plugin for Haskell Language Server +description: + Please see the README on GitHub at + +license: Apache-2.0 +license-file: LICENSE +author: The Haskell IDE Team +copyright: The Haskell IDE Team +maintainer: alan.zimm@gmail.com +category: Development +build-type: Simple +extra-source-files: + LICENSE + test/testdata/*.hs + test/testdata/*.yaml + +library + exposed-modules: Ide.Plugin.Pragmas + hs-source-dirs: src + build-depends: + base >=4.12 && <5 + , extra + , fuzzy + , ghcide >=1.2 && <1.4 + , hls-plugin-api ^>=1.1 + , lens + , lsp + , text + , transformers + , unordered-containers + default-language: Haskell2010 + +test-suite tests + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: test + main-is: Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: + , base + , filepath + , hls-pragmas-plugin + , hls-test-utils ^>=1.0 + , lens + , lsp-test + , lsp-types + , text diff --git a/plugins/default/src/Ide/Plugin/Pragmas.hs b/plugins/hls-pragmas-plugin/src/Ide/Plugin/Pragmas.hs similarity index 82% rename from plugins/default/src/Ide/Plugin/Pragmas.hs rename to plugins/hls-pragmas-plugin/src/Ide/Plugin/Pragmas.hs index 920e907ae7..3d91297ddf 100644 --- a/plugins/default/src/Ide/Plugin/Pragmas.hs +++ b/plugins/hls-pragmas-plugin/src/Ide/Plugin/Pragmas.hs @@ -1,6 +1,8 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeOperators #-} {-# LANGUAGE ViewPatterns #-} -- | Provides code actions to add missing pragmas (whenever GHC suggests to) @@ -9,7 +11,7 @@ module Ide.Plugin.Pragmas (descriptor) where import Control.Applicative ((<|>)) import Control.Lens hiding (List) import Control.Monad (join) -import Control.Monad.IO.Class +import Control.Monad.IO.Class (MonadIO (liftIO)) import qualified Data.HashMap.Strict as H import Data.List import Data.List.Extra (nubOrdOn) @@ -19,7 +21,6 @@ import Development.IDE as D import Development.IDE.GHC.Compat import Ide.Types import qualified Language.LSP.Server as LSP -import Language.LSP.Types import qualified Language.LSP.Types as J import qualified Language.LSP.Types.Lens as J import qualified Language.LSP.VFS as VFS @@ -29,8 +30,8 @@ import qualified Text.Fuzzy as Fuzzy descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = (defaultPluginDescriptor plId) - { pluginHandlers = mkPluginHandler STextDocumentCodeAction codeActionProvider - <> mkPluginHandler STextDocumentCompletion completion + { pluginHandlers = mkPluginHandler J.STextDocumentCodeAction codeActionProvider + <> mkPluginHandler J.STextDocumentCompletion completion } -- --------------------------------------------------------------------- @@ -41,12 +42,12 @@ type PragmaEdit = (T.Text, Pragma) data Pragma = LangExt T.Text | OptGHC T.Text deriving (Show, Eq, Ord) -codeActionProvider :: PluginMethodHandler IdeState TextDocumentCodeAction -codeActionProvider state _plId (CodeActionParams _ _ docId _ (J.CodeActionContext (J.List diags) _monly)) = do - let mFile = docId ^. J.uri & uriToFilePath <&> toNormalizedFilePath' +codeActionProvider :: PluginMethodHandler IdeState 'J.TextDocumentCodeAction +codeActionProvider state _plId (J.CodeActionParams _ _ docId _ (J.CodeActionContext (J.List diags) _monly)) = do + let mFile = docId ^. J.uri & J.uriToFilePath <&> toNormalizedFilePath' uri = docId ^. J.uri pm <- liftIO $ fmap join $ runAction "Pragmas.GetParsedModule" state $ getParsedModule `traverse` mFile - mbContents <- liftIO $ fmap (join . fmap snd) $ runAction "Pragmas.GetFileContents" state $ getFileContents `traverse` mFile + mbContents <- liftIO $ fmap (snd =<<) $ runAction "Pragmas.GetFileContents" state $ getFileContents `traverse` mFile let dflags = ms_hspp_opts . pm_mod_summary <$> pm insertRange = maybe (Range (Position 0 0) (Position 0 0)) endOfModuleHeader mbContents pedits = nubOrdOn snd . concat $ suggest dflags <$> diags @@ -55,9 +56,9 @@ codeActionProvider state _plId (CodeActionParams _ _ docId _ (J.CodeActionContex -- | Add a Pragma to the given URI at the top of the file. -- It is assumed that the pragma name is a valid pragma, -- thus, not validated. -pragmaEditToAction :: Uri -> Range -> PragmaEdit -> (Command |? CodeAction) +pragmaEditToAction :: Uri -> Range -> PragmaEdit -> (J.Command J.|? J.CodeAction) pragmaEditToAction uri range (title, p) = - InR $ J.CodeAction title (Just J.CodeActionQuickFix) (Just (J.List [])) Nothing Nothing (Just edit) Nothing Nothing + J.InR $ J.CodeAction title (Just J.CodeActionQuickFix) (Just (J.List [])) Nothing Nothing (Just edit) Nothing Nothing where render (OptGHC x) = "{-# OPTIONS_GHC -Wno-" <> x <> " #-}\n" render (LangExt x) = "{-# LANGUAGE " <> x <> " #-}\n" @@ -77,7 +78,7 @@ suggest dflags diag = suggestDisableWarning :: Diagnostic -> [PragmaEdit] suggestDisableWarning Diagnostic {_code} - | Just (InR (T.stripPrefix "-W" -> Just w)) <- _code = + | Just (J.InR (T.stripPrefix "-W" -> Just w)) <- _code = pure ("Disable \"" <> w <> "\" warnings", OptGHC w) | otherwise = [] @@ -140,26 +141,26 @@ allPragmas = -- --------------------------------------------------------------------- -completion :: PluginMethodHandler IdeState TextDocumentCompletion +completion :: PluginMethodHandler IdeState 'J.TextDocumentCompletion completion _ide _ complParams = do - let (TextDocumentIdentifier uri) = complParams ^. J.textDocument + let (J.TextDocumentIdentifier uri) = complParams ^. J.textDocument position = complParams ^. J.position contents <- LSP.getVirtualFile $ toNormalizedUri uri - fmap (Right . InL) $ case (contents, uriToFilePath' uri) of + fmap (Right . J.InL) $ case (contents, uriToFilePath' uri) of (Just cnts, Just _path) -> result <$> VFS.getCompletionPrefix position cnts where result (Just pfix) | "{-# LANGUAGE" `T.isPrefixOf` VFS.fullLine pfix - = List $ map buildCompletion + = J.List $ map buildCompletion (Fuzzy.simpleFilter (VFS.prefixText pfix) allPragmas) | otherwise - = List [] - result Nothing = List [] + = J.List [] + result Nothing = J.List [] buildCompletion p = - CompletionItem + J.CompletionItem { _label = p, - _kind = Just CiKeyword, + _kind = Just J.CiKeyword, _tags = Nothing, _detail = Nothing, _documentation = Nothing, @@ -176,7 +177,7 @@ completion _ide _ complParams = do _command = Nothing, _xdata = Nothing } - _ -> return $ List [] + _ -> return $ J.List [] -- --------------------------------------------------------------------- diff --git a/plugins/hls-pragmas-plugin/test/Main.hs b/plugins/hls-pragmas-plugin/test/Main.hs new file mode 100644 index 0000000000..5ed53aaba9 --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/Main.hs @@ -0,0 +1,197 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +module Main ( + main, +) where + +import Control.Lens ((^.)) +import qualified Data.Text as T +import qualified Data.Text.IO as T +import qualified Data.Text.Lazy as TL +import qualified Data.Text.Lazy.Encoding as TL +import qualified Ide.Plugin.Pragmas as Pragmas +import qualified Language.LSP.Types.Lens as L +import System.FilePath +import Test.Hls + +main :: IO () +main = defaultTestRunner tests + +pragmasPlugin :: PluginDescriptor IdeState +pragmasPlugin = Pragmas.descriptor "pragmas" + +tests :: TestTree +tests = + testGroup "pragmas" + [ codeActionTests + , completionTests + ] + +codeActionTests :: TestTree +codeActionTests = + testGroup "code actions" + [ pragmasGolden "adds TypeSynonymInstances pragma" "NeedsPragmas" $ \path -> do + doc <- openDoc path "haskell" + _ <- waitForDiagnosticsFromSource doc "typecheck" + cas <- map fromAction <$> getAllCodeActions doc + liftIO $ "Add \"TypeSynonymInstances\"" `elem` map (^. L.title) cas @? "Contains TypeSynonymInstances code action" + liftIO $ "Add \"FlexibleInstances\"" `elem` map (^. L.title) cas @? "Contains FlexibleInstances code action" + executeCodeAction $ head cas + documentContents doc + + , pragmasGolden "adds TypeApplications pragma" "TypeApplications" $ \path -> do + doc <- openDoc path "haskell" + _ <- waitForDiagnosticsFrom doc + cas <- map fromAction <$> getAllCodeActions doc + liftIO $ "Add \"TypeApplications\"" `elem` map (^. L.title) cas @? "Contains TypeApplications code action" + executeCodeAction $ head cas + documentContents doc + + , pragmasGolden "no duplication" "NamedFieldPuns" $ \path -> do + doc <- openDoc path "haskell" + _ <- waitForDiagnosticsFrom doc + cas <- map fromAction <$> getCodeActions doc (Range (Position 8 9) (Position 8 9)) + liftIO $ length cas == 1 @? "Expected one code action, but got: " <> show cas + let ca = head cas + liftIO $ (ca ^. L.title == "Add \"NamedFieldPuns\"") @? "NamedFieldPuns code action" + executeCodeAction ca + documentContents doc + + , pragmasGolden "after shebang" "AfterShebang" $ \path -> do + doc <- openDoc path "haskell" + _ <- waitForDiagnosticsFrom doc + cas <- map fromAction <$> getAllCodeActions doc + liftIO $ "Add \"NamedFieldPuns\"" `elem` map (^. L.title) cas @? "Contains NamedFieldPuns code action" + executeCodeAction $ head cas + documentContents doc + + , pragmasGolden "append to existing pragmas" "AppendToExisting" $ \path -> do + doc <- openDoc path "haskell" + _ <- waitForDiagnosticsFrom doc + cas <- map fromAction <$> getAllCodeActions doc + liftIO $ "Add \"NamedFieldPuns\"" `elem` map (^. L.title) cas @? "Contains NamedFieldPuns code action" + executeCodeAction $ head cas + documentContents doc + + , pragmasGolden "before doc comments" "BeforeDocComment" $ \path -> do + doc <- openDoc path "haskell" + _ <- waitForDiagnosticsFrom doc + cas <- map fromAction <$> getAllCodeActions doc + liftIO $ "Add \"NamedFieldPuns\"" `elem` map (^. L.title) cas @? "Contains NamedFieldPuns code action" + executeCodeAction $ head cas + documentContents doc + + , pragmasGolden "before doc comments" "MissingSignatures" $ \path -> do + doc <- openDoc path "haskell" + _ <- waitForDiagnosticsFrom doc + cas <- map fromAction <$> getAllCodeActions doc + liftIO $ "Disable \"missing-signatures\" warnings" `elem` map (^. L.title) cas @? "Contains missing-signatures code action" + executeCodeAction $ head cas + documentContents doc + + , pragmasGolden "before doc comments" "UnusedImports" $ \path -> do + doc <- openDoc path "haskell" + _ <- waitForDiagnosticsFrom doc + cas <- map fromAction <$> getAllCodeActions doc + liftIO $ "Disable \"unused-imports\" warnings" `elem` map (^. L.title) cas @? "Contains unused-imports code action" + executeCodeAction $ head cas + documentContents doc + ] + +completionTests :: TestTree +completionTests = + testGroup "completions" + [ testCase "completes pragmas" $ runSessionWithServer pragmasPlugin testDirectory $ do + doc <- openDoc "Completion.hs" "haskell" + _ <- waitForDiagnostics + let te = TextEdit (Range (Position 0 4) (Position 0 34)) "" + _ <- applyEdit doc te + compls <- getCompletions doc (Position 0 4) + let item = head $ filter ((== "LANGUAGE") . (^. L.label)) compls + liftIO $ do + item ^. L.label @?= "LANGUAGE" + item ^. L.kind @?= Just CiKeyword + item ^. L.insertTextFormat @?= Just Snippet + item ^. L.insertText @?= Just "LANGUAGE ${1:extension} #-}" + + , testCase "completes pragmas no close" $ runSessionWithServer pragmasPlugin testDirectory $ do + doc <- openDoc "Completion.hs" "haskell" + let te = TextEdit (Range (Position 0 4) (Position 0 24)) "" + _ <- applyEdit doc te + compls <- getCompletions doc (Position 0 4) + let item = head $ filter ((== "LANGUAGE") . (^. L.label)) compls + liftIO $ do + item ^. L.label @?= "LANGUAGE" + item ^. L.kind @?= Just CiKeyword + item ^. L.insertTextFormat @?= Just Snippet + item ^. L.insertText @?= Just "LANGUAGE ${1:extension}" + + , testCase "completes options pragma" $ runSessionWithServer pragmasPlugin testDirectory $ do + doc <- openDoc "Completion.hs" "haskell" + _ <- waitForDiagnostics + let te = TextEdit (Range (Position 0 4) (Position 0 34)) "OPTIONS" + _ <- applyEdit doc te + compls <- getCompletions doc (Position 0 4) + let item = head $ filter ((== "OPTIONS_GHC") . (^. L.label)) compls + liftIO $ do + item ^. L.label @?= "OPTIONS_GHC" + item ^. L.kind @?= Just CiKeyword + item ^. L.insertTextFormat @?= Just Snippet + item ^. L.insertText @?= Just "OPTIONS_GHC -${1:option} #-}" + + , testCase "completes ghc options pragma values" $ runSessionWithServer pragmasPlugin testDirectory $ do + doc <- openDoc "Completion.hs" "haskell" + let te = TextEdit (Range (Position 0 0) (Position 0 0)) "{-# OPTIONS_GHC -Wno-red #-}\n" + _ <- applyEdit doc te + compls <- getCompletions doc (Position 0 24) + let item = head $ filter ((== "Wno-redundant-constraints") . (^. L.label)) compls + liftIO $ do + item ^. L.label @?= "Wno-redundant-constraints" + item ^. L.kind @?= Just CiKeyword + item ^. L.insertTextFormat @?= Nothing + item ^. L.insertText @?= Nothing + + , testCase "completes language extensions" $ runSessionWithServer pragmasPlugin testDirectory $ do + doc <- openDoc "Completion.hs" "haskell" + _ <- waitForDiagnostics + let te = TextEdit (Range (Position 0 24) (Position 0 31)) "" + _ <- applyEdit doc te + compls <- getCompletions doc (Position 0 24) + let item = head $ filter ((== "OverloadedStrings") . (^. L.label)) compls + liftIO $ do + item ^. L.label @?= "OverloadedStrings" + item ^. L.kind @?= Just CiKeyword + + , testCase "completes the Strict language extension" $ runSessionWithServer pragmasPlugin testDirectory $ do + doc <- openDoc "Completion.hs" "haskell" + _ <- waitForDiagnostics + let te = TextEdit (Range (Position 0 13) (Position 0 31)) "Str" + _ <- applyEdit doc te + compls <- getCompletions doc (Position 0 16) + let item = head $ filter ((== "Strict") . (^. L.label)) compls + liftIO $ do + item ^. L.label @?= "Strict" + item ^. L.kind @?= Just CiKeyword + + , testCase "completes No- language extensions" $ runSessionWithServer pragmasPlugin testDirectory $ do + doc <- openDoc "Completion.hs" "haskell" + _ <- waitForDiagnostics + let te = TextEdit (Range (Position 0 13) (Position 0 31)) "NoOverload" + _ <- applyEdit doc te + compls <- getCompletions doc (Position 0 23) + let item = head $ filter ((== "NoOverloadedStrings") . (^. L.label)) compls + liftIO $ do + item ^. L.label @?= "NoOverloadedStrings" + item ^. L.kind @?= Just CiKeyword + ] + +pragmasGolden :: TestName -> FilePath -> (FilePath -> Session T.Text) -> TestTree +pragmasGolden title path action = + goldenGitDiff title (testDirectory path <.> "expected.hs") + $ runSessionWithServer pragmasPlugin testDirectory + $ TL.encodeUtf8 . TL.fromStrict + <$> action (path <.> "hs") + +testDirectory :: FilePath +testDirectory = "test" "testdata" diff --git a/plugins/hls-pragmas-plugin/test/testdata/AfterShebang.expected.hs b/plugins/hls-pragmas-plugin/test/testdata/AfterShebang.expected.hs new file mode 100644 index 0000000000..674522f897 --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/AfterShebang.expected.hs @@ -0,0 +1,13 @@ +#! /usr/bin/env nix-shell +#! nix-shell --pure -i runghc -p "haskellPackages.ghcWithPackages (hp: with hp; [ turtle ])" +{-# LANGUAGE NamedFieldPuns #-} + +module AfterShebang where + +data Record = Record + { a :: Int, + b :: Double, + c :: String + } + +f Record{a, b} = a diff --git a/test/testdata/addPragmas/AfterShebang.hs b/plugins/hls-pragmas-plugin/test/testdata/AfterShebang.hs similarity index 100% rename from test/testdata/addPragmas/AfterShebang.hs rename to plugins/hls-pragmas-plugin/test/testdata/AfterShebang.hs diff --git a/plugins/hls-pragmas-plugin/test/testdata/AppendToExisting.expected.hs b/plugins/hls-pragmas-plugin/test/testdata/AppendToExisting.expected.hs new file mode 100644 index 0000000000..46c37a9ffa --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/AppendToExisting.expected.hs @@ -0,0 +1,12 @@ +-- | Doc before pragma +{-# OPTIONS_GHC -Wno-dodgy-imports #-} +{-# LANGUAGE NamedFieldPuns #-} +module AppendToExisting where + +data Record = Record + { a :: Int, + b :: Double, + c :: String + } + +f Record{a, b} = a diff --git a/test/testdata/addPragmas/AppendToExisting.hs b/plugins/hls-pragmas-plugin/test/testdata/AppendToExisting.hs similarity index 100% rename from test/testdata/addPragmas/AppendToExisting.hs rename to plugins/hls-pragmas-plugin/test/testdata/AppendToExisting.hs diff --git a/plugins/hls-pragmas-plugin/test/testdata/BeforeDocComment.expected.hs b/plugins/hls-pragmas-plugin/test/testdata/BeforeDocComment.expected.hs new file mode 100644 index 0000000000..e5201b9892 --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/BeforeDocComment.expected.hs @@ -0,0 +1,15 @@ +#! /usr/bin/env nix-shell +#! nix-shell --pure -i runghc -p "haskellPackages.ghcWithPackages (hp: with hp; [ turtle ])" +{-# LANGUAGE NamedFieldPuns #-} +-- | Doc Comment +{- Block -} + +module BeforeDocComment where + +data Record = Record + { a :: Int, + b :: Double, + c :: String + } + +f Record{a, b} = a diff --git a/test/testdata/addPragmas/BeforeDocComment.hs b/plugins/hls-pragmas-plugin/test/testdata/BeforeDocComment.hs similarity index 100% rename from test/testdata/addPragmas/BeforeDocComment.hs rename to plugins/hls-pragmas-plugin/test/testdata/BeforeDocComment.hs diff --git a/plugins/hls-pragmas-plugin/test/testdata/Completion.hs b/plugins/hls-pragmas-plugin/test/testdata/Completion.hs new file mode 100644 index 0000000000..9427f3dc03 --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/Completion.hs @@ -0,0 +1,9 @@ +{-# LANGUAGE OverloadedStrings #-} +import Data.Maybe +import qualified Data.List + +main :: IO () +main = putStrLn "hello" + +foo :: Either a b -> Either a b +foo = id diff --git a/plugins/hls-pragmas-plugin/test/testdata/MissingSignatures.expected.hs b/plugins/hls-pragmas-plugin/test/testdata/MissingSignatures.expected.hs new file mode 100644 index 0000000000..4db5b18f68 --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/MissingSignatures.expected.hs @@ -0,0 +1,3 @@ +{-# OPTIONS_GHC -Wall #-} +{-# OPTIONS_GHC -Wno-missing-signatures #-} +main = putStrLn "hello" diff --git a/plugins/hls-pragmas-plugin/test/testdata/MissingSignatures.hs b/plugins/hls-pragmas-plugin/test/testdata/MissingSignatures.hs new file mode 100644 index 0000000000..9d2f668112 --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/MissingSignatures.hs @@ -0,0 +1,2 @@ +{-# OPTIONS_GHC -Wall #-} +main = putStrLn "hello" diff --git a/plugins/hls-pragmas-plugin/test/testdata/NamedFieldPuns.expected.hs b/plugins/hls-pragmas-plugin/test/testdata/NamedFieldPuns.expected.hs new file mode 100644 index 0000000000..4de5a51e2b --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/NamedFieldPuns.expected.hs @@ -0,0 +1,10 @@ +{-# LANGUAGE NamedFieldPuns #-} +module NamedFieldPuns where + +data Record = Record + { a :: Int, + b :: Double, + c :: String + } + +f Record{a, b} = a diff --git a/test/testdata/addPragmas/NamedFieldPuns.hs b/plugins/hls-pragmas-plugin/test/testdata/NamedFieldPuns.hs similarity index 100% rename from test/testdata/addPragmas/NamedFieldPuns.hs rename to plugins/hls-pragmas-plugin/test/testdata/NamedFieldPuns.hs diff --git a/plugins/hls-pragmas-plugin/test/testdata/NeedsPragmas.expected.hs b/plugins/hls-pragmas-plugin/test/testdata/NeedsPragmas.expected.hs new file mode 100644 index 0000000000..668e381a9f --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/NeedsPragmas.expected.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE TypeSynonymInstances #-} +module NeedsPragmas where + +import GHC.Generics + +main = putStrLn "hello" + +type Foo = Int + +instance Show Foo where + show x = undefined + +instance Show (Int,String) where + show = undefined + +data FFF a = FFF Int String a + deriving (Generic,Functor,Traversable) diff --git a/test/testdata/addPragmas/NeedsPragmas.hs b/plugins/hls-pragmas-plugin/test/testdata/NeedsPragmas.hs similarity index 100% rename from test/testdata/addPragmas/NeedsPragmas.hs rename to plugins/hls-pragmas-plugin/test/testdata/NeedsPragmas.hs diff --git a/plugins/hls-pragmas-plugin/test/testdata/TypeApplications.expected.hs b/plugins/hls-pragmas-plugin/test/testdata/TypeApplications.expected.hs new file mode 100644 index 0000000000..cdbba9c0cb --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/TypeApplications.expected.hs @@ -0,0 +1,6 @@ +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +module TypeApplications where + +foo :: forall a. a -> a +foo = id @a diff --git a/test/testdata/addPragmas/TypeApplications.hs b/plugins/hls-pragmas-plugin/test/testdata/TypeApplications.hs similarity index 100% rename from test/testdata/addPragmas/TypeApplications.hs rename to plugins/hls-pragmas-plugin/test/testdata/TypeApplications.hs diff --git a/plugins/hls-pragmas-plugin/test/testdata/UnusedImports.expected.hs b/plugins/hls-pragmas-plugin/test/testdata/UnusedImports.expected.hs new file mode 100644 index 0000000000..a18d6959e9 --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/UnusedImports.expected.hs @@ -0,0 +1,7 @@ +{-# OPTIONS_GHC -Wall #-} +{-# OPTIONS_GHC -Wno-unused-imports #-} + + +module M where + +import Data.Functor diff --git a/plugins/hls-pragmas-plugin/test/testdata/UnusedImports.hs b/plugins/hls-pragmas-plugin/test/testdata/UnusedImports.hs new file mode 100644 index 0000000000..9d49c0c95d --- /dev/null +++ b/plugins/hls-pragmas-plugin/test/testdata/UnusedImports.hs @@ -0,0 +1,6 @@ +{-# OPTIONS_GHC -Wall #-} + + +module M where + +import Data.Functor diff --git a/test/testdata/addPragmas/hie.yaml b/plugins/hls-pragmas-plugin/test/testdata/hie.yaml similarity index 100% rename from test/testdata/addPragmas/hie.yaml rename to plugins/hls-pragmas-plugin/test/testdata/hie.yaml diff --git a/stack-8.10.2.yaml b/stack-8.10.2.yaml index eee5e16672..ef1eb8a5e7 100644 --- a/stack-8.10.2.yaml +++ b/stack-8.10.2.yaml @@ -21,6 +21,7 @@ packages: - ./plugins/hls-stylish-haskell-plugin - ./plugins/hls-floskell-plugin - ./plugins/hls-fourmolu-plugin + - ./plugins/hls-pragmas-plugin ghc-options: "$everything": -haddock diff --git a/stack-8.10.3.yaml b/stack-8.10.3.yaml index 04957ff3ae..cfc7c4699b 100644 --- a/stack-8.10.3.yaml +++ b/stack-8.10.3.yaml @@ -21,6 +21,7 @@ packages: - ./plugins/hls-stylish-haskell-plugin - ./plugins/hls-floskell-plugin - ./plugins/hls-fourmolu-plugin + - ./plugins/hls-pragmas-plugin ghc-options: "$everything": -haddock diff --git a/stack-8.10.4.yaml b/stack-8.10.4.yaml index a02346ae5b..f70dda59fc 100644 --- a/stack-8.10.4.yaml +++ b/stack-8.10.4.yaml @@ -21,6 +21,7 @@ packages: - ./plugins/hls-stylish-haskell-plugin - ./plugins/hls-floskell-plugin - ./plugins/hls-fourmolu-plugin + - ./plugins/hls-pragmas-plugin ghc-options: "$everything": -haddock diff --git a/stack-8.6.4.yaml b/stack-8.6.4.yaml index cf92e8810e..b8a8ce9eb5 100644 --- a/stack-8.6.4.yaml +++ b/stack-8.6.4.yaml @@ -22,6 +22,7 @@ packages: - ./plugins/hls-stylish-haskell-plugin - ./plugins/hls-floskell-plugin - ./plugins/hls-fourmolu-plugin + - ./plugins/hls-pragmas-plugin ghc-options: "$everything": -haddock diff --git a/stack-8.6.5.yaml b/stack-8.6.5.yaml index 94be6ebf8b..110e2ca671 100644 --- a/stack-8.6.5.yaml +++ b/stack-8.6.5.yaml @@ -21,6 +21,7 @@ packages: - ./plugins/hls-stylish-haskell-plugin - ./plugins/hls-floskell-plugin - ./plugins/hls-fourmolu-plugin + - ./plugins/hls-pragmas-plugin ghc-options: "$everything": -haddock diff --git a/stack-8.8.2.yaml b/stack-8.8.2.yaml index 82e34bff3d..e0ea2be6fc 100644 --- a/stack-8.8.2.yaml +++ b/stack-8.8.2.yaml @@ -21,6 +21,7 @@ packages: - ./plugins/hls-stylish-haskell-plugin - ./plugins/hls-floskell-plugin - ./plugins/hls-fourmolu-plugin + - ./plugins/hls-pragmas-plugin ghc-options: "$everything": -haddock diff --git a/stack-8.8.3.yaml b/stack-8.8.3.yaml index 78d46b7089..baf1d28b34 100644 --- a/stack-8.8.3.yaml +++ b/stack-8.8.3.yaml @@ -21,6 +21,7 @@ packages: - ./plugins/hls-stylish-haskell-plugin - ./plugins/hls-floskell-plugin - ./plugins/hls-fourmolu-plugin + - ./plugins/hls-pragmas-plugin ghc-options: "$everything": -haddock diff --git a/stack-8.8.4.yaml b/stack-8.8.4.yaml index 33838beffb..6b403b628b 100644 --- a/stack-8.8.4.yaml +++ b/stack-8.8.4.yaml @@ -21,6 +21,7 @@ packages: - ./plugins/hls-stylish-haskell-plugin - ./plugins/hls-floskell-plugin - ./plugins/hls-fourmolu-plugin + - ./plugins/hls-pragmas-plugin ghc-options: "$everything": -haddock diff --git a/stack.yaml b/stack.yaml index 90df02f69c..6958060f18 100644 --- a/stack.yaml +++ b/stack.yaml @@ -21,6 +21,7 @@ packages: - ./plugins/hls-stylish-haskell-plugin - ./plugins/hls-floskell-plugin - ./plugins/hls-fourmolu-plugin + - ./plugins/hls-pragmas-plugin ghc-options: "$everything": -haddock diff --git a/test/functional/Completion.hs b/test/functional/Completion.hs index 35af2387cb..2585a1e00d 100644 --- a/test/functional/Completion.hs +++ b/test/functional/Completion.hs @@ -76,108 +76,6 @@ tests = testGroup "completions" [ item ^. detail @?= Just "Data.List" item ^. kind @?= Just CiModule - , testCase "completes language extensions" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do - doc <- openDoc "Completion.hs" "haskell" - - _ <- waitForDiagnostics - - let te = TextEdit (Range (Position 0 24) (Position 0 31)) "" - _ <- applyEdit doc te - - compls <- getCompletions doc (Position 0 24) - let item = head $ filter ((== "OverloadedStrings") . (^. label)) compls - liftIO $ do - item ^. label @?= "OverloadedStrings" - item ^. kind @?= Just CiKeyword - - , testCase "completes the Strict language extension" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do - doc <- openDoc "Completion.hs" "haskell" - - _ <- waitForDiagnostics - - let te = TextEdit (Range (Position 0 13) (Position 0 31)) "Str" - _ <- applyEdit doc te - - compls <- getCompletions doc (Position 0 16) - let item = head $ filter ((== "Strict") . (^. label)) compls - liftIO $ do - item ^. label @?= "Strict" - item ^. kind @?= Just CiKeyword - - , testCase "completes No- language extensions" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do - doc <- openDoc "Completion.hs" "haskell" - - _ <- waitForDiagnostics - - let te = TextEdit (Range (Position 0 13) (Position 0 31)) "NoOverload" - _ <- applyEdit doc te - - compls <- getCompletions doc (Position 0 23) - let item = head $ filter ((== "NoOverloadedStrings") . (^. label)) compls - liftIO $ do - item ^. label @?= "NoOverloadedStrings" - item ^. kind @?= Just CiKeyword - - , testCase "completes pragmas" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do - doc <- openDoc "Completion.hs" "haskell" - - _ <- waitForDiagnostics - - let te = TextEdit (Range (Position 0 4) (Position 0 34)) "" - _ <- applyEdit doc te - - compls <- getCompletions doc (Position 0 4) - let item = head $ filter ((== "LANGUAGE") . (^. label)) compls - liftIO $ do - item ^. label @?= "LANGUAGE" - item ^. kind @?= Just CiKeyword - item ^. insertTextFormat @?= Just Snippet - item ^. insertText @?= Just "LANGUAGE ${1:extension} #-}" - - , testCase "completes pragmas no close" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do - doc <- openDoc "Completion.hs" "haskell" - - let te = TextEdit (Range (Position 0 4) (Position 0 24)) "" - _ <- applyEdit doc te - - compls <- getCompletions doc (Position 0 4) - let item = head $ filter ((== "LANGUAGE") . (^. label)) compls - liftIO $ do - item ^. label @?= "LANGUAGE" - item ^. kind @?= Just CiKeyword - item ^. insertTextFormat @?= Just Snippet - item ^. insertText @?= Just "LANGUAGE ${1:extension}" - - , testCase "completes options pragma" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do - doc <- openDoc "Completion.hs" "haskell" - - _ <- waitForDiagnostics - - let te = TextEdit (Range (Position 0 4) (Position 0 34)) "OPTIONS" - _ <- applyEdit doc te - - compls <- getCompletions doc (Position 0 4) - let item = head $ filter ((== "OPTIONS_GHC") . (^. label)) compls - liftIO $ do - item ^. label @?= "OPTIONS_GHC" - item ^. kind @?= Just CiKeyword - item ^. insertTextFormat @?= Just Snippet - item ^. insertText @?= Just "OPTIONS_GHC -${1:option} #-}" - - , testCase "completes ghc options pragma values" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do - doc <- openDoc "Completion.hs" "haskell" - - let te = TextEdit (Range (Position 0 0) (Position 0 0)) "{-# OPTIONS_GHC -Wno-red #-}\n" - _ <- applyEdit doc te - - compls <- getCompletions doc (Position 0 24) - let item = head $ filter ((== "Wno-redundant-constraints") . (^. label)) compls - liftIO $ do - item ^. label @?= "Wno-redundant-constraints" - item ^. kind @?= Just CiKeyword - item ^. insertTextFormat @?= Nothing - item ^. insertText @?= Nothing - , testCase "completes with no prefix" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do doc <- openDoc "Completion.hs" "haskell" diff --git a/test/functional/FunctionalCodeAction.hs b/test/functional/FunctionalCodeAction.hs index 80d7201905..13dd7eca67 100644 --- a/test/functional/FunctionalCodeAction.hs +++ b/test/functional/FunctionalCodeAction.hs @@ -1,7 +1,5 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module FunctionalCodeAction (tests) where @@ -21,7 +19,6 @@ import Test.Hls import Test.Hspec.Expectations import System.FilePath (()) -import System.IO.Extra (withTempDir) import Test.Hls.Command {-# ANN module ("HLint: ignore Reduce duplication"::String) #-} @@ -30,8 +27,6 @@ tests :: TestTree tests = testGroup "code actions" [ hlintTests , importTests - , missingPragmaTests - , disableWarningTests , packageTests , redundantImportTests , renameTests @@ -476,239 +471,6 @@ signatureTests = testGroup "missing top level signature code actions" [ liftIO $ T.lines contents @?= expected ] -missingPragmaTests :: TestTree -missingPragmaTests = testGroup "missing pragma warning code actions" [ - testCase "Adds TypeSynonymInstances pragma" $ do - runSession hlsCommand fullCaps "test/testdata/addPragmas" $ do - doc <- openDoc "NeedsPragmas.hs" "haskell" - - _ <- waitForDiagnosticsFromSource doc "typecheck" - cas <- map fromAction <$> getAllCodeActions doc - - liftIO $ "Add \"TypeSynonymInstances\"" `elem` map (^. L.title) cas @? "Contains TypeSynonymInstances code action" - liftIO $ "Add \"FlexibleInstances\"" `elem` map (^. L.title) cas @? "Contains FlexibleInstances code action" - - executeCodeAction $ head cas - - contents <- documentContents doc - - let expected = [ "{-# LANGUAGE TypeSynonymInstances #-}" - , "module NeedsPragmas where" - , "" - , "import GHC.Generics" - , "" - , "main = putStrLn \"hello\"" - , "" - , "type Foo = Int" - , "" - , "instance Show Foo where" - , " show x = undefined" - , "" - , "instance Show (Int,String) where" - , " show = undefined" - , "" - , "data FFF a = FFF Int String a" - , " deriving (Generic,Functor,Traversable)" - ] - - 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 ScopedTypeVariables #-}" - , "{-# LANGUAGE TypeApplications #-}" - , "module TypeApplications where" - , "" - , "foo :: forall a. a -> a" - , "foo = id @a" - ] - - liftIO $ T.lines contents @?= expected - , testCase "No duplication" $ do - runSession hlsCommand fullCaps "test/testdata/addPragmas" $ do - doc <- openDoc "NamedFieldPuns.hs" "haskell" - - _ <- waitForDiagnosticsFrom doc - cas <- map fromAction <$> getCodeActions doc (Range (Position 8 9) (Position 8 9)) - - liftIO $ length cas == 1 @? "Expected one code action, but got: " <> show cas - let ca = head cas - - liftIO $ (ca ^. L.title == "Add \"NamedFieldPuns\"") @? "NamedFieldPuns code action" - - executeCodeAction ca - - contents <- documentContents doc - - let expected = - [ "{-# LANGUAGE NamedFieldPuns #-}" - , "module NamedFieldPuns where" - , "" - , "data Record = Record" - , " { a :: Int," - , " b :: Double," - , " c :: String" - , " }" - , "" - , "f Record{a, b} = a" - ] - liftIO $ T.lines contents @?= expected - , testCase "After shebang" $ do - runSession hlsCommand fullCaps "test/testdata/addPragmas" $ do - doc <- openDoc "AfterShebang.hs" "haskell" - - _ <- waitForDiagnosticsFrom doc - cas <- map fromAction <$> getAllCodeActions doc - - liftIO $ "Add \"NamedFieldPuns\"" `elem` map (^. L.title) cas @? "Contains NamedFieldPuns code action" - - executeCodeAction $ head cas - - contents <- documentContents doc - - let expected = - [ "#! /usr/bin/env nix-shell" - , "#! nix-shell --pure -i runghc -p \"haskellPackages.ghcWithPackages (hp: with hp; [ turtle ])\"" - , "{-# LANGUAGE NamedFieldPuns #-}" - , "" - , "module AfterShebang where" - , "" - , "data Record = Record" - , " { a :: Int," - , " b :: Double," - , " c :: String" - , " }" - , "" - , "f Record{a, b} = a" - ] - - liftIO $ T.lines contents @?= expected - , testCase "Append to existing pragmas" $ do - runSession hlsCommand fullCaps "test/testdata/addPragmas" $ do - doc <- openDoc "AppendToExisting.hs" "haskell" - - _ <- waitForDiagnosticsFrom doc - cas <- map fromAction <$> getAllCodeActions doc - - liftIO $ "Add \"NamedFieldPuns\"" `elem` map (^. L.title) cas @? "Contains NamedFieldPuns code action" - - executeCodeAction $ head cas - - contents <- documentContents doc - - let expected = - [ "-- | Doc before pragma" - , "{-# OPTIONS_GHC -Wno-dodgy-imports #-}" - , "{-# LANGUAGE NamedFieldPuns #-}" - , "module AppendToExisting where" - , "" - , "data Record = Record" - , " { a :: Int," - , " b :: Double," - , " c :: String" - , " }" - , "" - , "f Record{a, b} = a" - ] - - liftIO $ T.lines contents @?= expected - , testCase "Before Doc Comments" $ do - runSession hlsCommand fullCaps "test/testdata/addPragmas" $ do - doc <- openDoc "BeforeDocComment.hs" "haskell" - - _ <- waitForDiagnosticsFrom doc - cas <- map fromAction <$> getAllCodeActions doc - - liftIO $ "Add \"NamedFieldPuns\"" `elem` map (^. L.title) cas @? "Contains NamedFieldPuns code action" - - executeCodeAction $ head cas - - contents <- documentContents doc - - let expected = - [ "#! /usr/bin/env nix-shell" - , "#! nix-shell --pure -i runghc -p \"haskellPackages.ghcWithPackages (hp: with hp; [ turtle ])\"" - , "{-# LANGUAGE NamedFieldPuns #-}" - , "-- | Doc Comment" - , "{- Block -}" - , "" - , "module BeforeDocComment where" - , "" - , "data Record = Record" - , " { a :: Int," - , " b :: Double," - , " c :: String" - , " }" - , "" - , "f Record{a, b} = a" - ] - - liftIO $ T.lines contents @?= expected - ] - -disableWarningTests :: TestTree -disableWarningTests = - testGroup "disable warnings" $ - [ - ( "missing-signatures" - , T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "main = putStrLn \"hello\"" - ] - , T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "{-# OPTIONS_GHC -Wno-missing-signatures #-}" - , "main = putStrLn \"hello\"" - ] - ) - , - ( "unused-imports" - , T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "" - , "" - , "module M where" - , "" - , "import Data.Functor" - ] - , T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "{-# OPTIONS_GHC -Wno-unused-imports #-}" - , "" - , "" - , "module M where" - , "" - , "import Data.Functor" - ] - ) - ] - <&> \(warning, initialContent, expectedContent) -> testSession (T.unpack warning) $ do - doc <- createDoc "Module.hs" "haskell" initialContent - _ <- waitForDiagnostics - codeActs <- mapMaybe caResultToCodeAct <$> getAllCodeActions doc - case find (\CodeAction{_title} -> _title == "Disable \"" <> warning <> "\" warnings") codeActs of - Nothing -> liftIO $ assertFailure "No code action with expected title" - Just action -> do - executeCodeAction action - contentAfterAction <- documentContents doc - liftIO $ expectedContent @=? contentAfterAction - where - caResultToCodeAct = \case - InL _ -> Nothing - InR c -> Just c - unusedTermTests :: TestTree unusedTermTests = testGroup "unused term code actions" [ ignoreTestBecause "no support for prefixing unused names with _" $ testCase "Prefixes with '_'" $ @@ -765,7 +527,3 @@ noLiteralCaps = def { C._textDocument = Just textDocumentCaps } where textDocumentCaps = def { C._codeAction = Just codeActionCaps } codeActionCaps = CodeActionClientCapabilities (Just True) Nothing Nothing Nothing Nothing Nothing Nothing - -testSession :: String -> Session () -> TestTree -testSession name s = testCase name $ withTempDir $ \dir -> - runSession hlsCommand fullCaps dir s