require 0.4.5 → 0.4.6
raw patch · 8 files changed
+141/−208 lines, 8 filesdep +reludedep −universumPVP ok
version bump matches the API change (PVP)
Dependencies added: relude
Dependencies removed: universum
API changes (from Hackage documentation)
Files
- benchmark/Main.hs +1/−1
- executable/AutoRequire/Main.hs +2/−1
- executable/Require/Main.hs +2/−1
- library/Require.hs +92/−88
- package.yaml +2/−2
- require.cabal +7/−7
- stack.yaml +3/−64
- test-suite/Main.hs +32/−44
benchmark/Main.hs view
@@ -1,7 +1,7 @@ -- You can benchmark your code quickly and effectively with Criterion. See its -- website for help: <http://www.serpentine.com/criterion/>. -import Universum+import Relude main :: IO () main = putTextLn "Hello"
executable/AutoRequire/Main.hs view
@@ -1,8 +1,9 @@ -- It is generally a good idea to keep all your business logic in your library -- and only use it in the executable. Doing so allows others to use what you -- wrote in their libraries.++import Relude import qualified Require-import Universum main :: IO () main = Require.autorequireMain
executable/Require/Main.hs view
@@ -1,8 +1,9 @@ -- It is generally a good idea to keep all your business logic in your library -- and only use it in the executable. Doing so allows others to use what you -- wrote in their libraries.++import Relude import qualified Require-import Universum main :: IO () main = Require.requireMain
library/Require.hs view
@@ -1,27 +1,30 @@ module Require where -import Universum--import qualified Text.Megaparsec as Megaparsec-import qualified Text.Megaparsec.Char as Megaparsec-import qualified Data.Text as Text+import qualified Data.Text as Text import Options.Generic+import Relude import System.Directory+import qualified Text.Megaparsec as Megaparsec+import qualified Text.Megaparsec.Char as Megaparsec -newtype FileName = FileName { unFileName :: Text }+newtype FileName = FileName {unFileName :: Text}+ newtype LineNumber = LineNumber Int-type Parser = Megaparsec.Parsec Void Text -data RequireInfo = RequireInfo- { riFullModuleName :: Text- , riModuleAlias :: Text- , riImportedTypes :: Maybe [Text]- } deriving Show+type Parser = Megaparsec.Parsec Void Text -data CommandArguments =- CommandArguments Text Text Text- deriving Generic+data RequireInfo+ = RequireInfo+ { riFullModuleName :: Text,+ riModuleAlias :: Text,+ riImportedTypes :: Maybe [Text]+ }+ deriving (Show) +data CommandArguments+ = CommandArguments Text Text Text+ deriving (Generic)+ instance ParseRecord CommandArguments findRequires :: IO (Maybe Text)@@ -29,24 +32,25 @@ currentDir <- getCurrentDirectory files <- getDirectoryContents currentDir let textFiles = fmap toText files- return $ head <$> (nonEmpty $ filter (Text.isSuffixOf "Requires") textFiles)+ return $ head <$> nonEmpty (filter (Text.isSuffixOf "Requires") textFiles) requireMain :: IO () requireMain = do- CommandArguments _ inputFile outputFile <- getRecord "Require Haskell preprocessor" :: IO CommandArguments+ CommandArguments inputFile _ outputFile <- getRecord "Require Haskell preprocessor" :: IO CommandArguments content <- readFile (toString inputFile) writeFile (toString outputFile)- ( Require.transform- False- (Require.FileName inputFile)- ""- content+ ( toString $+ Require.transform+ False+ (Require.FileName inputFile)+ ""+ (toText content) ) autorequireMain :: IO () autorequireMain = do- CommandArguments _ inputFile outputFile <- getRecord "Require Haskell preprocessor" :: IO CommandArguments+ CommandArguments inputFile _ outputFile <- getRecord "Require Haskell preprocessor" :: IO CommandArguments requiresFile <- findRequires case requiresFile of Nothing -> die "There is no Requires file in the system"@@ -55,97 +59,97 @@ content <- readFile (toString inputFile) writeFile (toString outputFile)- (Require.transform- True- (Require.FileName inputFile)- file- content)+ ( toString $+ Require.transform+ True+ (Require.FileName inputFile)+ (toText file)+ (toText content)+ ) transform :: Bool -> FileName -> Text -> Text -> Text transform autorequireEnabled filename imports input | autorequireEnabled = transform' True filename imports input- | noAutorequire = transform' False filename imports input- | otherwise = transform' True filename imports input- where- noAutorequire = (length $ filter (\t -> "autorequire" `Text.isPrefixOf` t) $ lines input) == 0-+ | noAutorequire = transform' False filename imports input+ | otherwise = transform' True filename imports input+ where+ noAutorequire = not (any ("autorequire" `Text.isPrefixOf`) $ lines input) transform' :: Bool -> FileName -> Text -> Text -> Text transform' shouldPrepend filename prepended input = Text.lines input- & filter (\t -> not $ "autorequire" `Text.isPrefixOf` t)- & zip [1..]- >>= prependAfterModuleLine- <&> (\(ln, text) -> maybe (text <> "\n") (renderImport filename (LineNumber ln)) $ Megaparsec.parseMaybe requireParser text)- & (lineTag filename (LineNumber 1) :)- & Text.concat- where- enumeratedPrepend ln- | shouldPrepend = zip (repeat ln) (Text.lines prepended)- | otherwise = []- prependAfterModuleLine (ln, text)- | ("module" `Text.isPrefixOf` text)- && ("where" `Text.isSuffixOf`) text = (ln, text) : enumeratedPrepend ln- | ("instance" `Text.isPrefixOf` text)- && ("where" `Text.isSuffixOf`) text = [(ln, text)]- | ("data" `Text.isPrefixOf` text)- && ("where" `Text.isSuffixOf`) text = [(ln, text)]- | ("class" `Text.isPrefixOf` text)- && ("where" `Text.isSuffixOf`) text = [(ln, text)]- | (not $ "instance" `Text.isPrefixOf` text)- && (not $ "class" `Text.isPrefixOf` text)- && (not $ "data" `Text.isPrefixOf` text)- && ("where" `Text.isPrefixOf`) text = (ln, text) : enumeratedPrepend (ln)- | otherwise = [(ln, text)]+ & filter (\t -> not $ "autorequire" `Text.isPrefixOf` t)+ & zip [1 ..]+ >>= prependAfterModuleLine+ <&> (\(ln, text) -> maybe (text <> "\n") (renderImport filename (LineNumber ln)) $ Megaparsec.parseMaybe requireParser text)+ & (lineTag filename (LineNumber 1) :)+ & Text.concat+ where+ enumeratedPrepend ln+ | shouldPrepend = zip (repeat ln) (Text.lines prepended)+ | otherwise = []+ prependAfterModuleLine (ln, text)+ | ("module" `Text.isPrefixOf` text)+ && ("where" `Text.isSuffixOf`) text =+ (ln, text) : enumeratedPrepend ln+ | ("instance" `Text.isPrefixOf` text)+ && ("where" `Text.isSuffixOf`) text =+ [(ln, text)]+ | ("data" `Text.isPrefixOf` text)+ && ("where" `Text.isSuffixOf`) text =+ [(ln, text)]+ | ("class" `Text.isPrefixOf` text)+ && ("where" `Text.isSuffixOf`) text =+ [(ln, text)]+ | not ("instance" `Text.isPrefixOf` text)+ && not ("class" `Text.isPrefixOf` text)+ && not ("data" `Text.isPrefixOf` text)+ && ("where" `Text.isPrefixOf`) text =+ (ln, text) : enumeratedPrepend ln+ | otherwise = [(ln, text)] lineTag :: FileName -> LineNumber -> Text lineTag (FileName fn) (LineNumber ln) = "{-# LINE "- <> show ln- <> " \""- <> fn- <> "\" #-}\n"-+ <> show ln+ <> " \""+ <> fn+ <> "\" #-}\n" renderImport :: FileName -> LineNumber -> RequireInfo -> Text renderImport filename linenumber RequireInfo {..} =- case (Text.isInfixOf riFullModuleName (unFileName filename)) of- True -> ""- False -> typesImport <> lineTag filename linenumber <> qualifiedImport- where- types = maybe (Text.takeWhileEnd (/= '.') riFullModuleName) (Text.intercalate ",") riImportedTypes- typesImport = "import " <> riFullModuleName <> " (" <> types <> ")\n"- qualifiedImport = "import qualified " <> riFullModuleName <> " as " <> riModuleAlias <> "\n"-+ if Text.isInfixOf riFullModuleName (unFileName filename)+ then ""+ else typesImport <> lineTag filename linenumber <> qualifiedImport+ where+ types = maybe (Text.takeWhileEnd (/= '.') riFullModuleName) (Text.intercalate ",") riImportedTypes+ typesImport = "import " <> riFullModuleName <> " (" <> types <> ")\n"+ qualifiedImport = "import qualified " <> riFullModuleName <> " as " <> riModuleAlias <> "\n" requireParser :: Parser RequireInfo requireParser = do void $ Megaparsec.string "require"- void $ Megaparsec.space1+ void Megaparsec.space1 module' <- Megaparsec.some (Megaparsec.alphaNumChar <|> Megaparsec.punctuationChar)- void $ Megaparsec.space-- alias' <- Megaparsec.try $ Megaparsec.option Nothing $ do+ void Megaparsec.space+ alias' <- Megaparsec.try $ Megaparsec.option Nothing $ do void $ Megaparsec.string "as"- void $ Megaparsec.space1- Just <$> Megaparsec.some (Megaparsec.alphaNumChar)-- void $ Megaparsec.space-- types' <- Megaparsec.option Nothing $ do+ void Megaparsec.space1+ Just <$> Megaparsec.some Megaparsec.alphaNumChar+ void Megaparsec.space+ types' <- Megaparsec.option Nothing $ do void $ Megaparsec.char '(' t' <- Megaparsec.many (Megaparsec.alphaNumChar <|> Megaparsec.char ',' <|> Megaparsec.char ' ') void $ Megaparsec.char ')' return $ Just t'- void $ Megaparsec.option Nothing $ do- void $ Megaparsec.space+ void Megaparsec.space void $ Megaparsec.some (Megaparsec.char '-') void $ Megaparsec.many (Megaparsec.alphaNumChar <|> Megaparsec.char ' ') return Nothing-- return RequireInfo- { riFullModuleName = toText $ module'- , riModuleAlias = maybe (Text.takeWhileEnd (/= '.') $ toText module') toText alias'- , riImportedTypes = fmap Text.strip <$> Text.splitOn "," <$> toText <$> types'- }+ return+ RequireInfo+ { riFullModuleName = toText module',+ riModuleAlias = maybe (Text.takeWhileEnd (/= '.') $ toText module') toText alias',+ riImportedTypes = (fmap Text.strip <$> Text.splitOn ",") . toText <$> types'+ }
package.yaml view
@@ -1,5 +1,5 @@ name: require-version: "0.4.5"+version: "0.4.6" github: "theam/require" license: Apache-2.0 author: "The Agile Monkeys"@@ -29,7 +29,7 @@ dependencies: - base >= 4.9 && < 5- - universum >= 1.2.0 && < 2+ - relude - bytestring >= 0.10 && < 0.11 - text >= 1.2.3.0 && < 2 - megaparsec >= 7 && < 8
require.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: b849a629980c70464aa83e3846fceb3efdc2bed39b17efb17ad90970ee0da8e7+-- hash: f6bc9c2371f0a167829d05ea4cb94a51e5d3ca3e2bc863a82685611213b5cb05 name: require-version: 0.4.5+version: 0.4.6 synopsis: Scrap your qualified import clutter description: See <https://theam.github.io/require> category: Other@@ -47,8 +47,8 @@ , inliterate , megaparsec >=7 && <8 , optparse-generic+ , relude , text >=1.2.3.0 && <2- , universum >=1.2.0 && <2 default-language: Haskell2010 executable autorequirepp@@ -66,9 +66,9 @@ , inliterate , megaparsec >=7 && <8 , optparse-generic+ , relude , require , text >=1.2.3.0 && <2- , universum >=1.2.0 && <2 default-language: Haskell2010 executable requirepp@@ -86,9 +86,9 @@ , inliterate , megaparsec >=7 && <8 , optparse-generic+ , relude , require , text >=1.2.3.0 && <2- , universum >=1.2.0 && <2 default-language: Haskell2010 test-suite require-test-suite@@ -107,11 +107,11 @@ , inliterate , megaparsec >=7 && <8 , optparse-generic+ , relude , require , tasty , tasty-hspec , text >=1.2.3.0 && <2- , universum >=1.2.0 && <2 default-language: Haskell2010 benchmark require-benchmarks@@ -131,7 +131,7 @@ , inliterate , megaparsec >=7 && <8 , optparse-generic+ , relude , require , text >=1.2.3.0 && <2- , universum >=1.2.0 && <2 default-language: Haskell2010
stack.yaml view
@@ -1,67 +1,6 @@-# This file was automatically generated by 'stack init'-#-# Some commonly used options have been documented as comments in this file.-# For advanced use and comprehensive documentation of the format, please see:-# https://docs.haskellstack.org/en/stable/yaml_configuration/--# Resolver to choose a 'specific' stackage snapshot or a compiler version.-# A snapshot resolver dictates the compiler version and the set of packages-# to be used for project dependencies. For example:-#-# resolver: lts-3.5-# resolver: nightly-2015-09-21-# resolver: ghc-7.10.2-# resolver: ghcjs-0.1.0_ghc-7.10.2-# resolver:-# name: custom-snapshot-# location: "./custom-snapshot.yaml"-resolver: lts-13.3+resolver: lts-14.27 -# User packages to be built.-# Various formats can be used as shown in the example below.-#-# packages:-# - some-directory-# - https://example.com/foo/bar/baz-0.0.2.tar.gz-# - location:-# git: https://github.com/commercialhaskell/stack.git-# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a-# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a-# extra-dep: true-# subdirs:-# - auto-update-# - wai-#-# A package marked 'extra-dep: true' will only be built if demanded by a-# non-dependency (i.e. a user package), and its test suites and benchmarks-# will not be run. This is useful for tweaking upstream packages. packages:-- .-# Dependency packages to be pulled from upstream that are not in the resolver-# (e.g., acme-missiles-0.3)-extra-deps:- - universum-1.2.0--# Override default flag values for local packages and extra-deps-# flags: {}--# Extra package databases containing global packages-# extra-package-dbs: []+ - . -# Control whether we use the GHC we find on the path-# system-ghc: true-#-# Require a specific version of stack, using version ranges-# require-stack-version: -any # Default-# require-stack-version: ">=1.6"-#-# Override the architecture used by stack, especially useful on Windows-# arch: i386-# arch: x86_64-#-# Extra directories used by stack for building-# extra-include-dirs: [/path/to/dir]-# extra-lib-dirs: [/path/to/dir]-#-# Allow a newer minor version of GHC than the snapshot specifies-# compiler-check: newer-minor+extra-deps: []
test-suite/Main.hs view
@@ -1,81 +1,69 @@-import Universum--import qualified Data.Text as Text-import qualified Test.Tasty-import Test.Tasty.Hspec-+import qualified Data.Text as Text+import Relude import qualified Require-+import qualified Test.Tasty+import Test.Tasty.Hspec main :: IO () main = do- test <- testSpec "require" spec- Test.Tasty.defaultMain test-+ test <- testSpec "require" spec+ Test.Tasty.defaultMain test spec :: Spec spec = parallel $ do it "transforms the 'require' keyword into a properly qualified import" $ do- let input = "require Data.Text"+ let input = "require Data.Text" let expected = "import qualified Data.Text as Text"- let actual = Require.transform False (Require.FileName "Foo.hs") "" input+ let actual = Require.transform False (Require.FileName "Foo.hs") "" input expected `Text.isInfixOf` actual- it "imports the type based on the module" $ do- let input = "require Data.Text"+ let input = "require Data.Text" let expected = "import Data.Text (Text)"- let actual = Require.transform False (Require.FileName "Foo.hs") "" input+ let actual = Require.transform False (Require.FileName "Foo.hs") "" input expected `Text.isInfixOf` actual- it "keeps the rest of the content intact" $ do- let input = "module Foo where\nrequire Data.Text\nfoo = 42"- let expectedStart = "{-# LINE 1"- let expectedModule = "module Foo where"- let expectedTypeImport = "import Data.Text (Text)"+ let input = "module Foo where\nrequire Data.Text\nfoo = 42"+ let expectedStart = "{-# LINE 1"+ let expectedModule = "module Foo where"+ let expectedTypeImport = "import Data.Text (Text)" let expectedQualifiedImport = "import qualified Data.Text as Text"- let expectedContent = "foo = 42\n"- let actual = toString $ Require.transform False (Require.FileName "Foo.hs") "" input+ let expectedContent = "foo = 42\n"+ let actual = toString $ Require.transform False (Require.FileName "Foo.hs") "" input actual `shouldStartWith` expectedStart- actual `shouldContain` expectedModule- actual `shouldContain` expectedTypeImport- actual `shouldContain` expectedQualifiedImport- actual `shouldEndWith` expectedContent-+ actual `shouldContain` expectedModule+ actual `shouldContain` expectedTypeImport+ actual `shouldContain` expectedQualifiedImport+ actual `shouldEndWith` expectedContent it "aliases the modules properly" $ do let input = "require Data.Text as Foo" let expectedTypeImport = "import Data.Text (Text)" let expectedQualifiedImport = "import qualified Data.Text as Foo" let actual = toString $ Require.transform False (Require.FileName "Foo.hs") "" input- actual `shouldContain` expectedTypeImport- actual `shouldContain` expectedQualifiedImport-+ actual `shouldContain` expectedTypeImport+ actual `shouldContain` expectedQualifiedImport it "imports the types properly" $ do let input = "require Data.Text (Foo)" let expectedTypeImport = "import Data.Text (Foo)" let expectedQualifiedImport = "import qualified Data.Text as Text" let actual = toString $ Require.transform False (Require.FileName "Foo.hs") "" input- actual `shouldContain` expectedTypeImport- actual `shouldContain` expectedQualifiedImport-+ actual `shouldContain` expectedTypeImport+ actual `shouldContain` expectedQualifiedImport it "imports the types and aliases the modules properly" $ do let input = "require Data.Text as Quux (Foo)" let expectedTypeImport = "import Data.Text (Foo)" let expectedQualifiedImport = "import qualified Data.Text as Quux" let actual = toString $ Require.transform False (Require.FileName "Foo.hs") "" input- actual `shouldContain` expectedTypeImport- actual `shouldContain` expectedQualifiedImport-+ actual `shouldContain` expectedTypeImport+ actual `shouldContain` expectedQualifiedImport it "skips comments" $ do- let input = "require Data.Text -- test of comments"+ let input = "require Data.Text -- test of comments" let expected = "import Data.Text (Text)"- let actual = Require.transform False (Require.FileName "Foo.hs") "" input+ let actual = Require.transform False (Require.FileName "Foo.hs") "" input expected `Text.isInfixOf` actual- it "allows empty parentheses" $ do- let input = "require Data.Text ()"- let expected1 = "import Data.Text ()"- let expected2 = "import qualified Data.Text as Text"- let actual = lines $ Require.transform False (Require.FileName "Foo.hs") "" input+ let input = "require Data.Text ()"+ let expected1 = "import Data.Text ()"+ let expected2 = "import qualified Data.Text as Text"+ let actual = lines $ Require.transform False (Require.FileName "Foo.hs") "" input actual `shouldSatisfy` elem expected1 actual `shouldSatisfy` elem expected2-