diff --git a/haskell-ide-engine.cabal b/haskell-ide-engine.cabal index 7aba47ce1..f95ee16e4 100644 --- a/haskell-ide-engine.cabal +++ b/haskell-ide-engine.cabal @@ -220,6 +220,7 @@ test-suite unit-test , ghc , haskell-ide-engine , haskell-lsp-types == 0.19.* + , hie-bios , hie-test-utils , hie-plugin-api , hoogle > 5.0.11 diff --git a/hie-plugin-api/Haskell/Ide/Engine/Cradle.hs b/hie-plugin-api/Haskell/Ide/Engine/Cradle.hs index 0a5fe02ef..f75be8901 100644 --- a/hie-plugin-api/Haskell/Ide/Engine/Cradle.hs +++ b/hie-plugin-api/Haskell/Ide/Engine/Cradle.hs @@ -684,11 +684,8 @@ partOfComponent :: -- | Component to check whether the given FilePath is part of it. ChComponentInfo -> Bool -partOfComponent fp' comp - | inTargets (ciSourceDirs comp) fp' (getTargets comp fp') - = True - | otherwise - = False +partOfComponent fp' comp = + inTargets (ciSourceDirs comp) fp' (getTargets comp fp') where -- Check if the FilePath is in an executable or setup's main-is field inMainIs :: FilePath -> Bool @@ -698,11 +695,15 @@ partOfComponent fp' comp | otherwise = False inTargets :: [FilePath] -> FilePath -> [String] -> Bool - inTargets sourceDirs fp targets - | Just relative <- relativeTo fp sourceDirs - = any (`elem` targets) [getModuleName relative, fp] || inMainIs relative - | otherwise - = False + inTargets sourceDirs fp targets = + let candidates = relativeTo fp sourceDirs + in any (existsInTargets targets fp) candidates + + existsInTargets :: [String] -> FilePath -> FilePath -> Bool + existsInTargets targets absFp relFp = or + [ any (`elem` targets) [getModuleName relFp, absFp] + , inMainIs relFp + ] getModuleName :: FilePath -> String getModuleName fp = map @@ -846,24 +847,23 @@ ancestors dir subdir = takeDirectory dir -- | Assuming a FilePath @"src\/Lib\/Lib.hs"@ and a list of directories --- such as @["src", "app"]@, returns either the given FilePath +-- such as @["src", "app"]@, returns the given FilePath -- with a matching directory stripped away. -- If there are multiple matches, e.g. multiple directories are a prefix --- of the given FilePath, return the first match in the list. --- Returns Nothing, if not a single --- given directory is a prefix of the FilePath. +-- of the given FilePath we return all matches. +-- Returns an empty list if no prefix matches the given FilePath. -- -- >>> relativeTo "src/Lib/Lib.hs" ["src"] --- Just "Lib/Lib.hs" +-- ["Lib/Lib.hs"] -- -- >>> relativeTo "src/Lib/Lib.hs" ["app"] --- Nothing +-- [] -- -- >>> relativeTo "src/Lib/Lib.hs" ["src", "src/Lib"] --- Just "Lib/Lib.hs" -relativeTo :: FilePath -> [FilePath] -> Maybe FilePath -relativeTo file sourceDirs = listToMaybe - $ mapMaybe (`stripFilePath` file) sourceDirs +-- ["Lib/Lib.hs", "Lib.hs"] +relativeTo :: FilePath -> [FilePath] -> [FilePath] +relativeTo file sourceDirs = + mapMaybe (`stripFilePath` file) sourceDirs -- | Returns a user facing display name for the cradle type, -- e.g. "Stack project" or "GHC session" diff --git a/test/testdata/cabal-helper/implicit-exe/implicit-exe.cabal b/test/testdata/cabal-helper/implicit-exe/implicit-exe.cabal index 28a0728f4..3aca1b42f 100644 --- a/test/testdata/cabal-helper/implicit-exe/implicit-exe.cabal +++ b/test/testdata/cabal-helper/implicit-exe/implicit-exe.cabal @@ -7,11 +7,11 @@ build-type: Simple library exposed-modules: Lib hs-source-dirs: src - build-depends: base >=4.8 && <4.14 + build-depends: base default-language: Haskell2010 executable implicit-exe main-is: src/Exe.hs - build-depends: base >=4.8 && <4.14, implicit-exe + build-depends: base, implicit-exe default-language: Haskell2010 \ No newline at end of file diff --git a/test/testdata/cabal-helper/mono-repo/A/A.cabal b/test/testdata/cabal-helper/mono-repo/A/A.cabal index dd682ddd0..e70b43fc1 100644 --- a/test/testdata/cabal-helper/mono-repo/A/A.cabal +++ b/test/testdata/cabal-helper/mono-repo/A/A.cabal @@ -1,15 +1,15 @@ -cabal-version: >=2.0 +cabal-version: >=1.10 name: A version: 0.1.0.0 build-type: Simple library exposed-modules: MyLib - build-depends: base >=4.9 && < 5 + build-depends: base default-language: Haskell2010 executable A main-is: Main.hs other-modules: MyLib - build-depends: base >= 4.9 && < 5, A + build-depends: base, A default-language: Haskell2010 diff --git a/test/testdata/cabal-helper/mono-repo/B/B.cabal b/test/testdata/cabal-helper/mono-repo/B/B.cabal index 96805e98e..4093e1d0f 100644 --- a/test/testdata/cabal-helper/mono-repo/B/B.cabal +++ b/test/testdata/cabal-helper/mono-repo/B/B.cabal @@ -1,15 +1,15 @@ -cabal-version: >=2.0 +cabal-version: >=1.10 name: B version: 0.1.0.0 build-type: Simple library exposed-modules: MyLib - build-depends: base >= 4.9 && < 5 + build-depends: base default-language: Haskell2010 executable B main-is: Main.hs other-modules: MyLib - build-depends: base >= 4.9 && < 5, B + build-depends: base, B default-language: Haskell2010 diff --git a/test/testdata/cabal-helper/mono-repo/C/C.cabal b/test/testdata/cabal-helper/mono-repo/C/C.cabal index 016a83201..db5e380f4 100644 --- a/test/testdata/cabal-helper/mono-repo/C/C.cabal +++ b/test/testdata/cabal-helper/mono-repo/C/C.cabal @@ -1,9 +1,9 @@ -cabal-version: >=2.0 +cabal-version: >=1.10 name: C version: 0.1.0.0 build-type: Simple library exposed-modules: MyLib - build-depends: base>= 4.9 && < 5 + build-depends: base default-language: Haskell2010 diff --git a/test/testdata/cabal-helper/multi-source-dirs/Setup.hs b/test/testdata/cabal-helper/multi-source-dirs/Setup.hs new file mode 100644 index 000000000..9a994af67 --- /dev/null +++ b/test/testdata/cabal-helper/multi-source-dirs/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/test/testdata/cabal-helper/multi-source-dirs/multi-source-dirs.cabal b/test/testdata/cabal-helper/multi-source-dirs/multi-source-dirs.cabal new file mode 100644 index 000000000..58568683d --- /dev/null +++ b/test/testdata/cabal-helper/multi-source-dirs/multi-source-dirs.cabal @@ -0,0 +1,11 @@ +cabal-version: >=1.10 +name: multi-source-dirs +version: 0.1.0.0 +license-file: LICENSE +build-type: Simple + +library + exposed-modules: Lib, BetterLib + hs-source-dirs: src, src/input + build-depends: base + default-language: Haskell2010 diff --git a/test/testdata/cabal-helper/multi-source-dirs/src/BetterLib.hs b/test/testdata/cabal-helper/multi-source-dirs/src/BetterLib.hs new file mode 100644 index 000000000..0784c76d4 --- /dev/null +++ b/test/testdata/cabal-helper/multi-source-dirs/src/BetterLib.hs @@ -0,0 +1,5 @@ +module BetterLib where + + +foo = 3 +bar = "String" \ No newline at end of file diff --git a/test/testdata/cabal-helper/multi-source-dirs/src/input/Lib.hs b/test/testdata/cabal-helper/multi-source-dirs/src/input/Lib.hs new file mode 100644 index 000000000..6c3723491 --- /dev/null +++ b/test/testdata/cabal-helper/multi-source-dirs/src/input/Lib.hs @@ -0,0 +1,6 @@ +module Lib where + +foobar = 15 + +fizbuzz :: Int -> String +fizbuzz n = "Fizz" \ No newline at end of file diff --git a/test/testdata/cabal-helper/simple-cabal/simple-cabal-test.cabal b/test/testdata/cabal-helper/simple-cabal/simple-cabal-test.cabal index 75e30b0dd..3c8be5d86 100644 --- a/test/testdata/cabal-helper/simple-cabal/simple-cabal-test.cabal +++ b/test/testdata/cabal-helper/simple-cabal/simple-cabal-test.cabal @@ -6,5 +6,5 @@ build-type: Simple library exposed-modules: MyLib - build-depends: base >=4.12 && <4.13 + build-depends: base default-language: Haskell2010 diff --git a/test/testdata/cabal-helper/simple-stack/simple-stack-test.cabal b/test/testdata/cabal-helper/simple-stack/simple-stack-test.cabal index 1f4d908b9..264baebfd 100644 --- a/test/testdata/cabal-helper/simple-stack/simple-stack-test.cabal +++ b/test/testdata/cabal-helper/simple-stack/simple-stack-test.cabal @@ -6,5 +6,5 @@ build-type: Simple library exposed-modules: MyLib - build-depends: base >=4.12 && <4.13 + build-depends: base default-language: Haskell2010 diff --git a/test/testdata/cabal-helper/sub-package/plugins-api/plugins-api.cabal b/test/testdata/cabal-helper/sub-package/plugins-api/plugins-api.cabal index 129dc9e06..223fa73b9 100644 --- a/test/testdata/cabal-helper/sub-package/plugins-api/plugins-api.cabal +++ b/test/testdata/cabal-helper/sub-package/plugins-api/plugins-api.cabal @@ -6,5 +6,5 @@ build-type: Simple library exposed-modules: PluginLib - build-depends: base >=4.12 && <4.13 + build-depends: base default-language: Haskell2010 diff --git a/test/testdata/cabal-helper/sub-package/sub-package.cabal b/test/testdata/cabal-helper/sub-package/sub-package.cabal index d54355aea..ba36f1b4d 100644 --- a/test/testdata/cabal-helper/sub-package/sub-package.cabal +++ b/test/testdata/cabal-helper/sub-package/sub-package.cabal @@ -6,12 +6,12 @@ build-type: Simple library exposed-modules: MyLib - build-depends: base >=4.12 && <4.13, plugins-api + build-depends: base, plugins-api hs-source-dirs: src default-language: Haskell2010 executable sub-package main-is: Main.hs - build-depends: base >=4.12 && <4.13, sub-package + build-depends: base, sub-package hs-source-dirs: app default-language: Haskell2010 diff --git a/test/unit/CabalHelperSpec.hs b/test/unit/CabalHelperSpec.hs index 5df817d75..4859ad6e0 100644 --- a/test/unit/CabalHelperSpec.hs +++ b/test/unit/CabalHelperSpec.hs @@ -3,6 +3,7 @@ module CabalHelperSpec where import Data.Maybe (isJust) import Haskell.Ide.Engine.Cradle +import HIE.Bios.Types (runCradle, cradleOptsProg, Cradle, CradleLoadResult(..)) import Test.Hspec import System.FilePath import System.Directory (findExecutable, getCurrentDirectory, removeFile) @@ -26,6 +27,9 @@ simpleCabalPath cwd = rootPath cwd "simple-cabal" simpleStackPath :: FilePath -> FilePath simpleStackPath cwd = rootPath cwd "simple-stack" +multiSourceDirsPath :: FilePath -> FilePath +multiSourceDirsPath cwd = rootPath cwd "multi-source-dirs" + spec :: Spec spec = beforeAll_ setupStackFiles $ do describe "stack and cabal executables should be accesible" $ do @@ -36,88 +40,125 @@ spec = beforeAll_ setupStackFiles $ do cabal <- findExecutable "stack" cabal `shouldSatisfy` isJust describe "cabal-helper spec" $ do - describe "find cabal entry point spec" findCabalHelperEntryPointSpec - describe "cradle discovery" cabalHelperCradleSpec + describe "find entry point" findCabalHelperEntryPointSpec + describe "cradle discovery and loading" cabalHelperCradleSpec cabalHelperCradleSpec :: Spec cabalHelperCradleSpec = do cwd <- runIO getCurrentDirectory describe "dummy filepath, finds none-cradle" $ do - it "implicit exe, dummy filepath" $ do + it "implicit exe" $ do crdl <- cabalHelperCradle (implicitExePath cwd "File.hs") crdl `shouldSatisfy` isCabalCradle - it "mono repo, dummy filepath" $ do + it "mono repo" $ do crdl <- cabalHelperCradle (monoRepoPath cwd "File.hs") crdl `shouldSatisfy` isCabalCradle - it "stack repo, dummy filepath" $ do + it "stack repo" $ do crdl <- cabalHelperCradle (simpleStackPath cwd "File.hs") crdl `shouldSatisfy` isStackCradle - it "cabal repo, dummy filepath" $ + it "cabal repo" $ pendingWith "Can not work because of global `cabal.project`" -- crdl <- cabalHelperCradle (simpleCabalPath cwd "File.hs") -- crdl `shouldSatisfy` isCabalCradle - it "sub package, dummy filepath" $ do + it "sub package" $ do crdl <- cabalHelperCradle (subPackagePath cwd "File.hs") crdl `shouldSatisfy` isStackCradle + it "multi-source-dirs" $ do + crdl <- cabalHelperCradle (multiSourceDirsPath cwd "File.hs") + crdl `shouldSatisfy` isStackCradle - describe "Existing projects" $ do + describe "existing projects" $ do it "implicit exe" $ do - crdl <- cabalHelperCradle (implicitExePath cwd "src" "Exe.hs") - crdl `shouldSatisfy` isCabalCradle + let fp = implicitExePath cwd "src" "Exe.hs" + componentTest fp isCabalCradle it "mono repo" $ do - crdl <- cabalHelperCradle (monoRepoPath cwd "A" "Main.hs") - crdl `shouldSatisfy` isCabalCradle + let fp = monoRepoPath cwd "A" "Main.hs" + componentTest fp isCabalCradle it "stack repo" $ do - crdl <- cabalHelperCradle (simpleStackPath cwd "MyLib.hs") - crdl `shouldSatisfy` isStackCradle + let fp = simpleStackPath cwd "MyLib.hs" + componentTest fp isStackCradle it "cabal repo" $ pendingWith "Can not work because of global `cabal.project`" - -- crdl <- cabalHelperCradle (simpleCabalPath cwd "MyLib.hs") - -- crdl `shouldSatisfy` isCabalCradle + -- let fp = (simpleCabalPath cwd "MyLib.hs") + -- componentTest fp isStackCradle it "sub package" $ do - crdl <- cabalHelperCradle (subPackagePath cwd "plugins-api" "PluginLib.hs") - crdl `shouldSatisfy` isStackCradle + let fp = subPackagePath cwd "plugins-api" "PluginLib.hs" + componentTest fp isStackCradle + it "multi-source-dirs, nested dir" $ do + let fp = multiSourceDirsPath cwd "src" "input" "Lib.hs" + componentTest fp isStackCradle + it "multi-source-dirs" $ do + let fp = multiSourceDirsPath cwd "src" "BetterLib.hs" + componentTest fp isStackCradle + +componentTest :: FilePath -> (Cradle -> Bool) -> Expectation +componentTest fp testCradleType = do + crdl <- cabalHelperCradle fp + crdl `shouldSatisfy` testCradleType + -- TODO: this works but CI crashes + -- loadComponent crdl fp + +loadComponent :: Cradle -> FilePath -> Expectation +loadComponent crdl fp = do + result <- runCradle (cradleOptsProg crdl) (\_ -> return ()) fp + case result of + CradleFail err -> expectationFailure $ "Loading should not have failed: " ++ show err + _ -> return () + return () findCabalHelperEntryPointSpec :: Spec findCabalHelperEntryPointSpec = do cwd <- runIO getCurrentDirectory describe "implicit exe" $ do - it "Find project root with dummy filepath" $ do + it "dummy filepath" $ do let dummyFile = implicitExePath cwd "File.hs" cabalTest dummyFile - it "Find project root from source component" $ do + it "source component" $ do let libFile = implicitExePath cwd "src" "Lib.hs" cabalTest libFile - it "Find project root from executable component" $ do + it "executable component" $ do let mainFile = implicitExePath cwd "src" "Exe.hs" cabalTest mainFile describe "mono repo" $ do - it "Find project root with dummy filepath" $ do + it "dummy filepath" $ do let dummyFile = monoRepoPath cwd "File.hs" cabalTest dummyFile - it "Find project root with existing executable" $ do + it "existing executable" $ do let mainFile = monoRepoPath cwd "A" "Main.hs" cabalTest mainFile describe "sub package repo" $ do - it "Find project root with dummy filepath" $ do + it "dummy filepath" $ do let dummyFile = subPackagePath cwd "File.hs" stackTest dummyFile - it "Find project root with existing executable" $ do + it "existing executable" $ do let mainFile = subPackagePath cwd "plugins-api" "PluginLib.hs" stackTest mainFile describe "stack repo" $ do - it "Find project root with dummy filepath" $ do + it "dummy filepath" $ do let dummyFile = simpleStackPath cwd "File.hs" stackTest dummyFile - it "Find project root with real filepath" $ do + it "real filepath" $ do let dummyFile = simpleStackPath cwd "MyLib.hs" stackTest dummyFile + describe "multi-source-dirs" $ do + it "dummy filepath" $ do + let dummyFile = multiSourceDirsPath cwd "File.hs" + stackTest dummyFile + + it "real filepath" $ do + let dummyFile = multiSourceDirsPath cwd "src" "BetterLib.hs" + stackTest dummyFile + + it "nested filpath" $ do + let dummyFile = multiSourceDirsPath cwd "src" "input" "Lib.hs" + stackTest dummyFile + describe "simple cabal repo" $ - it "Find porject root with dummy filepath" $ + it "Find project root with dummy filepath" $ pendingWith "Change test-setup, we will always find `cabal.project` in root dir" -- ------------------------------------------------------------- @@ -141,20 +182,23 @@ stackTest fp = do setupStackFiles :: IO () setupStackFiles = do resolver <- readResolver - cwd <- getCurrentDirectory + cwd <- getCurrentDirectory writeFile (implicitExePath cwd "stack.yaml") (standardStackYaml resolver) - writeFile (monoRepoPath cwd "stack.yaml") (monoRepoStackYaml resolver) - writeFile (subPackagePath cwd "stack.yaml") (subPackageStackYaml resolver) + writeFile (monoRepoPath cwd "stack.yaml") (monoRepoStackYaml resolver) + writeFile (subPackagePath cwd "stack.yaml") (subPackageStackYaml resolver) writeFile (simpleStackPath cwd "stack.yaml") (standardStackYaml resolver) + writeFile (multiSourceDirsPath cwd "stack.yaml") + (standardStackYaml resolver) cleanupStackFiles :: IO () cleanupStackFiles = do - cwd <- getCurrentDirectory + cwd <- getCurrentDirectory removeFile (implicitExePath cwd "stack.yaml") - removeFile (monoRepoPath cwd "stack.yaml") - removeFile (subPackagePath cwd "stack.yaml") + removeFile (monoRepoPath cwd "stack.yaml") + removeFile (subPackagePath cwd "stack.yaml") removeFile (simpleStackPath cwd "stack.yaml") + removeFile (multiSourceDirsPath cwd "stack.yaml") -- -------------------------------------------------------------