implicit-hie 0.1.3.0 → 0.1.4.0
raw patch · 6 files changed
+352/−122 lines, 6 filesdep −Cabal-syntaxdep −bytestringPVP ok
version bump matches the API change (PVP)
Dependencies removed: 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 +3/−4
- src/Hie/Cabal/Parser.hs +135/−96
- test/Spec.hs +190/−9
- test/benchSection +0/−3
- test/hie.yaml.cbl +20/−0
- test/stackHie.yaml +4/−10
implicit-hie.cabal view
@@ -7,7 +7,7 @@ -- hash: b2b829a658e33ea328c725dea732391089d03ffd2a216d413a75d88aefa7c181 name: implicit-hie-version: 0.1.3.0+version: 0.1.4.0 description: Auto generate a stack or cabal multi component hie.yaml file @@ -46,16 +46,15 @@ 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@@ -89,7 +88,7 @@ , yaml default-language: Haskell2010-+ if !flag(executable) buildable: False
src/Hie/Cabal/Parser.hs view
@@ -1,51 +1,17 @@+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-} -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.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 ((<.>), (</>))+module Hie.Cabal.Parser 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 ((</>)) type Name = Text @@ -63,6 +29,83 @@ = 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 '\''@@ -99,28 +142,65 @@ 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- _ <- asum $ map asciiCI f+ _ <- asciiCI f skipMany tabOrSpace _ <- char ':' skipMany tabOrSpace@@ -128,6 +208,13 @@ 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@@ -135,52 +222,4 @@ 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))--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]- _ -> []+extractPkgs = join . catMaybes <$> many' (Just <$> field 0 "packages" parseList <|> (skipToNextLine >> pure Nothing))
test/Spec.hs view
@@ -14,31 +14,212 @@ 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"- parsePackage' cf+ cf ~> parsePackage `shouldParse` Package "implicit-hie"- [ Comp Test "implicit-hie-test" "test/Spec.hs",+ [ Comp Lib "" "src", Comp Exe "gen-hie" "app/Main.hs",- Comp Lib "" "src/Hie/Cabal/Parser.hs",- Comp Lib "" "src/Hie/Locate.hs",- Comp Lib "" "src/Hie/Yaml.hs"+ Comp Test "implicit-hie-test" "test" ]- 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" . (: []) <$> parsePackage' cf)+ (hieYaml "stack" . fmtPkgs "stack" . (: []) <$> parseOnly 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" . (: []) <$> parsePackage' f)+ (hieYaml "cabal" . fmtPkgs "cabal" . (: []) <$> parseOnly 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,6 +1,3 @@-name: folds-test-version: 0.1- benchmark folds default-language: Haskell2010 hs-source-dirs: benchmarks
test/hie.yaml.cbl view
@@ -1,2 +1,22 @@ 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,16 +1,10 @@ cradle: stack:- - path: "test/Spec.hs"- component: "implicit-hie:test:implicit-hie-test"+ - path: "src"+ component: "implicit-hie:lib" - path: "app/Main.hs" component: "implicit-hie:exe:gen-hie" - - 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"+ - path: "test"+ component: "implicit-hie:test:implicit-hie-test"