Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EntityListAlign to imports step #469

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions data/stylish-haskell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,29 @@ steps:
# Default: inline
long_list_align: inline

# Entity list align can be used to alter alignment of import list entities
# like 'Bar' record names in the following example:
#
# > import Foo (Bar(x, y))
#
# - inline: Entities are always formatted inline
#
# - multiline: The list of entities will start on new line,
# except for a case, when it cotains only a single entity.
#
# > import Foo
# > (Bar
# > ( x
# > , y
# > )
# > )
#
# This is useful in combination with long_list_aling multiline
# and similar to get a git friendly formatting.
#
# Default: inline
entity_list_align: inline

# Align empty list (importing instances)
#
# Empty list align has following options
Expand Down
6 changes: 6 additions & 0 deletions lib/Language/Haskell/Stylish/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ parseImports config o = fmap (Imports.step columns) $ Imports.Options
<*> (o A..:? "pad_module_names" A..!= def Imports.padModuleNames)
<*> (o A..:? "long_list_align" >>= parseEnum longListAligns (def Imports.longListAlign))
<*> (o A..:? "empty_list_align" >>= parseEnum emptyListAligns (def Imports.emptyListAlign))
<*> (o A..:? "entity_list_align" >>= parseEnum entityListAligns (def Imports.entityListAlign))
-- Note that padding has to be at least 1. Default is 4.
<*> (o A..:? "list_padding" >>= maybe (pure $ def Imports.listPadding) parseListPadding)
<*> o A..:? "separate_lists" A..!= def Imports.separateLists
Expand Down Expand Up @@ -322,6 +323,11 @@ parseImports config o = fmap (Imports.step columns) $ Imports.Options
, ("right_after", Imports.RightAfter)
]

entityListAligns =
[ ("inline", Imports.ELInline)
, ("multiline", Imports.ELMultiline)
]

parseListPadding = \case
A.String "module_name" -> pure Imports.LPModuleName
A.Number n | n >= 1 -> pure $ Imports.LPConstant (truncate n)
Expand Down
114 changes: 84 additions & 30 deletions lib/Language/Haskell/Stylish/Step/Imports.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module Language.Haskell.Stylish.Step.Imports
, ImportAlign (..)
, ListAlign (..)
, LongListAlign (..)
, EntityListAlign (..)
, EmptyListAlign (..)
, ListPadding (..)
, GroupRule (..)
Expand Down Expand Up @@ -61,32 +62,34 @@ import Language.Haskell.Stylish.Util

--------------------------------------------------------------------------------
data Options = Options
{ importAlign :: ImportAlign
, listAlign :: ListAlign
, padModuleNames :: Bool
, longListAlign :: LongListAlign
, emptyListAlign :: EmptyListAlign
, listPadding :: ListPadding
, separateLists :: Bool
, spaceSurround :: Bool
, postQualified :: Bool
, groupImports :: Bool
, groupRules :: [GroupRule]
{ importAlign :: ImportAlign
, listAlign :: ListAlign
, padModuleNames :: Bool
, longListAlign :: LongListAlign
, emptyListAlign :: EmptyListAlign
, entityListAlign :: EntityListAlign
, listPadding :: ListPadding
, separateLists :: Bool
, spaceSurround :: Bool
, postQualified :: Bool
, groupImports :: Bool
, groupRules :: [GroupRule]
} deriving (Eq, Show)

defaultOptions :: Options
defaultOptions = Options
{ importAlign = Global
, listAlign = AfterAlias
, padModuleNames = True
, longListAlign = Inline
, emptyListAlign = Inherit
, listPadding = LPConstant 4
, separateLists = True
, spaceSurround = False
, postQualified = False
, groupImports = False
, groupRules = [defaultGroupRule]
{ importAlign = Global
, listAlign = AfterAlias
, padModuleNames = True
, longListAlign = Inline
, emptyListAlign = Inherit
, entityListAlign = ELInline
, listPadding = LPConstant 4
, separateLists = True
, spaceSurround = False
, postQualified = False
, groupImports = False
, groupRules = [defaultGroupRule]
}
where defaultGroupRule = GroupRule
{ match = unsafeParsePattern ".*"
Expand Down Expand Up @@ -125,6 +128,28 @@ data LongListAlign
| Multiline -- multiline
deriving (Eq, Show)

-- | Alignment of lists of constructors, class methods, fields names
-- in import lists entities.
-- @ELMultiline@ causes
--
-- import Foo (Bar(x, y))
--
-- To be expanded to
--
-- import Foo
-- (Bar
-- ( x
-- , y
-- )
-- )
--
-- This is useful in combination with @LongListAlign@ @Multiline@
-- to get a git friendly formatting.
data EntityListAlign
= ELInline -- default
| ELMultiline -- multiline
deriving (Eq, Show)

-- | A rule for grouping imports that specifies which module names
-- belong in a group and (optionally) how to break them up into
-- sub-groups.
Expand Down Expand Up @@ -347,7 +372,7 @@ groupByRules rules allImports = toList $ go rules allImports Seq.empty
--------------------------------------------------------------------------------
printQualified
:: Options -> Bool -> ImportStats -> GHC.LImportDecl GHC.GhcPs -> P ()
printQualified Options{..} padNames stats ldecl = do
printQualified options@Options{..} padNames stats ldecl = do
putText "import" >> space

case (isSource decl, isAnySource stats) of
Expand Down Expand Up @@ -409,7 +434,7 @@ printQualified Options{..} padNames stats ldecl = do
Just limports -> do
let imports = GHC.unLoc limports
printedImports = flagEnds $ -- [P ()]
(printImport separateLists) . GHC.unLoc <$>
(printImport options) . GHC.unLoc <$>
prepareImportList imports

-- Since we might need to output the import module name several times, we
Expand Down Expand Up @@ -506,27 +531,56 @@ printQualified Options{..} padNames stats ldecl = do


--------------------------------------------------------------------------------
printImport :: Bool -> GHC.IE GHC.GhcPs -> P ()
printImport :: Options -> GHC.IE GHC.GhcPs -> P ()
printImport _ (GHC.IEVar _ name) = do
printIeWrappedName name
printImport _ (GHC.IEThingAbs _ name) = do
printIeWrappedName name
printImport separateLists (GHC.IEThingAll _ name) = do
printImport Options{..} (GHC.IEThingAll _ name) = do
printIeWrappedName name
when separateLists space
putText "(..)"
printImport _ (GHC.IEModuleContents _ modu) = do
putText "module"
space
putText . GHC.moduleNameString $ GHC.unLoc modu
printImport separateLists (GHC.IEThingWith _ name wildcard imps) = do
printImport Options{..} (GHC.IEThingWith _ name wildcard imps) = do
printIeWrappedName name
when separateLists space

let ellipsis = case wildcard of
GHC.IEWildcard _position -> [putText ".."]
GHC.NoIEWildcard -> []
parenthesize $
sep (comma >> space) (ellipsis <> fmap printIeWrappedName imps)

importNamePosition <- length <$> getCurrentLine

let putOffset = putText $ replicate offset ' '
offset = case listPadding of
LPConstant n -> 2 + 2 * n
LPModuleName -> importNamePosition

entityListSeparator = case entityListAlign of
ELInline -> comma >> space
ELMultiline -> newline >> putOffset >> comma >> space

printInline = do
when separateLists space
parenthesize $
sep (comma >> space) (ellipsis <> fmap printIeWrappedName imps)

case entityListAlign of
ELInline -> printInline
ELMultiline -> do
if length imps == 1
then
printInline
else do
newline
putOffset
parenthesize $ do
space
sep entityListSeparator (ellipsis <> fmap printIeWrappedName imps)
newline
putOffset
printImport _ (GHC.IEGroup _ _ _ ) =
error "Language.Haskell.Stylish.Printer.Imports.printImportExport: unhandled case 'IEGroup'"
printImport _ (GHC.IEDoc _ _) =
Expand Down
5 changes: 4 additions & 1 deletion lib/Language/Haskell/Stylish/Step/ModuleHeader.hs
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,7 @@ printMultiLineExportList conf exports = do
-- NOTE(jaspervdj): This code is almost the same as the import printing in
-- 'Imports' and should be merged.
putExport :: Config -> GHC.LIE GHC.GhcPs -> P ()
putExport conf = Imports.printImport (separateLists conf) . GHC.unLoc
putExport conf =
Imports.printImport
(Imports.defaultOptions { Imports.separateLists = separateLists conf})
. GHC.unLoc
20 changes: 20 additions & 0 deletions tests/Language/Haskell/Stylish/Step/Imports/Tests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ tests = testGroup "Language.Haskell.Stylish.Step.Imports.Tests"
, testCase "case 44a" case44a
, testCase "case 44b" case44b
, testCase "case 44c" case44c
, testCase "case 45" case45
]


Expand Down Expand Up @@ -1291,3 +1292,22 @@ case44c =
]
, importAlign = None
}


--------------------------------------------------------------------------------
case45 :: Assertion
case45 =
let
options = defaultOptions { importAlign = None, entityListAlign = ELMultiline }
in
assertSnippet (step (Just 40) options)
case45input
[ "import Foo (Bar"
, " ( x"
, " , y"
, " ))"
]

case45input :: Snippet
case45input =
[ "import Foo (Bar(x, y))" ]