Skip to content

Implements :type [+v/+d] in Eval Plugin #361

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

Merged
merged 14 commits into from
Aug 29, 2020
70 changes: 56 additions & 14 deletions src/Ide/Plugin/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import Development.IDE.Types.Location (toNormalizedFilePath',
uriToFilePath')
import DynamicLoading (initializePlugins)
import DynFlags (targetPlatform)
import GHC (DynFlags, ExecResult (..), GeneralFlag (Opt_IgnoreHpcChanges, Opt_IgnoreOptimChanges, Opt_ImplicitImportQualified),
import GHC (TcRnExprMode(..), DynFlags, ExecResult (..), GeneralFlag (Opt_IgnoreHpcChanges, Opt_IgnoreOptimChanges, Opt_ImplicitImportQualified),
GhcLink (LinkInMemory),
GhcMode (CompManager),
HscTarget (HscInterpreted),
Expand Down Expand Up @@ -87,7 +87,11 @@ import Control.DeepSeq ( NFData
, deepseq
)
import Outputable (Outputable(ppr), showSDoc)
import Control.Applicative ((<|>))
import Data.Char (isSpace)
import Control.Arrow (Arrow(second))
import GHC (Ghc)
import Type.Reflection (Typeable)
import GHC (exprType)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's reorganise these imports please

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pushed the commit that reorganises import list.
Anyway, is there any standard formatter and their configuration file in HLS, that could take care of import lists?
It seems ormolu doesn't reorganise import list.


descriptor :: PluginId -> PluginDescriptor
descriptor plId =
Expand Down Expand Up @@ -247,18 +251,8 @@ done, we want to switch back to GhcSessionDeps:

df <- liftIO $ evalGhcEnv hscEnv' getSessionDynFlags
let eval (stmt, l)
| let stmt0 = T.strip $ T.pack stmt -- For stripping and de-prefixing
, Just (reduce, type_) <-
(True,) <$> T.stripPrefix ":kind! " stmt0
<|> (False,) <$> T.stripPrefix ":kind " stmt0
= do
let input = T.strip type_
(ty, kind) <- typeKind reduce $ T.unpack input
pure $ Just
$ T.unlines
$ map ("-- " <>)
$ (input <> " :: " <> T.pack (showSDoc df $ ppr kind))
: [ "= " <> T.pack (showSDoc df $ ppr ty) | reduce]
| Just (cmd, arg) <- parseGhciLikeCmd $ T.pack stmt
= evalGhciLikeCmd cmd arg
| isStmt df stmt = do
-- set up a custom interactive print function
liftIO $ writeFile temp ""
Expand Down Expand Up @@ -309,6 +303,54 @@ done, we want to switch back to GhcSessionDeps:

return (WorkspaceApplyEdit, ApplyWorkspaceEditParams workspaceEdits)

evalGhciLikeCmd :: Text -> Text -> Ghc (Maybe Text)
evalGhciLikeCmd cmd arg = do
df <- getSessionDynFlags
let tppr = T.pack . showSDoc df . ppr
case cmd of
"kind" -> do
let input = T.strip arg
(_, kind) <- typeKind False $ T.unpack input
pure $ Just $ "-- " <> input <> " :: " <> tppr kind <> "\n"
"kind!" -> do
let input = T.strip arg
(ty, kind) <- typeKind True $ T.unpack input
pure
$ Just
$ T.unlines
$ map ("-- " <>)
[ input <> " :: " <> tppr kind
, "= " <> tppr ty
]
"type" -> do
let (emod, expr) = parseExprMode arg
ty <- exprType emod $ T.unpack expr
pure $ Just $
"-- " <> expr <> " :: " <> tppr ty <> "\n"
_ -> E.throw $ GhciLikeCmdNotImplemented cmd arg

parseExprMode :: Text -> (TcRnExprMode, T.Text)
parseExprMode rawArg =
case T.break isSpace rawArg of
("+v", rest) -> (TM_NoInst, T.strip rest)
("+d", rest) -> (TM_Default, T.strip rest)
_ -> (TM_Inst, rawArg)

data GhciLikeCmdException = GhciLikeCmdNotImplemented Text Text
deriving (Typeable)

instance Show GhciLikeCmdException where
showsPrec _ (GhciLikeCmdNotImplemented cmd _arg) =
showString "unknown command '" .
showString (T.unpack cmd) . showChar '\''

instance E.Exception GhciLikeCmdException

parseGhciLikeCmd :: Text -> Maybe (Text, Text)
parseGhciLikeCmd input = do
(':', rest) <- T.uncons $ T.stripStart input
pure $ second T.strip $ T.break isSpace rest

strictTry :: NFData b => IO b -> IO (Either String b)
strictTry op = E.catch
(op >>= \v -> return $! Right $! deepseq v v)
Expand Down
12 changes: 12 additions & 0 deletions test/functional/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ tests = testGroup
, testCase "Shows a kind with :kind" $ goldenTest "T12.hs"
, testCase "Reports an error for an incorrect type with :kind"
$ goldenTest "T13.hs"
, testCase "Returns a fully-instantiated type for :type"
$ goldenTest "T14.hs"
, testCase "Returns an uninstantiated type for :type +v, admitting multiple whitespaces around arguments"
$ goldenTest "T15.hs"
, testCase "Returns defaulted type for :type +d, admitting multiple whitespaces around arguments"
$ goldenTest "T16.hs"
, testCase ":type reports an error when given with unknown +x option"
$ goldenTest "T17.hs"
, testCase "Reports an error when given with unknown command"
$ goldenTest "T18.hs"
, testCase "Returns defaulted type for :type +d reflecting the default declaration specified in the >>> prompt"
$ goldenTest "T19.hs"
]

goldenTest :: FilePath -> IO ()
Expand Down
7 changes: 7 additions & 0 deletions test/testdata/eval/T14.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{-# LANGUAGE TypeApplications #-}
module T14 where

foo :: Show a => a -> String
foo = show

-- >>> :type foo @Int
8 changes: 8 additions & 0 deletions test/testdata/eval/T14.hs.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{-# LANGUAGE TypeApplications #-}
module T14 where

foo :: Show a => a -> String
foo = show

-- >>> :type foo @Int
-- foo @Int :: Int -> String
7 changes: 7 additions & 0 deletions test/testdata/eval/T15.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{-# LANGUAGE TypeApplications #-}
module T15 where

foo :: Show a => a -> String
foo = show

-- >>> :type +v foo @Int
8 changes: 8 additions & 0 deletions test/testdata/eval/T15.hs.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{-# LANGUAGE TypeApplications #-}
module T15 where

foo :: Show a => a -> String
foo = show

-- >>> :type +v foo @Int
-- foo @Int :: Show Int => Int -> String
3 changes: 3 additions & 0 deletions test/testdata/eval/T16.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module T16 where

-- >>> :type +d 40+ 2
4 changes: 4 additions & 0 deletions test/testdata/eval/T16.hs.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module T16 where

-- >>> :type +d 40+ 2
-- 40+ 2 :: Integer
3 changes: 3 additions & 0 deletions test/testdata/eval/T17.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module T17 where

-- >>> :type +no 42
4 changes: 4 additions & 0 deletions test/testdata/eval/T17.hs.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module T17 where

-- >>> :type +no 42
-- parse error on input ‘+’
4 changes: 4 additions & 0 deletions test/testdata/eval/T18.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{-# LANGUAGE TypeApplications #-}
module T18 where

-- >>> :noooop foo bar
5 changes: 5 additions & 0 deletions test/testdata/eval/T18.hs.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{-# LANGUAGE TypeApplications #-}
module T18 where

-- >>> :noooop foo bar
-- unknown command 'noooop'
6 changes: 6 additions & 0 deletions test/testdata/eval/T19.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module T19 where
import Data.Word (Word)
type W = Word

-- >>> default (Word)
-- >>> :type +d 40+ 2
7 changes: 7 additions & 0 deletions test/testdata/eval/T19.hs.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module T19 where
import Data.Word (Word)
type W = Word

-- >>> default (Word)
-- >>> :type +d 40+ 2
-- 40+ 2 :: Word