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 support for post qualified import formatting. #335

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
24 changes: 14 additions & 10 deletions data/stylish-haskell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -261,21 +261,25 @@ steps:
# Default: false
space_surround: false

# Enabling this argument will use the new GHC lib parse to format imports.
# Post qualify option moves any qualifies found in import declarations
# to the end of the declaration. This also adjust padding for any
# unqualified import declarations.
#
# This currently assumes a few things, it will assume that you want post
# qualified imports. It is also not as feature complete as the old
# imports formatting.
# - true: Qualified as <module name> is moved to the end of the
# declaration.
#
# It does not remove redundant lines or merge lines. As such, the full
# feature scope is still pending.
# > import Data.Bar
# > import Data.Foo qualified as F
#
# It _is_ however, a fine alternative if you are using features that are
# not parseable by haskell src extensions and you're comfortable with the
# presets.
# - false: Qualified remains in the default location and unqualified
# imports are padded to align with qualified imports.
#
# > import Data.Bar
# > import qualified Data.Foo as F
#
# Default: false
ghc_lib_parser: false
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ghc_lib_parser option was removed since it is not used.

post_qualify: false


# Language pragmas
- language_pragmas:
Expand Down
1 change: 1 addition & 0 deletions lib/Language/Haskell/Stylish/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ parseImports config o = fmap (Imports.step columns) $ Imports.Options
<*> (o A..:? "list_padding" >>= maybe (pure $ def Imports.listPadding) parseListPadding)
<*> o A..:? "separate_lists" A..!= def Imports.separateLists
<*> o A..:? "space_surround" A..!= def Imports.spaceSurround
<*> o A..:? "post_qualify" A..!= def Imports.postQualified
where
def f = f Imports.defaultOptions

Expand Down
28 changes: 20 additions & 8 deletions lib/Language/Haskell/Stylish/Step/Imports.hs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ data Options = Options
, listPadding :: ListPadding
, separateLists :: Bool
, spaceSurround :: Bool
, postQualified :: Bool
} deriving (Eq, Show)

defaultOptions :: Options
Expand All @@ -73,6 +74,7 @@ defaultOptions = Options
, listPadding = LPConstant 4
, separateLists = True
, spaceSurround = False
, postQualified = False
}

data ListPadding
Expand Down Expand Up @@ -143,8 +145,8 @@ formatImports
-> NonEmpty (Located Import) -> Lines
formatImports maxCols options m moduleStats rawGroup =
runPrinter_ (PrinterConfig maxCols) [] m do
let
let

group
= NonEmpty.sortWith unLocated rawGroup
& mergeImports
Expand Down Expand Up @@ -177,10 +179,10 @@ printQualified Options{..} padNames stats (L _ decl) = do

when (isSafe decl) (putText "safe" >> space)

case (isQualified decl, isAnyQualified stats) of
(True, _) -> putText "qualified" >> space
(_, True) -> putText " " >> space
_ -> pure ()
case (postQualified, isQualified decl, isAnyQualified stats) of
(False, True, _) -> putText "qualified" >> space
(False, _, True) -> putText " " >> space
_ -> pure ()

moduleNamePosition <- length <$> getCurrentLine
forM_ (ideclPkgQual decl') $ \pkg -> putText (stringLiteral pkg) >> space
Expand All @@ -194,8 +196,11 @@ printQualified Options{..} padNames stats (L _ decl) = do
replicate (isLongestImport stats - importModuleNameLength decl) ' '

beforeAliasPosition <- length <$> getCurrentLine
forM_ (ideclAs decl') \(L _ name) ->
space >> putText "as" >> space >> putText (moduleNameString name)
if not postQualified then
forM_ (ideclAs decl') \(L _ name) ->
space >> putText "as" >> space >> putText (moduleNameString name)
else
pure ()
afterAliasPosition <- length <$> getCurrentLine

when (isHiding decl) (space >> putText "hiding")
Expand Down Expand Up @@ -301,6 +306,13 @@ printQualified Options{..} padNames stats (L _ decl) = do
modifyCurrentLine trimRight
newline >> putOffset >> printAsSingleLine)
printAsMultiLine)
if postQualified && isQualified decl
then do
space
putText "qualified"
forM_ (ideclAs decl') \(L _ name) ->
space >> putText "as" >> space >> putText (moduleNameString name)
else pure ()
where
-- We cannot wrap/repeat 'hiding' imports since then we would get multiple
-- imports hiding different things.
Expand Down
91 changes: 67 additions & 24 deletions tests/Language/Haskell/Stylish/Step/Imports/Tests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ tests = testGroup "Language.Haskell.Stylish.Step.Imports.Tests"
, testCase "case 28" case28
, testCase "case 29" case29
, testCase "case 30" case30
, testCase "case 31" case31
, testCase "case 32" case32
, testCase "case 33" case33
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
]
, testCase "case 34" case34
, testCase "case 35" case35
, testCase "case 36" case36
, testCase "case 37" case37
]



Expand Down Expand Up @@ -190,7 +193,7 @@ case07 = assertSnippet (step (Just 80) $ fromImportAlign File)
case08 :: Assertion
case08 =
let
options = Options Global WithAlias True Inline Inherit (LPConstant 4) True False
options = Options Global WithAlias True Inline Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 80) options) input
[ "module Herp where"
Expand All @@ -214,7 +217,7 @@ case08 =
case08b :: Assertion
case08b =
let
options = Options Global WithModuleName True Inline Inherit (LPConstant 4) True False
options = Options Global WithModuleName True Inline Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 80) options) input
["module Herp where"
Expand All @@ -237,7 +240,7 @@ case08b =
case09 :: Assertion
case09 =
let
options = Options Global WithAlias True Multiline Inherit (LPConstant 4) True False
options = Options Global WithAlias True Multiline Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 80) options) input
[ "module Herp where"
Expand Down Expand Up @@ -272,7 +275,7 @@ case09 =
case10 :: Assertion
case10 =
let
options = Options Group WithAlias True Multiline Inherit (LPConstant 4) True False
options = Options Group WithAlias True Multiline Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 40) options) input
[ "module Herp where"
Expand Down Expand Up @@ -313,7 +316,7 @@ case10 =
case11 :: Assertion
case11 =
let
options = Options Group NewLine True Inline Inherit (LPConstant 4) True False
options = Options Group NewLine True Inline Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 80) options) input
[ "module Herp where"
Expand All @@ -340,7 +343,7 @@ case11 =
case11b :: Assertion
case11b =
let
options = Options Group WithModuleName True Inline Inherit (LPConstant 4) True False
options = Options Group WithModuleName True Inline Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 80) options) input
[ "module Herp where"
Expand All @@ -363,7 +366,7 @@ case11b =
case12 :: Assertion
case12 =
let
options = Options Group NewLine True Inline Inherit (LPConstant 2) True False
options = Options Group NewLine True Inline Inherit (LPConstant 2) True False False
in
assertSnippet (step (Just 80) options)
[ "import Data.List (map)"
Expand All @@ -377,7 +380,7 @@ case12 =
case12b :: Assertion
case12b =
let
options = Options Group WithModuleName True Inline Inherit (LPConstant 2) True False
options = Options Group WithModuleName True Inline Inherit (LPConstant 2) True False False
in
assertSnippet (step (Just 80) options)
["import Data.List (map)"]
Expand All @@ -388,7 +391,7 @@ case12b =
case13 :: Assertion
case13 =
let
options = Options None WithAlias True InlineWithBreak Inherit (LPConstant 4) True False
options = Options None WithAlias True InlineWithBreak Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 80) options)
[ "import qualified Data.List as List (concat, foldl, foldr, head, init,"
Expand All @@ -402,7 +405,7 @@ case13 =
case13b :: Assertion
case13b =
let
options = Options None WithModuleName True InlineWithBreak Inherit (LPConstant 4) True False
options = Options None WithModuleName True InlineWithBreak Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 80) options)
[ "import qualified Data.List as List (concat, foldl, foldr, head, init,"
Expand All @@ -418,7 +421,7 @@ case13b =
case14 :: Assertion
case14 =
let
options = Options None WithAlias True InlineWithBreak Inherit (LPConstant 10) True False
options = Options None WithAlias True InlineWithBreak Inherit (LPConstant 10) True False False
in
assertSnippet (step (Just 80) options)
[ "import qualified Data.List as List (concat, map, null, reverse, tail, (++))"
Expand All @@ -431,7 +434,7 @@ case14 =
case15 :: Assertion
case15 =
let
options = Options None AfterAlias True Multiline Inherit (LPConstant 4) True False
options = Options None AfterAlias True Multiline Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 80) options)
[ "import Data.Acid (AcidState)"
Expand All @@ -456,7 +459,7 @@ case15 =
case16 :: Assertion
case16 =
let
options = Options None AfterAlias True Multiline Inherit (LPConstant 4) False False
options = Options None AfterAlias True Multiline Inherit (LPConstant 4) False False False
in
assertSnippet (step (Just 80) options)
[ "import Data.Acid (AcidState)"
Expand All @@ -479,7 +482,7 @@ case16 =
case17 :: Assertion
case17 =
let
options = Options None AfterAlias True Multiline Inherit (LPConstant 4) True False
options = Options None AfterAlias True Multiline Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 80) options)
[ "import Control.Applicative (Applicative ((<*>),pure))"
Expand All @@ -496,7 +499,7 @@ case17 =
case18 :: Assertion
case18 =
let
options = Options None AfterAlias True InlineToMultiline Inherit (LPConstant 4) True False
options = Options None AfterAlias True InlineToMultiline Inherit (LPConstant 4) True False False
in
assertSnippet (step (Just 40) options)
[ "import Data.Foo as Foo (Bar, Baz, Foo)"
Expand All @@ -523,7 +526,7 @@ case18 =
case19 :: Assertion
case19 =
let
options = Options Global NewLine True InlineWithBreak RightAfter (LPConstant 17) True False
options = Options Global NewLine True InlineWithBreak RightAfter (LPConstant 17) True False False
in
assertSnippet (step (Just 40) options) case19input
----------------------------------------
Expand All @@ -539,7 +542,7 @@ case19 =
case19b :: Assertion
case19b =
let
options = Options File NewLine True InlineWithBreak RightAfter (LPConstant 17) True False
options = Options File NewLine True InlineWithBreak RightAfter (LPConstant 17) True False False
in
assertSnippet (step (Just 40) options) case19input
----------------------------------------
Expand All @@ -554,7 +557,7 @@ case19b =
case19c :: Assertion
case19c =
let
options = Options File NewLine True InlineWithBreak RightAfter LPModuleName True False
options = Options File NewLine True InlineWithBreak RightAfter LPModuleName True False False
in
assertSnippet (step (Just 40) options) case19input
----------------------------------------
Expand All @@ -569,7 +572,7 @@ case19c =
case19d :: Assertion
case19d =
let
options = Options Global NewLine True InlineWithBreak RightAfter LPModuleName True False
options = Options Global NewLine True InlineWithBreak RightAfter LPModuleName True False False
in
assertSnippet (step (Just 40) options) case19input
----------------------------------------
Expand Down Expand Up @@ -665,7 +668,7 @@ case22 = assertSnippet (step (Just 80) defaultOptions)
case23 :: Assertion
case23 =
let
options = Options None AfterAlias False Inline Inherit (LPConstant 4) True True
options = Options None AfterAlias False Inline Inherit (LPConstant 4) True True False
in
assertSnippet (step (Just 40) options)
[ "import Data.Acid (AcidState)"
Expand All @@ -690,7 +693,7 @@ case23 =
case23b :: Assertion
case23b =
let
options = Options None WithModuleName False Inline Inherit (LPConstant 4) True True
options = Options None WithModuleName False Inline Inherit (LPConstant 4) True True False
in
assertSnippet (step (Just 40) options)
[ "import Data.Acid (AcidState)"
Expand All @@ -716,7 +719,7 @@ case23b =
case24 :: Assertion
case24 =
let
options = Options None AfterAlias False InlineWithBreak Inherit (LPConstant 4) True True
options = Options None AfterAlias False InlineWithBreak Inherit (LPConstant 4) True True False
in
assertSnippet (step (Just 40) options)
[ "import Data.Acid (AcidState)"
Expand All @@ -740,7 +743,7 @@ case24 =
case25 :: Assertion
case25 =
let
options = Options Group AfterAlias False Multiline Inherit (LPConstant 4) False False
options = Options Group AfterAlias False Multiline Inherit (LPConstant 4) False False False
in
assertSnippet (step (Just 80) options)
[ "import Data.Acid (AcidState)"
Expand Down Expand Up @@ -807,7 +810,7 @@ case28 = assertSnippet (step (Just 80) $ fromImportAlign Global)
, "import Data.Set (empty, nub)"
]
[ "import Control.Monad"
, "import qualified Data.Aeson as JSON"
, "import qualified Data.Aeson as JSON"
, "import Data.Default.Class (Default (def))"
, ""
, "import Data.Maybe (Maybe (Just, Nothing))"
Expand Down Expand Up @@ -842,3 +845,43 @@ case30 :: Assertion
case30 = assertSnippet (step Nothing defaultOptions {separateLists = False})
["import Data.Monoid (Monoid (..))"]
["import Data.Monoid (Monoid(..))"]

--------------------------------------------------------------------------------
case31 :: Assertion
case31 = assertSnippet (step Nothing defaultOptions {postQualified = True})
["import Data.Monoid (Monoid (..))"]
["import Data.Monoid (Monoid (..))"]

--------------------------------------------------------------------------------
case32 :: Assertion
case32 = assertSnippet (step Nothing defaultOptions {postQualified = True})
["import qualified Data.Monoid as M"]
["import Data.Monoid qualified as M"]

--------------------------------------------------------------------------------
case33 :: Assertion
case33 = assertSnippet (step Nothing defaultOptions {postQualified = True})
[ "import Data.Default.Class (Default(def))"
, "import qualified Data.Aeson as JSON"
, "import qualified Data.Aeson as JSON"
, "import Control.Monad"
, "import Control.Monad"
, ""
, "import Data.Maybe (Maybe (Just, Nothing))"
, "import qualified Data.Maybe.Extra (Maybe(Just, Nothing))"
, ""
, "import Data.Foo (Foo (Foo,Bar), Goo(Goo))"
, "import Data.Foo (Foo (Foo,Bar))"
, "import Data.Set (empty, intersect)"
, "import Data.Set (empty, nub)"
]
[ "import Control.Monad"
, "import Data.Aeson qualified as JSON"
, "import Data.Default.Class (Default (def))"
, ""
, "import Data.Maybe (Maybe (Just, Nothing))"
, "import Data.Maybe.Extra (Maybe (Just, Nothing)) qualified"
, ""
, "import Data.Foo (Foo (Bar, Foo), Goo (Goo))"
, "import Data.Set (empty, intersect, nub)"
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
]
]
--------------------------------------------------------------------------------
case34 :: Assertion
case34 = assertSnippet (step Nothing defaultOptions {postQualified = True})
[ "import qualified Data.Aeson as JSON (Value)"
]
[ "import Data.Aeson qualified as JSON (Value)"
]
--------------------------------------------------------------------------------
case35 :: Assertion
case35 = assertSnippet (step Nothing defaultOptions {postQualified = True})
[ "import Data.Aeson qualified as JSON (Value)"
]
[ "import Data.Aeson qualified as JSON (Value)"
]
--------------------------------------------------------------------------------
case36 :: Assertion
case36 = assertSnippet (step Nothing defaultOptions {postQualified = True})
[ "import qualified Data.Aeson as JSON (Value)"
, "import qualified Data.Aeson as JSON (encode, decode)"
]
[ "import Data.Aeson qualified as JSON (Value, decode, encode)"
]
--------------------------------------------------------------------------------
case37 :: Assertion
case37 = assertSnippet (step Nothing defaultOptions {postQualified = True})
[ "import Data.Aeson qualified as JSON (Value)"
, "import Data.Aeson qualified as JSON (encode, decode)"
]
[ "import Data.Aeson qualified as JSON (Value, decode, encode)"
]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jhmcstanton I've also added these changes as a PR to your branch, in case that's easier to work with