implicit-hie 0.1.2.7 → 0.1.3.0
raw patch · 6 files changed
+122/−352 lines, 6 filesdep +Cabal-syntaxdep +bytestringPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: Cabal-syntax, bytestring
API changes (from Hackage documentation)
- Hie.Cabal.Parser: (<//>) :: Text -> Text -> Text
- Hie.Cabal.Parser: comment :: Parser ()
- Hie.Cabal.Parser: componentHeader :: Indent -> Text -> Parser Name
- Hie.Cabal.Parser: emptyOrComLine :: Parser ()
- Hie.Cabal.Parser: extractPath :: Indent -> [Path] -> Parser [Path]
- Hie.Cabal.Parser: field :: Indent -> Text -> (Indent -> Parser a) -> Parser a
- Hie.Cabal.Parser: hsSourceDir :: Indent -> Parser [Text]
- Hie.Cabal.Parser: indent :: Indent -> Parser Int
- Hie.Cabal.Parser: infixr 5 <//>
- Hie.Cabal.Parser: parseBench :: Indent -> Parser [Component]
- Hie.Cabal.Parser: parseComponent :: Indent -> Parser [Component]
- Hie.Cabal.Parser: parseExe :: Indent -> Parser [Component]
- Hie.Cabal.Parser: parseLib :: Indent -> Parser [Component]
- Hie.Cabal.Parser: parseList :: Indent -> Parser [Text]
- Hie.Cabal.Parser: parsePackage :: Parser Package
- Hie.Cabal.Parser: parseQuoted :: Parser Text
- Hie.Cabal.Parser: parseSec :: Indent -> Text -> (Name -> Path -> Component) -> Parser [Component]
- Hie.Cabal.Parser: parseSecMain :: (Name -> Path -> Component) -> Text -> Indent -> Parser [Component]
- Hie.Cabal.Parser: parseString :: Parser Name
- Hie.Cabal.Parser: parseTestSuite :: Indent -> Parser [Component]
- Hie.Cabal.Parser: pathMain :: Indent -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
- Hie.Cabal.Parser: skipBlock :: Indent -> Parser ()
- Hie.Cabal.Parser: skipBlockLine :: Indent -> Parser ()
- Hie.Cabal.Parser: skipFreeformField :: Parser ()
- Hie.Cabal.Parser: skipToNextLine :: Parser ()
- Hie.Cabal.Parser: tabOrSpace :: Parser Char
- Hie.Cabal.Parser: type Indent = Int
- Hie.Cabal.Parser: type Path = Text
- Hie.Cabal.Parser: unqualName :: Parser Text
Files
- implicit-hie.cabal +4/−3
- src/Hie/Cabal/Parser.hs +96/−135
- test/Spec.hs +9/−190
- test/benchSection +3/−0
- test/hie.yaml.cbl +0/−20
- test/stackHie.yaml +10/−4
implicit-hie.cabal view
@@ -7,7 +7,7 @@ -- hash: b2b829a658e33ea328c725dea732391089d03ffd2a216d413a75d88aefa7c181 name: implicit-hie-version: 0.1.2.7+version: 0.1.3.0 description: Auto generate a stack or cabal multi component hie.yaml file @@ -46,15 +46,16 @@ hs-source-dirs: src ghc-options: -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns- -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-name-shadowing -fwarn-redundant-constraints build-depends: attoparsec >=0.13 , base >=4.7 && <5+ , bytestring , directory >=1.3 , filepath >=1.4 , filepattern >=0.1+ , Cabal-syntax >=3.8 , text >=1.2 , transformers >=0.5 , yaml >=0.5@@ -88,7 +89,7 @@ , yaml default-language: Haskell2010- + if !flag(executable) buildable: False
src/Hie/Cabal/Parser.hs view
@@ -1,18 +1,52 @@-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} -module Hie.Cabal.Parser where+module Hie.Cabal.Parser+( Package(..)+, Component(..)+, CompType(..)+, Name+, extractPkgs+, parsePackage'+) where -import Control.Applicative-import Control.Monad-import Data.Attoparsec.Text-import Data.Char-import Data.Functor-import Data.Maybe-import Data.Text (Text)-import qualified Data.Text as T-import System.FilePath.Posix ((</>))+import Control.Applicative+import Control.Monad+import Data.Attoparsec.Text+import Data.Char+import Data.Foldable (asum)+import Data.Maybe (catMaybes,+ maybeToList)+import Data.Text (Text)+import qualified Data.Text as T+import Data.Text.Encoding (encodeUtf8)+import Distribution.ModuleName (ModuleName,+ toFilePath)+import Distribution.Package (pkgName,+ unPackageName)+import Distribution.PackageDescription (Benchmark (benchmarkBuildInfo, benchmarkInterface, benchmarkName),+ BenchmarkInterface (BenchmarkExeV10),+ Executable (buildInfo, exeName, modulePath),+ ForeignLib (foreignLibBuildInfo, foreignLibName),+ Library (libBuildInfo, libName),+ LibraryName (..),+ TestSuiteInterface (TestSuiteExeV10),+ benchmarkModules,+ exeModules,+ explicitLibModules,+ foreignLibModules)+import Distribution.PackageDescription.Configuration+import Distribution.PackageDescription.Parsec+import Distribution.Types.BuildInfo+import Distribution.Types.PackageDescription+import Distribution.Types.TestSuite+import Distribution.Types.UnqualComponentName+import Distribution.Utils.Path (getSymbolicPath)+import GHC.IO (unsafePerformIO)+import System.Directory (doesFileExist)+import System.FilePath ((<.>), (</>)) + type Name = Text type Path = Text@@ -29,83 +63,6 @@ = Comp CompType Name Path deriving (Show, Eq, Ord) -parsePackage' :: Text -> Either String Package-parsePackage' = parseOnly parsePackage---- Skip over entire fields that are known to be free-form. Ensures lines that--- look like the beginning of sections/stanzas are not inadvertently intepreted--- as such.--- List gathered by searching "free text field" in:--- https://cabal.readthedocs.io/en/3.4/buildinfo-fields-reference.html--- May be subject to change across Cabal versions.-skipFreeformField :: Parser ()-skipFreeformField =- choice $- flip (field 0) skipBlock- <$> [ "author",- "bug-reports",- "category",- "copyright",- "description",- "homepage",- "maintainer",- "package-url",- "stability",- "synopsis"- ]--parsePackage :: Parser Package-parsePackage =- ( do- n <- field 0 "name" $ const parseString- (Package _ t) <- parsePackage- pure $ Package n t- )- <|> (skipFreeformField >> parsePackage)- <|> ( do- h <- parseComponent 0- (Package n t) <- parsePackage- pure $ Package n (h <> t)- )- <|> (skipToNextLine >> parsePackage)- <|> pure (Package "" [])--componentHeader :: Indent -> Text -> Parser Name-componentHeader i t = do- _ <- indent i- _ <- asciiCI t- skipMany tabOrSpace- n <- parseString <|> pure ""- skipToNextLine- pure n--parseComponent :: Indent -> Parser [Component]-parseComponent i =- parseExe i- <|> parseLib i- <|> parseBench i- <|> parseTestSuite i--parseLib :: Indent -> Parser [Component]-parseLib i =- (parseSec i "library" $ Comp Lib)- <|> (parseSec i "foreign-library" $ Comp Lib)--parseTestSuite :: Indent -> Parser [Component]-parseTestSuite i = parseSec i "test-suite" $ Comp Test--parseExe :: Indent -> Parser [Component]-parseExe = parseSecMain (Comp Exe) "executable"--parseBench :: Indent -> Parser [Component]-parseBench = parseSecMain (Comp Bench) "benchmark"--parseSecMain :: (Name -> Path -> Component) -> Text -> Indent -> Parser [Component]-parseSecMain c s i = do- n <- componentHeader i s- p <- pathMain (i + 1) ["./"] "" [] []- pure $ map (c n) p- parseQuoted :: Parser Text parseQuoted = do q <- char '"' <|> char '\''@@ -142,65 +99,28 @@ skipMany com pure x -pathMain :: Indent -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]-pathMain i p m o a =- (hsSourceDir i >>= (\p' -> pathMain i p' m o a))- <|> (field i "main-is" (const parseString) >>= (\m' -> pathMain i p m' o a))- <|> (field i "other-modules" parseList >>= flip (pathMain i p m) a)- <|> (field i "autogen-modules" parseList >>= pathMain i p m o)- <|> (skipBlockLine i >> pathMain i p m o a)- <|> pure- ( map (<//> m) p- <> [ p' <//> (o'' <> ".hs")- | p' <- p,- o' <- filter (`notElem` a) o,- let o'' = T.replace "." "/" o'- ]- )--(<//>) :: Text -> Text -> Text-a <//> b = T.pack (T.unpack a </> T.unpack b)--infixr 5 <//>--parseSec :: Indent -> Text -> (Name -> Path -> Component) -> Parser [Component]-parseSec i compType compCon = do- n <- componentHeader i compType- p <- extractPath (i + 1) []- let p' = if null p then ["./"] else p- pure $ map (compCon n) p'- skipToNextLine :: Parser () skipToNextLine = skipWhile (not . isEndOfLine) >> endOfLine -skipBlock :: Indent -> Parser ()-skipBlock i = skipMany $ skipBlockLine i- comment :: Parser () comment = skipMany tabOrSpace >> "--" >> skipToNextLine -skipBlockLine :: Indent -> Parser ()-skipBlockLine i = (indent i >> skipToNextLine) <|> emptyOrComLine- emptyOrComLine :: Parser () emptyOrComLine = (skipMany tabOrSpace >> endOfLine) <|> comment tabOrSpace :: Parser Char tabOrSpace = char ' ' <|> char '\t' -hsSourceDir :: Indent -> Parser [Text]-hsSourceDir i = field i "hs-source-dirs" parseList- -- field :: Indent -> Text -> Parser Text field :: Indent ->- Text ->+ [Text] -> (Indent -> Parser a) -> Parser a field i f p = do i' <- indent i- _ <- asciiCI f+ _ <- asum $ map asciiCI f skipMany tabOrSpace _ <- char ':' skipMany tabOrSpace@@ -208,13 +128,6 @@ skipToNextLine pure p' -extractPath :: Indent -> [Path] -> Parser [Path]-extractPath i ps =- (field i "hs-source-dirs" parseList >>= (\p -> extractPath i $ ps <> p))- <|> (skipBlockLine i >> extractPath i ps)- <|> (comment >> extractPath i ps)- <|> pure ps- -- | Skip at least n spaces indent :: Indent -> Parser Int indent i = do@@ -222,4 +135,52 @@ if c >= i then pure c else fail "insufficient indent" extractPkgs :: Parser [T.Text]-extractPkgs = join . catMaybes <$> many' (Just <$> field 0 "packages" parseList <|> (skipToNextLine >> pure Nothing))+extractPkgs = join . catMaybes <$> many' (Just <$> field 0 ["packages"] parseList <|> (skipToNextLine >> pure Nothing))++parsePackage' :: T.Text -> Either String Package+parsePackage' t = do+ let bytes = encodeUtf8 t+ case runParseResult (parseGenericPackageDescription bytes) of+ (_warnings, Left err) ->+ error $ "Cannot parse Cabal file: " <> show err+ (_warnings, Right res) -> do+ let pkg = flattenPackageDescription res+ Right $ extractPackage pkg++extractPackage :: PackageDescription -> Package+extractPackage PackageDescription{..} = Package n cc where+ n = T.pack . unPackageName $ pkgName package++ cc = concat $+ [mkComp Test (unqName $ testName t) (testBuildInfo t) (testExePath t) (testModules t) | t <- testSuites] +++ [mkComp Bench (unqName $ benchmarkName b) (benchmarkBuildInfo b) (benchmarkExePath b) (benchmarkModules b) | b <- benchmarks] +++ [mkComp Exe (unqName $ exeName e) (buildInfo e) [modulePath e] (exeModules e) | e <- executables] +++ [mkComp Lib (libName' l) (libBuildInfo l) [] (explicitLibModules l) | l <- maybeToList library ++ subLibraries ] +++ [mkComp Lib (unqName $ foreignLibName f) (foreignLibBuildInfo f) [] (foreignLibModules f) | f <- foreignLibs]++ mkComp :: CompType -> T.Text -> BuildInfo -> [FilePath] -> [ModuleName] -> [Component]+ mkComp typ name bi fps mods =+ [Comp typ name (T.pack fp)+ | fp0 <- fps <> concatMap toFilePath' mods+ , srcDir <- map getSymbolicPath $ hsSourceDirs bi+ , let fp = srcDir </> fp0+ , unsafePerformIO $ doesFileExist fp+ ]++ unqName = T.pack . unUnqualComponentName+ libName' x = case libName x of+ LMainLibName -> ""+ LSubLibName u -> unqName u++benchmarkExePath :: Benchmark -> [FilePath]+benchmarkExePath b = case benchmarkInterface b of+ BenchmarkExeV10 _ f -> [f]+ _ -> []++toFilePath' :: ModuleName -> [FilePath]+toFilePath' mod = [ toFilePath mod <.> ext | ext <- ["hs", "lhs"]]++testExePath :: TestSuite -> [FilePath]+testExePath t = case testInterface t of+ TestSuiteExeV10 _ fp -> [fp]+ _ -> []
test/Spec.hs view
@@ -14,212 +14,31 @@ spec :: Spec spec = do describe "Should Succeed" $- it "successfully parses executable section" $- exeSection ~> parseExe 0- `shouldParse` [Comp Exe "gen-hie" "app/Main.hs"]- describe "Should Succeed" $- it "successfully parses test section" $- testSection ~> parseTestSuite 0- `shouldParse` [Comp Test "implicit-hie-test" "test"]- describe "Should Succeed" $- it "successfully parses library section" $- libSection ~> parseLib 0- `shouldParse` [Comp Lib "" "src"]- describe "Should Succeed" $- it "successfully parses library section with 2 hs-source-dirs" $- libSection2 ~> parseLib 0- `shouldParse` [Comp Lib "" "src", Comp Lib "" "src2"]- describe "Should Succeed" $- it "successfully parses library section with 2 paths under hs-source-dirs" $- libSection3 ~> parseLib 0- `shouldParse` [Comp Lib "" "src", Comp Lib "" "src2"]- describe "Should Succeed" $- it "successfully parses bench section" $- do- bs <- T.readFile "test/benchSection"- bs ~> parseBench 0- `shouldParse` [Comp Bench "folds" "benchmarks/folds.hs"]- describe "Should Succeed" $ it "successfully parses package" $ do cf <- T.readFile "implicit-hie.cabal"- cf ~> parsePackage+ parsePackage' cf `shouldParse` Package "implicit-hie"- [ Comp Lib "" "src",+ [ Comp Test "implicit-hie-test" "test/Spec.hs", Comp Exe "gen-hie" "app/Main.hs",- Comp Test "implicit-hie-test" "test"+ Comp Lib "" "src/Hie/Cabal/Parser.hs",+ Comp Lib "" "src/Hie/Locate.hs",+ Comp Lib "" "src/Hie/Yaml.hs" ]- describe "Should Succeed" $- it- "skips to end of block section"- $ let r = "test\n"- in (libSection <> r) ~?> parseLib 0- `leavesUnconsumed` r+ describe "Should Succeed" $ it "successfully generates stack hie.yaml" $ do sf <- readFile "test/stackHie.yaml" cf <- T.readFile "implicit-hie.cabal"- (hieYaml "stack" . fmtPkgs "stack" . (: []) <$> parseOnly parsePackage cf)+ (hieYaml "stack" . fmtPkgs "stack" . (: []) <$> parsePackage' cf) `shouldBe` Right sf+ describe "Should Succeed" $ it "successfully generates cabal hie.yaml for haskell-language-server" $ do f <- T.readFile "test/haskell-language-server-cabal" o <- readFile "test/hie.yaml.cbl"- (hieYaml "cabal" . fmtPkgs "cabal" . (: []) <$> parseOnly parsePackage f)+ (hieYaml "cabal" . fmtPkgs "cabal" . (: []) <$> parsePackage' f) `shouldBe` Right o- describe "Should Succeed" $- it "successfully parses comma list" $- ("one, two" :: Text) ~> parseList 1 `shouldParse` ["one", "two"]- describe "Should Succeed" $- it "successfully parses newline list" $- ("one\n two \n three3" :: Text) ~> parseList 1- `shouldParse` ["one", "two", "three3"]- describe "Should Succeed" $- it "successfully parses newline comma list" $- ("one\n two, three3" :: Text) ~> parseList 1- `shouldParse` ["one", "two", "three3"]- describe "Should Succeed" $- it "quoted list" $- ("\"one\"\n two\n three3" :: Text) ~> parseList 1- `shouldParse` ["one", "two", "three3"]- describe "Should Succeed" $- it "list with leading commas" $- ("one\n , two\n , three3" :: Text) ~> parseList 1- `shouldParse` ["one", "two", "three3"]- describe "Should Succeed" $- it "list with a comment" $- ("foo\n -- need to include this too\n bar\n" :: Text) ~> parseList 1- `shouldParse` ["foo", "bar"]- describe "Should Succeed" $- it "list2 with a comment" $- ("foo -- need to include this too\n bar\n" :: Text) ~> parseList 1- `shouldParse` ["foo", "bar"]- describe "Should Succeed" $- it "list3 with a comment" $- ("foo -- need to include this too\n bar" :: Text) ~> parseList 1- `shouldParse` ["foo", "bar"]- describe "Should Succeed" $- it "list4 with a comment" $- ("foo\n bar\n -- need to include this too" :: Text) ~> parseList 1- `shouldParse` ["foo", "bar"]- describe "Should Succeed" $- it "list5 with a comment" $- ("foo\n bar -- need to include this too" :: Text) ~> parseList 1- `shouldParse` ["foo", "bar"]- describe "Should Succeed" $- it "succesfully parses exe component with other-modules containing dots" $- exeSection2 ~> parseExe 0- `shouldParse` [ Comp Exe "gen-hie" "app/Main.hs",- Comp Exe "gen-hie" "app/Hie/Executable/Helper.hs",- Comp Exe "gen-hie" "app/Hie/Executable/Utils.hs"- ]- describe "Should Succeed" $- it "succesfully parses single other-modules" $- ("other-modules: test\ndefault-language: Haskell2011" :: Text) ~?> field 0 "other-modules" parseList- `leavesUnconsumed` "default-language: Haskell2011"- describe "Should Succeed" $- it "succesfully parses empty other-modules1" $- ("other-modules: test\ndefault-language: Haskell2011" :: Text) ~?> field 0 "other-modules" parseList- `leavesUnconsumed` "default-language: Haskell2011"- describe "Should Succeed" $- it "succesfully parses empty other-modules2" $- (" other-modules: \n build-depends:\n base >=4.9 && <5" :: Text) ~> field 0 "other-modules" parseList- `shouldParse` []--exeSection :: Text-exeSection =- "executable gen-hie\n\- \ other-modules:\n\- \ Paths_implicit_hie\n\- \ autogen-modules:\n\- \ Paths_implicit_hie\n\- \ hs-source-dirs:\n\- \ app\n\- \ ghc-options: -O2\n\- \ main-is: Main.hs \n"--testSection :: Text-testSection =- "test-suite implicit-hie-test\n\- \ type: exitcode-stdio-1.0\n\- \ other-modules:\n\- \ Paths_implicit_hie\n\- \ hs-source-dirs:\n\- \ test\n\- \ ghc-options: -fspecialize-aggressively -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-name-shadowing -fwarn-redundant-constraints -threaded -rtsopts -with-rtsopts=-N\n\- \ main-is: Spec.hs\n\- \ build-depends:\n\- \ attoparsec\n\- \ , base >=4.7 && <5\n\- \ , hspec\n\- \ , hspec-attoparsec\n\- \ , implicit-hie\n\- \ , text\n\- \ default-language: Haskell2010\n"--libSection :: Text-libSection =- "library\n\- \ exposed-modules:\n\- \ Lib\n\- \ other-modules:\n\- \ Paths_implicit_hie\n\- \ hs-source-dirs:\n\- \ src\n\- \ ghc-options: -fspecialize-aggressively -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-name-shadowing -fwarn-redundant-constraints\n\- \ build-depends:\n\- \ attoparsec\n\- \ , base >=4.7 && <5\n\- \ , text\n\- \ default-language: Haskell2010\n\- \"--libSection2 :: Text-libSection2 =- "library\n\- \ exposed-modules:\n\- \ Lib\n\- \ other-modules:\n\- \ Paths_implicit_hie\n\- \ hs-source-dirs:\n\- \ src\n\- \ hs-source-dirs:\n\- \ src2\n\- \ ghc-options: -fspecialize-aggressively -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-name-shadowing -fwarn-redundant-constraints\n\- \ build-depends:\n\- \ attoparsec\n\- \ , base >=4.7 && <5\n\- \ , text\n\- \ default-language: Haskell2010\n\- \"--libSection3 :: Text-libSection3 =- "library\n\- \ exposed-modules:\n\- \ Lib\n\- \ other-modules:\n\- \ Paths_implicit_hie\n\- \ hs-source-dirs:\n\- \ src,\n\- \ src2\n\- \ ghc-options: -fspecialize-aggressively -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-name-shadowing -fwarn-redundant-constraints\n\- \ build-depends:\n\- \ attoparsec\n\- \ , base >=4.7 && <5\n\- \ , text\n\- \ default-language: Haskell2010\n\- \"--exeSection2 :: Text-exeSection2 =- "executable gen-hie\n\- \ other-modules:\n\- \ Hie.Executable.Helper\n\- \ Hie.Executable.Utils\n\- \ hs-source-dirs:\n\- \ app\n\- \ main-is: Main.hs \n"
test/benchSection view
@@ -1,3 +1,6 @@+name: folds-test+version: 0.1+ benchmark folds default-language: Haskell2010 hs-source-dirs: benchmarks
test/hie.yaml.cbl view
@@ -1,22 +1,2 @@ cradle: cabal:- - path: "src"- component: "lib:haskell-language-server"-- - path: "exe/Main.hs"- component: "haskell-language-server:exe:haskell-language-server"-- - path: "exe/Arguments.hs"- component: "haskell-language-server:exe:haskell-language-server"-- - path: "exe/Wrapper.hs"- component: "haskell-language-server:exe:haskell-language-server-wrapper"-- - path: "exe/Arguments.hs"- component: "haskell-language-server:exe:haskell-language-server-wrapper"-- - path: "test/functional"- component: "haskell-language-server:test:func-test"-- - path: "test/utils"- component: "haskell-language-server:lib:hls-test-utils"
test/stackHie.yaml view
@@ -1,10 +1,16 @@ cradle: stack:- - path: "src"- component: "implicit-hie:lib"+ - path: "test/Spec.hs"+ component: "implicit-hie:test:implicit-hie-test" - path: "app/Main.hs" component: "implicit-hie:exe:gen-hie" - - path: "test"- component: "implicit-hie:test:implicit-hie-test"+ - path: "src/Hie/Cabal/Parser.hs"+ component: "implicit-hie:lib"++ - path: "src/Hie/Locate.hs"+ component: "implicit-hie:lib"++ - path: "src/Hie/Yaml.hs"+ component: "implicit-hie:lib"