stylish-haskell 0.7.1.0 → 0.8.0.0
raw patch · 9 files changed
+223/−27 lines, 9 filesdep ~HUnitdep ~aesondep ~sybPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: HUnit, aeson, syb
API changes (from Hackage documentation)
+ Language.Haskell.Stylish.Step.Imports: instance GHC.Classes.Eq l => GHC.Classes.Eq (Language.Haskell.Stylish.Step.Imports.ImportEntity l)
+ Language.Haskell.Stylish.Step.Imports: instance GHC.Classes.Ord l => GHC.Base.Monoid (Language.Haskell.Stylish.Step.Imports.ImportPortion l)
+ Language.Haskell.Stylish.Step.Imports: instance GHC.Classes.Ord l => GHC.Classes.Ord (Language.Haskell.Stylish.Step.Imports.ImportEntity l)
Files
- CHANGELOG +14/−0
- data/stylish-haskell.yaml +4/−4
- lib/Language/Haskell/Stylish/Parse.hs +0/−1
- lib/Language/Haskell/Stylish/Step/Imports.hs +102/−13
- lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs +1/−1
- stylish-haskell.cabal +8/−8
- tests/Language/Haskell/Stylish/Parse/Tests.hs +6/−0
- tests/Language/Haskell/Stylish/Step/Imports/Tests.hs +58/−0
- tests/Language/Haskell/Stylish/Step/LanguagePragmas/Tests.hs +30/−0
CHANGELOG view
@@ -1,5 +1,19 @@ # CHANGELOG +- 0.8.0.0+ * Remove `MagicHash` from whitelisted language extensions, since it was+ causing parsing errors (by Artyom Kazak)+ * Don't leave a `#-}` hanging on the next line when `language_pragmas`+ is set to `compact` and the `#-}` doesn't fit into character limit+ (by Artyom Kazak)+ * Deduplicate import specs (i.e. `import Foo (a, a, b)` becomes+ `import Foo (a, b)`) (by Artyom Kazak)+ * Take package imports into account when prettifying imports+ (by Artyom Kazak)+ * Bump `aeson` to 1.2+ * Bump `syb` to 0.7+ * Bump `HUnit` to 1.6+ - 0.7.1.0 * Keep `safe` and `{-# SOURCE #-}` import annotations (by Moritz Drexl)
data/stylish-haskell.yaml view
@@ -41,7 +41,7 @@ # Default: global. align: global - # Folowing options affect only import list alignment.+ # The following options affect only import list alignment. # # List align has following options: #@@ -75,7 +75,7 @@ # short enough to fit to single line. Otherwise it'll be multiline. # # - multiline: One line per import list entry.- # Type with contructor list acts like single import.+ # Type with constructor list acts like single import. # # > import qualified Data.Map as M # > ( empty@@ -109,7 +109,7 @@ # Useful for 'file' and 'group' align settings. list_padding: 4 - # Separate lists option affects formating of import list for type+ # Separate lists option affects formatting of import list for type # or class. The only difference is single space between type and list # of constructors, selectors and class functions. #@@ -142,7 +142,7 @@ # Align affects alignment of closing pragma brackets. #- # - true: Brackets are aligned in same collumn.+ # - true: Brackets are aligned in same column. # # - false: Brackets are not aligned together. There is only one space # between actual import and closing bracket.
lib/Language/Haskell/Stylish/Parse.hs view
@@ -24,7 +24,6 @@ [ H.GADTs , H.HereDocuments , H.KindSignatures- , H.MagicHash , H.NewQualifiedOperators , H.PatternGuards , H.StandaloneDeriving
lib/Language/Haskell/Stylish/Step/Imports.hs view
@@ -16,10 +16,13 @@ -------------------------------------------------------------------------------- import Control.Arrow ((&&&)) import Control.Monad (void)+import Data.Monoid ((<>)) import Data.Char (toLower) import Data.List (intercalate, sortBy) import Data.Maybe (isJust, maybeToList) import Data.Ord (comparing)+import qualified Data.Map as M+import qualified Data.Set as S import qualified Language.Haskell.Exts as H import qualified Data.Aeson as A import qualified Data.Aeson.Types as A@@ -81,7 +84,17 @@ | Multiline deriving (Eq, Show) + --------------------------------------------------------------------------------++modifyImportSpecs :: ([H.ImportSpec l] -> [H.ImportSpec l])+ -> H.ImportDecl l -> H.ImportDecl l+modifyImportSpecs f imp = imp {H.importSpecs = f' <$> H.importSpecs imp}+ where+ f' (H.ImportSpecList l h specs) = H.ImportSpecList l h (f specs)+++-------------------------------------------------------------------------------- imports :: H.Module l -> [H.ImportDecl l] imports (H.Module _ _ _ is _) = is imports _ = []@@ -91,19 +104,97 @@ importName :: H.ImportDecl l -> String importName i = let (H.ModuleName _ n) = H.importModule i in n +importPackage :: H.ImportDecl l -> Maybe String+importPackage i = H.importPkg i + --------------------------------------------------------------------------------+-- | A "compound import name" is import's name and package (if present). For+-- instance, if you have an import @Foo.Bar@ from package @foobar@, the full+-- name will be @"foobar" Foo.Bar@.+compoundImportName :: H.ImportDecl l -> String+compoundImportName i =+ case importPackage i of+ Nothing -> importName i+ Just pkg -> show pkg ++ " " ++ importName i+++-------------------------------------------------------------------------------- longestImport :: [H.ImportDecl l] -> Int-longestImport = maximum . map (length . importName)+longestImport = maximum . map (length . compoundImportName) -------------------------------------------------------------------------------- -- | Compare imports for ordering compareImports :: H.ImportDecl l -> H.ImportDecl l -> Ordering-compareImports = comparing (map toLower . importName &&& H.importQualified)+compareImports =+ comparing (map toLower . importName &&&+ fmap (map toLower) . importPackage &&&+ H.importQualified) --------------------------------------------------------------------------------+-- | Remove (or merge) duplicated import specs.+--+-- * When something is mentioned twice, it's removed: @A, A@ -> A+-- * More general forms take priority: @A, A(..)@ -> @A(..)@+-- * Sometimes we have to combine imports: @A(x), A(y)@ -> @A(x, y)@+--+-- Import specs are always sorted by subsequent steps so we don't have to care+-- about preserving order.+deduplicateImportSpecs :: Ord l => H.ImportDecl l -> H.ImportDecl l+deduplicateImportSpecs =+ modifyImportSpecs $+ map recomposeImportSpec .+ M.toList . M.fromListWith (<>) .+ map decomposeImportSpec++-- | What we are importing (variable, class, etc)+data ImportEntity l+ -- | A variable+ = ImportVar l (H.Name l)+ -- | Something that can be imported partially+ | ImportClassOrData l (H.Name l)+ -- | Something else ('H.IAbs')+ | ImportOther l (H.Namespace l) (H.Name l)+ deriving (Eq, Ord)++-- | What we are importing from an 'ImportClassOrData'+data ImportPortion l+ = ImportSome [H.CName l] -- ^ @A(x, y, z)@+ | ImportAll -- ^ @A(..)@++instance Ord l => Monoid (ImportPortion l) where+ mempty = ImportSome []+ mappend (ImportSome a) (ImportSome b) = ImportSome (setUnion a b)+ mappend _ _ = ImportAll++-- | O(n log n) union.+setUnion :: Ord a => [a] -> [a] -> [a]+setUnion a b = S.toList (S.fromList a `S.union` S.fromList b)++decomposeImportSpec :: H.ImportSpec l -> (ImportEntity l, ImportPortion l)+decomposeImportSpec x = case x of+ -- I checked and it looks like namespace's 'l' is always equal to x's 'l'+ H.IAbs l space n -> case space of+ H.NoNamespace _ -> (ImportClassOrData l n, ImportSome [])+ H.TypeNamespace _ -> (ImportOther l space n, ImportSome [])+ H.PatternNamespace _ -> (ImportOther l space n, ImportSome [])+ H.IVar l n -> (ImportVar l n, ImportSome [])+ H.IThingAll l n -> (ImportClassOrData l n, ImportAll)+ H.IThingWith l n names -> (ImportClassOrData l n, ImportSome names)++recomposeImportSpec :: (ImportEntity l, ImportPortion l) -> H.ImportSpec l+recomposeImportSpec (e, p) = case e of+ ImportClassOrData l n -> case p of+ ImportSome [] -> H.IAbs l (H.NoNamespace l) n+ ImportSome names -> H.IThingWith l n names+ ImportAll -> H.IThingAll l n+ ImportVar l n -> H.IVar l n+ ImportOther l space n -> H.IAbs l space n+++-------------------------------------------------------------------------------- -- | The implementation is a bit hacky to get proper sorting for input specs: -- constructors first, followed by functions, and then operators. compareImportSpecs :: H.ImportSpec l -> H.ImportSpec l -> Ordering@@ -119,10 +210,7 @@ -------------------------------------------------------------------------------- -- | Sort the input spec list inside an 'H.ImportDecl' sortImportSpecs :: H.ImportDecl l -> H.ImportDecl l-sortImportSpecs imp = imp {H.importSpecs = sort' <$> H.importSpecs imp}- where- sort' (H.ImportSpecList l h specs) = H.ImportSpecList l h $- sortBy compareImportSpecs specs+sortImportSpecs = modifyImportSpecs (sortBy compareImportSpecs) --------------------------------------------------------------------------------@@ -221,9 +309,9 @@ . withTail (", " ++)) ++ [")"]) - paddedBase = base $ padImport $ importName imp+ paddedBase = base $ padImport $ compoundImportName imp - paddedNoSpecBase = base $ padImportNoSpec $ importName imp+ paddedNoSpecBase = base $ padImportNoSpec $ compoundImportName imp padImport = if hasExtras && padName then padRight longest@@ -233,12 +321,11 @@ then padRight longest else id - base' baseName importAs hasHiding' = unwords $ concat $ filter (not . null)+ base' baseName importAs hasHiding' = unwords $ concat $ [ ["import"] , source , safe , qualified- , show <$> maybeToList (H.importPkg imp) , [baseName] , importAs , hasHiding'@@ -248,9 +335,10 @@ ["as " ++ as | H.ModuleName _ as <- maybeToList $ H.importAs imp] ["hiding" | hasHiding] - inlineBaseLength = length $ base' (padImport $ importName imp) [] []+ inlineBaseLength = length $+ base' (padImport $ compoundImportName imp) [] [] - afterAliasBaseLength = length $ base' (padImport $ importName imp)+ afterAliasBaseLength = length $ base' (padImport $ compoundImportName imp) ["as " ++ as | H.ModuleName _ as <- maybeToList $ H.importAs imp] [] (hasHiding, importSpecs) = case H.importSpecs imp of@@ -320,7 +408,8 @@ ] ls where- imps = map sortImportSpecs $ imports $ fmap linesFromSrcSpan module'+ imps = map (sortImportSpecs . deduplicateImportSpecs) $+ imports $ fmap linesFromSrcSpan module' longest = longestImport imps groups = groupAdjacent [(H.ann i, i) | i <- imps]
lib/Language/Haskell/Stylish/Step/LanguagePragmas.hs view
@@ -56,7 +56,7 @@ -------------------------------------------------------------------------------- compactPragmas :: Int -> [String] -> Lines compactPragmas columns pragmas' = wrap columns "{-# LANGUAGE" 13 $- map (++ ",") (init pragmas') ++ [last pragmas', "#-}"]+ map (++ ",") (init pragmas') ++ [last pragmas' ++ " #-}"] --------------------------------------------------------------------------------
stylish-haskell.cabal view
@@ -1,5 +1,5 @@ Name: stylish-haskell-Version: 0.7.1.0+Version: 0.8.0.0 Synopsis: Haskell code prettifier Homepage: https://github.com/jaspervdj/stylish-haskell License: BSD3@@ -49,7 +49,7 @@ Paths_stylish_haskell Build-depends:- aeson >= 0.6 && < 1.2,+ aeson >= 0.6 && < 1.3, base >= 4.8 && < 5, bytestring >= 0.9 && < 0.11, containers >= 0.3 && < 0.6,@@ -57,7 +57,7 @@ filepath >= 1.1 && < 1.5, haskell-src-exts >= 1.18 && < 1.20, mtl >= 2.0 && < 2.3,- syb >= 0.3 && < 0.7,+ syb >= 0.3 && < 0.8, yaml >= 0.7 && < 0.9 Executable stylish-haskell@@ -70,7 +70,7 @@ strict >= 0.3 && < 0.4, optparse-applicative >= 0.12 && < 0.14, -- Copied from regular dependencies...- aeson >= 0.6 && < 1.2,+ aeson >= 0.6 && < 1.3, base >= 4.8 && < 5, bytestring >= 0.9 && < 0.11, containers >= 0.3 && < 0.6,@@ -78,7 +78,7 @@ filepath >= 1.1 && < 1.5, haskell-src-exts >= 1.18 && < 1.20, mtl >= 2.0 && < 2.3,- syb >= 0.3 && < 0.7,+ syb >= 0.3 && < 0.8, yaml >= 0.7 && < 0.9 Test-suite stylish-haskell-tests@@ -112,11 +112,11 @@ Language.Haskell.Stylish.Verbose Build-depends:- HUnit >= 1.2 && < 1.6,+ HUnit >= 1.2 && < 1.7, test-framework >= 0.4 && < 0.9, test-framework-hunit >= 0.2 && < 0.4, -- Copied from regular dependencies...- aeson >= 0.6 && < 1.2,+ aeson >= 0.6 && < 1.3, base >= 4.8 && < 5, bytestring >= 0.9 && < 0.11, containers >= 0.3 && < 0.6,@@ -124,7 +124,7 @@ filepath >= 1.1 && < 1.5, haskell-src-exts >= 1.18 && < 1.20, mtl >= 2.0 && < 2.3,- syb >= 0.3 && < 0.7,+ syb >= 0.3 && < 0.8, yaml >= 0.7 && < 0.9 Source-repository head
tests/Language/Haskell/Stylish/Parse/Tests.hs view
@@ -27,6 +27,7 @@ , testCase "StandalonDeriving extension" testStandaloneDeriving , testCase "UnicodeSyntax extension" testUnicodeSyntax , testCase "XmlSyntax regression" testXmlSyntaxRegression+ , testCase "MagicHash regression" testMagicHashRegression ] --------------------------------------------------------------------------------@@ -118,6 +119,11 @@ testXmlSyntaxRegression :: Assertion testXmlSyntaxRegression = assert $ isRight $ parseModule [] Nothing $ unlines [ "smaller a b = a <b"+ ]++testMagicHashRegression :: Assertion+testMagicHashRegression = assert $ isRight $ parseModule [] Nothing $ unlines+ [ "xs = \"foo\"#|1#|'a'#|bar#|Nil" ] --------------------------------------------------------------------------------
tests/Language/Haskell/Stylish/Step/Imports/Tests.hs view
@@ -47,6 +47,8 @@ , testCase "case 19d" case19c , testCase "case 19d" case19d , testCase "case 20" case20+ , testCase "case 21" case21+ , testCase "case 22" case22 ] @@ -530,4 +532,60 @@ , "import {-# SOURCE #-} qualified Data.Text as T" , "import qualified Data.Map as Map" , "import Data.Set (empty)"+ ]++--------------------------------------------------------------------------------+case21 :: Assertion+case21 = expected+ @=? testStep (step 80 defaultOptions) input'+ where+ expected = unlines+ [ "{-# LANGUAGE ExplicitNamespaces #-}"+ , "import X1 (A, B, C)"+ , "import X2 (A, B, C)"+ , "import X3 (A (..))"+ , "import X4 (A (..))"+ , "import X5 (A (..))"+ , "import X6 (A (a, b, c), B (m, n, o))"+ , "import X7 (a, b, c)"+ , "import X8 (type (+), (+))"+ , "import X9 hiding (x, y, z)"+ ]+ input' = unlines+ [ "{-# LANGUAGE ExplicitNamespaces #-}"+ , "import X1 (A, B, A, C, A, B, A)"+ , "import X2 (C(), B(), A())"+ , "import X3 (A(..))"+ , "import X4 (A, A(..))"+ , "import X5 (A(..), A(x))"+ , "import X6 (A(a,b), B(m,n), A(c), B(o))"+ , "import X7 (a, b, a, c)"+ , "import X8 (type (+), (+))"+ , "import X9 hiding (x, y, z, x)"+ ]++--------------------------------------------------------------------------------+case22 :: Assertion+case22 = expected+ @=? testStep (step 80 defaultOptions) input'+ where+ expected = unlines+ [ "{-# LANGUAGE PackageImports #-}"+ , "import A"+ , "import \"blah\" A"+ , "import \"foo\" A"+ , "import qualified \"foo\" A as X"+ , "import \"foo\" B (shortName, someLongName, someLongerName,"+ , " theLongestNameYet)"+ ]+ input' = unlines+ [ "{-# LANGUAGE PackageImports #-}"+ , "import A"+ , "import \"foo\" A"+ , "import \"blah\" A"+ , "import qualified \"foo\" A as X"+ -- this import fits into 80 chats without "foo",+ -- but doesn't fit when "foo" is included into the calculation+ , "import \"foo\" B (someLongName, someLongerName, " +++ "theLongestNameYet, shortName)" ]
tests/Language/Haskell/Stylish/Step/LanguagePragmas/Tests.hs view
@@ -26,6 +26,8 @@ , testCase "case 06" case06 , testCase "case 07" case07 , testCase "case 08" case08+ , testCase "case 09" case09+ , testCase "case 10" case10 ] @@ -166,4 +168,32 @@ [ "{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving, " ++ "TemplateHaskell #-}" , "{-# LANGUAGE TypeOperators, ViewPatterns #-}"+ ]+++--------------------------------------------------------------------------------+case09 :: Assertion+case09 = expected @=? testStep (step 80 Compact True False) input+ where+ input = unlines+ [ "{-# LANGUAGE DefaultSignatures, FlexibleInstances, LambdaCase, " +++ "TypeApplications"+ , " #-}"+ ]+ expected = unlines+ [ "{-# LANGUAGE DefaultSignatures, FlexibleInstances, LambdaCase,"+ , " TypeApplications #-}"+ ]++--------------------------------------------------------------------------------+case10 :: Assertion+case10 = expected @=? testStep (step 80 Compact True False) input+ where+ input = unlines+ [ "{-# LANGUAGE NondecreasingIndentation, ScopedTypeVariables,"+ , " TypeApplications #-}"+ ]+ expected = unlines+ [ "{-# LANGUAGE NondecreasingIndentation, ScopedTypeVariables, " +++ "TypeApplications #-}" ]