Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit 2a49384

Browse files
committed
Add different Contexts for Module, import etc...
1 parent ff9a5b2 commit 2a49384

File tree

6 files changed

+161
-1
lines changed

6 files changed

+161
-1
lines changed

haskell-ide-engine.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ test-suite unit-test
177177
main-is: Main.hs
178178
other-modules: ApplyRefactPluginSpec
179179
CodeActionsSpec
180+
ContextSpec
180181
DiffSpec
181182
ExtensibleStateSpec
182183
GhcModPluginSpec
@@ -196,6 +197,7 @@ test-suite unit-test
196197
, directory
197198
, filepath
198199
, free
200+
, ghc
199201
, haskell-ide-engine
200202
, haskell-lsp-types >= 0.15.0.0
201203
, hie-test-utils

hie-plugin-api/Haskell/Ide/Engine/Context.hs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
{-# LANGUAGE LambdaCase #-}
12
module Haskell.Ide.Engine.Context where
23

34
import Data.Generics
5+
import Data.List (find)
46
import Language.Haskell.LSP.Types
57
import GHC
68
import qualified GhcModCore as GM (GhcPs) -- for GHC 8.2.2
@@ -13,13 +15,37 @@ import Haskell.Ide.Engine.PluginUtils
1315
-- smarter code completion
1416
data Context = TypeContext
1517
| ValueContext
18+
| ModuleContext String
19+
| ImportContext String
20+
| ExportContext
1621
deriving (Show, Eq)
1722

1823
-- | Generates a map of where the context is a type and where the context is a value
1924
-- i.e. where are the value decls and the type decls
2025
getContext :: Position -> ParsedModule -> Maybe Context
21-
getContext pos pm = everything join (Nothing `mkQ` go `extQ` goInline) decl
26+
getContext pos pm
27+
| Just (L (RealSrcSpan r) modName) <- moduleHeader
28+
, pos `isInsideRange` r
29+
= Just (ModuleContext (moduleNameString modName))
30+
31+
| Just (L (RealSrcSpan r) _) <- exportList
32+
, pos `isInsideRange` r
33+
= Just ExportContext
34+
35+
| Just ctx <- everything join (Nothing `mkQ` go `extQ` goInline) decl
36+
= Just ctx
37+
38+
| Just (L _ impDecl) <- importRegion
39+
= Just (ImportContext (moduleNameString $ unLoc $ ideclName impDecl))
40+
41+
| otherwise
42+
= Nothing
43+
2244
where decl = hsmodDecls $ unLoc $ pm_parsed_source pm
45+
moduleHeader = hsmodName $ unLoc $ pm_parsed_source pm
46+
exportList = hsmodExports $ unLoc $ pm_parsed_source pm
47+
imports = hsmodImports $ unLoc $ pm_parsed_source pm
48+
2349
go :: LHsDecl GM.GhcPs -> Maybe Context
2450
go (L (RealSrcSpan r) (SigD {}))
2551
| pos `isInsideRange` r = Just TypeContext
@@ -37,3 +63,11 @@ getContext pos pm = everything join (Nothing `mkQ` go `extQ` goInline) decl
3763
join (Just x) _ = Just x
3864
p `isInsideRange` r = sp <= p && p <= ep
3965
where (sp, ep) = unpackRealSrcSpan r
66+
67+
importRegion = find
68+
(\case
69+
(L (RealSrcSpan r) _) -> pos `isInsideRange` r
70+
_ -> False
71+
)
72+
imports
73+

src/Haskell/Ide/Engine/LSP/Completions.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ getCompletions uri prefixInfo (WithSnippets withSnippets) =
373373
ctxCompls' = case context of
374374
TypeContext -> filter isTypeCompl compls
375375
ValueContext -> filter (not . isTypeCompl) compls
376+
_ -> []
376377
-- Add whether the text to insert has backticks
377378
ctxCompls = map (\comp -> comp { isInfix = infixCompls }) ctxCompls'
378379

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module ExampleContext (foo) where
2+
3+
import Data.List
4+
import Control.Monad
5+
6+
foo :: Int -> Int
7+
foo xs = xs + 1
8+

test/testdata/context/Foo/Bar.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module Foo.Bar where
2+
3+

test/unit/ContextSpec.hs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module ContextSpec where
3+
4+
5+
import Test.Hspec
6+
7+
import GHC ( tm_parsed_module )
8+
import System.Directory
9+
10+
import Haskell.Ide.Engine.PluginApi
11+
import Haskell.Ide.Engine.PluginsIdeMonads
12+
import Haskell.Ide.Engine.PluginUtils
13+
import Haskell.Ide.Engine.ModuleCache
14+
import Haskell.Ide.Engine.Context
15+
16+
import TestUtils
17+
18+
spec :: Spec
19+
spec = describe "Context of different cursor positions" $ do
20+
it "can set the module as type checked"
21+
$ withCurrentDirectory "./test/testdata/context"
22+
$ do
23+
fp <- makeAbsolute "./ExampleContext.hs"
24+
let arg = filePathToUri fp
25+
let res = IdeResultOk (Nothing :: Maybe Context)
26+
actual <- runSingle (IdePlugins mempty) $ do
27+
_ <- setTypecheckedModule arg
28+
return $ IdeResultOk Nothing
29+
30+
actual `shouldBe` res
31+
32+
it "module header context"
33+
$ withCurrentDirectory "./test/testdata/context"
34+
$ do
35+
fp_ <- makeAbsolute "./ExampleContext.hs"
36+
let res = IdeResultOk (Just (ModuleContext "ExampleContext"))
37+
38+
actual <- getContextAt fp_ (toPos (1, 10))
39+
40+
actual `shouldBe` res
41+
42+
43+
it "module export list context"
44+
$ withCurrentDirectory "./test/testdata/context"
45+
$ do
46+
fp_ <- makeAbsolute "./ExampleContext.hs"
47+
let res = IdeResultOk (Just ExportContext)
48+
actual <- getContextAt fp_ (toPos (1, 24))
49+
50+
actual `shouldBe` res
51+
52+
it "value context" $ withCurrentDirectory "./test/testdata/context" $ do
53+
fp_ <- makeAbsolute "./ExampleContext.hs"
54+
let res = IdeResultOk (Just ValueContext)
55+
actual <- getContextAt fp_ (toPos (7, 6))
56+
57+
actual `shouldBe` res
58+
59+
it "value context" $ withCurrentDirectory "./test/testdata/context" $ do
60+
fp_ <- makeAbsolute "./ExampleContext.hs"
61+
let res = IdeResultOk (Just ValueContext)
62+
actual <- getContextAt fp_ (toPos (7, 12))
63+
64+
actual `shouldBe` res
65+
66+
it "import context" $ withCurrentDirectory "./test/testdata/context" $ do
67+
fp_ <- makeAbsolute "./ExampleContext.hs"
68+
let res = IdeResultOk (Just (ImportContext "Data.List"))
69+
actual <- getContextAt fp_ (toPos (3, 8))
70+
71+
actual `shouldBe` res
72+
73+
it "function declaration context"
74+
$ withCurrentDirectory "./test/testdata/context"
75+
$ do
76+
fp_ <- makeAbsolute "./ExampleContext.hs"
77+
let res = IdeResultOk (Just TypeContext)
78+
actual <- getContextAt fp_ (toPos (6, 1))
79+
80+
actual `shouldBe` res
81+
82+
83+
it "function definition context"
84+
$ withCurrentDirectory "./test/testdata/context"
85+
$ do
86+
fp_ <- makeAbsolute "./ExampleContext.hs"
87+
let res = IdeResultOk (Just ValueContext)
88+
actual <- getContextAt fp_ (toPos (7, 1))
89+
actual `shouldBe` res
90+
91+
it "function signature context"
92+
$ withCurrentDirectory "./test/testdata/context"
93+
$ do
94+
fp_ <- makeAbsolute "./ExampleContext.hs"
95+
let res = IdeResultOk (Just TypeContext)
96+
actual <- getContextAt fp_ (toPos (6, 8))
97+
actual `shouldBe` res
98+
99+
it "nothing" $ withCurrentDirectory "./test/testdata/context" $ do
100+
fp_ <- makeAbsolute "./ExampleContext.hs"
101+
let res = IdeResultOk Nothing
102+
actual <- getContextAt fp_ (toPos (2, 1))
103+
actual `shouldBe` res
104+
105+
getContextAt :: [Char] -> Position -> IO (IdeResult (Maybe Context))
106+
getContextAt fp_ pos = do
107+
let arg = filePathToUri fp_
108+
runSingle (IdePlugins mempty) $ do
109+
_ <- setTypecheckedModule arg
110+
pluginGetFile "getContext: " arg $ \fp ->
111+
ifCachedModuleAndData fp (IdeResultOk Nothing) $ \tm _ () ->
112+
return $ IdeResultOk $ getContext pos (tm_parsed_module tm)

0 commit comments

Comments
 (0)