From 9b36847119f42d215f904fc821579bd7b5d76cf3 Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Wed, 3 Feb 2021 11:57:16 +0100
Subject: [PATCH 01/11] Add new step Signature that will format function
 signatures

The step is a NOP still but additional tests were already created
specifing how we anticipate the step to behave. This step is heavily
inspired by https://github.com/input-output-hk/ouroboros-network/blob/bf8579cc2ff2a7bc4ba23150eff659cfd1c6ccca/ouroboros-consensus/docs/StyleGuide.md
---
 .../Haskell/Stylish/Step/Signature.hs         |  10 ++
 stylish-haskell.cabal                         |   3 +
 .../Haskell/Stylish/Step/Signature/Tests.hs   | 144 ++++++++++++++++++
 tests/TestSuite.hs                            |   2 +
 4 files changed, 159 insertions(+)
 create mode 100644 lib/Language/Haskell/Stylish/Step/Signature.hs
 create mode 100644 tests/Language/Haskell/Stylish/Step/Signature/Tests.hs

diff --git a/lib/Language/Haskell/Stylish/Step/Signature.hs b/lib/Language/Haskell/Stylish/Step/Signature.hs
new file mode 100644
index 00000000..6b1124ce
--- /dev/null
+++ b/lib/Language/Haskell/Stylish/Step/Signature.hs
@@ -0,0 +1,10 @@
+module Language.Haskell.Stylish.Step.Signature where
+
+import           Language.Haskell.Stylish.Step
+
+data Config = Config
+  { maxColumnLength :: Int
+  }
+
+step :: Config -> Step
+step _ = makeStep "Signature" (\ls _ -> ls)
diff --git a/stylish-haskell.cabal b/stylish-haskell.cabal
index 8e870f59..162c9cd1 100644
--- a/stylish-haskell.cabal
+++ b/stylish-haskell.cabal
@@ -37,6 +37,7 @@ Library
     Language.Haskell.Stylish.Step.Imports
     Language.Haskell.Stylish.Step.ModuleHeader
     Language.Haskell.Stylish.Step.LanguagePragmas
+    Language.Haskell.Stylish.Step.Signature
     Language.Haskell.Stylish.Step.SimpleAlign
     Language.Haskell.Stylish.Step.Squash
     Language.Haskell.Stylish.Step.Tabs
@@ -137,6 +138,8 @@ Test-suite stylish-haskell-tests
     Language.Haskell.Stylish.Step.ModuleHeader.Tests
     Language.Haskell.Stylish.Step.LanguagePragmas
     Language.Haskell.Stylish.Step.LanguagePragmas.Tests
+    Language.Haskell.Stylish.Step.Signature
+    Language.Haskell.Stylish.Step.Signature.Tests
     Language.Haskell.Stylish.Step.SimpleAlign
     Language.Haskell.Stylish.Step.SimpleAlign.Tests
     Language.Haskell.Stylish.Step.Squash
diff --git a/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs b/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
new file mode 100644
index 00000000..b2a8edbf
--- /dev/null
+++ b/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
@@ -0,0 +1,144 @@
+{-# LANGUAGE OverloadedLists   #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Language.Haskell.Stylish.Step.Signature.Tests
+    ( tests
+    ) where
+
+import           Language.Haskell.Stylish.Step.Signature
+import           Language.Haskell.Stylish.Tests.Util (assertSnippet, testStep)
+import           Test.Framework                      (Test, testGroup)
+import           Test.Framework.Providers.HUnit      (testCase)
+import           Test.HUnit                          (Assertion, (@=?))
+
+tests :: Test
+tests = testGroup "Language.Haskell.Stylish.Step.Signature.Tests"
+    [ testCase "do not wrap signature if it fits max column length" case00
+    -- , testCase "wrap signature if it does not fit max column length" case01
+    -- , testCase "how it behaves when there is a list of constraints" case02
+    -- , testCase "how it behaves when there is a explicit forall" case03
+    -- , testCase "how it behaves when there is a explicit forall" case04
+    -- , testCase "how it behaves when there is a large function in the argument" case05
+    ]
+
+config :: Int -> Config
+config maxColumnLength = Config
+  { maxColumnLength = maxColumnLength
+  }
+
+case00 :: Assertion
+case00 = expected @=? testStep (step $ config 80) input
+  where
+    input = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar :: a -> b -> a"
+      , "fooBar v _ = v"
+      ]
+    expected = input
+
+case01 :: Assertion
+case01 = expected @=? testStep (step $ config 20) input
+  where
+    input = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar :: a -> b -> a"
+      , "fooBar v _ = v"
+      ]
+    expected = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar ::"
+      , "     a"
+      , "  -> b"
+      , "  -> a"
+      , "fooBar v _ = v"
+      ]
+
+case02 :: Assertion
+case02 = expected @=? testStep (step $ config 20) input
+  where
+    input = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar :: (Eq a, Show b) => a -> b -> a"
+      , "fooBar v _ = v"
+      ]
+    expected = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar ::"
+      , "     (Eq a, Show b)"
+      , "  => a"
+      , "  -> b"
+      , "  -> a"
+      , "fooBar v _ = v"
+      ]
+
+case03 :: Assertion
+case03 = expected @=? testStep (step $ config 20) input
+  where
+    input = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar :: forall a . b. (Eq a, Show b) => a -> b -> a"
+      , "fooBar v _ = v"
+      ]
+    expected = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar ::"
+      , "     forall a . b."
+      , "     (Eq a, Show b)"
+      , "  => a"
+      , "  -> b"
+      , "  -> a"
+      , "fooBar v _ = v"
+      ]
+
+case04 :: Assertion
+case04 = expected @=? testStep (step $ config 20) input
+  where
+    input = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar :: forall a . b. c. (Eq a, Show b, Ord c) => a -> b -> c -> a"
+      , "fooBar v _ _ = v"
+      ]
+    expected = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar ::"
+      , "     forall a . b. ("
+      , "       Eq a"
+      , "     , Show b"
+      , "     , Ord c)"
+      , "     )"
+      , "  => a"
+      , "  -> b"
+      , "  -> a"
+      , "fooBar v _ = v"
+      ]
+
+case05 :: Assertion
+case05 = expected @=? testStep (step $ config 20) input
+  where
+    input = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar :: => a -> (forall c. Eq c => c -> a -> a) -> a"
+      , "fooBar v _ = v"
+      ]
+    expected = unlines
+      [ "module Herp where"
+      , ""
+      , "fooBar ::"
+      , "  => a"
+      , "  -> (   forall c. Eq c"
+      , "      => c"
+      , "      -> a"
+      , "      -> a"
+      , "     )"
+      , "  -> a"
+      , "fooBar v _ = v"
+      ]
diff --git a/tests/TestSuite.hs b/tests/TestSuite.hs
index 501821b2..4c73ddb9 100644
--- a/tests/TestSuite.hs
+++ b/tests/TestSuite.hs
@@ -16,6 +16,7 @@ import qualified Language.Haskell.Stylish.Step.Imports.Tests
 import qualified Language.Haskell.Stylish.Step.Imports.FelixTests
 import qualified Language.Haskell.Stylish.Step.ModuleHeader.Tests
 import qualified Language.Haskell.Stylish.Step.LanguagePragmas.Tests
+import qualified Language.Haskell.Stylish.Step.Signature.Tests
 import qualified Language.Haskell.Stylish.Step.SimpleAlign.Tests
 import qualified Language.Haskell.Stylish.Step.Squash.Tests
 import qualified Language.Haskell.Stylish.Step.Tabs.Tests
@@ -34,6 +35,7 @@ main = defaultMain
     , Language.Haskell.Stylish.Step.Imports.FelixTests.tests
     , Language.Haskell.Stylish.Step.LanguagePragmas.Tests.tests
     , Language.Haskell.Stylish.Step.ModuleHeader.Tests.tests
+    , Language.Haskell.Stylish.Step.Signature.Tests.tests
     , Language.Haskell.Stylish.Step.SimpleAlign.Tests.tests
     , Language.Haskell.Stylish.Step.Squash.Tests.tests
     , Language.Haskell.Stylish.Step.Tabs.Tests.tests

From b30c9447584f8930263a8fdfc381694a5b55ce17 Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Fri, 5 Feb 2021 12:51:59 +0100
Subject: [PATCH 02/11] Modify Signature to look top level func signatures and
 prepare for formatting

---
 .../Haskell/Stylish/Step/Signature.hs         | 40 ++++++++++++++++++-
 .../Haskell/Stylish/Step/Signature/Tests.hs   |  4 +-
 2 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/lib/Language/Haskell/Stylish/Step/Signature.hs b/lib/Language/Haskell/Stylish/Step/Signature.hs
index 6b1124ce..b4b067a5 100644
--- a/lib/Language/Haskell/Stylish/Step/Signature.hs
+++ b/lib/Language/Haskell/Stylish/Step/Signature.hs
@@ -1,10 +1,46 @@
+{-# LANGUAGE BlockArguments  #-}
+{-# LANGUAGE LambdaCase      #-}
+{-# LANGUAGE TypeApplications #-}
 module Language.Haskell.Stylish.Step.Signature where
 
+import           RdrName                          (RdrName)
+import           SrcLoc                           (GenLocated (..), Located)
+import GHC.Hs.Decls (HsDecl(..))
+import GHC.Hs.Binds (Sig(..))
+import           GHC.Hs.Extension                 (GhcPs)
+
+--------------------------------------------------------------------------------
+import           Language.Haskell.Stylish.Block
 import           Language.Haskell.Stylish.Step
+import           Language.Haskell.Stylish.Module
+import Language.Haskell.Stylish.Editor (change)
+import           Language.Haskell.Stylish.GHC (getStartLineUnsafe, getEndLineUnsafe)
+import           Language.Haskell.Stylish.Editor (Change, applyChanges)
+
+-- TODO unify with type alias from Data.hs
+type ChangeLine = Change String
 
 data Config = Config
-  { maxColumnLength :: Int
+  { cMaxColumns :: Int
   }
 
 step :: Config -> Step
-step _ = makeStep "Signature" (\ls _ -> ls)
+step cfg = makeStep "Signature" (\ls m -> applyChanges (changes cfg m) ls)
+
+changes :: Config -> Module -> [ChangeLine]
+changes cfg m = fmap (formatSignatureDecl cfg m) (topLevelFunctionSignatures m)
+
+topLevelFunctionSignatures :: Module -> [Located SignatureDecl]
+topLevelFunctionSignatures = queryModule @(Located (HsDecl GhcPs)) \case
+  L pos (SigD _ (TypeSig _ [name] _)) -> [L pos $ MkSignatureDecl name]
+  _ -> []
+
+data SignatureDecl = MkSignatureDecl
+  { sigName :: Located RdrName
+  }
+
+formatSignatureDecl :: Config -> Module -> Located SignatureDecl -> ChangeLine
+formatSignatureDecl _cfg _m ldecl = change originalDeclBlock id
+  where
+    originalDeclBlock =
+      Block (getStartLineUnsafe ldecl) (getEndLineUnsafe ldecl)
diff --git a/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs b/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
index b2a8edbf..267c6ea0 100644
--- a/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
+++ b/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
@@ -21,8 +21,8 @@ tests = testGroup "Language.Haskell.Stylish.Step.Signature.Tests"
     ]
 
 config :: Int -> Config
-config maxColumnLength = Config
-  { maxColumnLength = maxColumnLength
+config cMaxColumns = Config
+  { cMaxColumns = cMaxColumns
   }
 
 case00 :: Assertion

From d66228d1bc512bbe41160c7e0681c3716561605d Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Thu, 18 Feb 2021 12:28:06 +0100
Subject: [PATCH 03/11] Add noop change function

---
 lib/Language/Haskell/Stylish/Editor.hs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lib/Language/Haskell/Stylish/Editor.hs b/lib/Language/Haskell/Stylish/Editor.hs
index f71d1f6d..51fa533d 100644
--- a/lib/Language/Haskell/Stylish/Editor.hs
+++ b/lib/Language/Haskell/Stylish/Editor.hs
@@ -17,6 +17,7 @@ module Language.Haskell.Stylish.Editor
     , delete
     , deleteLine
     , insert
+    , noop
     ) where
 
 
@@ -84,6 +85,9 @@ applyChanges changes0
 change :: Block a -> ([a] -> [a]) -> Change a
 change = Change
 
+--------------------------------------------------------------------------------
+noop :: Block a -> Change a
+noop = flip change $ id
 
 --------------------------------------------------------------------------------
 -- | Change a single line for some other lines

From 0bd98512461cdf8e548e4c6783aafbc4d34dbcc2 Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Thu, 18 Feb 2021 12:29:12 +0100
Subject: [PATCH 04/11] Add getEndColumnUnsafe for a given Located element

---
 lib/Language/Haskell/Stylish/GHC.hs | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lib/Language/Haskell/Stylish/GHC.hs b/lib/Language/Haskell/Stylish/GHC.hs
index c99d4bf6..bb9bc4f4 100644
--- a/lib/Language/Haskell/Stylish/GHC.hs
+++ b/lib/Language/Haskell/Stylish/GHC.hs
@@ -8,6 +8,7 @@ module Language.Haskell.Stylish.GHC
     -- * Unsafe getters
   , unsafeGetRealSrcSpan
   , getEndLineUnsafe
+  , getEndColumnUnsafe
   , getStartLineUnsafe
     -- * Standard settings
   , baseDynFlags
@@ -33,7 +34,7 @@ import qualified Outputable        as GHC
 import           PlatformConstants (PlatformConstants (..))
 import           SrcLoc            (GenLocated (..), Located, RealLocated,
                                     RealSrcSpan, SrcSpan (..), srcSpanEndLine,
-                                    srcSpanStartLine)
+                                    srcSpanStartLine, srcSpanEndCol)
 import           ToolSettings      (ToolSettings (..))
 
 unsafeGetRealSrcSpan :: Located a -> RealSrcSpan
@@ -47,6 +48,9 @@ getStartLineUnsafe = srcSpanStartLine . unsafeGetRealSrcSpan
 getEndLineUnsafe :: Located a -> Int
 getEndLineUnsafe = srcSpanEndLine . unsafeGetRealSrcSpan
 
+getEndColumnUnsafe :: Located a -> Int
+getEndColumnUnsafe = srcSpanEndCol . unsafeGetRealSrcSpan
+
 dropAfterLocated :: Maybe (Located a) -> [RealLocated b] -> [RealLocated b]
 dropAfterLocated loc xs = case loc of
   Just (L (RealSrcSpan rloc) _) ->

From 53f6d6c90a950c55d3457684c795ddc30f4fee89 Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Thu, 18 Feb 2021 12:29:42 +0100
Subject: [PATCH 05/11] Implement test case 'wrap signature if it does not fit
 max column length'

---
 .../Haskell/Stylish/Step/Signature.hs         | 51 ++++++++++++++++---
 .../Haskell/Stylish/Step/Signature/Tests.hs   |  4 +-
 2 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/lib/Language/Haskell/Stylish/Step/Signature.hs b/lib/Language/Haskell/Stylish/Step/Signature.hs
index b4b067a5..10c5e875 100644
--- a/lib/Language/Haskell/Stylish/Step/Signature.hs
+++ b/lib/Language/Haskell/Stylish/Step/Signature.hs
@@ -1,27 +1,39 @@
 {-# LANGUAGE BlockArguments  #-}
 {-# LANGUAGE LambdaCase      #-}
 {-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE RecordWildCards #-}
 module Language.Haskell.Stylish.Step.Signature where
 
 import           RdrName                          (RdrName)
 import           SrcLoc                           (GenLocated (..), Located)
-import GHC.Hs.Decls (HsDecl(..))
-import GHC.Hs.Binds (Sig(..))
+import GHC.Hs.Decls
+import GHC.Hs.Binds
+import GHC.Hs.Types
 import           GHC.Hs.Extension                 (GhcPs)
 
 --------------------------------------------------------------------------------
 import           Language.Haskell.Stylish.Block
 import           Language.Haskell.Stylish.Step
 import           Language.Haskell.Stylish.Module
-import Language.Haskell.Stylish.Editor (change)
-import           Language.Haskell.Stylish.GHC (getStartLineUnsafe, getEndLineUnsafe)
+import Language.Haskell.Stylish.Editor (change, noop)
+import           Language.Haskell.Stylish.GHC (getStartLineUnsafe, getEndLineUnsafe, getEndColumnUnsafe)
 import           Language.Haskell.Stylish.Editor (Change, applyChanges)
+import           Language.Haskell.Stylish.Printer
 
 -- TODO unify with type alias from Data.hs
 type ChangeLine = Change String
 
+data MaxColumns
+  = MaxColumns !Int
+  | NoMaxColumns
+  deriving (Show, Eq)
+
+fits :: Int -> MaxColumns -> Bool
+fits _ NoMaxColumns = True
+fits v (MaxColumns limit) = v <= limit
+
 data Config = Config
-  { cMaxColumns :: Int
+  { cMaxColumns :: MaxColumns
   }
 
 step :: Config -> Step
@@ -32,15 +44,40 @@ changes cfg m = fmap (formatSignatureDecl cfg m) (topLevelFunctionSignatures m)
 
 topLevelFunctionSignatures :: Module -> [Located SignatureDecl]
 topLevelFunctionSignatures = queryModule @(Located (HsDecl GhcPs)) \case
-  L pos (SigD _ (TypeSig _ [name] _)) -> [L pos $ MkSignatureDecl name]
+  L pos (SigD _ (TypeSig _ [name] (HsWC _ (HsIB _ (L _ funTy@(HsFunTy _ _ _ )))))) ->
+    [L pos $ MkSignatureDecl name (listParameters funTy)]
   _ -> []
 
+listParameters :: HsType GhcPs -> [Located RdrName]
+listParameters (HsFunTy _ (L _ arg2) (L _ arg3)) = listParameters arg2 <> listParameters arg3
+listParameters (HsTyVar _ _promotionFlag name) = [name]
+listParameters _ = []
+
 data SignatureDecl = MkSignatureDecl
   { sigName :: Located RdrName
+  , sigParameters :: [Located RdrName]
   }
 
 formatSignatureDecl :: Config -> Module -> Located SignatureDecl -> ChangeLine
-formatSignatureDecl _cfg _m ldecl = change originalDeclBlock id
+formatSignatureDecl Config{..} m ldecl@(L _declPos decl) = do
+  let block = originalDeclBlock
+      declLength = getEndColumnUnsafe ldecl
+  if fits declLength cMaxColumns then
+    noop block
+  else
+    change block (const printDecl)
+
   where
     originalDeclBlock =
       Block (getStartLineUnsafe ldecl) (getEndLineUnsafe ldecl)
+
+    printerConfig = PrinterConfig
+      { columns = case cMaxColumns of
+          NoMaxColumns -> Nothing
+          MaxColumns n -> Just n
+      }
+
+    printDecl = runPrinter_ printerConfig [] m do
+      (putRdrName $ sigName decl) >> space >> putText "::" >> newline
+      spaces 5 >> (putRdrName $ head $ sigParameters decl) >> newline
+      traverse (\para -> spaces 2 >> putText "->" >> space >> (putRdrName para) >> newline) (tail $ sigParameters decl)
diff --git a/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs b/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
index 267c6ea0..b1ed6c87 100644
--- a/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
+++ b/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
@@ -13,7 +13,7 @@ import           Test.HUnit                          (Assertion, (@=?))
 tests :: Test
 tests = testGroup "Language.Haskell.Stylish.Step.Signature.Tests"
     [ testCase "do not wrap signature if it fits max column length" case00
-    -- , testCase "wrap signature if it does not fit max column length" case01
+    , testCase "wrap signature if it does not fit max column length" case01
     -- , testCase "how it behaves when there is a list of constraints" case02
     -- , testCase "how it behaves when there is a explicit forall" case03
     -- , testCase "how it behaves when there is a explicit forall" case04
@@ -22,7 +22,7 @@ tests = testGroup "Language.Haskell.Stylish.Step.Signature.Tests"
 
 config :: Int -> Config
 config cMaxColumns = Config
-  { cMaxColumns = cMaxColumns
+  { cMaxColumns = MaxColumns cMaxColumns
   }
 
 case00 :: Assertion

From 0497835a63b5dda7a92fd70f44794310cff4a0cc Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Thu, 18 Feb 2021 21:12:13 +0100
Subject: [PATCH 06/11] Extract printDecl to separate toplevel function

---
 lib/Language/Haskell/Stylish/Step/Signature.hs | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/lib/Language/Haskell/Stylish/Step/Signature.hs b/lib/Language/Haskell/Stylish/Step/Signature.hs
index 10c5e875..13cb0108 100644
--- a/lib/Language/Haskell/Stylish/Step/Signature.hs
+++ b/lib/Language/Haskell/Stylish/Step/Signature.hs
@@ -59,25 +59,26 @@ data SignatureDecl = MkSignatureDecl
   }
 
 formatSignatureDecl :: Config -> Module -> Located SignatureDecl -> ChangeLine
-formatSignatureDecl Config{..} m ldecl@(L _declPos decl) = do
+formatSignatureDecl cfg@Config{..} m ldecl = do
   let block = originalDeclBlock
       declLength = getEndColumnUnsafe ldecl
   if fits declLength cMaxColumns then
     noop block
   else
-    change block (const printDecl)
+    change block (const (printDecl cfg m ldecl))
 
   where
     originalDeclBlock =
       Block (getStartLineUnsafe ldecl) (getEndLineUnsafe ldecl)
 
+printDecl :: Config -> Module -> Located SignatureDecl -> Lines
+printDecl Config{..} m ldecl@(L _declPos decl) = runPrinter_ printerConfig [] m do
+  (putRdrName $ sigName decl) >> space >> putText "::" >> newline
+  spaces 5 >> (putRdrName $ head $ sigParameters decl) >> newline
+  traverse (\para -> spaces 2 >> putText "->" >> space >> (putRdrName para) >> newline) (tail $ sigParameters decl)
+  where
     printerConfig = PrinterConfig
       { columns = case cMaxColumns of
           NoMaxColumns -> Nothing
           MaxColumns n -> Just n
       }
-
-    printDecl = runPrinter_ printerConfig [] m do
-      (putRdrName $ sigName decl) >> space >> putText "::" >> newline
-      spaces 5 >> (putRdrName $ head $ sigParameters decl) >> newline
-      traverse (\para -> spaces 2 >> putText "->" >> space >> (putRdrName para) >> newline) (tail $ sigParameters decl)

From d9ccfdb32351b15b1de12fe967346e13d42b586d Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Thu, 18 Feb 2021 21:16:36 +0100
Subject: [PATCH 07/11] Extract helper printing functions

---
 lib/Language/Haskell/Stylish/Step/Signature.hs | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/lib/Language/Haskell/Stylish/Step/Signature.hs b/lib/Language/Haskell/Stylish/Step/Signature.hs
index 13cb0108..ed4b273c 100644
--- a/lib/Language/Haskell/Stylish/Step/Signature.hs
+++ b/lib/Language/Haskell/Stylish/Step/Signature.hs
@@ -72,11 +72,21 @@ formatSignatureDecl cfg@Config{..} m ldecl = do
       Block (getStartLineUnsafe ldecl) (getEndLineUnsafe ldecl)
 
 printDecl :: Config -> Module -> Located SignatureDecl -> Lines
-printDecl Config{..} m ldecl@(L _declPos decl) = runPrinter_ printerConfig [] m do
-  (putRdrName $ sigName decl) >> space >> putText "::" >> newline
-  spaces 5 >> (putRdrName $ head $ sigParameters decl) >> newline
-  traverse (\para -> spaces 2 >> putText "->" >> space >> (putRdrName para) >> newline) (tail $ sigParameters decl)
+printDecl Config{..} m (L _declPos decl) = runPrinter_ printerConfig [] m do
+  printFirstLine
+  printSecondLine
+  printRemainingLines
   where
+
+    printFirstLine =
+      (putRdrName $ sigName decl) >> space >> putText "::" >> newline
+
+    printSecondLine =
+      spaces 5 >> (putRdrName $ head $ sigParameters decl) >> newline
+
+    printRemainingLines =
+      traverse (\para -> spaces 2 >> putText "->" >> space >> (putRdrName para) >> newline) (tail $ sigParameters decl)
+
     printerConfig = PrinterConfig
       { columns = case cMaxColumns of
           NoMaxColumns -> Nothing

From ce99839a0b7e583135d177f7e6eb0d1de3341379 Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Thu, 18 Feb 2021 21:20:25 +0100
Subject: [PATCH 08/11] Simplify formatSignatureDecl

---
 lib/Language/Haskell/Stylish/Step/Signature.hs | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/lib/Language/Haskell/Stylish/Step/Signature.hs b/lib/Language/Haskell/Stylish/Step/Signature.hs
index ed4b273c..46886b47 100644
--- a/lib/Language/Haskell/Stylish/Step/Signature.hs
+++ b/lib/Language/Haskell/Stylish/Step/Signature.hs
@@ -59,17 +59,13 @@ data SignatureDecl = MkSignatureDecl
   }
 
 formatSignatureDecl :: Config -> Module -> Located SignatureDecl -> ChangeLine
-formatSignatureDecl cfg@Config{..} m ldecl = do
-  let block = originalDeclBlock
-      declLength = getEndColumnUnsafe ldecl
-  if fits declLength cMaxColumns then
-    noop block
-  else
-    change block (const (printDecl cfg m ldecl))
+formatSignatureDecl cfg@Config{..} m ldecl
+  | fits declLength cMaxColumns = noop block
+  | otherwise = change block (const (printDecl cfg m ldecl))
 
   where
-    originalDeclBlock =
-      Block (getStartLineUnsafe ldecl) (getEndLineUnsafe ldecl)
+    block = Block (getStartLineUnsafe ldecl) (getEndLineUnsafe ldecl)
+    declLength = getEndColumnUnsafe ldecl
 
 printDecl :: Config -> Module -> Located SignatureDecl -> Lines
 printDecl Config{..} m (L _declPos decl) = runPrinter_ printerConfig [] m do

From f331870f7253b4512388df072d42929a178b61cb Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Thu, 18 Feb 2021 21:47:33 +0100
Subject: [PATCH 09/11] Simplify access to SignatureDecl inside printDecl

---
 lib/Language/Haskell/Stylish/Step/Signature.hs | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/lib/Language/Haskell/Stylish/Step/Signature.hs b/lib/Language/Haskell/Stylish/Step/Signature.hs
index 46886b47..f6005573 100644
--- a/lib/Language/Haskell/Stylish/Step/Signature.hs
+++ b/lib/Language/Haskell/Stylish/Step/Signature.hs
@@ -59,29 +59,29 @@ data SignatureDecl = MkSignatureDecl
   }
 
 formatSignatureDecl :: Config -> Module -> Located SignatureDecl -> ChangeLine
-formatSignatureDecl cfg@Config{..} m ldecl
+formatSignatureDecl cfg@Config{..} m ldecl@(L _ decl)
   | fits declLength cMaxColumns = noop block
-  | otherwise = change block (const (printDecl cfg m ldecl))
+  | otherwise = change block (const (printDecl cfg m decl))
 
   where
     block = Block (getStartLineUnsafe ldecl) (getEndLineUnsafe ldecl)
     declLength = getEndColumnUnsafe ldecl
 
-printDecl :: Config -> Module -> Located SignatureDecl -> Lines
-printDecl Config{..} m (L _declPos decl) = runPrinter_ printerConfig [] m do
+printDecl :: Config -> Module -> SignatureDecl -> Lines
+printDecl Config{..} m MkSignatureDecl{..} = runPrinter_ printerConfig [] m do
   printFirstLine
   printSecondLine
   printRemainingLines
   where
 
     printFirstLine =
-      (putRdrName $ sigName decl) >> space >> putText "::" >> newline
+      putRdrName sigName >> space >> putText "::" >> newline
 
     printSecondLine =
-      spaces 5 >> (putRdrName $ head $ sigParameters decl) >> newline
+      spaces 5 >> (putRdrName $ head sigParameters) >> newline
 
     printRemainingLines =
-      traverse (\para -> spaces 2 >> putText "->" >> space >> (putRdrName para) >> newline) (tail $ sigParameters decl)
+      traverse (\para -> spaces 2 >> putText "->" >> space >> (putRdrName para) >> newline) (tail $ sigParameters)
 
     printerConfig = PrinterConfig
       { columns = case cMaxColumns of

From 63e3bbfcd448ef3448cb3080966b74da2edb5682 Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Thu, 18 Feb 2021 21:49:22 +0100
Subject: [PATCH 10/11] Extract printRemainingLine function

---
 lib/Language/Haskell/Stylish/Step/Signature.hs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/lib/Language/Haskell/Stylish/Step/Signature.hs b/lib/Language/Haskell/Stylish/Step/Signature.hs
index f6005573..b5d3d3f6 100644
--- a/lib/Language/Haskell/Stylish/Step/Signature.hs
+++ b/lib/Language/Haskell/Stylish/Step/Signature.hs
@@ -81,7 +81,10 @@ printDecl Config{..} m MkSignatureDecl{..} = runPrinter_ printerConfig [] m do
       spaces 5 >> (putRdrName $ head sigParameters) >> newline
 
     printRemainingLines =
-      traverse (\para -> spaces 2 >> putText "->" >> space >> (putRdrName para) >> newline) (tail $ sigParameters)
+      traverse printRemainingLine (tail sigParameters)
+
+    printRemainingLine parameter =
+      spaces 2 >> putText "->" >> space >> (putRdrName parameter) >> newline
 
     printerConfig = PrinterConfig
       { columns = case cMaxColumns of

From 41ca2df3a0f53a3591d9d640749606228717d656 Mon Sep 17 00:00:00 2001
From: EncodePanda <paul.szulc@gmail.com>
Date: Fri, 19 Feb 2021 11:26:50 +0100
Subject: [PATCH 11/11] Implement "how it behaves when there is a list of
 constraints"

---
 .../Haskell/Stylish/Step/Signature.hs         | 52 +++++++++++++++++--
 .../Haskell/Stylish/Step/Signature/Tests.hs   |  2 +-
 2 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/lib/Language/Haskell/Stylish/Step/Signature.hs b/lib/Language/Haskell/Stylish/Step/Signature.hs
index b5d3d3f6..3b2b45cd 100644
--- a/lib/Language/Haskell/Stylish/Step/Signature.hs
+++ b/lib/Language/Haskell/Stylish/Step/Signature.hs
@@ -45,7 +45,9 @@ changes cfg m = fmap (formatSignatureDecl cfg m) (topLevelFunctionSignatures m)
 topLevelFunctionSignatures :: Module -> [Located SignatureDecl]
 topLevelFunctionSignatures = queryModule @(Located (HsDecl GhcPs)) \case
   L pos (SigD _ (TypeSig _ [name] (HsWC _ (HsIB _ (L _ funTy@(HsFunTy _ _ _ )))))) ->
-    [L pos $ MkSignatureDecl name (listParameters funTy)]
+    [L pos $ MkSignatureDecl name (listParameters funTy) []]
+  L pos (SigD _ (TypeSig _ [name] (HsWC _ (HsIB _ (L _ (HsQualTy _ (L _ contexts) (L _ funTy))))))) ->
+    [L pos $ MkSignatureDecl name (listParameters funTy) (contexts >>= listContexts)]
   _ -> []
 
 listParameters :: HsType GhcPs -> [Located RdrName]
@@ -53,9 +55,15 @@ listParameters (HsFunTy _ (L _ arg2) (L _ arg3)) = listParameters arg2 <> listPa
 listParameters (HsTyVar _ _promotionFlag name) = [name]
 listParameters _ = []
 
+listContexts :: Located (HsType GhcPs) -> [Located RdrName]
+listContexts (L _ (HsTyVar _ _ name)) = [name]
+listContexts (L _ (HsAppTy _ arg1 arg2)) = listContexts arg1 <> listContexts arg2
+listContexts _ = []
+
 data SignatureDecl = MkSignatureDecl
   { sigName :: Located RdrName
   , sigParameters :: [Located RdrName]
+  , sigConstraints :: [Located RdrName]
   }
 
 formatSignatureDecl :: Config -> Module -> Located SignatureDecl -> ChangeLine
@@ -74,20 +82,54 @@ printDecl Config{..} m MkSignatureDecl{..} = runPrinter_ printerConfig [] m do
   printRemainingLines
   where
 
+    ----------------------------------------------------------------------------------------
+
     printFirstLine =
       putRdrName sigName >> space >> putText "::" >> newline
 
+    ----------------------------------------------------------------------------------------
+
     printSecondLine =
-      spaces 5 >> (putRdrName $ head sigParameters) >> newline
+      if hasConstraints then printConstraints
+      else printFirstParameter
+
+    printConstraints =
+        spaces 5 >> putText "("
+        >> (traverse (\ctr -> printConstraint ctr >> putText ", ") (init groupConstraints))
+        >> (printConstraint $ last groupConstraints)
+        >> putText ")" >> newline
+
+    groupConstraints = zip (dropEvery sigConstraints 2) (dropEvery (tail sigConstraints) 2)
+
+    printConstraint (tc, tp) = putRdrName tc >> space >> putRdrName tp
+
+    printFirstParameter =
+        spaces 5 >> (putRdrName $ head sigParameters) >> newline
+
+    ----------------------------------------------------------------------------------------
 
     printRemainingLines =
-      traverse printRemainingLine (tail sigParameters)
+      if hasConstraints then
+        printRemainingLine "=>" (head sigParameters)
+        >> traverse (printRemainingLine "->") (tail sigParameters)
+      else
+        traverse (printRemainingLine "->") (tail sigParameters)
+
+    printRemainingLine prefix parameter =
+      spaces 2 >> putText prefix >> space >> (putRdrName parameter) >> newline
 
-    printRemainingLine parameter =
-      spaces 2 >> putText "->" >> space >> (putRdrName parameter) >> newline
+    ----------------------------------------------------------------------------------------
 
     printerConfig = PrinterConfig
       { columns = case cMaxColumns of
           NoMaxColumns -> Nothing
           MaxColumns n -> Just n
       }
+
+    hasConstraints = not $ null sigConstraints
+
+-- 99 problems :)
+dropEvery :: [a] -> Int -> [a]
+dropEvery xs n
+  | length xs < n = xs
+  | otherwise     = take (n-1) xs ++ dropEvery (drop n xs) n
diff --git a/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs b/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
index b1ed6c87..370efced 100644
--- a/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
+++ b/tests/Language/Haskell/Stylish/Step/Signature/Tests.hs
@@ -14,7 +14,7 @@ tests :: Test
 tests = testGroup "Language.Haskell.Stylish.Step.Signature.Tests"
     [ testCase "do not wrap signature if it fits max column length" case00
     , testCase "wrap signature if it does not fit max column length" case01
-    -- , testCase "how it behaves when there is a list of constraints" case02
+    , testCase "how it behaves when there is a list of constraints" case02
     -- , testCase "how it behaves when there is a explicit forall" case03
     -- , testCase "how it behaves when there is a explicit forall" case04
     -- , testCase "how it behaves when there is a large function in the argument" case05