-
-
Notifications
You must be signed in to change notification settings - Fork 367
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
Add :info command in Eval plugin #1948
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,22 @@ | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE RecordWildCards #-} | ||
{-# LANGUAGE TypeApplications #-} | ||
{-# LANGUAGE ViewPatterns #-} | ||
|
||
module Main | ||
( main | ||
) where | ||
|
||
import Control.Lens (_Just, preview, view) | ||
import Control.Monad (when) | ||
import Control.Lens (_Just, preview, toListOf, view) | ||
import Data.Aeson (fromJSON) | ||
import Data.Aeson.Types (Result (Success)) | ||
import Data.List (isInfixOf) | ||
import Data.List.Extra (nubOrdOn) | ||
import qualified Ide.Plugin.Eval as Eval | ||
import Ide.Plugin.Eval.Types (EvalParams (..)) | ||
import Language.LSP.Types.Lens (command, range, title) | ||
import System.Directory (doesFileExist) | ||
import System.FilePath ((<.>), (</>)) | ||
import Ide.Plugin.Eval.Types (EvalParams (..), Section (..), | ||
testOutput) | ||
import Language.LSP.Types.Lens (arguments, command, range, title) | ||
import System.FilePath ((</>)) | ||
import Test.Hls | ||
|
||
main :: IO () | ||
|
@@ -107,11 +108,56 @@ tests = | |
] | ||
, goldenWithEval "Works with NoImplicitPrelude" "TNoImplicitPrelude" "hs" | ||
, goldenWithEval "Variable 'it' works" "TIt" "hs" | ||
|
||
, testGroup ":info command" | ||
[ testCase ":info reports type, constructors and instances" $ do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As suggested by @jneira, these tests now only look for the relevant infix strings (or their absence) |
||
[output] <- map (unlines . codeLensTestOutput) <$> evalLenses "TInfo.hs" | ||
"data Foo = Foo1 | Foo2" `isInfixOf` output @? "Output does not include Foo data declaration" | ||
"Eq Foo" `isInfixOf` output @? "Output does not include instance Eq Foo" | ||
"Ord Foo" `isInfixOf` output @? "Output does not include instance Ord Foo" | ||
not ("Baz Foo" `isInfixOf` output) @? "Output includes instance Baz Foo" | ||
, testCase ":info reports type, constructors and instances for multiple types" $ do | ||
[output] <- map (unlines . codeLensTestOutput) <$> evalLenses "TInfoMany.hs" | ||
"data Foo = Foo1 | Foo2" `isInfixOf` output @? "Output does not include Foo data declaration" | ||
"Eq Foo" `isInfixOf` output @? "Output does not include instance Eq Foo" | ||
"Ord Foo" `isInfixOf` output @? "Output does not include instance Ord Foo" | ||
not ("Baz Foo" `isInfixOf` output) @? "Output includes instance Baz Foo" | ||
"data Bar = Bar1 | Bar2 | Bar3" `isInfixOf` output @? "Output does not include Bar data declaration" | ||
"Eq Bar" `isInfixOf` output @? "Output does not include instance Eq Bar" | ||
"Ord Bar" `isInfixOf` output @? "Output does not include instance Ord Bar" | ||
not ("Baz Bar" `isInfixOf` output) @? "Output includes instance Baz Bar" | ||
, testCase ":info! reports type, constructors and unfiltered instances" $ do | ||
[output] <- map (unlines . codeLensTestOutput) <$> evalLenses "TInfoBang.hs" | ||
"data Foo = Foo1 | Foo2" `isInfixOf` output @? "Output does not include Foo data declaration" | ||
"Eq Foo" `isInfixOf` output @? "Output does not include instance Eq Foo" | ||
"Ord Foo" `isInfixOf` output @? "Output does not include instance Ord Foo" | ||
"Baz Foo" `isInfixOf` output @? "Output does not include instance Baz Foo" | ||
, testCase ":info! reports type, constructors and unfiltered instances for multiple types" $ do | ||
[output] <- map (unlines . codeLensTestOutput) <$> evalLenses "TInfoBangMany.hs" | ||
"data Foo = Foo1 | Foo2" `isInfixOf` output @? "Output does not include Foo data declaration" | ||
"Eq Foo" `isInfixOf` output @? "Output does not include instance Eq Foo" | ||
"Ord Foo" `isInfixOf` output @? "Output does not include instance Ord Foo" | ||
"Baz Foo" `isInfixOf` output @? "Output does not include instance Baz Foo" | ||
"data Bar = Bar1 | Bar2 | Bar3" `isInfixOf` output @? "Output does not include Bar data declaration" | ||
"Eq Bar" `isInfixOf` output @? "Output does not include instance Eq Bar" | ||
"Ord Bar" `isInfixOf` output @? "Output does not include instance Ord Bar" | ||
"Baz Bar" `isInfixOf` output @? "Output does not include instance Baz Bar" | ||
, testCase ":i behaves exactly the same as :info" $ do | ||
[output] <- map (unlines . codeLensTestOutput) <$> evalLenses "TI_Info.hs" | ||
"data Foo = Foo1 | Foo2" `isInfixOf` output @? "Output does not include Foo data declaration" | ||
"Eq Foo" `isInfixOf` output @? "Output does not include instance Eq Foo" | ||
"Ord Foo" `isInfixOf` output @? "Output does not include instance Ord Foo" | ||
not ("Baz Foo" `isInfixOf` output) @? "Output includes instance Baz Foo" | ||
] | ||
] | ||
|
||
goldenWithEval :: TestName -> FilePath -> FilePath -> TestTree | ||
goldenWithEval title path ext = goldenWithHaskellDoc evalPlugin title testDataDir path "expected" ext $ \doc -> do | ||
-- Execute lenses backwards, to avoid affecting their position in the source file | ||
goldenWithEval title path ext = | ||
goldenWithHaskellDoc evalPlugin title testDataDir path "expected" ext executeLensesBackwards | ||
|
||
-- | Execute lenses backwards, to avoid affecting their position in the source file | ||
executeLensesBackwards :: TextDocumentIdentifier -> Session () | ||
executeLensesBackwards doc = do | ||
codeLenses <- reverse <$> getCodeLenses doc | ||
-- liftIO $ print codeLenses | ||
|
||
|
@@ -133,5 +179,19 @@ executeCmd cmd = do | |
-- liftIO $ print _resp | ||
pure () | ||
|
||
evalLenses :: FilePath -> IO [CodeLens] | ||
evalLenses path = runSessionWithServer evalPlugin testDataDir $ do | ||
doc <- openDoc path "haskell" | ||
executeLensesBackwards doc | ||
getCodeLenses doc | ||
|
||
codeLensTestOutput :: CodeLens -> [String] | ||
codeLensTestOutput codeLens = do | ||
CodeLens { _command = Just command } <- [codeLens] | ||
Command { _arguments = Just (List args) } <- [command] | ||
Success EvalParams { sections = sections } <- fromJSON @EvalParams <$> args | ||
Section { sectionTests = sectionTests } <- sections | ||
testOutput =<< sectionTests | ||
|
||
testDataDir :: FilePath | ||
testDataDir = "test" </> "testdata" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
packages: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved the |
||
testdata/ | ||
info-util/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
module InfoUtil | ||
( Eq | ||
, Ord | ||
, Foo (..) | ||
, Bar (..) | ||
, Baz | ||
) | ||
where | ||
|
||
import Prelude (Eq, Ord) | ||
|
||
data Foo = Foo1 | Foo2 | ||
deriving (Eq, Ord) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I ended up dropping the |
||
|
||
data Bar = Bar1 | Bar2 | Bar3 | ||
deriving (Eq, Ord) | ||
|
||
class Baz t | ||
instance Baz Foo | ||
instance Baz Bar |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
name: info-util | ||
version: 0.1.0.0 | ||
-- synopsis: | ||
-- description: | ||
license: BSD3 | ||
author: Author name here | ||
maintainer: [email protected] | ||
copyright: 2017 Author name here | ||
category: Web | ||
build-type: Simple | ||
cabal-version: >=1.10 | ||
|
||
library | ||
exposed-modules: | ||
InfoUtil | ||
build-depends: base >= 4.7 && < 5 | ||
default-language: Haskell2010 | ||
ghc-options: -Wall -fwarn-unused-imports |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module TI_Info (Eq, Ord, Foo) where | ||
|
||
import InfoUtil (Eq, Ord, Foo) | ||
|
||
-- >>> :i Foo |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module TInfo (Eq, Ord, Foo) where | ||
|
||
import InfoUtil (Eq, Ord, Foo) | ||
|
||
-- >>> :info Foo |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module TInfoBang (Eq, Ord, Foo) where | ||
|
||
import InfoUtil (Eq, Ord, Foo) | ||
|
||
-- >>> :info! Foo |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module TInfoBangMany (Eq, Ord, Foo, Bar) where | ||
|
||
import InfoUtil (Eq, Ord, Foo, Bar) | ||
|
||
-- >>> :info! Foo Bar |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module TInfoMany (Eq, Ord, Foo, Bar) where | ||
|
||
import InfoUtil (Eq, Ord, Foo, Bar) | ||
|
||
-- >>> :info Foo Bar |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could always sort the
filtered
children before printing to ensure that the output is deterministicThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, the problem is the
[ClsInst]
part of the tuple returned byGHC.getInfo
. Sadly,ClsInst
doesn't have anOrd
instance, and I didn't want to impose any custom ordering