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

Commit b56ea38

Browse files
authored
Merge pull request #1268 from alanz/reinstate-pr-1237
Revert "Revert "Merge pull request #1237 from fendor/add-package-tests""
2 parents 7b8c9fb + 53f3b37 commit b56ea38

File tree

22 files changed

+639
-47
lines changed

22 files changed

+639
-47
lines changed

haskell-ide-engine.cabal

+2-1
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,11 @@ test-suite unit-test
173173
DiffSpec
174174
ExtensibleStateSpec
175175
GhcModPluginSpec
176-
LiquidSpec
177176
HaRePluginSpec
178177
HooglePluginSpec
179178
JsonSpec
179+
LiquidSpec
180+
PackagePluginSpec
180181
Spec
181182
-- Technically cabal-helper should be a 'run-tool-depends', but that doesn't exist yet
182183
build-tool-depends: cabal-helper:cabal-helper-main, hspec-discover:hspec-discover

src/Haskell/Ide/Engine/Plugin/Package.hs

+42-16
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ data AddParams = AddParams
6767
{ rootDirParam :: FilePath -- ^ The root directory.
6868
, fileParam :: ModulePath -- ^ A path to a module inside the
6969
-- library/executable/test-suite you want to
70-
-- add the package to. May be a realtive oir
70+
-- add the package to. May be a relative or
7171
-- absolute path, thus, must be normalised.
7272
, packageParam :: Package -- ^ The name of the package to add.
7373
}
@@ -76,7 +76,7 @@ data AddParams = AddParams
7676
-- | FilePath to a cabal package description file.
7777
type CabalFilePath = FilePath
7878
-- | FilePath to a package.yaml package description file.
79-
type PackageYamlFilePath = FilePath
79+
type HpackFilePath = FilePath
8080
-- | FilePath to a module within the project.
8181
-- May be used to establish what component the dependency shall be added to.
8282
type ModulePath = FilePath
@@ -88,8 +88,14 @@ type Package = T.Text
8888
-- Supported are `*.cabal` and `package.yaml` specifications.
8989
-- Moreover, may fail with an IOException in case of a filesystem problem.
9090
addCmd :: CommandFunc AddParams J.WorkspaceEdit
91-
addCmd = CmdSync $ \(AddParams rootDir modulePath pkg) -> do
91+
addCmd = CmdSync addCmd'
9292

93+
-- | Add a package to the project's dependencies.
94+
-- May fail if no project dependency specification can be found.
95+
-- Supported are `*.cabal` and `package.yaml` specifications.
96+
-- Moreover, may fail with an IOException in case of a filesystem problem.
97+
addCmd' :: AddParams -> IdeGhcM (IdeResult J.WorkspaceEdit)
98+
addCmd' (AddParams rootDir modulePath pkg) = do
9399
packageType <- liftIO $ findPackageType rootDir
94100
fileMap <- GM.mkRevRedirMapFunc
95101

@@ -105,9 +111,10 @@ addCmd = CmdSync $ \(AddParams rootDir modulePath pkg) -> do
105111
liftToGhc $ editHpackPackage absFp relModulePath pkg
106112
NoPackage -> return $ IdeResultFail (IdeError PluginError "No package.yaml or .cabal found" Null)
107113

108-
data PackageType = CabalPackage FilePath -- ^ Location of Cabal File.
109-
| HpackPackage FilePath -- ^ Location of `package.yaml`
114+
data PackageType = CabalPackage FilePath -- ^ Location of Cabal File. May be relative.
115+
| HpackPackage FilePath -- ^ Location of `package.yaml`. May be relative.
110116
| NoPackage -- ^ No package format has been found.
117+
deriving (Show, Eq)
111118

112119
-- | Find the package type the project with the given root uses.
113120
-- Might have weird results if there is more than one cabal package specification
@@ -129,12 +136,13 @@ findPackageType rootDir = do
129136
return $ fromMaybe NoPackage $ asum [HpackPackage <$> mHpack, CabalPackage <$> mCabal]
130137

131138
-- | Edit a hpack package to add the given package to the package.yaml.
139+
-- If package.yaml is not in an expected format, will fail fatally.
132140
--
133141
-- Currently does not preserve format.
134142
-- Keep an eye out on this other GSOC project!
135143
-- https://github.com/wisn/format-preserving-yaml
136-
editHpackPackage :: PackageYamlFilePath -- ^ Path to the package.yaml file
137-
-- containing the package description.
144+
editHpackPackage :: HpackFilePath -- ^ Path to the package.yaml file
145+
-- containing the package description.
138146
-> ModulePath -- ^ Path to the module where the command has
139147
-- been issued in.
140148
-- Used to find out what component the
@@ -148,19 +156,29 @@ editHpackPackage fp modulePath pkgName = do
148156

149157
case Y.decodeThrow contents :: Maybe Object of
150158
Just obj -> do
159+
-- Map over all major components, such as "executable", "executables",
160+
-- "tests" and "benchmarks". Note, that "library" is a major component,
161+
-- but its structure is different and can not be mapped over in the same way.
162+
--
163+
-- Only adds the package if the declared "source-dirs" field is part of the
164+
-- module path, or if no "source-dirs" is declared.
151165
let compsMapped = mapComponentTypes (ensureObject $ mapComponents (ensureObject $ mapCompDependencies addDep)) obj
152166

153-
let addDepToMainLib = fromMaybe True $ do
154-
Object lib <- HM.lookup "library" compsMapped
155-
sourceDirs <- HM.lookup "source-dirs" lib
156-
return $ isInSourceDir sourceDirs
167+
-- Is there a global "dependencies" yaml object?
168+
let addDepToMainDep = fromMaybe False $ do
169+
Array _ <- HM.lookup "dependencies" compsMapped
170+
return True
157171

158-
let newPkg = if addDepToMainLib
159-
then mapMainDependencies addDep compsMapped
160-
else compsMapped
172+
-- Either add the package to only the top-level "dependencies",
173+
-- or to all main components of which the given module is part of.
174+
let newPkg
175+
| addDepToMainDep = mapMainDependencies addDep obj
176+
-- Map over the library component at last, since it has different structure.
177+
| otherwise = mapLibraryDependency addDep compsMapped
161178

162-
newPkgText = T.decodeUtf8 $ Y.encode newPkg
179+
let newPkgText = T.decodeUtf8 $ Y.encode newPkg
163180

181+
-- Construct the WorkSpaceEdit
164182
let numOldLines = length $ T.lines $ T.decodeUtf8 contents
165183
range = J.Range (J.Position 0 0) (J.Position numOldLines 0)
166184
textEdit = J.TextEdit range newPkgText
@@ -179,10 +197,18 @@ editHpackPackage fp modulePath pkgName = do
179197

180198
mapMainDependencies :: (Value -> Value) -> Object -> Object
181199
mapMainDependencies f o =
182-
let g "dependencies" = f
200+
let g :: T.Text -> Value -> Value
201+
g "dependencies" = f
183202
g _ = id
184203
in HM.mapWithKey g o
185204

205+
mapLibraryDependency :: (Value -> Value) -> Object -> Object
206+
mapLibraryDependency f o =
207+
let g :: T.Text -> Value -> Value
208+
g "library" (Y.Object o') = Y.Object (mapCompDependencies f o')
209+
g _ x = x
210+
in HM.mapWithKey g o
211+
186212
mapComponentTypes :: (Value -> Value) -> Object -> Object
187213
mapComponentTypes f o =
188214
let g "executables" = f

test/functional/FunctionalCodeActionsSpec.hs

+40-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE OverloadedStrings #-}
2+
{-# LANGUAGE CPP #-}
23

34
module FunctionalCodeActionsSpec where
45

@@ -179,7 +180,9 @@ spec = describe "code actions" $ do
179180
]
180181
]
181182
describe "add package suggestions" $ do
182-
it "adds to .cabal files" $ runSession hieCommand fullCaps "test/testdata/addPackageTest/cabal" $ do
183+
-- Only execute this test with ghc 8.4.4, below seems to be broken in the package.
184+
#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,4,0,0)))
185+
it "adds to .cabal files" $ runSession hieCommand fullCaps "test/testdata/addPackageTest/cabal-exe" $ do
183186
doc <- openDoc "AddPackage.hs" "haskell"
184187

185188
-- ignore the first empty hlint diagnostic publish
@@ -203,9 +206,9 @@ spec = describe "code actions" $ do
203206

204207
contents <- getDocumentEdit . TextDocumentIdentifier =<< getDocUri "add-package-test.cabal"
205208
liftIO $ T.lines contents `shouldSatisfy` \x -> any (\l -> "text -any" `T.isSuffixOf` (x !! l)) [15, 16]
206-
209+
#endif
207210
it "adds to hpack package.yaml files" $
208-
runSession hieCommand fullCaps "test/testdata/addPackageTest/hpack" $ do
211+
runSession hieCommand fullCaps "test/testdata/addPackageTest/hpack-exe" $ do
209212
doc <- openDoc "app/Asdf.hs" "haskell"
210213

211214
-- ignore the first empty hlint diagnostic publish
@@ -229,9 +232,35 @@ spec = describe "code actions" $ do
229232

230233
contents <- getDocumentEdit . TextDocumentIdentifier =<< getDocUri "package.yaml"
231234
liftIO $ do
232-
T.lines contents !! 33 `shouldSatisfy` T.isSuffixOf "zlib"
233-
T.lines contents !! 12 `shouldNotSatisfy` T.isSuffixOf "zlib"
234-
T.lines contents !! 13 `shouldNotSatisfy` T.isSuffixOf "zlib"
235+
T.lines contents !! 3 `shouldSatisfy` T.isSuffixOf "zlib"
236+
T.lines contents !! 21 `shouldNotSatisfy` T.isSuffixOf "zlib"
237+
238+
it "adds to hpack package.yaml files if both are present" $
239+
runSession hieCommand fullCaps "test/testdata/addPackageTest/hybrid-exe" $ do
240+
doc <- openDoc "app/Asdf.hs" "haskell"
241+
242+
-- ignore the first empty hlint diagnostic publish
243+
[_,diag:_] <- count 2 waitForDiagnostics
244+
245+
let preds = [ T.isPrefixOf "Could not load module ‘Codec.Compression.GZip’"
246+
, T.isPrefixOf "Could not find module ‘Codec.Compression.GZip’"
247+
]
248+
in liftIO $ diag ^. L.message `shouldSatisfy` \x -> any (\f -> f x) preds
249+
250+
mActions <- getAllCodeActions doc
251+
let allActions = map fromAction mActions
252+
action = head allActions
253+
254+
liftIO $ do
255+
action ^. L.title `shouldBe` "Add zlib as a dependency"
256+
forM_ allActions $ \a -> a ^. L.kind `shouldBe` Just CodeActionQuickFix
257+
forM_ allActions $ \a -> a ^. L.command . _Just . L.command `shouldSatisfy` T.isSuffixOf "package:add"
258+
259+
executeCodeAction action
260+
261+
contents <- getDocumentEdit . TextDocumentIdentifier =<< getDocUri "package.yaml"
262+
liftIO $
263+
T.lines contents !! 21 `shouldSatisfy` T.isSuffixOf "zlib"
235264

236265
-- -----------------------------------
237266

@@ -318,10 +347,11 @@ spec = describe "code actions" $ do
318347

319348
contents <- documentContents doc
320349

321-
liftIO $ contents `shouldBe`
322-
"module TypedHoles where\n\
323-
\foo :: [Int] -> Int\n\
324-
\foo x = " <> suggestion
350+
liftIO $ contents `shouldBe` T.concat
351+
[ "module TypedHoles where\n"
352+
, "foo :: [Int] -> Int\n"
353+
, "foo x = " <> suggestion
354+
]
325355

326356
it "shows more suggestions" $
327357
runSession hieCommand fullCaps "test/testdata" $ do
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: add-package-test
2+
version: 0.1.0.0
3+
license: BSD3
4+
author: Luke Lau
5+
maintainer: [email protected]
6+
build-type: Simple
7+
extra-source-files: ChangeLog.md
8+
cabal-version: >=1.10
9+
10+
executable AddPackage
11+
exposed-modules: ./.
12+
main-is: AddPackage.hs
13+
build-depends: base >=4.7 && <5
14+
default-language: Haskell2010
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module AddPackage where
2+
3+
import Data.Text
4+
foo = pack "I'm a Text"

test/testdata/addPackageTest/hpack/package.yaml renamed to test/testdata/addPackageTest/hpack-exe/package.yaml

+1-15
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ description: Please see the README on GitHub at <https://github.com/gith
2222
dependencies:
2323
- base >= 4.7 && < 5
2424

25-
library:
26-
source-dirs: src
27-
2825
executables:
2926
asdf-exe:
3027
main: Main.hs
@@ -34,15 +31,4 @@ executables:
3431
- -rtsopts
3532
- -with-rtsopts=-N
3633
dependencies:
37-
- asdf
38-
39-
tests:
40-
asdf-test:
41-
main: Spec.hs
42-
source-dirs: test
43-
ghc-options:
44-
- -threaded
45-
- -rtsopts
46-
- -with-rtsopts=-N
47-
dependencies:
48-
- asdf
34+
- asdf
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
3+
module Asdf where
4+
5+
import Codec.Compression.GZip
6+
7+
main = return $ compress "hello"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: asdf
2+
version: 0.1.0.0
3+
github: "githubuser/asdf"
4+
license: BSD3
5+
author: "Author name here"
6+
maintainer: "[email protected]"
7+
copyright: "2018 Author name here"
8+
9+
extra-source-files:
10+
- README.md
11+
- ChangeLog.md
12+
13+
# Metadata used when publishing your package
14+
# synopsis: Short description of your package
15+
# category: Web
16+
17+
# To avoid duplicated efforts in documentation and dealing with the
18+
# complications of embedding Haddock markup inside cabal files, it is
19+
# common to point users to the README.md file.
20+
description: Please see the README on GitHub at <https://github.com/githubuser/asdf#readme>
21+
22+
library:
23+
source-dirs: app
24+
dependencies:
25+
- base >= 4.7 && < 5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import Data.Text
2+
foo = pack "I'm a Text"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
3+
import Codec.Compression.GZip
4+
5+
main = return $ compress "hello"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
cabal-version: 1.12
2+
3+
-- This file has been generated from package.yaml by hpack version 0.31.2.
4+
--
5+
-- see: https://github.com/sol/hpack
6+
--
7+
-- hash: a63a1c272a979a805027c5855cbe062ec4698b6ea6dbe59dd5f7aa34b15656a6
8+
9+
name: asdf
10+
version: 0.1.0.0
11+
description: Please see the README on GitHub at <https://github.com/githubuser/asdf#readme>
12+
homepage: https://github.com/githubuser/asdf#readme
13+
bug-reports: https://github.com/githubuser/asdf/issues
14+
author: Author name here
15+
maintainer: [email protected]
16+
copyright: 2018 Author name here
17+
license: BSD3
18+
build-type: Simple
19+
extra-source-files:
20+
README.md
21+
ChangeLog.md
22+
23+
source-repository head
24+
type: git
25+
location: https://github.com/githubuser/asdf
26+
27+
library
28+
other-modules:
29+
Paths_asdf
30+
hs-source-dirs:
31+
src
32+
build-depends:
33+
base >=4.7 && <5
34+
default-language: Haskell2010
35+
36+
executable asdf-exe
37+
main-is: Main.hs
38+
other-modules:
39+
Asdf
40+
Paths_asdf
41+
hs-source-dirs:
42+
app
43+
ghc-options: -threaded -rtsopts -with-rtsopts=-N
44+
build-depends:
45+
asdf
46+
, base >=4.7 && <5
47+
default-language: Haskell2010
48+
49+
test-suite asdf-test
50+
type: exitcode-stdio-1.0
51+
main-is: Spec.hs
52+
other-modules:
53+
Paths_asdf
54+
hs-source-dirs:
55+
test
56+
ghc-options: -threaded -rtsopts -with-rtsopts=-N
57+
build-depends:
58+
asdf
59+
, base >=4.7 && <5
60+
default-language: Haskell2010

0 commit comments

Comments
 (0)