{-# LANGUAGE OverloadedStrings #-} module ContextSpec where import Test.Hspec import GHC ( tm_parsed_module ) import System.Directory import Haskell.Ide.Engine.PluginApi import Haskell.Ide.Engine.PluginsIdeMonads import Haskell.Ide.Engine.PluginUtils import Haskell.Ide.Engine.ModuleCache import Haskell.Ide.Engine.Context import TestUtils spec :: Spec spec = describe "Context of different cursor positions" $ do it "can set the module as type checked" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let arg = filePathToUri fp let res = IdeResultOk (Nothing :: Maybe Context) actual <- runSingle (IdePlugins mempty) fp $ do _ <- setTypecheckedModule arg return $ IdeResultOk Nothing actual `shouldBe` res it "module header context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just (ModuleContext "ExampleContext")) actual <- getContextAt fp (toPos (1, 10)) actual `shouldBe` res it "module export list context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just ExportContext) actual <- getContextAt fp (toPos (1, 24)) actual `shouldBe` res it "value context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just ValueContext) actual <- getContextAt fp (toPos (7, 6)) actual `shouldBe` res it "value addition context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just ValueContext) actual <- getContextAt fp (toPos (7, 12)) actual `shouldBe` res it "import context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just (ImportContext "Data.List")) actual <- getContextAt fp (toPos (3, 8)) actual `shouldBe` res it "import list context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just (ImportListContext "Data.List")) actual <- getContextAt fp (toPos (3, 20)) actual `shouldBe` res it "import hiding context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just (ImportHidingContext "Control.Monad")) actual <- getContextAt fp (toPos (4, 32)) actual `shouldBe` res it "function declaration context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just TypeContext) actual <- getContextAt fp (toPos (6, 1)) actual `shouldBe` res it "function signature context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just TypeContext) actual <- getContextAt fp (toPos (6, 8)) actual `shouldBe` res it "function definition context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just ValueContext) actual <- getContextAt fp (toPos (7, 1)) actual `shouldBe` res -- This is interesting, the context for this is assumed to be ValueContext -- although the cursor is at the signature of a function in a where clause. -- Reason is probably that we only traverse the AST until we know that -- that we are in a ValueContext, however, within a ValueContext, another -- TypeContext may arise, like in this case. it "inner function declaration context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just ValueContext) actual <- getContextAt fp (toPos (9, 10)) actual `shouldBe` res it "inner function value context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just ValueContext) actual <- getContextAt fp (toPos (10, 10)) actual `shouldBe` res -- Declare a datatype, is Nothing, could be DataContext it "data declaration context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk Nothing actual <- getContextAt fp (toPos (12, 8)) actual `shouldBe` res -- Define a datatype. it "data definition context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just TypeContext) actual <- getContextAt fp (toPos (12, 18)) actual `shouldBe` res -- Declaration of a class. Should be something with types. it "class declaration context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk Nothing actual <- getContextAt fp (toPos (15, 8)) actual `shouldBe` res -- Function signature in class declaration. -- Ought to be TypeContext it "class declaration function sig context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk Nothing actual <- getContextAt fp (toPos (16, 7)) actual `shouldBe` res it "instance declaration context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk Nothing actual <- getContextAt fp (toPos (18, 7)) actual `shouldBe` res -- Function definition in an instance declaration -- Should be ValueContext, but nothing is fine, too for now it "instance declaration function def context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk Nothing actual <- getContextAt fp (toPos (19, 6)) actual `shouldBe` res -- This seems plain wrong, if the cursor is on the String "deriving", -- we would expect the context to be DerivingContext, but it is not. -- May require investigation if this is important. it "deriving context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk Nothing actual <- getContextAt fp (toPos (13, 9)) actual `shouldBe` res -- Cursor is directly before the open parenthesis of a deriving clause. -- E.g. deriving (...) -- ^---- cursor is here -- Context is still Nothing. it "deriving parenthesis context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk Nothing actual <- getContextAt fp (toPos (13, 14)) actual `shouldBe` res -- Cursor is directly after the open parenthesis of a deriving clause. -- E.g. deriving (...) -- ^---- cursor is here -- Context is now Type. This makes sense, but an extension may be to be -- aware of the context of a deriving clause, thus offering only Type Classes -- as a completion. it "deriving parenthesis context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just TypeContext) actual <- getContextAt fp (toPos (13, 15)) actual `shouldBe` res it "deriving typeclass context" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk (Just TypeContext) actual <- getContextAt fp (toPos (13, 18)) actual `shouldBe` res -- Point at an empty line. -- There is no context it "nothing" $ withCurrentDirectory "./test/testdata/context" $ do fp <- makeAbsolute "./ExampleContext.hs" let res = IdeResultOk Nothing actual <- getContextAt fp (toPos (2, 1)) actual `shouldBe` res getContextAt :: FilePath -> Position -> IO (IdeResult (Maybe Context)) getContextAt fp pos = do let arg = filePathToUri fp runSingle (IdePlugins mempty) fp $ do _ <- setTypecheckedModule arg pluginGetFile "getContext: " arg $ \fp_ -> ifCachedModuleAndData fp_ (IdeResultOk Nothing) $ \tm _ () -> return $ IdeResultOk $ getContext pos (tm_parsed_module tm)