packages feed

fourmolu 0.19.0.1 → 0.20.0.0

raw patch · 137 files changed

+2283/−953 lines, 137 filesdep ~Cabal-syntaxdep ~containersdep ~ghc-lib-parser

Dependency ranges changed: Cabal-syntax, containers, ghc-lib-parser

Files

CHANGELOG.md view
@@ -1,4 +1,61 @@+## Fourmolu 0.20.0.0++Now installable with GHCup! See README for details.++* Improve `import-grouping` configuration++    * Add the `scope` option for import grouping ([#525](https://github.com/fourmolu/fourmolu/pull/525)).++    * Deprecate the `match` option for import grouping in favor of `glob` and `scope` ([#525](https://github.com/fourmolu/fourmolu/pull/525)).++    * Add the `import-list` option for import grouping ([#522](https://github.com/fourmolu/fourmolu/pull/522)).++* Add a `record-style` to configure the placement of record braces. ([#467](https://github.com/fourmolu/fourmolu/issues/467))++* Fix applying `indent-wheres` configuration to pattern synonyms with explicit bidirectional `where` clauses ([#486](https://github.com/fourmolu/fourmolu/issues/486))++### Upstream changed:++#### Ormolu 0.8.1.1++* Add missing braces for case expressions in single‑line do blocks. [Issue 1180](https://github.com/tweag/ormolu/issues/1180).++* Fix the import grouping logic in the presence of imports with explicit levels. [Issue 1192](https://github.com/tweag/ormolu/issues/1192).++#### Ormolu 0.8.1.0++* Fixed printing of guards on pattern binds. [Issue+  1178](https://github.com/tweag/ormolu/issues/1178).++* Switched to `ghc-lib-parser-9.14`, with the following new syntactic features:+   * GHC proposal [#493](https://github.com/ghc-proposals/ghc-proposals/blob/e2c683698323cec3e33625369ae2b5f585387c70/proposals/0493-specialise-expressions.rst): expressions in SPECIALISE pragmas+   * Multiline strings in foreign import declarations.+   * `ExplicitNamespaces` supports the `data` namespace specifier in import and export lists, replacing `pattern`.+   * `LinearTypes` adds new syntax to support non-linear record fields.+   * `RequiredTypeArguments` allows visible forall in GADT syntax.++* Updated to `Cabal-syntax-3.16`.++* Correctly format string literals containing the `\^\` escape sequence. [Issue+  1165](https://github.com/tweag/ormolu/issues/1165).++* Correctly preserve consecutive blank lines in multiline strings. [Issue+  1194](https://github.com/tweag/ormolu/issues/1194).++* Fixed printing of multi-line or-patterns inside as-patterns. [Issue+  1183](https://github.com/tweag/ormolu/issues/1183).++* Fixed an issue where or-patterns would be indented twice. [Issue+  1188](https://github.com/tweag/ormolu/issues/1188).++* Add support for `ExplicitLevelImports`. [Issue+  1192](https://github.com/tweag/ormolu/issues/1192).+ ## Fourmolu 0.19.0.1++### Upstream changed:++#### Ormolu 0.8.0.2  * Fixed a performance regression introduced in 0.8.0.0. [Issue   1176](https://github.com/tweag/ormolu/issues/1176).
README.md view
@@ -49,12 +49,23 @@  ## Installation -To install the latest release from Hackage, simply install with Cabal or Stack:+### (Recommended) Install with GHCup -```console-$ cabal install fourmolu-$ stack install fourmolu+```bash+ghcup config add-release-channel 3rdparty+ghcup install fourmolu latest ```++### Install with Cabal/Stack++```bash+cabal install fourmolu+stack install fourmolu+```++### Install with [dotslash](https://dotslash-cli.com/docs/)++Copy the configuration in the GitHub release notes.  ## Building from source 
app/Main.hs view
@@ -468,7 +468,7 @@         help "Fail if formatting is not idempotent"       ]     -- We cannot parse the source type here, because we might need to do-    -- autodection based on the input file extension (not available here)+    -- autodetection based on the input file extension (not available here)     -- before storing the resolved value in the config struct.     <*> pure ModuleSource     <*> (option parseColorMode . mconcat)@@ -492,7 +492,6 @@     <*> pure defaultPrinterOpts -- unused; overwritten in resolveConfig     <*> (fmap Set.fromList . many . strOption . mconcat)       [ long "local-modules",-        short 'm',         metavar "LOCAL_MODULES",         help "Modules Fourmolu should consider as local by the current Cabal package"       ]
+ data/examples/declaration/data/linear-four-out.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE LinearTypes #-}++data Record = Rec {x %'Many :: Int, y :: Char}++data T2 a b c where+    MkT2 :: a -> b %1 -> c %1 -> T2 a b c++data T2 a b c = MkT2 {x %Many :: a, y :: b, z :: c}++data T3 a m where+    MkT3 :: a %m -> T3 a m
+ data/examples/declaration/data/linear-out.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE LinearTypes #-}++data Record = Rec {x %'Many :: Int, y :: Char}++data T2 a b c where+  MkT2 :: a -> b %1 -> c %1 -> T2 a b c++data T2 a b c = MkT2 {x %Many :: a, y :: b, z :: c}++data T3 a m where+  MkT3 :: a %m -> T3 a m
+ data/examples/declaration/data/linear.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE LinearTypes #-}+data Record = Rec { x %'Many :: Int, y :: Char }++data T2 a b c where+    MkT2 :: a -> b %1 -> c %1 -> T2 a b c++data T2 a b c = MkT2 { x %Many :: a, y :: b, z :: c }++data T3 a m where+    MkT3 :: a %m -> T3 a m
data/examples/declaration/data/record-four-out.hs view
@@ -11,7 +11,7 @@     , fooGag       , fooGog ::         NonEmpty-            ( Indentity+            ( Identity                 Bool             )     -- ^ GagGog
data/examples/declaration/data/record-out.hs view
@@ -12,7 +12,7 @@     fooGag,     fooGog ::       NonEmpty-        ( Indentity+        ( Identity             Bool         ),     -- | Huh!
data/examples/declaration/data/record.hs view
@@ -6,7 +6,7 @@   { fooX :: Int -- ^ X   , fooY :: Int -- ^ Y   , fooBar, fooBaz :: NonEmpty (Identity Bool) -- ^ BarBaz-  , fooGag, fooGog :: NonEmpty (Indentity+  , fooGag, fooGog :: NonEmpty (Identity                                   Bool)     -- ^ GagGog   , fooFoo
+ data/examples/declaration/data/required-type-arguments-four-out.hs view
@@ -0,0 +1,25 @@+data T a where+    Typed :: forall a -> a -> T a++f1 (Typed a x) = x :: a+f2 (Typed Int n) = n * 2+f3 (Typed ((->) w Bool) g) = not . g++data D x where+    MkD1 ::+        forall a b ->+        a ->+        b ->+        D (a, b)+    MkD2 ::+        forall a.+        forall b ->+        a ->+        b ->+        D (a, b)+    MkD3 ::+        forall a ->+        a ->+        forall b ->+        b ->+        D (a, b)
+ data/examples/declaration/data/required-type-arguments-out.hs view
@@ -0,0 +1,27 @@+data T a where+  Typed :: forall a -> a -> T a++f1 (Typed a x) = x :: a++f2 (Typed Int n) = n * 2++f3 (Typed ((->) w Bool) g) = not . g++data D x where+  MkD1 ::+    forall a b ->+    a ->+    b ->+    D (a, b)+  MkD2 ::+    forall a.+    forall b ->+    a ->+    b ->+    D (a, b)+  MkD3 ::+    forall a ->+    a ->+    forall b ->+    b ->+    D (a, b)
+ data/examples/declaration/data/required-type-arguments.hs view
@@ -0,0 +1,24 @@+data T a where+  Typed :: forall a -> a -> T a++f1 (Typed a x) = x :: a+f2 (Typed Int n) = n*2+f3 (Typed ((->) w Bool) g) = not . g++data D x where+  MkD1 :: forall a b ->+          a ->+          b ->+          D (a, b)++  MkD2 :: forall a.+          forall b ->+          a ->+          b ->+          D (a, b)++  MkD3 :: forall a ->+          a ->+          forall b ->+          b ->+          D (a, b)
+ data/examples/declaration/foreign/foreign-import-multiline-four-out.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE MultilineStrings #-}++foreign import capi+    """+    foo+       bar+    """+    foo :: Int -> Int
+ data/examples/declaration/foreign/foreign-import-multiline-out.hs view
@@ -0,0 +1,8 @@+{-# LANGUAGE MultilineStrings #-}++foreign import capi+  """+  foo+     bar+  """+  foo :: Int -> Int
+ data/examples/declaration/foreign/foreign-import-multiline.hs view
@@ -0,0 +1,6 @@+{-# language MultilineStrings #-}++foreign import capi """+         foo+            bar+     """ foo :: Int -> Int
data/examples/declaration/rewrite-rule/prelude2-four-out.hs view
@@ -11,7 +11,7 @@ -- when we disable the rule that expands (++) into foldr  -- The foldr/cons rule looks nice, but it can give disastrously--- bloated code when commpiling+-- bloated code when compiling --      array (a,b) [(1,2), (2,2), (3,2), ...very long list... ] -- i.e. when there are very very long literal lists -- So I've disabled it for now. We could have special cases
data/examples/declaration/rewrite-rule/prelude2-out.hs view
@@ -11,7 +11,7 @@ -- when we disable the rule that expands (++) into foldr  -- The foldr/cons rule looks nice, but it can give disastrously--- bloated code when commpiling+-- bloated code when compiling --      array (a,b) [(1,2), (2,2), (3,2), ...very long list... ] -- i.e. when there are very very long literal lists -- So I've disabled it for now. We could have special cases
data/examples/declaration/rewrite-rule/prelude2.hs view
@@ -11,7 +11,7 @@         -- when we disable the rule that expands (++) into foldr  -- The foldr/cons rule looks nice, but it can give disastrously--- bloated code when commpiling+-- bloated code when compiling --      array (a,b) [(1,2), (2,2), (3,2), ...very long list... ] -- i.e. when there are very very long literal lists -- So I've disabled it for now. We could have special cases
+ data/examples/declaration/signature/specialize/specialize-2-four-out.hs view
@@ -0,0 +1,8 @@+{-# SPECIALIZE addMult @Double #-}+{-# SPECIALIZE addMult (5 :: Int) #-}+{-# SPECIALIZE addMult 5 :: Int -> Int #-}++{-# SPECIALIZE [1] forall x y. f @Int True (x, y) #-}++{-# SPECIALIZE forall x xs. loop (x : xs)+    #-}
+ data/examples/declaration/signature/specialize/specialize-2-out.hs view
@@ -0,0 +1,8 @@+{-# SPECIALIZE addMult @Double #-}+{-# SPECIALIZE addMult (5 :: Int) #-}+{-# SPECIALIZE addMult 5 :: Int -> Int #-}++{-# SPECIALIZE [1] forall x y. f @Int True (x, y) #-}++{-# SPECIALIZE forall x xs. loop (x : xs)+  #-}
+ data/examples/declaration/signature/specialize/specialize-2.hs view
@@ -0,0 +1,9 @@+{-# SPECIALISE addMult @Double #-}+{-# SPECIALISE addMult (5 :: Int) #-}+{-# SPECIALISE addMult 5 :: Int -> Int #-}++{-# SPECIALISE [1] forall x y. f @Int True (x,y) #-}++{-# SPECIALISE+  forall x xs .+  loop (x:xs) #-}
+ data/examples/declaration/signature/specialize/specialize-3-four-out.hs view
@@ -0,0 +1,10 @@+sep, fsep, hsep :: (Applicative m, Foldable t) => t (m Doc) -> m Doc+sep = fmap P.sep . sequenceAFoldable+{-# SPECIALIZE NOINLINE sep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE sep :: List1 (TCM Doc) -> TCM Doc #-}+fsep = fmap P.fsep . sequenceAFoldable+{-# SPECIALIZE NOINLINE [2] fsep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE [2] fsep :: List1 (TCM Doc) -> TCM Doc #-}+hsep = fmap P.hsep . sequenceAFoldable+{-# SPECIALIZE NOINLINE [~2] hsep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE [~2] hsep :: List1 (TCM Doc) -> TCM Doc #-}
+ data/examples/declaration/signature/specialize/specialize-3-out.hs view
@@ -0,0 +1,10 @@+sep, fsep, hsep :: (Applicative m, Foldable t) => t (m Doc) -> m Doc+sep = fmap P.sep . sequenceAFoldable+{-# SPECIALIZE NOINLINE sep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE sep :: List1 (TCM Doc) -> TCM Doc #-}+fsep = fmap P.fsep . sequenceAFoldable+{-# SPECIALIZE NOINLINE [2] fsep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE [2] fsep :: List1 (TCM Doc) -> TCM Doc #-}+hsep = fmap P.hsep . sequenceAFoldable+{-# SPECIALIZE NOINLINE [~2] hsep :: [TCM Doc] -> TCM Doc #-}+{-# SPECIALIZE NOINLINE [~2] hsep :: List1 (TCM Doc) -> TCM Doc #-}
+ data/examples/declaration/signature/specialize/specialize-3.hs view
@@ -0,0 +1,4 @@+sep, fsep, hsep :: (Applicative m, Foldable t) => t (m Doc) -> m Doc+sep  = fmap P.sep  . sequenceAFoldable  ; {-# SPECIALIZE NOINLINE sep  :: [TCM Doc] -> TCM Doc #-} ; {-# SPECIALIZE NOINLINE sep  :: List1 (TCM Doc) -> TCM Doc #-}+fsep = fmap P.fsep . sequenceAFoldable  ; {-# SPECIALIZE NOINLINE [2] fsep :: [TCM Doc] -> TCM Doc #-} ; {-# SPECIALIZE NOINLINE [2] fsep :: List1 (TCM Doc) -> TCM Doc #-}+hsep = fmap P.hsep . sequenceAFoldable  ; {-# SPECIALIZE NOINLINE [~2] hsep :: [TCM Doc] -> TCM Doc #-} ; {-# SPECIALIZE NOINLINE [~2] hsep :: List1 (TCM Doc) -> TCM Doc #-}
data/examples/declaration/value/function/arrow/proc-do-complex-four-out.hs view
@@ -12,7 +12,7 @@                 )         -> do             -- Begin do-            (x, y) <- -- GHC parser fails if layed out over multiple lines+            (x, y) <- -- GHC parser fails if laid out over multiple lines                 f -- Call into f                     ( a                     , c -- Tuple together arguments
data/examples/declaration/value/function/arrow/proc-do-complex-out.hs view
@@ -12,7 +12,7 @@         )     -> do       -- Begin do-      (x, y) <- -- GHC parser fails if layed out over multiple lines+      (x, y) <- -- GHC parser fails if laid out over multiple lines         f -- Call into f           ( a,             c -- Tuple together arguments
data/examples/declaration/value/function/arrow/proc-do-complex.hs view
@@ -9,7 +9,7 @@         (e, f)       ) ->     do -- Begin do-        (x,y) -- GHC parser fails if layed out over multiple lines+        (x,y) -- GHC parser fails if laid out over multiple lines          <- f -- Call into f               (a,                c) -- Tuple together arguments
+ data/examples/declaration/value/function/case-single-line-with-braces-four-out.hs view
@@ -0,0 +1,2 @@+getValue :: Maybe Int -> Int+getValue x = case x of Just n -> n; Nothing -> 0
+ data/examples/declaration/value/function/case-single-line-with-braces-out.hs view
@@ -0,0 +1,2 @@+getValue :: Maybe Int -> Int+getValue x = case x of Just n -> n; Nothing -> 0
+ data/examples/declaration/value/function/case-single-line-with-braces.hs view
@@ -0,0 +1,2 @@+getValue :: Maybe Int -> Int+getValue x = case x of {Just n -> n; Nothing -> 0}
+ data/examples/declaration/value/function/do-multiline-with-case-four-out.hs view
@@ -0,0 +1,13 @@+handleInput :: IO ()+handleInput = do+    putStrLn "Enter command:"+    cmd <- getLine+    case cmd of+        "quit" -> putStrLn "Goodbye"+        "help" -> do+            putStrLn "Available commands:"+            putStrLn "  quit - exit the program"+            putStrLn "  help - show this message"+        _ -> do+            putStrLn $ "Unknown command: " ++ cmd+            handleInput
+ data/examples/declaration/value/function/do-multiline-with-case-out.hs view
@@ -0,0 +1,13 @@+handleInput :: IO ()+handleInput = do+  putStrLn "Enter command:"+  cmd <- getLine+  case cmd of+    "quit" -> putStrLn "Goodbye"+    "help" -> do+      putStrLn "Available commands:"+      putStrLn "  quit - exit the program"+      putStrLn "  help - show this message"+    _ -> do+      putStrLn $ "Unknown command: " ++ cmd+      handleInput
+ data/examples/declaration/value/function/do-multiline-with-case.hs view
@@ -0,0 +1,13 @@+handleInput :: IO ()+handleInput = do+  putStrLn "Enter command:"+  cmd <- getLine+  case cmd of+    "quit" -> putStrLn "Goodbye"+    "help" -> do+      putStrLn "Available commands:"+      putStrLn "  quit - exit the program"+      putStrLn "  help - show this message"+    _ -> do+      putStrLn $ "Unknown command: " ++ cmd+      handleInput
+ data/examples/declaration/value/function/do-single-line-case-guards-four-out.hs view
@@ -0,0 +1,2 @@+checkValue :: Int -> IO ()+checkValue n = do putStr "Value is: "; case () of { _ | n < 0 -> putStrLn "negative" | n == 0 -> putStrLn "zero" | otherwise -> putStrLn "positive" }
+ data/examples/declaration/value/function/do-single-line-case-guards-out.hs view
@@ -0,0 +1,2 @@+checkValue :: Int -> IO ()+checkValue n = do putStr "Value is: "; case () of { _ | n < 0 -> putStrLn "negative" | n == 0 -> putStrLn "zero" | otherwise -> putStrLn "positive" }
+ data/examples/declaration/value/function/do-single-line-case-guards.hs view
@@ -0,0 +1,2 @@+checkValue :: Int -> IO ()+checkValue n = do {putStr "Value is: "; case () of {_ | n < 0 -> putStrLn "negative" | n == 0 -> putStrLn "zero" | otherwise -> putStrLn "positive"}}
+ data/examples/declaration/value/function/do-single-line-lambda-case-four-out.hs view
@@ -0,0 +1,2 @@+processValue :: Maybe Int -> IO ()+processValue x = do putStrLn "Processing:"; \case { Just n -> print n; Nothing -> putStrLn "Empty" } x; putStrLn "Done"
+ data/examples/declaration/value/function/do-single-line-lambda-case-out.hs view
@@ -0,0 +1,2 @@+processValue :: Maybe Int -> IO ()+processValue x = do putStrLn "Processing:"; \case { Just n -> print n; Nothing -> putStrLn "Empty" } x; putStrLn "Done"
+ data/examples/declaration/value/function/do-single-line-lambda-case.hs view
@@ -0,0 +1,2 @@+processValue :: Maybe Int -> IO ()+processValue x = do {putStrLn "Processing:"; \case {Just n -> print n; Nothing -> putStrLn "Empty"} x; putStrLn "Done"}
+ data/examples/declaration/value/function/do-single-line-multiple-cases-four-out.hs view
@@ -0,0 +1,2 @@+processPair :: Maybe Int -> Maybe String -> IO ()+processPair x y = do case x of { Just n -> print n; Nothing -> putStrLn "No number" }; case y of { Just s -> putStrLn s; Nothing -> putStrLn "No string" }
+ data/examples/declaration/value/function/do-single-line-multiple-cases-out.hs view
@@ -0,0 +1,2 @@+processPair :: Maybe Int -> Maybe String -> IO ()+processPair x y = do case x of { Just n -> print n; Nothing -> putStrLn "No number" }; case y of { Just s -> putStrLn s; Nothing -> putStrLn "No string" }
+ data/examples/declaration/value/function/do-single-line-multiple-cases.hs view
@@ -0,0 +1,2 @@+processPair :: Maybe Int -> Maybe String -> IO ()+processPair x y = do {case x of {Just n -> print n; Nothing -> putStrLn "No number"}; case y of {Just s -> putStrLn s; Nothing -> putStrLn "No string"}}
+ data/examples/declaration/value/function/do-single-line-nested-case-four-out.hs view
@@ -0,0 +1,2 @@+nestedDo :: Either String Int -> IO ()+nestedDo e = do putStr "Start: "; case e of { Left s -> do { putStr "Error: "; putStrLn s }; Right n -> do { putStr "Value: "; print n } }; putStrLn "End"
+ data/examples/declaration/value/function/do-single-line-nested-case-out.hs view
@@ -0,0 +1,2 @@+nestedDo :: Either String Int -> IO ()+nestedDo e = do putStr "Start: "; case e of { Left s -> do { putStr "Error: "; putStrLn s }; Right n -> do { putStr "Value: "; print n } }; putStrLn "End"
+ data/examples/declaration/value/function/do-single-line-nested-case.hs view
@@ -0,0 +1,2 @@+nestedDo :: Either String Int -> IO ()+nestedDo e = do {putStr "Start: "; case e of {Left s -> do {putStr "Error: "; putStrLn s}; Right n -> do {putStr "Value: "; print n}}; putStrLn "End"}
+ data/examples/declaration/value/function/do-single-line-with-case-four-out.hs view
@@ -0,0 +1,2 @@+doGuessing :: (Ord t, Read t) => t -> IO ()+doGuessing num = do putStrLn "Enter your guess:"; guess <- getLine; case compare (read guess) num of { LT -> do { putStrLn "Too low!"; doGuessing num }; GT -> do { putStrLn "Too high!"; doGuessing num }; EQ -> putStrLn "You win!" }
+ data/examples/declaration/value/function/do-single-line-with-case-out.hs view
@@ -0,0 +1,2 @@+doGuessing :: (Ord t, Read t) => t -> IO ()+doGuessing num = do putStrLn "Enter your guess:"; guess <- getLine; case compare (read guess) num of { LT -> do { putStrLn "Too low!"; doGuessing num }; GT -> do { putStrLn "Too high!"; doGuessing num }; EQ -> putStrLn "You win!" }
+ data/examples/declaration/value/function/do-single-line-with-case.hs view
@@ -0,0 +1,2 @@+doGuessing :: (Ord t, Read t) => t -> IO ()+doGuessing num = do {putStrLn "Enter your guess:"; guess <- getLine; case compare (read guess) num of {LT -> do {putStrLn "Too low!"; doGuessing num}; GT -> do { putStrLn "Too high!"; doGuessing num}; EQ -> putStrLn "You win!"}}
data/examples/declaration/value/function/guards-four-out.hs view
@@ -10,3 +10,5 @@ quux :: Int -> Int quux x | x < 0 = x quux x = x++(a, b) | c = d
data/examples/declaration/value/function/guards-out.hs view
@@ -10,3 +10,5 @@ quux :: Int -> Int quux x | x < 0 = x quux x = x++(a, b) | c = d
data/examples/declaration/value/function/guards.hs view
@@ -10,3 +10,5 @@ quux :: Int -> Int quux x | x < 0 = x quux x = x++(a, b) | c = d
+ data/examples/declaration/value/function/linear-bindings-four-out.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE LinearTypes #-}++h x = g y+  where+    %1 y = f x++let %1 x = u in ()+let %Many (x, y) = u in ()+let %1 ~(x, y) = u in ()
+ data/examples/declaration/value/function/linear-bindings-out.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE LinearTypes #-}++h x = g y+  where+    %1 y = f x++let %1 x = u in ()+let %Many (x, y) = u in ()+let %1 ~(x, y) = u in ()
+ data/examples/declaration/value/function/linear-bindings.hs view
@@ -0,0 +1,9 @@+{-# Language LinearTypes #-}++h x = g y+  where+    %1 y = f x++let %1 x = u in ()+let %Many (x, y) = u in ()+let %1 ~(x, y) = u in ()
+ data/examples/declaration/value/function/multiline-strings-9-four-out.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE MultilineStrings #-}++multilineBlank =+    """+    1+++++    6+    """
+ data/examples/declaration/value/function/multiline-strings-9-out.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE MultilineStrings #-}++multilineBlank =+  """+  1+++++  6+  """
+ data/examples/declaration/value/function/multiline-strings-9.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE MultilineStrings #-}++multilineBlank =+  """+  1+++++  6+  """
data/examples/declaration/value/function/pattern/or-patterns-four-out.hs view
@@ -20,7 +20,8 @@ sane e = case e of     1     2-    3 -> a+    3 ->+        a     4     5     6 -> b@@ -33,3 +34,10 @@     (D; E (Just _) Nothing) ->         4     F -> 5++food+    foo@( A;+            B;+            C+            ) = Just foo+food _ = Nothing
data/examples/declaration/value/function/pattern/or-patterns-out.hs view
@@ -21,7 +21,8 @@ sane e = case e of   1   2-  3 -> a+  3 ->+    a   4   5   6 -> b@@ -34,3 +35,10 @@   (D; E (Just _) Nothing) ->     4   F -> 5++food+  foo@( A;+        B;+        C+        ) = Just foo+food _ = Nothing
data/examples/declaration/value/function/pattern/or-patterns.hs view
@@ -20,7 +20,8 @@ sane e = case e of   1   2-  3 -> a+  3 ->+    a   4   5;6 -> b   7;8 -> c@@ -31,3 +32,8 @@   (D; E (Just _) Nothing)    -> 4   F -> 5++food foo@(A;+          B;+          C) = Just foo+food _ = Nothing
data/examples/declaration/value/function/strings-four-out.hs view
@@ -8,3 +8,5 @@     \baz"  weirdGap = "\65\ \0"++weirdEscape = "\^\ "
data/examples/declaration/value/function/strings-out.hs view
@@ -10,3 +10,5 @@   \baz"  weirdGap = "\65\ \0"++weirdEscape = "\^\ "
data/examples/declaration/value/function/strings.hs view
@@ -7,3 +7,5 @@     \baz"  weirdGap = "\65\ \0"++weirdEscape = "\^\ "
data/examples/declaration/value/pattern-synonyms/explicitely-bidirectional-four-out.hs view
@@ -3,19 +3,19 @@ pattern P a <- C a where P a = C a  pattern HeadC x <- x : xs-    where-        HeadC x = [x]+  where+    HeadC x = [x]  pattern HeadC' x <-     x : xs-    where-        HeadC' x = [x]+  where+    HeadC' x = [x]  pattern Simple <- "Simple"-    where-        Simple = "Complicated"+  where+    Simple = "Complicated"  pattern a :< b <-     (a, b)-    where-        a :< b = (a, b)+  where+    a :< b = (a, b)
+ data/examples/import/data-four-out.hs view
@@ -0,0 +1,3 @@+module Bar (data P, T (data P), data f) where++import N (T (data P), data P, data f)
+ data/examples/import/data-out.hs view
@@ -0,0 +1,3 @@+module Bar (data P, T (data P), data f) where++import N (T (data P), data P, data f)
+ data/examples/import/data.hs view
@@ -0,0 +1,6 @@++module Bar (data P, T(data P), data f) where++import N (data P)+import N (T(data P))+import N (data f)
+ data/examples/import/explicit-level-imports-four-out.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE ExplicitLevelImports #-}++import A splice+import {-# SOURCE #-} safe qualified A splice as QA hiding (a, b, c, d, e, f)+import quote qualified B as QB+import qualified C splice as SC+import qualified D splice+import Data.ByteString (e)+import Data.ByteString.Lazy quote (d)+import splice Data.Text (a, b, c)+import PyF ()+import splice PyF (fmt, tmf)+import quote PyF (abc)
+ data/examples/import/explicit-level-imports-out.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE ExplicitLevelImports #-}++import A splice+import {-# SOURCE #-} safe qualified A splice as QA hiding (a, b, c, d, e, f)+import quote qualified B as QB+import qualified C splice as SC+import qualified D splice+import Data.ByteString (e)+import Data.ByteString.Lazy quote (d)+import splice Data.Text (a, b, c)+import PyF ()+import splice PyF (fmt, tmf)+import quote PyF (abc)
+ data/examples/import/explicit-level-imports-qualified-post-four-out.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE ExplicitLevelImports #-}+{-# LANGUAGE ImportQualifiedPost #-}++import quote A qualified as QA+import B quote qualified as QB
+ data/examples/import/explicit-level-imports-qualified-post-out.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE ExplicitLevelImports #-}+{-# LANGUAGE ImportQualifiedPost #-}++import quote A qualified as QA+import B quote qualified as QB
+ data/examples/import/explicit-level-imports-qualified-post.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE ExplicitLevelImports #-}+{-# LANGUAGE ImportQualifiedPost #-}++import qualified B quote as QB+import quote qualified A as QA
+ data/examples/import/explicit-level-imports.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE ExplicitLevelImports #-}++import splice Data.Text (a, b, c)+import Data.ByteString.Lazy quote (d)+import Data.ByteString (e)+import {-# SOURCE #-} safe qualified A splice as QA hiding (a, b, c, d, e, f)+import quote qualified B as QB+import qualified C splice as SC+import A splice+import qualified D splice+import quote PyF (abc)+import splice PyF (fmt)+import splice PyF (tmf)+import PyF ()
data/examples/other/empty-forall-four-out.hs view
@@ -12,7 +12,7 @@     forall. T x = x  {-# RULES-"r"+"r" forall.     r a =         ()     #-}
data/examples/other/empty-forall-out.hs view
@@ -12,7 +12,7 @@   forall. T x = x  {-# RULES-"r"+"r" forall.   r a =     ()   #-}
data/fourmolu/haddock-style/output-multi_line-module=multi_line.hs view
@@ -37,6 +37,6 @@  {- | This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line-module=multi_line_compact.hs view
@@ -37,6 +37,6 @@  {- | This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line-module=single_line.hs view
@@ -36,6 +36,6 @@  {- | This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line.hs view
@@ -37,6 +37,6 @@  {- | This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line_compact-module=multi_line.hs view
@@ -37,6 +37,6 @@  {-| This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line_compact-module=multi_line_compact.hs view
@@ -37,6 +37,6 @@  {-| This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line_compact-module=single_line.hs view
@@ -36,6 +36,6 @@  {-| This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/haddock-style/output-multi_line_compact.hs view
@@ -37,6 +37,6 @@  {-| This is a haddock containing another haddock -> {\-# LANGUAGE ScopedTypeVariables #-\}+> {-# LANGUAGE ScopedTypeVariables #-} -} haddock_in_haddock :: Int
data/fourmolu/import-grouping/input.hs view
@@ -1,13 +1,16 @@ module Main where +import Data.Either import Data.Text (Text) import Data.Maybe (maybe)  import qualified Data.Text import Control.Monad (Monad (..))+import Data.Functor  import qualified System.IO as SIO import SomeInternal.Module1 (anotherDefinition, someDefinition) import qualified SomeInternal.Module2 as Mod2 import Text.Printf (printf) import qualified SomeModule+import SomeInternal.Module2
data/fourmolu/import-grouping/output-by_qualified.hs view
@@ -1,9 +1,12 @@ module Main where  import Control.Monad (Monad (..))+import Data.Either+import Data.Functor import Data.Maybe (maybe) import Data.Text (Text) import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2 import Text.Printf (printf)  import qualified Data.Text
data/fourmolu/import-grouping/output-by_scope.hs view
@@ -1,6 +1,8 @@ module Main where  import Control.Monad (Monad (..))+import Data.Either+import Data.Functor import Data.Maybe (maybe) import Data.Text (Text) import qualified Data.Text@@ -9,4 +11,5 @@ import Text.Printf (printf)  import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2 import qualified SomeInternal.Module2 as Mod2
data/fourmolu/import-grouping/output-by_scope_then_qualified.hs view
@@ -1,6 +1,8 @@ module Main where  import Control.Monad (Monad (..))+import Data.Either+import Data.Functor import Data.Maybe (maybe) import Data.Text (Text) import Text.Printf (printf)@@ -10,5 +12,6 @@ import qualified System.IO as SIO  import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2  import qualified SomeInternal.Module2 as Mod2
data/fourmolu/import-grouping/output-custom.hs view
@@ -1,5 +1,9 @@ module Main where +import Data.Either+import Data.Functor+import SomeInternal.Module2+ import Data.Text (Text) import qualified Data.Text 
data/fourmolu/import-grouping/output-preserve.hs view
@@ -1,12 +1,15 @@ module Main where +import Data.Either import Data.Maybe (maybe) import Data.Text (Text)  import Control.Monad (Monad (..))+import Data.Functor import qualified Data.Text  import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2 import qualified SomeInternal.Module2 as Mod2 import qualified SomeModule import qualified System.IO as SIO
data/fourmolu/import-grouping/output-single.hs view
@@ -1,10 +1,13 @@ module Main where  import Control.Monad (Monad (..))+import Data.Either+import Data.Functor import Data.Maybe (maybe) import Data.Text (Text) import qualified Data.Text import SomeInternal.Module1 (anotherDefinition, someDefinition)+import SomeInternal.Module2 import qualified SomeInternal.Module2 as Mod2 import qualified SomeModule import qualified System.IO as SIO
data/fourmolu/indentation/input.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo (   asdf,   bar,@@ -21,3 +23,7 @@  class Foo a where   foo :: a -> Int++pattern HeadC x <- x : xs+  where+    HeadC x = [x]
data/fourmolu/indentation/output-2-indent_wheres.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo (   asdf,   bar,@@ -21,3 +23,7 @@  class Foo a where   foo :: a -> Int++pattern HeadC x <- x : xs+  where+    HeadC x = [x]
data/fourmolu/indentation/output-2.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo (   asdf,   bar,@@ -21,3 +23,7 @@  class Foo a where   foo :: a -> Int++pattern HeadC x <- x : xs+ where+  HeadC x = [x]
data/fourmolu/indentation/output-3-indent_wheres.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo (    asdf,    bar,@@ -21,3 +23,7 @@  class Foo a where    foo :: a -> Int++pattern HeadC x <- x : xs+   where+      HeadC x = [x]
data/fourmolu/indentation/output-3.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo (    asdf,    bar,@@ -21,3 +23,7 @@  class Foo a where    foo :: a -> Int++pattern HeadC x <- x : xs+  where+   HeadC x = [x]
data/fourmolu/indentation/output-4-indent_wheres.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo (     asdf,     bar,@@ -21,3 +23,7 @@  class Foo a where     foo :: a -> Int++pattern HeadC x <- x : xs+    where+        HeadC x = [x]
data/fourmolu/indentation/output-4.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE PatternSynonyms #-}+ module Foo (     asdf,     bar,@@ -21,3 +23,7 @@  class Foo a where     foo :: a -> Int++pattern HeadC x <- x : xs+  where+    HeadC x = [x]
+ data/fourmolu/record-style/input.hs view
@@ -0,0 +1,24 @@+data Foo = Foo+  { a :: Int+  , b :: Int+  , c :: Int+  }++-- Test nesting+data Client = PremiumClient { name :: String,+    address :: String, aFieldWithAnUnneccessarilyLongFieldName :: String }+  -- And some comment here as well+  | NotSoPremiumClient { name :: String,+    address :: String, aFieldWithAnUnneccessarilyLongFieldName :: String }++x :: Foo -> [Int]+x Foo{ a = possiblyLongName,+    b = anotherLongName,+    c = longNameAsWell+    } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell = Foo{ a = possiblyLongName,+    b = anotherLongName,+    c = longNameAsWell+  }
+ data/fourmolu/record-style/output-aligned-leading.hs view
@@ -0,0 +1,35 @@+data Foo = Foo+    { a :: Int+    , b :: Int+    , c :: Int+    }++-- Test nesting+data Client+    = PremiumClient+        { name :: String+        , address :: String+        , aFieldWithAnUnneccessarilyLongFieldName :: String+        }+    | -- And some comment here as well+      NotSoPremiumClient+        { name :: String+        , address :: String+        , aFieldWithAnUnneccessarilyLongFieldName :: String+        }++x :: Foo -> [Int]+x+    Foo+        { a = possiblyLongName+        , b = anotherLongName+        , c = longNameAsWell+        } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell =+    Foo+        { a = possiblyLongName+        , b = anotherLongName+        , c = longNameAsWell+        }
+ data/fourmolu/record-style/output-aligned-trailing.hs view
@@ -0,0 +1,35 @@+data Foo = Foo+    { a :: Int,+      b :: Int,+      c :: Int+    }++-- Test nesting+data Client+    = PremiumClient+        { name :: String,+          address :: String,+          aFieldWithAnUnneccessarilyLongFieldName :: String+        }+    | -- And some comment here as well+      NotSoPremiumClient+        { name :: String,+          address :: String,+          aFieldWithAnUnneccessarilyLongFieldName :: String+        }++x :: Foo -> [Int]+x+    Foo+        { a = possiblyLongName,+          b = anotherLongName,+          c = longNameAsWell+        } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell =+    Foo+        { a = possiblyLongName,+          b = anotherLongName,+          c = longNameAsWell+        }
+ data/fourmolu/record-style/output-knr-leading.hs view
@@ -0,0 +1,35 @@+data Foo = Foo {+    a :: Int+    , b :: Int+    , c :: Int+    }++-- Test nesting+data Client+    = PremiumClient {+        name :: String+        , address :: String+        , aFieldWithAnUnneccessarilyLongFieldName :: String+        }+    | -- And some comment here as well+      NotSoPremiumClient {+        name :: String+        , address :: String+        , aFieldWithAnUnneccessarilyLongFieldName :: String+        }++x :: Foo -> [Int]+x+    Foo {+        a = possiblyLongName+        , b = anotherLongName+        , c = longNameAsWell+        } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell =+    Foo {+        a = possiblyLongName+        , b = anotherLongName+        , c = longNameAsWell+        }
+ data/fourmolu/record-style/output-knr-trailing.hs view
@@ -0,0 +1,35 @@+data Foo = Foo {+    a :: Int,+    b :: Int,+    c :: Int+}++-- Test nesting+data Client+    = PremiumClient {+        name :: String,+        address :: String,+        aFieldWithAnUnneccessarilyLongFieldName :: String+    }+    | -- And some comment here as well+      NotSoPremiumClient {+        name :: String,+        address :: String,+        aFieldWithAnUnneccessarilyLongFieldName :: String+    }++x :: Foo -> [Int]+x+    Foo {+        a = possiblyLongName,+        b = anotherLongName,+        c = longNameAsWell+    } = [possiblyLongName, anotherLongName, longNameAsWell]++y :: Int -> Int -> Int -> Foo+y possiblyLongName anotherLongName longNameAsWell =+    Foo {+        a = possiblyLongName,+        b = anotherLongName,+        c = longNameAsWell+    }
data/fourmolu/unicode-syntax/output-always.hs view
@@ -55,8 +55,8 @@ deconstruct (MkT1 x) = x  pattern HeadC x ← x : xs-    where-        HeadC x = [x]+  where+    HeadC x = [x]  pattern Point ∷ Int → Int → (Int, Int) pattern Point{x, y} = (x, y)
data/fourmolu/unicode-syntax/output-detect.hs view
@@ -55,8 +55,8 @@ deconstruct (MkT1 x) = x  pattern HeadC x ← x : xs-    where-        HeadC x = [x]+  where+    HeadC x = [x]  pattern Point ∷ Int → Int → (Int, Int) pattern Point{x, y} = (x, y)
data/fourmolu/unicode-syntax/output-never.hs view
@@ -55,8 +55,8 @@ deconstruct (MkT1 x) = x  pattern HeadC x <- x : xs-    where-        HeadC x = [x]+  where+    HeadC x = [x]  pattern Point :: Int -> Int -> (Int, Int) pattern Point{x, y} = (x, y)
fourmolu.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: fourmolu-version: 0.19.0.1+version: 0.20.0.0 license: BSD-3-Clause license-file: LICENSE.md maintainer:@@ -8,9 +8,9 @@     George Thomas <georgefsthomas@gmail.com>     Brandon Chinn <brandonchinn178@gmail.com> tested-with:-  ghc ==9.8.4-  ghc ==9.10.1-  ghc ==9.12.1+  ghc ==9.10.2+  ghc ==9.12.2+  ghc ==9.14.1  homepage: https://github.com/fourmolu/fourmolu bug-reports: https://github.com/fourmolu/fourmolu/issues@@ -84,6 +84,7 @@     Ormolu.Printer.Meat.Module     Ormolu.Printer.Meat.Pragma     Ormolu.Printer.Meat.Type+    Ormolu.Printer.Meat.Type.Function     Ormolu.Printer.Operators     Ormolu.Printer.SpanStream     Ormolu.Processing.Common@@ -100,7 +101,7 @@   other-modules: GHC.DynFlags   default-language: GHC2021   build-depends:-    Cabal-syntax >=3.14 && <3.15,+    Cabal-syntax >=3.16 && <3.17,     Diff >=0.4 && <2,     MemoTrie >=0.6 && <0.7,     ansi-terminal >=0.10 && <1.2,@@ -109,11 +110,11 @@     binary >=0.8 && <0.9,     bytestring >=0.2 && <0.13,     choice >=0.2.4.1 && <0.3,-    containers >=0.5 && <0.8,+    containers >=0.5 && <0.9,     directory ^>=1.3,     file-embed >=0.0.15 && <0.1,     filepath >=1.2 && <1.6,-    ghc-lib-parser >=9.12 && <9.13,+    ghc-lib-parser >=9.14 && <9.15,     megaparsec >=9,     mtl >=2 && <3,     syb >=0.7 && <0.8,@@ -152,13 +153,13 @@   autogen-modules: Paths_fourmolu   default-language: GHC2021   build-depends:-    Cabal-syntax >=3.14 && <3.15,+    Cabal-syntax >=3.16 && <3.17,     base >=4.12 && <5,-    containers >=0.5 && <0.8,+    containers >=0.5 && <0.9,     directory ^>=1.3,     filepath >=1.2 && <1.6,-    ghc-lib-parser >=9.12 && <9.13,-    optparse-applicative >=0.14 && <0.19,+    ghc-lib-parser >=9.14 && <9.15,+    optparse-applicative >=0.14 && <0.20,     text >=2.1 && <3,     th-env >=0.1.1 && <0.2,     unliftio >=0.2.10 && <0.3,@@ -210,14 +211,14 @@    default-language: GHC2021   build-depends:-    Cabal-syntax >=3.14 && <3.15,+    Cabal-syntax >=3.16 && <3.17,     QuickCheck >=2.14,     base >=4.14 && <5,     choice >=0.2.4.1 && <0.3,-    containers >=0.5 && <0.8,+    containers >=0.5 && <0.9,     directory ^>=1.3,     filepath >=1.2 && <1.6,-    ghc-lib-parser >=9.12 && <9.13,+    ghc-lib-parser >=9.14 && <9.15,     hspec >=2 && <3,     hspec-megaparsec >=2.2,     megaparsec >=9,
fourmolu.yaml view
@@ -5,6 +5,7 @@ column-limit: none function-arrows: trailing comma-style: trailing+record-style: aligned import-export-style: trailing import-grouping: legacy indent-wheres: true
src/GHC/DynFlags.hs view
@@ -7,10 +7,12 @@   ) where +import GHC.Data.FastString import GHC.Driver.Session import GHC.Platform import GHC.Settings import GHC.Settings.Config+import GHC.Unit.Types import GHC.Utils.Fingerprint  fakeSettings :: Settings@@ -43,6 +45,10 @@             platform_constants = Nothing           },       sPlatformMisc = PlatformMisc {},+      sUnitSettings =+        UnitSettings+          { unitSettings_baseUnitId = UnitId $ fsLit "ormolu"+          },       sToolSettings =         ToolSettings           { toolSettings_opt_P_fingerprint = fingerprint0,
src/Ormolu/Config.hs view
@@ -35,6 +35,7 @@     fillMissingPrinterOpts,     resolvePrinterOpts,     CommaStyle (..),+    RecordStyle (..),     FunctionArrowsStyle (..),     HaddockPrintStyle (..),     HaddockPrintStyleModule (..),@@ -43,12 +44,13 @@     ImportGrouping (..),     ImportGroup (..),     ImportGroupRule (..),-    ImportModuleMatcher (..),     ImportRulePriority (..),     matchAllRulePriority,     matchLocalRulePriority,     defaultImportRulePriority,+    ImportListMatcher (..),     QualifiedImportMatcher (..),+    ImportScopeMatcher (..),     LetStyle (..),     InStyle (..),     IfStyle (..),
src/Ormolu/Config/Gen.hs view
@@ -9,6 +9,7 @@ module Ormolu.Config.Gen   ( PrinterOpts (..)   , CommaStyle (..)+  , RecordStyle (..)   , FunctionArrowsStyle (..)   , HaddockPrintStyle (..)   , HaddockPrintStyleModule (..)@@ -54,6 +55,8 @@       poFunctionArrows :: f FunctionArrowsStyle     , -- | How to place commas in multi-line lists, records, etc.       poCommaStyle :: f CommaStyle+    , -- | How to place braces in records+      poRecordStyle :: f RecordStyle     , -- | Styling of import/export lists       poImportExportStyle :: f ImportExportStyle     , -- | Rules for grouping import declarations@@ -102,6 +105,7 @@     , poColumnLimit = Nothing     , poFunctionArrows = Nothing     , poCommaStyle = Nothing+    , poRecordStyle = Nothing     , poImportExportStyle = Nothing     , poImportGrouping = Nothing     , poIndentWheres = Nothing@@ -130,6 +134,7 @@     , poColumnLimit = pure NoLimit     , poFunctionArrows = pure TrailingArrows     , poCommaStyle = pure Leading+    , poRecordStyle = pure RecordStyleAligned     , poImportExportStyle = pure ImportExportDiffFriendly     , poImportGrouping = pure ImportGroupLegacy     , poIndentWheres = pure False@@ -165,6 +170,7 @@     , poColumnLimit = maybe (poColumnLimit p2) pure (poColumnLimit p1)     , poFunctionArrows = maybe (poFunctionArrows p2) pure (poFunctionArrows p1)     , poCommaStyle = maybe (poCommaStyle p2) pure (poCommaStyle p1)+    , poRecordStyle = maybe (poRecordStyle p2) pure (poRecordStyle p1)     , poImportExportStyle = maybe (poImportExportStyle p2) pure (poImportExportStyle p1)     , poImportGrouping = maybe (poImportGrouping p2) pure (poImportGrouping p1)     , poIndentWheres = maybe (poIndentWheres p2) pure (poIndentWheres p1)@@ -209,6 +215,10 @@       "How to place commas in multi-line lists, records, etc. (choices: \"leading\" or \"trailing\") (default: leading)"       "OPTION"     <*> f+      "record-style"+      "How to place braces in records (choices: \"aligned\" or \"knr\") (default: aligned)"+      "OPTION"+    <*> f       "import-export-style"       "Styling of import/export lists (choices: \"leading\", \"trailing\", or \"diff-friendly\") (default: diff-friendly)"       "OPTION"@@ -295,6 +305,7 @@     <*> f "column-limit"     <*> f "function-arrows"     <*> f "comma-style"+    <*> f "record-style"     <*> f "import-export-style"     <*> f "import-grouping"     <*> f "indent-wheres"@@ -348,6 +359,11 @@   | Trailing   deriving (Eq, Show, Enum, Bounded) +data RecordStyle+  = RecordStyleAligned+  | RecordStyleKnr+  deriving (Eq, Show, Enum, Bounded)+ data FunctionArrowsStyle   = TrailingArrows   | LeadingArrows@@ -450,6 +466,28 @@     Leading -> "leading"     Trailing -> "trailing" +instance Aeson.FromJSON RecordStyle where+  parseJSON =+    Aeson.withText "RecordStyle" $ \s ->+      either Aeson.parseFail pure $+        parsePrinterOptType (Text.unpack s)++instance PrinterOptsFieldType RecordStyle where+  parsePrinterOptType s =+    case s of+      "aligned" -> Right RecordStyleAligned+      "knr" -> Right RecordStyleKnr+      _ ->+        Left . unlines $+          [ "unknown value: " <> show s+          , "Valid values are: \"aligned\" or \"knr\""+          ]++instance RenderPrinterOpt RecordStyle where+  renderPrinterOpt = \case+    RecordStyleAligned -> "aligned"+    RecordStyleKnr -> "knr"+ instance Aeson.FromJSON FunctionArrowsStyle where   parseJSON =     Aeson.withText "FunctionArrowsStyle" $ \s ->@@ -776,6 +814,9 @@     , ""     , "# How to place commas in multi-line lists, records, etc. (choices: leading or trailing)"     , "comma-style: leading"+    , ""+    , "# How to place braces in records (choices: aligned or knr)"+    , "record-style: aligned"     , ""     , "# Styling of import/export lists (choices: leading, trailing, or diff-friendly)"     , "import-export-style: diff-friendly"
src/Ormolu/Config/Types.hs view
@@ -1,20 +1,22 @@+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}  module Ormolu.Config.Types   ( ImportGroup (..),     ImportGroupRule (..),-    ImportModuleMatcher (..),     ImportRulePriority (..),     matchAllRulePriority,     matchLocalRulePriority,     defaultImportRulePriority,+    ImportListMatcher (..),     QualifiedImportMatcher (..),+    ImportScopeMatcher (..),   ) where -import Control.Applicative (Alternative (..), asum)-import Data.Aeson ((.!=), (.:), (.:?))+import Control.Applicative (Alternative (..))+import Data.Aeson ((.!=), (.:?)) import Data.Aeson qualified as Aeson import Data.Aeson.Types qualified as Aeson import Data.List.NonEmpty (NonEmpty)@@ -34,28 +36,47 @@       <*> Aeson.parseField o "rules"  data ImportGroupRule = ImportGroupRule-  { igrModuleMatcher :: !ImportModuleMatcher,+  { igrGlob :: !Glob,+    igrImportListMatcher :: !ImportListMatcher,     igrQualifiedMatcher :: !QualifiedImportMatcher,+    igrScopeMatcher :: !ImportScopeMatcher,     igrPriority :: !ImportRulePriority   }   deriving (Eq, Show)  instance Aeson.FromJSON ImportGroupRule where   parseJSON = Aeson.withObject "rule" $ \o -> do-    let parseModuleMatcher = Aeson.parseJSON (Aeson.Object o)-        failUnknownModuleMatcher = Aeson.parseFail "Unknown or invalid module matcher"-        attemptParseModuleMatcher = parseModuleMatcher <|> failUnknownModuleMatcher-    igrModuleMatcher <- attemptParseModuleMatcher+    legacyMatch <- o .:? "match"+    (defaultPriority, defaultScope) <- case legacyMatch of+      Just "all" -> pure (matchAllRulePriority, pure MatchAllModules)+      Just "local-modules" -> pure (matchLocalRulePriority, pure MatchLocalModules)+      Just other -> Aeson.parseFail $ "Unknown legacy match value: " <> other+      Nothing -> pure (defaultImportRulePriority, empty) -    qualified <- o .:? "qualified"-    igrQualifiedMatcher <- case qualified of-      Just True -> pure MatchQualifiedOnly-      Just False -> pure MatchUnqualifiedOnly-      Nothing -> pure MatchBothQualifiedAndUnqualified+    igrGlob <- mkGlob <$> o .:? "glob" .!= "**" -    let defaultPriority-          | MatchAllModules <- igrModuleMatcher = matchAllRulePriority-          | otherwise = defaultImportRulePriority+    igrImportListMatcher <-+      o .:? "import-list" .!= "any" >>= \case+        "any" -> pure MatchAnyImportDeclaration+        "explicit" -> pure MatchExplicitImportList+        "hiding" -> pure MatchHidingImportClause+        "none" -> pure MatchWholeModuleImport+        other -> Aeson.parseFail $ "Unknown import list matcher: " <> other++    igrQualifiedMatcher <-+      o .:? "qualified" >>= \case+        Just True -> pure MatchQualifiedOnly+        Just False -> pure MatchUnqualifiedOnly+        Nothing -> pure MatchBothQualifiedAndUnqualified++    igrScopeMatcher <-+      o .:? "scope" >>= \case+        Just "any" -> pure MatchAllModules+        Just "local" -> pure MatchLocalModules+        Just "external" -> pure MatchExternalModules+        Nothing -> defaultScope <|> pure MatchAllModules+        Just other -> Aeson.parseFail ("Unknown scope matcher: " <> other)+     igrPriority <- o .:? "priority" .!= defaultPriority      pure ImportGroupRule {..}@@ -75,35 +96,21 @@ defaultImportRulePriority :: ImportRulePriority defaultImportRulePriority = ImportRulePriority 50 +data ImportListMatcher+  = MatchExplicitImportList+  | MatchHidingImportClause+  | MatchWholeModuleImport+  | MatchAnyImportDeclaration+  deriving (Eq, Show)+ data QualifiedImportMatcher   = MatchQualifiedOnly   | MatchUnqualifiedOnly   | MatchBothQualifiedAndUnqualified   deriving (Eq, Show) -data ImportModuleMatcher-  = MatchAllModules+data ImportScopeMatcher+  = MatchExternalModules   | MatchLocalModules-  | MatchGlob !Glob+  | MatchAllModules   deriving (Eq, Show)--instance Aeson.FromJSON ImportModuleMatcher where-  parseJSON v =-    asum-      [ parseMatchModuleMatcher v,-        parseGlobModuleMatcher v-      ]-    where-      parseMatchModuleMatcher :: Aeson.Value -> Aeson.Parser ImportModuleMatcher-      parseMatchModuleMatcher = Aeson.withObject "ImportModuleMatcher" $ \o -> do-        c <- Aeson.parseField @String o "match"-        case c of-          "all" -> pure MatchAllModules-          "local-modules" -> pure MatchLocalModules-          other -> Aeson.parseFail $ "Unknown matcher: " <> other--      parseGlobModuleMatcher :: Aeson.Value -> Aeson.Parser ImportModuleMatcher-      parseGlobModuleMatcher = Aeson.withObject "ImportModuleMatcher" $ \o -> do-        rawGlob <- o .: "glob"-        let glob = mkGlob rawGlob-        pure (MatchGlob glob)
src/Ormolu/Diff/ParseResult.hs view
@@ -106,11 +106,13 @@                   `extQ` considerEqual @EpLayout                   `extQ` considerEqual @AnnSig                   `extQ` considerEqual @HsRuleAnn-                  `extQ` considerEqual @EpLinearArrow+                  `extQ` considerEqual @EpLinear                   `extQ` considerEqual @AnnSynDecl                   `extQ` considerEqual @IsUnicodeSyntax                   -- FastString (for example for string literals)                   `extQ` considerEqualVia' ((==) @FastString)+                  -- ModuleName is a newtype of FastString+                  `extQ` considerEqualVia' ((==) @ModuleName)                   -- Haddock strings                   `extQ` hsDocStringEq                   -- Whether imports are pre- or post-qualified@@ -170,6 +172,7 @@     epAnnEq :: EpAnn a -> b -> ParseResultDiff     epAnnEq _ _ = Same +    importDeclQualifiedStyleEq :: forall a. (Data a) => ImportDeclQualifiedStyle -> a -> ParseResultDiff     importDeclQualifiedStyleEq = considerEqualVia' f       where         f QualifiedPre QualifiedPost = True@@ -177,7 +180,7 @@         f x x' = x == x'      hsDocStringEq :: HsDocString -> GenericQ ParseResultDiff-    hsDocStringEq = considerEqualVia' ((==) `on` (map (T.dropWhile isSpace) . splitDocString True))+    hsDocStringEq = considerEqualVia' ((==) `on` (map (T.dropWhile isSpace) . splitDocString))      forLocated ::       (Data e0, Data e1) =>
src/Ormolu/Diff/Text.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QualifiedDo #-}@@ -24,9 +23,6 @@ import GHC.Types.SrcLoc import Ormolu.Terminal import Ormolu.Terminal.QualifiedDo qualified as Term-#if !MIN_VERSION_base(4,20,0)-import Data.List (foldl')-#endif  ---------------------------------------------------------------------------- -- Types
src/Ormolu/Imports.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-}@@ -28,11 +27,8 @@ import GHC.Types.SourceText import GHC.Types.SrcLoc import Ormolu.Config (ImportGrouping)-import Ormolu.Imports.Grouping (Import (..), groupImports, prepareExistingGroups)+import Ormolu.Imports.Grouping (Import (..), ImportList (..), groupImports, prepareExistingGroups) import Ormolu.Utils (notImplemented, showOutputable)-#if !MIN_VERSION_base(4,20,0)-import Data.List (foldl')-#endif  -- | Sort, group and normalize imports. --@@ -51,7 +47,15 @@     . prepareExistingGroups importGrouping respectful   where     toImport :: (ImportId, x) -> Import-    toImport (ImportId {..}, _) = Import {importName = importIdName, importQualified}+    toImport (ImportId {..}, _) =+      Import+        { importName = importIdName,+          importList = case importHiding of+            Just (ImportListInterpretationOrd Exactly) -> Just ImportList+            Just (ImportListInterpretationOrd EverythingBut) -> Just HidingList+            Nothing -> Nothing,+          importQualified+        }      g :: LImportDecl GhcPs -> LImportDecl GhcPs     g (L l ImportDecl {..}) =@@ -90,10 +94,23 @@     importSafe :: Bool,     importQualified :: Bool,     importAs :: Maybe ModuleName,-    importHiding :: Maybe ImportListInterpretationOrd+    importHiding :: Maybe ImportListInterpretationOrd,+    importLevel :: Maybe ImportDeclLevelOrd   }   deriving (Eq, Ord) +-- | A wrapper for 'ImportDeclLevel' that provides an 'Ord' instance.+newtype ImportDeclLevelOrd = ImportDeclLevelOrd+  { unImportDeclLevelOrd :: ImportDeclLevel+  }+  deriving stock (Eq)++instance Ord ImportDeclLevelOrd where+  compare = compare `on` toBool . unImportDeclLevelOrd+    where+      toBool ImportDeclSplice = False+      toBool ImportDeclQuote = True+ data ImportPkgQual   = -- | The import is not qualified by a package name.     NoImportPkgQual@@ -137,11 +154,16 @@         QualifiedPost -> True         NotQualified -> False,       importAs = unLoc <$> ideclAs,-      importHiding = ImportListInterpretationOrd . fst <$> ideclImportList+      importHiding = ImportListInterpretationOrd . fst <$> ideclImportList,+      importLevel = importLevelOf ideclLevelSpec     }   where     isPrelude = moduleNameString moduleName == "Prelude"     moduleName = unLoc ideclName+    importLevelOf = \case+      LevelStylePre l -> Just (ImportDeclLevelOrd l)+      LevelStylePost l -> Just (ImportDeclLevelOrd l)+      NotLevelled -> Nothing  -- | Normalize a collection of import items. normalizeLies :: [LIE GhcPs] -> [LIE GhcPs]@@ -231,6 +253,7 @@       IEDefault _ x -> (1, x)       IEPattern _ x -> (2, x)       IEType _ x -> (3, x)+      IEData _ x -> (4, x)  compareRdrName :: RdrName -> RdrName -> Ordering compareRdrName x y =
src/Ormolu/Imports/Grouping.hs view
@@ -3,6 +3,7 @@  module Ormolu.Imports.Grouping   ( Import (..),+    ImportList (..),     prepareExistingGroups,     groupImports,   )@@ -19,18 +20,24 @@ import Distribution.ModuleName qualified as Cabal import GHC.Hs (GhcPs, getLocA) import Language.Haskell.Syntax (LImportDecl, ModuleName, moduleNameString)-import Ormolu.Config (ImportGroup (..), ImportGroupRule (..), ImportGrouping (..), ImportModuleMatcher (..))+import Ormolu.Config (ImportGroup (..), ImportGroupRule (..), ImportGrouping (..)) import Ormolu.Config qualified as Config import Ormolu.Utils (ghcModuleNameToCabal, groupBy', separatedByBlank)-import Ormolu.Utils.Glob (matchesGlob)+import Ormolu.Utils.Glob (matchAllGlob, matchesGlob)  newtype ImportGroups = ImportGroups (NonEmpty ImportGroup)  data Import = Import   { importName :: ModuleName,+    importList :: Maybe ImportList,     importQualified :: Bool   } +data ImportList+  = ImportList+  | HidingList+  deriving (Eq)+ importGroupSingleStrategy :: ImportGroups importGroupSingleStrategy =   ImportGroups $@@ -94,16 +101,20 @@ matchAllImportRule :: ImportGroupRule matchAllImportRule =   ImportGroupRule-    { igrModuleMatcher = MatchAllModules,+    { igrGlob = matchAllGlob,+      igrImportListMatcher = Config.MatchAnyImportDeclaration,       igrQualifiedMatcher = Config.MatchBothQualifiedAndUnqualified,+      igrScopeMatcher = Config.MatchAllModules,       igrPriority = Config.matchAllRulePriority     }  matchLocalModulesRule :: ImportGroupRule matchLocalModulesRule =   ImportGroupRule-    { igrModuleMatcher = MatchLocalModules,+    { igrGlob = matchAllGlob,+      igrImportListMatcher = Config.MatchAnyImportDeclaration,       igrQualifiedMatcher = Config.MatchBothQualifiedAndUnqualified,+      igrScopeMatcher = Config.MatchLocalModules,       igrPriority = Config.matchLocalRulePriority     } @@ -122,16 +133,30 @@     }  matchesRule :: Set Cabal.ModuleName -> Import -> ImportGroupRule -> Bool-matchesRule localMods Import {..} ImportGroupRule {..} = matchesModules && matchesQualified+matchesRule localMods Import {..} ImportGroupRule {..} =+  and+    [ matchingGlob,+      matchingImportList,+      matchingQualified,+      matchingScope+    ]   where-    matchesModules = case igrModuleMatcher of-      MatchAllModules -> True-      MatchLocalModules -> ghcModuleNameToCabal importName `Set.member` localMods-      MatchGlob gl -> moduleNameString importName `matchesGlob` gl-    matchesQualified = case igrQualifiedMatcher of+    matchingGlob = moduleNameString importName `matchesGlob` igrGlob+    matchingImportList = case igrImportListMatcher of+      Config.MatchExplicitImportList -> importList == Just ImportList+      Config.MatchHidingImportClause -> importList == Just HidingList+      Config.MatchWholeModuleImport -> importList == Nothing+      Config.MatchAnyImportDeclaration -> True+    matchingQualified = case igrQualifiedMatcher of       Config.MatchQualifiedOnly -> importQualified       Config.MatchUnqualifiedOnly -> not importQualified       Config.MatchBothQualifiedAndUnqualified -> True+    matchingScope =+      let isLocalModule = ghcModuleNameToCabal importName `Set.member` localMods+       in case igrScopeMatcher of+            Config.MatchAllModules -> True+            Config.MatchExternalModules -> not isLocalModule+            Config.MatchLocalModules -> isLocalModule  prepareExistingGroups :: ImportGrouping -> Bool -> [LImportDecl GhcPs] -> [[LImportDecl GhcPs]] prepareExistingGroups ig respectful =
src/Ormolu/Parser.hs view
@@ -30,7 +30,7 @@ import GHC.Data.FastString qualified as GHC import GHC.Data.Maybe (orElse) import GHC.Data.StringBuffer (StringBuffer)-import GHC.Driver.Config.Parser (initParserOpts)+import GHC.Driver.Config.Parser (initParserOpts, supportedLanguagePragmas) import GHC.Driver.Errors.Types qualified as GHC import GHC.Driver.Session as GHC import GHC.DynFlags (baseDynFlags)@@ -45,6 +45,7 @@ import GHC.Types.SrcLoc import GHC.Utils.Error import GHC.Utils.Exception (ExceptionMonad)+import GHC.Utils.Logger (initLogger) import GHC.Utils.Panic qualified as GHC import Ormolu.Config import Ormolu.Config.Gen (SingleConstraintParens (..))@@ -317,10 +318,14 @@     let (_warnings, fileOpts) =           GHC.getOptions             (initParserOpts flags)+            (supportedLanguagePragmas flags)             input             filepath+    -- 'initLogger' does not have any hooks installed, so we don't get any+    -- (unwanted) output.+    logger <- initLogger     (flags', leftovers, warnings) <--      parseDynamicFilePragma flags (extraOpts <> fileOpts)+      parseDynamicFilePragma logger flags (extraOpts <> fileOpts)     case NE.nonEmpty leftovers of       Nothing -> return ()       Just unrecognizedOpts ->
src/Ormolu/Printer/Combinators.hs view
@@ -24,6 +24,7 @@     newline,     declNewline,     multilineCommentNewline,+    newlineLiteral,     inci,     inciBy,     inciIf,@@ -48,6 +49,7 @@     -- ** Formatting lists     sep,     sepSemi,+    sepSemi',     canUseBraces,     useBraces,     dontUseBraces,@@ -58,6 +60,7 @@     backticks,     banana,     braces,+    recordBraces,     brackets,     parens,     parensHash,@@ -256,7 +259,25 @@   -- | Elements to render   [a] ->   R ()-sepSemi f xs = vlayout singleLine multiLine+sepSemi = sepSemi' False++-- | A version of 'sepSemi' that allows to control whether semicolons should+-- be inserted in multi-line layout.+--+-- > useBraces $ sepSemi' False txt ["foo", "bar"]+-- >   == vlayout (txt "{ foo; bar }") (txt "foo\nbar")+--+-- > dontUseBraces $ sepSemi' True txt ["foo", "bar"]+-- >   == vlayout (txt "foo; bar") (txt "foo;\nbar")+sepSemi' ::+  -- | Whether to insert semicolons in multi-line layout+  Bool ->+  -- | How to render an element+  (a -> R ()) ->+  -- | Elements to render+  [a] ->+  R ()+sepSemi' addMultiColSemi f xs = vlayout singleLine multiLine   where     singleLine = do       ub <- canUseBraces@@ -272,7 +293,10 @@               txt "}"             else sep (txt ";" >> space) f xs'     multiLine =-      sep newline (dontUseBraces . f) xs+      sep+        (if addMultiColSemi then txt ";" >> newline else newline)+        (dontUseBraces . f)+        xs  ---------------------------------------------------------------------------- -- Wrapping@@ -368,6 +392,31 @@             else space >> sitcc m       newline       inciIf (style == S) close++-- With leading commas align the close brace with commas+-- otherwise move the close brace back to the left+recordBraces :: R () -> R ()+recordBraces m = do+  commaStyle <- getPrinterOpt poCommaStyle+  recordBraces_ (commaStyle == Trailing) m++recordBraces_ :: Bool -> R () -> R ()+recordBraces_ moveBraceBack m = do+  style <- getPrinterOpt poRecordStyle+  case style of+    RecordStyleAligned -> braces N m+    RecordStyleKnr ->+      vlayout+        (txt "{" >> m >> txt "}")+        ( do+            txt "{"+            newline+            sitcc m+            newline+            if moveBraceBack+              then inciByFrac (-1) (txt "}")+              else txt "}"+        )  ---------------------------------------------------------------------------- -- Literals
src/Ormolu/Printer/Internal.hs view
@@ -20,6 +20,7 @@     newline,     declNewline,     multilineCommentNewline,+    newlineLiteral,     askSourceType,     askModuleFixityMap,     askDebug,@@ -437,6 +438,19 @@             VeryBeginning -> VeryBeginning             _ -> AfterNewline         }++-- | Insert a newline literal without modifying the internal state of the+-- parser. This is to be used exceptionally, e.g. for printing multiline+-- string literals.+newlineLiteral :: R ()+newlineLiteral = R . modify $ \sc ->+  sc+    { scBuilder = scBuilder sc <> "\n",+      scColumn = 0,+      scIndent = 0,+      scThisLineSpans = [],+      scRequestedDelimiter = AfterNewline+    }  -- | Return the source type. askSourceType :: R SourceType
src/Ormolu/Printer/Meat/Common.hs view
@@ -15,6 +15,7 @@     p_hsDoc',     p_sourceText,     p_namespaceSpec,+    p_hsMultAnn,     p_arrow,   ) where@@ -29,13 +30,13 @@ import GHC.Hs.Doc import GHC.Hs.Extension (GhcPs) import GHC.Hs.ImpExp+import GHC.Hs.Type import GHC.LanguageExtensions.Type (Extension (..)) import GHC.Parser.Annotation import GHC.Types.Name.Occurrence (OccName (..), occNameString) import GHC.Types.Name.Reader import GHC.Types.SourceText import GHC.Types.SrcLoc-import Language.Haskell.Syntax (HsArrowOf (..)) import Language.Haskell.Syntax.Module.Name import Ormolu.Config import Ormolu.Printer.Combinators@@ -48,7 +49,7 @@   | -- | Top-level declarations     Free --- | Outputs the name of the module-like entity, preceeded by the correct prefix ("module" or "signature").+-- | Outputs the name of the module-like entity, preceded by the correct prefix ("module" or "signature"). p_hsmodName :: ModuleName -> R () p_hsmodName mname = do   sourceType <- askSourceType@@ -73,6 +74,10 @@     txt "type"     space     p_rdrName x+  IEData _ x -> do+    txt "data"+    space+    p_rdrName x  -- | Render a @'LocatedN' 'RdrName'@. p_rdrName :: LocatedN RdrName -> R ()@@ -93,7 +98,7 @@       -- insert spaces when we have a parenthesized operator starting with `#`.       handleUnboxedSumsAndHashInteraction         | unboxedSums,-          -- Qualified names do not start wth a `#`.+          -- Qualified names do not start with a `#`.           Unqual (occNameString -> '#' : _) <- x =             \y -> space *> y <* space         | otherwise = id@@ -190,12 +195,7 @@   -- Make sure the Haddock is separated by a newline from other comments.   when goesAfterComment newline -  let shouldEscapeCommentBraces =-        case poHStyle of-          HaddockSingleLine -> False-          HaddockMultiLine -> True-          HaddockMultiLineCompact -> True-  let docStringLines = splitDocString shouldEscapeCommentBraces $ hsDocString str+  let docStringLines = splitDocString $ hsDocString str    if poHStyle == HaddockSingleLine || length docStringLines <= 1     then do@@ -242,10 +242,18 @@   TypeNamespaceSpecifier _ -> txt "type" *> space   DataNamespaceSpecifier _ -> txt "data" *> space -p_arrow :: (mult -> R ()) -> HsArrowOf mult GhcPs -> R ()+p_hsMultAnn :: (mult -> R ()) -> HsMultAnnOf mult GhcPs -> R ()+p_hsMultAnn p_mult = \case+  HsUnannotated _ -> pure ()+  HsLinearAnn _ -> txt "%1"+  HsExplicitMult _ mult -> txt "%" *> p_mult mult++-- | Like 'p_hsMultAnn', except specifically for arrows, taking -XUnicodeSyntax+--   into account.+p_arrow :: (mult -> R ()) -> HsMultAnnOf mult GhcPs -> R () p_arrow p_mult = \case-  HsUnrestrictedArrow _ -> token'rarrow-  HsLinearArrow _ -> token'lolly+  HsUnannotated _ -> token'rarrow+  HsLinearAnn _ -> token'lolly   HsExplicitMult _ mult -> do     txt "%"     p_mult mult
src/Ormolu/Printer/Meat/Declaration.hs view
@@ -256,7 +256,7 @@   TypeSynonym ::     RdrName -> HsDecl GhcPs pattern InlinePragma n <- SigD _ (InlineSig _ (L _ n) _)-pattern SpecializePragma n <- SigD _ (SpecSig _ (L _ n) _ _)+pattern SpecializePragma n <- SigD _ (isSpecSig -> Just n) pattern SCCPragma n <- SigD _ (SCCFunSig _ (L _ n) _) pattern AnnTypePragma n <- AnnD _ (HsAnnotation _ (TypeAnnProvenance (L _ n)) _) pattern AnnValuePragma n <- AnnD _ (HsAnnotation _ (ValueAnnProvenance (L _ n)) _)@@ -266,6 +266,12 @@ pattern KindSignature n <- KindSigD _ (StandaloneKindSig _ (L _ n) _) pattern FamilyDeclaration n <- TyClD _ (FamDecl _ (FamilyDecl _ _ _ (L _ n) _ _ _ _)) pattern TypeSynonym n <- TyClD _ (SynDecl _ (L _ n) _ _ _)++isSpecSig :: Sig GhcPs -> Maybe RdrName+isSpecSig = \case+  SpecSig _ (L _ n) _ _ -> Just n+  SpecSigE _ _ (deconstructExprFromSpecSigE -> (L _ n, _, _)) _ -> Just n+  _ -> Nothing  -- Declarations which can refer to multiple names 
src/Ormolu/Printer/Meat/Declaration/Data.hs view
@@ -3,9 +3,12 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS_GHC -Wno-orphans #-}  -- | Renedring of data type declarations. module Ormolu.Printer.Meat.Declaration.Data@@ -21,7 +24,6 @@ import Data.List.NonEmpty qualified as NE import Data.Maybe (isJust, isNothing, maybeToList) import Data.Text qualified as Text-import Data.Void import GHC.Hs import GHC.Types.Fixity import GHC.Types.ForeignCall@@ -154,7 +156,7 @@   deriving (Eq, Ord)  p_conDecl :: Choice "singleRecCon" -> ConDecl GhcPs -> R ()-p_conDecl _ ConDeclGADT {..} = do+p_conDecl _ decl@ConDeclGADT {..} = do   mapM_ (p_hsDoc Pipe (With #endNewline)) con_doc   switchLayout conDeclSpn $ do     let c :| cs = con_names@@ -162,61 +164,42 @@     unless (null cs) . inci $ do       commaDel       sep commaDel p_rdrName cs-    inci $ do-      let conTy = case con_g_args of-            PrefixConGADT NoExtField xs ->-              let go (HsScaled a b) t = addCLocA t b (HsFunTy NoExtField a b t)-               in foldr go con_res_ty xs-            RecConGADT _ r ->-              addCLocA r con_res_ty $-                HsFunTy-                  NoExtField-                  (HsUnrestrictedArrow noAnn)-                  (la2la $ HsRecTy noAnn <$> r)-                  con_res_ty-          qualTy = case con_mb_cxt of-            Nothing -> conTy-            Just qs ->-              addCLocA qs conTy $-                HsQualTy NoExtField qs conTy-          quantifiedTy :: LHsType GhcPs-          quantifiedTy =-            addCLocA con_bndrs qualTy $-              hsOuterTyVarBndrsToHsType (unLoc con_bndrs) qualTy-      p_hsTypeAnnotation quantifiedTy+    inci $ p_hsFun decl   where     conDeclSpn =-      fmap getLocA (NE.toList con_names)-        <> [getLocA con_bndrs]+      fmap getLocA (NE.toList con_names) <> conSigSpans+    conSigSpans =+      [getLocA con_outer_bndrs]         <> maybeToList (fmap getLocA con_mb_cxt)-        <> conArgsSpans-    conArgsSpans = case con_g_args of-      PrefixConGADT NoExtField xs -> getLocA . hsScaledThing <$> xs-      RecConGADT _ x -> [getLocA x]+        <> conArgResSpans+    conArgResSpans =+      getLocA con_res_ty : case con_g_args of+        PrefixConGADT NoExtField xs -> getLocA . cdf_type <$> xs+        RecConGADT _ x -> [getLocA x] p_conDecl singleRecCon ConDeclH98 {..} =   case con_args of-    PrefixCon (_ :: [Void]) xs -> do+    PrefixCon xs -> do       renderConDoc       renderContext       switchLayout conDeclSpn $ do         p_rdrName con_name-        let args = hsScaledThing <$> xs-            argsHaveDocs = conArgsHaveHaddocks args+        let argsHaveDocs = conArgsHaveHaddocks xs             delimiter = if argsHaveDocs then newline else breakpoint         unless (null xs) delimiter         inci . sitcc $-          sep delimiter (sitcc . located' p_hsType) args+          sep delimiter (sitcc . p_hsConDeclFieldWithDoc) xs     RecCon l -> do       renderConDoc       renderContext       switchLayout conDeclSpn $ do         p_rdrName con_name-        breakpoint-        inciIf (Choice.isFalse singleRecCon) (located l p_conDeclFields)-    InfixCon (HsScaled _ l) (HsScaled _ r) -> do+        recordStyle <- getPrinterOpt poRecordStyle+        if recordStyle == RecordStyleKnr then space else breakpoint+        inciIf (Choice.isFalse singleRecCon) (located l p_hsConDeclRecFields)+    InfixCon l r -> do       -- manually render these-      let (lType, larg_doc) = splitDocTy l-      let (rType, rarg_doc) = splitDocTy r+      let larg_doc = cdf_doc l+          rarg_doc = cdf_doc r        -- the constructor haddock can go on top of the entire constructor       -- only if neither argument has haddocks@@ -229,10 +212,10 @@         if isJust con_doc           then do             mapM_ (p_hsDoc Pipe (With #endNewline)) larg_doc-            located lType p_hsType+            p_hsConDeclField l             breakpoint           else do-            located lType p_hsType+            p_hsConDeclField l             case larg_doc of               Just doc -> space >> p_hsDoc Caret (With #endNewline) doc               Nothing -> breakpoint@@ -242,7 +225,7 @@           case rarg_doc of             Just doc -> newline >> p_hsDoc Pipe (With #endNewline) doc             Nothing -> breakpoint-          located rType p_hsType+          p_hsConDeclField r   where     renderConDoc = mapM_ (p_hsDoc Pipe (With #endNewline)) con_doc     renderContext =@@ -262,13 +245,9 @@     conDeclSpn = conNameSpn : conArgsSpans     conNameSpn = getLocA con_name     conArgsSpans = case con_args of-      PrefixCon (_ :: [Void]) xs -> getLocA . hsScaledThing <$> xs+      PrefixCon xs -> getLocA . cdf_type <$> xs       RecCon l -> [getLocA l]-      InfixCon x y -> getLocA . hsScaledThing <$> [x, y]--    splitDocTy = \case-      L _ (HsDocTy _ ty doc) -> (ty, Just doc)-      ty -> (ty, Nothing)+      InfixCon x y -> getLocA . cdf_type <$> [x, y]  p_lhsContext ::   LHsContext GhcPs ->@@ -337,6 +316,105 @@           located sigTy p_hsSigType  ----------------------------------------------------------------------------+-- FunRepr ConDeclGADT++-- | ConDecl, except should only be called with the ConDeclGADT constructor.+type ConDeclGADT = ConDecl++-- | FunRepr ConDeclGADT renders a GADT constructor type annotation, which looks+-- similar to a function type, except the arguments of the function have two+-- additional capabilities:+--   * They can specify UNPACK/strictness+--   * It can be a single argument with a record fields syntax+instance FunRepr (ConDeclGADT GhcPs) where+  parseFunRepr = \case+    L _ ConDeclGADT {..} ->+      fst+        . addSig+        . addOuter con_outer_bndrs+        . addInner con_inner_bndrs+        . addCtx con_mb_cxt+        . addArgs con_g_args+        $ mkRet con_res_ty+    _ -> error "parseFunRepr @ConDeclGADT unexpectedly called on non-GADT constructor"+    where+      addSig (next, loc) = (ParsedFunSig {sig = (), next}, loc)+      addOuter (L ann bndrs) =+        case bndrs :: HsOuterSigTyVarBndrs GhcPs of+          HsOuterImplicit {} -> id+          HsOuterExplicit _ bndrs' -> \(next, loc) ->+            let loc' = combineSrcSpans loc (getHasLoc ann)+                fun =+                  ParsedFunForall+                    { tele = L (l2l loc') $ mkHsForAllInvisTele noAnn bndrs',+                      next+                    }+             in (fun, loc')+      addInner =+        let go tele (next, loc) =+              let loc' =+                    combineSrcSpans loc $+                      case tele of+                        HsForAllVis x _ -> getHasLoc x+                        HsForAllInvis x _ -> getHasLoc x+                  fun = ParsedFunForall {tele = L (l2l loc') tele, next}+               in (fun, loc')+         in foldr (\tele acc -> go tele . acc) id+      addCtx = \case+        Nothing -> id+        Just ctxs -> \(next, loc) ->+          let loc' = combineSrcSpans loc (getHasLoc ctxs)+              fun = ParsedFunQuals {ctxs = [L (l2l loc') ctxs], next}+           in (fun, loc')+      addArgs details =+        case details :: HsConDeclGADTDetails GhcPs of+          PrefixConGADT _ fields ->+            let go field (next, loc) =+                  let loc' = combineSrcSpans loc (getHasLoc field.cdf_type)+                      fun =+                        ParsedFunArg+                          { span = l2l loc',+                            arg = L noAnn (Left field),+                            doc = field.cdf_doc,+                            multAnn = field.cdf_multiplicity,+                            next+                          }+                   in (fun, loc')+             in foldr (\field acc -> go field . acc) id fields+          RecConGADT _ fields -> \(next, loc) ->+            let loc' = combineSrcSpans (getHasLoc fields) loc+                fun =+                  ParsedFunArg+                    { span = l2l loc',+                      arg = la2la $ Right <$> fields,+                      doc = Nothing,+                      multAnn = HsUnannotated (EpArrow noAnn),+                      next+                    }+             in (fun, loc')+      mkRet ty =+        let fun =+              case ty of+                L _ (HsDocTy _ ret doc) -> ParsedFunReturn {ret, doc = Just doc}+                ret -> ParsedFunReturn {ret, doc = Nothing}+         in (fun, getHasLoc ty)++  type FunReprCtx (ConDeclGADT GhcPs) = HsType GhcPs+  renderFunReprCtx = p_hsType++  -- Invariant: Exactly one of the following must be true:+  --   * There's exactly one 'Right' arg+  --   * There are zero or more 'Left' args+  type FunReprArg (ConDeclGADT GhcPs) = Either (HsConDeclField GhcPs) [LocatedA (HsConDeclRecField GhcPs)]+  renderFunReprArg = either p_hsConDeclField p_hsConDeclRecFields++  type FunReprMult (ConDeclGADT GhcPs) = HsType GhcPs+  renderFunReprMult = p_hsType++  type FunReprRet (ConDeclGADT GhcPs) = HsType GhcPs+  renderFunReprRet = p_hsType++---------------------------------------------------------------------------- -- Helpers  isInfix :: LexicalFixity -> Bool@@ -349,14 +427,9 @@   where     f ConDeclH98 {..} =       isJust con_doc || case con_args of-        PrefixCon [] xs ->-          conArgsHaveHaddocks (hsScaledThing <$> xs)+        PrefixCon xs -> conArgsHaveHaddocks xs         _ -> False     f _ = False -conArgsHaveHaddocks :: [LBangType GhcPs] -> Bool-conArgsHaveHaddocks xs =-  let hasDocs = \case-        HsDocTy {} -> True-        _ -> False-   in any (hasDocs . unLoc) xs+conArgsHaveHaddocks :: [HsConDeclField GhcPs] -> Bool+conArgsHaveHaddocks = any (isJust . cdf_doc)
src/Ormolu/Printer/Meat/Declaration/Foreign.hs view
@@ -9,10 +9,12 @@ import Control.Monad import GHC.Hs import GHC.Types.ForeignCall+import GHC.Types.SourceText import GHC.Types.SrcLoc import Ormolu.Printer.Combinators import Ormolu.Printer.Meat.Common import Ormolu.Printer.Meat.Declaration.Signature+import Ormolu.Printer.Meat.Declaration.StringLiteral  p_foreignDecl :: ForeignDecl GhcPs -> R () p_foreignDecl = \case@@ -56,8 +58,11 @@   located cCallConv atom   -- Need to check for 'noLoc' for the 'safe' annotation   when (isGoodSrcSpan $ getLocA safety) (space >> atom safety)-  space-  located sourceText p_sourceText+  inci $ located sourceText $ \case+    NoSourceText -> pure ()+    SourceText lit -> do+      breakpoint+      p_stringLit lit  p_foreignExport :: ForeignExport GhcPs -> R () p_foreignExport (CExport sourceText (L loc (CExportStatic _ _ cCallConv))) = do
src/Ormolu/Printer/Meat/Declaration/OpTree.hs view
@@ -138,12 +138,7 @@       putOpsExprs prevExpr (opi : ops') (expr : exprs') = do         let isLast = null exprs'             ub' = if not isLast then ub else id-            -- Distinguish holes used in infix notation.-            -- eg. '1 _foo 2' and '1 `_foo` 2'-            opWrapper = case unLoc (opiOp opi) of-              HsUnboundVar _ _ -> backticks-              _ -> id-            p_op = located (opiOp opi) (opWrapper . p_hsExpr)+            p_op = located (opiOp opi) p_hsExpr             p_y = ub' $ p_exprOpTree N expr         if isTrailing           then do
src/Ormolu/Printer/Meat/Declaration/Rule.hs view
@@ -4,16 +4,16 @@  module Ormolu.Printer.Meat.Declaration.Rule   ( p_ruleDecls,+    p_ruleBndrs,   ) where -import Control.Monad (unless) import GHC.Hs import GHC.Types.Basic import GHC.Types.SourceText import Ormolu.Printer.Combinators import Ormolu.Printer.Meat.Common-import Ormolu.Printer.Meat.Declaration.Signature+import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.Signature import Ormolu.Printer.Meat.Declaration.Value import Ormolu.Printer.Meat.Type @@ -22,21 +22,12 @@   pragma "RULES" $ sep breakpoint (sitcc . located' p_ruleDecl) xs  p_ruleDecl :: RuleDecl GhcPs -> R ()-p_ruleDecl (HsRule _ ruleName activation tyvars ruleBndrs lhs rhs) = do+p_ruleDecl (HsRule _ ruleName activation ruleBndrs lhs rhs) = do   located ruleName p_ruleName   space   p_activation activation   space-  case tyvars of-    Nothing -> return ()-    Just xs -> do-      p_forallBndrs ForAllInvis p_hsTyVarBndr xs-      space-  -- It appears that there is no way to tell if there was an empty forall-  -- in the input or no forall at all. We do not want to add redundant-  -- foralls, so let's just skip the empty ones.-  unless (null ruleBndrs) $-    p_forallBndrs ForAllInvis p_ruleBndr ruleBndrs+  p_ruleBndrs ruleBndrs   breakpoint   inci $ do     located lhs p_hsExpr@@ -48,6 +39,17 @@  p_ruleName :: RuleName -> R () p_ruleName name = atom (HsString NoSourceText name :: HsLit GhcPs)++p_ruleBndrs :: RuleBndrs GhcPs -> R ()+p_ruleBndrs (RuleBndrs HsRuleBndrsAnn {..} tyvars ruleBndrs) = do+  case tyvars of+    Nothing -> return ()+    Just xs -> do+      p_forallBndrs ForAllInvis p_hsTyVarBndr xs+      space+  case rb_tmanns of+    Nothing -> pure ()+    Just _ -> p_forallBndrs ForAllInvis p_ruleBndr ruleBndrs  p_ruleBndr :: RuleBndr GhcPs -> R () p_ruleBndr = \case
src/Ormolu/Printer/Meat/Declaration/Signature.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ViewPatterns #-}  -- | Type signature declarations. module Ormolu.Printer.Meat.Declaration.Signature@@ -8,18 +9,24 @@     p_typeAscription,     p_activation,     p_standaloneKindSig,+    deconstructExprFromSpecSigE,   ) where  import Control.Monad+import Data.Maybe (maybeToList) import GHC.Data.BooleanFormula import GHC.Hs+import GHC.Stack (HasCallStack) import GHC.Types.Basic import GHC.Types.Fixity import GHC.Types.Name.Reader import GHC.Types.SourceText+import GHC.Types.SrcLoc import Ormolu.Printer.Combinators import Ormolu.Printer.Meat.Common+import Ormolu.Printer.Meat.Declaration.Rule+import Ormolu.Printer.Meat.Declaration.Value (p_hsExpr) import Ormolu.Printer.Meat.Type import Ormolu.Utils @@ -30,7 +37,9 @@   ClassOpSig _ def names sigType -> p_classOpSig def names sigType   FixSig _ sig -> p_fixSig sig   InlineSig _ name inlinePragma -> p_inlineSig name inlinePragma-  SpecSig _ name ts inlinePragma -> p_specSig name ts inlinePragma+  SpecSig _ name ts inlinePragma ->+    p_specSig Nothing (noLocA $ HsVar NoExtField name) ts inlinePragma+  SpecSigE _ ruleBndrs expr inlinePragma -> p_specSigE ruleBndrs expr inlinePragma   SpecInstSig _ sigType -> p_specInstSig sigType   MinimalSig _ booleanFormula -> p_minimalSig booleanFormula   CompleteMatchSig _ cs ty -> p_completeSig cs ty@@ -116,26 +125,71 @@   p_rdrName name  p_specSig ::-  -- | Name-  LocatedN RdrName ->+  -- | Rule binders+  Maybe (RuleBndrs GhcPs) ->+  -- | Expression to specialize+  LHsExpr GhcPs ->   -- | The types to specialize to   [LHsSigType GhcPs] ->   -- | For specialize inline   InlinePragma ->   R ()-p_specSig name ts InlinePragma {..} = pragmaBraces $ do+p_specSig mRuleBndrs specExpr ts InlinePragma {..} = pragmaBraces $ do   txt "SPECIALIZE"   space   p_inlineSpec inl_inline   space-  p_activation inl_act-  space-  p_rdrName name-  space-  token'dcolon-  breakpoint-  inci $ sep commaDel (located' p_hsSigType) ts+  case (inl_inline, inl_act) of+    (NoInline _, NeverActive) -> return ()+    _ -> p_activation inl_act+  inci $ do+    space+    forM_ mRuleBndrs $ \ruleBndrs -> do+      p_ruleBndrs ruleBndrs+      space+    located specExpr p_hsExpr+    case ts of+      [] -> pure ()+      _ -> do+        space+        token'dcolon+        breakpoint+        sep commaDel (located' p_hsSigType) ts +p_specSigE ::+  -- | Rule binders+  RuleBndrs GhcPs ->+  -- | Expression to specialize+  LHsExpr GhcPs ->+  -- | For specialize inline+  InlinePragma ->+  R ()+p_specSigE ruleBndrs expr =+  p_specSig (Just ruleBndrs) specExpr (maybeToList sigTy)+  where+    (_, specExpr, sigTy) = deconstructExprFromSpecSigE expr++-- | The 'LHsExpr' in a 'SpecSigE' can only be of a very specific form, namely a+-- variable applied to value/type-level arguments, optionally with a type+-- signature.+--+-- https://github.com/ghc-proposals/ghc-proposals/blob/e2c683698323cec3e33625369ae2b5f585387c70/proposals/0493-specialise-expressions.rst#2proposed-change-specification+deconstructExprFromSpecSigE ::+  (HasCallStack) =>+  LHsExpr GhcPs ->+  (LocatedN RdrName, LHsExpr GhcPs, Maybe (LHsSigType GhcPs))+deconstructExprFromSpecSigE = \case+  L _ (ExprWithTySig _ e HsWC {hswc_body}) ->+    (findVar e, e, Just hswc_body)+  e -> (findVar e, e, Nothing)+  where+    findVar :: LHsExpr GhcPs -> LocatedN RdrName+    findVar = \case+      L _ (HsVar _ name) -> name+      L _ (HsApp _ e _) -> findVar e+      L _ (HsAppType _ e _) -> findVar e+      _ -> error "unreachble"+ p_inlineSpec :: InlineSpec -> R () p_inlineSpec = \case   Inline _ -> txt "INLINE"@@ -165,7 +219,7 @@  p_minimalSig ::   -- | Boolean formula-  LBooleanFormula (LocatedN RdrName) ->+  LBooleanFormula GhcPs ->   R () p_minimalSig =   located' $ \booleanFormula ->@@ -173,7 +227,7 @@  p_booleanFormula ::   -- | Boolean formula-  BooleanFormula (LocatedN RdrName) ->+  BooleanFormula GhcPs ->   R () p_booleanFormula = \case   Var name -> p_rdrName name
+ src/Ormolu/Printer/Meat/Declaration/Signature.hs-boot view
@@ -0,0 +1,14 @@+module Ormolu.Printer.Meat.Declaration.Signature+  ( p_sigDecl,+    p_typeAscription,+    p_activation,+  )+where++import GHC.Hs+import GHC.Types.Basic+import Ormolu.Printer.Combinators++p_sigDecl :: Sig GhcPs -> R ()+p_typeAscription :: LHsSigType GhcPs -> R ()+p_activation :: Activation -> R ()
src/Ormolu/Printer/Meat/Declaration/StringLiteral.hs view
@@ -40,7 +40,7 @@                   LastPos -> txt "\\" *> txt s         vlayout singleLine multiLine       MultilineStringLiteral ->-        sep breakpoint' txt segments+        sep newlineLiteral txt segments     txt endMarker  -- | The start/end marker of the literal, whether it is a regular or a multiline@@ -91,12 +91,25 @@       where         go [] = [s]         go ((pre, suf) : bs) = case T.uncons suf of-          Just ('\\', T.uncons -> Just (c, s'))-            | is_space c,-              let rest = T.drop 1 $ T.dropWhile (/= '\\') s' ->+          Just ('\\', suf')+            | (gap, T.uncons -> Just ('\\', rest)) <- T.span is_space suf',+              -- If there is a space after the backslash, this definitely is a+              -- string gap. Continue parsing gaps after the next backslash.+              not $ T.null gap ->                 pre : splitGaps rest-            | otherwise -> go $ (if c == '\\' then drop 1 else id) bs+            | otherwise ->+                -- Check whether @suf@ starts with an escape sequence involving+                -- another backslash. If so, it can not be the start of a string+                -- gap, so we skip it.+                let skipNextBackslash =+                      any (`T.isPrefixOf` suf') escapesWithAnotherBackslash+                 in go $ (if skipNextBackslash then drop 1 else id) bs           _ -> go bs++        -- All escape sequences (without the initial backslash) with another+        -- backslash. See+        -- https://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-200002.6+        escapesWithAnotherBackslash = ["\\", "^\\"]      -- See the the MultilineStrings GHC proposal and 'lexMultilineString' from     -- "GHC.Parser.String" for reference.
src/Ormolu/Printer/Meat/Declaration/Value.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-}@@ -31,7 +32,6 @@ import Data.Maybe import Data.Text (Text) import Data.Text qualified as Text-import Data.Void import GHC.Data.Strict qualified as Strict import GHC.Hs import GHC.LanguageExtensions.Type (Extension (NegativeLiterals))@@ -47,7 +47,7 @@ import Ormolu.Printer.Meat.Common import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.OpTree-import Ormolu.Printer.Meat.Declaration.Signature+import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.Signature import Ormolu.Printer.Meat.Declaration.StringLiteral import Ormolu.Printer.Meat.Type import Ormolu.Printer.Operators@@ -100,15 +100,15 @@   MatchGroup GhcPs (LocatedA body) ->   R () p_matchGroup' placer render style mg@MG {..} = do-  let ob = case style of-        Case -> bracesIfEmpty-        LambdaCase -> bracesIfEmpty-        _ -> dontUseBraces-        where-          bracesIfEmpty = if isEmptyMatchGroup mg then useBraces else id   -- Since we are forcing braces on 'sepSemi' based on 'ob', we have to   -- restore the brace state inside the sepsemi.   ub <- bool dontUseBraces useBraces <$> canUseBraces+  let ob = case style of+        Case -> bracesIfNecessary+        LambdaCase -> bracesIfNecessary+        _ -> dontUseBraces+        where+          bracesIfNecessary = if isEmptyMatchGroup mg then useBraces else ub   ob $ sepSemi (located' (ub . p_Match)) (unLoc mg_alts)   where     p_Match m@Match {..} =@@ -117,7 +117,7 @@         render         (adjustMatchGroupStyle m style)         (isInfixMatch m)-        (HsNoMultAnn NoExtField)+        (HsUnannotated EpPatBind)         (matchStrictness m)         -- We use the spans of the individual patterns.         (unLoc m_pats)@@ -126,7 +126,7 @@ -- | Function id obtained through pattern matching on 'FunBind' should not -- be used to print the actual equations because the different ‘RdrNames’ -- used in the equations may have different “decorations” (such as backticks--- and paretheses) associated with them. It is necessary to use per-equation+-- and parentheses) associated with them. It is necessary to use per-equation -- names obtained from 'm_ctxt' of 'Match'. This function replaces function -- name inside of 'Function' accordingly. adjustMatchGroupStyle ::@@ -134,14 +134,13 @@   MatchGroupStyle ->   MatchGroupStyle adjustMatchGroupStyle m = \case-  Function _ -> (Function . mc_fun . m_ctxt) m+  Function _ | FunRhs {mc_fun = f} <- m_ctxt m -> Function f   style -> style  matchStrictness :: Match id body -> SrcStrictness-matchStrictness match =-  case m_ctxt match of-    FunRhs {mc_strictness = s} -> s-    _ -> NoSrcStrict+matchStrictness = \case+  Match {m_ctxt = FunRhs {mc_strictness = s}} -> s+  _ -> NoSrcStrict  p_match ::   -- | Style of the group@@ -186,17 +185,19 @@   -- would start with two indentation steps applied, which is ugly, so we   -- need to be a bit more clever here and bump indentation level only when   -- pattern group is multiline.+  p_hsMultAnn (located' p_hsType) multAnn   case multAnn of-    HsNoMultAnn NoExtField -> pure ()-    HsPct1Ann _ -> txt "%1" *> space-    HsMultAnn _ ty -> do-      txt "%"-      located ty p_hsType-      space+    HsUnannotated {} -> pure ()+    HsLinearAnn {} -> space+    HsExplicitMult {} -> space   case strictness of     NoSrcStrict -> return ()     SrcStrict -> txt "!"     SrcLazy -> txt "~"+  let isCase = \case+        Case -> True+        LambdaCase -> True+        _ -> False   indentBody <- case NE.nonEmpty m_pats of     Nothing ->       False <$ case style of@@ -207,7 +208,12 @@             Function name -> combineSrcSpans (getLocA name) patSpans             _ -> patSpans           patSpans = combineSrcSpans' (getLocA <$> ne_pats)-          indentBody = not (isOneLineSpan combinedSpans)+          containsOrPat = everything (||) $ \b -> case cast @_ @(Pat GhcPs) b of+            Just OrPat {} -> True+            _ -> False+          indentBody =+            not (isOneLineSpan combinedSpans)+              && not (isCase style && containsOrPat ne_pats)       switchLayoutNoLimit [combinedSpans] $ do         let stdCase = sep breakpoint (located' p_pat) m_pats         case style of@@ -244,14 +250,10 @@           Function name -> Just (getLocA name)           _ -> Nothing         Just pats -> (Just . getLocA . NE.last) pats-      isCase = \case-        Case -> True-        LambdaCase -> True-        _ -> False       hasGuards = withGuards grhssGRHSs       grhssSpan =         combineSrcSpans' $-          getGRHSSpan . unLoc <$> NE.fromList grhssGRHSs+          getGRHSSpan . unLoc <$> grhssGRHSs       patGrhssSpan =         maybe           grhssSpan@@ -277,7 +279,7 @@         sep           breakpoint           (located' (p_grhs' placement placer render groupStyle))-          grhssGRHSs+          (NE.toList grhssGRHSs)       p_where = do         unless (eqEmptyLocalBinds grhssLocalBinds) $ do           breakpoint@@ -290,6 +292,7 @@       case style of         Function _ | hasGuards -> return ()         Function _ -> space >> inci equals+        PatternBind | hasGuards -> return ()         PatternBind -> space >> inci equals         s | isCase s && hasGuards -> return ()         _ -> space >> token'rarrow@@ -375,10 +378,10 @@     located cmd (p_hsCmd' Applicand s)     breakpoint     inci $ located expr p_hsExpr-  HsCmdLam _ variant mgroup -> p_lam isApp variant cmdPlacement p_hsCmd mgroup+  HsCmdLam _ variant mgroup -> p_lam isApp s variant cmdPlacement p_hsCmd mgroup   HsCmdPar _ c -> parens N $ sitcc $ located c p_hsCmd   HsCmdCase _ e mgroup ->-    p_case isApp cmdPlacement p_hsCmd e mgroup+    p_case isApp s cmdPlacement p_hsCmd e mgroup   HsCmdIf anns _ if' then' else' ->     p_if cmdPlacement p_hsCmd anns if' then' else'   HsCmdLet (letToken, _) localBinds c ->@@ -565,8 +568,8 @@ p_dotFieldOcc =   p_rdrName . fmap (mkVarUnqual . field_label) . dfoLabel -p_dotFieldOccs :: [DotFieldOcc GhcPs] -> R ()-p_dotFieldOccs = sep (txt ".") p_dotFieldOcc+p_dotFieldOccs :: NonEmpty (DotFieldOcc GhcPs) -> R ()+p_dotFieldOccs = sep (txt ".") p_dotFieldOcc . NE.toList  p_fieldOcc :: FieldOcc GhcPs -> R () p_fieldOcc FieldOcc {..} = p_rdrName foLabel@@ -591,7 +594,7 @@ p_hsExpr = p_hsExpr' NotApplicand N  -- | An applicand is the left-hand side in a function application, i.e. @f@ in--- @f a@. We need to track this in order to add extra identation in cases like+-- @f a@. We need to track this in order to add extra indentation in cases like -- -- > foo = -- >   do@@ -604,10 +607,18 @@   Applicand -> inci . inci   NotApplicand -> inci +-- | Adjust bracing as needed for certain cases e.g. involving case+-- expressions and lambdas.+adjustBracing :: IsApplicand -> BracketStyle -> R () -> R ()+adjustBracing isApp s p = do+  layout <- getLayout+  case (s, layout, isApp) of+    (S, SingleLine, NotApplicand) -> useBraces p+    _ -> p+ p_hsExpr' :: IsApplicand -> BracketStyle -> HsExpr GhcPs -> R () p_hsExpr' isApp s = \case   HsVar _ name -> p_rdrName name-  HsUnboundVar _ occ -> atom occ   HsOverLabel sourceText _ -> do     txt "#"     p_sourceText sourceText@@ -622,7 +633,7 @@       HsMultilineString (SourceText stxt) _ -> p_stringLit stxt       r -> atom r   HsLam _ variant mgroup ->-    p_lam isApp variant exprPlacement p_hsExpr mgroup+    p_lam isApp s variant exprPlacement p_hsExpr mgroup   HsApp _ f x -> do     let -- In order to format function applications with multiple parameters         -- nicer, traverse the AST to gather the function and all the@@ -743,13 +754,14 @@   ExplicitSum _ tag arity e ->     p_unboxedSum N tag arity (located e p_hsExpr)   HsCase _ e mgroup ->-    p_case isApp exprPlacement p_hsExpr e mgroup+    p_case isApp s exprPlacement p_hsExpr e mgroup   HsIf anns if' then' else' ->     p_if exprPlacement p_hsExpr anns if' then' else'   HsMultiIf _ guards -> do     txt "if"     breakpoint-    inciApplicand isApp $ sep breakpoint (located' (p_grhs RightArrow)) guards+    inciApplicand isApp $+      sep breakpoint (located' (p_grhs RightArrow)) (NE.toList guards)   HsLet (letToken, _) localBinds e ->     p_let (s == S) p_hsExpr letToken localBinds e   HsDo _ doFlavor es -> do@@ -775,7 +787,7 @@         dotdot = case rec_dotdot of           Just {} -> [txt ".."]           Nothing -> []-    inci . braces N $+    inci . recordBraces $       sep commaDel sitcc (fields <> dotdot)   RecordUpd {..} -> do     located rupd_expr p_hsExpr@@ -784,7 +796,7 @@           sep commaDel (sitcc . located' (p_hsFieldBind p_lbl))         p_fieldLabelStrings (FieldLabelStrings flss) =           p_dotFieldOccs $ unLoc <$> flss-    inci . braces N $ case rupd_flds of+    inci . recordBraces $ case rupd_flds of       RegularRecUpdFields {..} ->         p_recFields (located' p_fieldOcc) recUpdFields       OverloadedRecUpdFields {..} ->@@ -795,7 +807,7 @@     located gf_field p_dotFieldOcc   HsProjection {..} -> parens N $ do     txt "."-    p_dotFieldOccs (NE.toList proj_flds)+    p_dotFieldOccs proj_flds   ExprWithTySig _ x HsWC {hswc_body} -> sitcc $ do     located x p_hsExpr     inci $ p_hsTypeAnnotation (hsSigTypeToType <$> hswc_body)@@ -828,7 +840,7 @@     breakpoint'     txt "||]"   HsUntypedBracket _ x -> p_hsQuote x-  HsTypedSplice _ expr -> p_hsSpliceTH True expr DollarSplice+  HsTypedSplice _ (HsTypedSpliceExpr _ expr) -> p_hsSpliceTH True expr DollarSplice   HsUntypedSplice _ untySplice -> p_hsUntypedSplice DollarSplice untySplice   HsProc _ p e -> do     txt "proc"@@ -855,6 +867,9 @@     txt "type"     space     located hswc_body p_hsType+  HsHole holeKind -> case holeKind of+    HoleVar name -> p_rdrName name+    HoleError -> error "parse error"   -- similar to HsForAllTy   expr@HsForAll {} ->     p_hsFun expr@@ -962,7 +977,7 @@   -- will be ParStmt.   [L _ (ParStmt _ blocks _ _)] ->     [ concatMap collectNonParStmts stmts-    | ParStmtBlock _ stmts _ _ <- blocks+    | ParStmtBlock _ stmts _ _ <- NE.toList blocks     ]   -- Otherwise, list will not contain any ParStmt   stmts ->@@ -998,12 +1013,17 @@               breakpoint               located psb_def p_pat             breakpoint-            txt "where"+            indentWhere <- getPrinterOpt poIndentWheres+            let (inciWhere, inciBody) =+                  if indentWhere+                    then (id, inci)+                    else (inciByFrac (-1 / 2), id)+            inciWhere $ txt "where"             breakpoint-            inci (p_matchGroup (Function psb_id) mgroup)+            inciBody $ p_matchGroup (Function psb_id) mgroup   txt "pattern"   case psb_args of-    PrefixCon [] xs -> do+    PrefixCon xs -> do       space       p_rdrName psb_id       inci $ do@@ -1012,7 +1032,6 @@           unless (null xs) breakpoint           sitcc (sep breakpoint p_rdrName xs)         rhs conSpans-    PrefixCon (v : _) _ -> absurd v     RecCon xs -> do       space       p_rdrName psb_id@@ -1020,7 +1039,7 @@         let conSpans = getLocA . recordPatSynPatVar <$> xs         switchLayout conSpans $ do           unless (null xs) breakpointPreRecordBrace-          braces N $+          recordBraces $             sep commaDel (p_rdrName . recordPatSynPatVar) xs         rhs conSpans     InfixCon l r -> do@@ -1040,6 +1059,7 @@     Anno (Match GhcPs (LocatedA body)) ~ SrcSpanAnnA   ) =>   IsApplicand ->+  BracketStyle ->   -- | Placer   (body -> Placement) ->   -- | Render@@ -1049,20 +1069,23 @@   -- | Match group   MatchGroup GhcPs (LocatedA body) ->   R ()-p_case isApp placer render e mgroup = do+p_case isApp s placer render e mgroup = do   txt "case"   space   located e p_hsExpr   space   txt "of"   breakpoint-  inciApplicand isApp (p_matchGroup' placer render Case mgroup)+  adjustBracing isApp s $+    inciApplicand isApp (p_matchGroup' placer render Case mgroup)  p_lam ::   ( Anno (GRHS GhcPs (LocatedA body)) ~ EpAnnCO,     Anno (Match GhcPs (LocatedA body)) ~ SrcSpanAnnA   ) =>   IsApplicand ->+  -- | BracketStyle (S when inside a do block)+  BracketStyle ->   -- | Variant (@\\@ or @\\case@ or @\\cases@)   HsLamVariant ->   -- | Placer@@ -1072,7 +1095,7 @@   -- | Expression   MatchGroup GhcPs (LocatedA body) ->   R ()-p_lam isApp variant placer render mgroup = do+p_lam isApp s variant placer render mgroup = do   let mCaseTxt = case variant of         LamSingle -> Nothing         LamCase -> Just "\\case"@@ -1084,7 +1107,7 @@     Just caseTxt -> do       txt caseTxt       breakpoint-      inciApplicand isApp pMatchGroup+      adjustBracing isApp s (inciApplicand isApp pMatchGroup)  p_if ::   -- | Placer@@ -1243,65 +1266,66 @@         _ -> Nothing  p_pat :: Pat GhcPs -> R ()-p_pat = \case+p_pat = p_pat' False++p_pat' :: Bool -> Pat GhcPs -> R ()+p_pat' inAsPat = \case   WildPat _ -> txt "_"   VarPat _ name -> p_rdrName name   LazyPat _ pat -> do     txt "~"-    located pat p_pat+    located pat (p_pat' inAsPat)   AsPat _ name pat -> do     p_rdrName name     txt "@"-    located pat p_pat+    located pat (p_pat' True)   ParPat _ pat ->-    located pat (parens S . sitcc . p_pat)+    located pat (parens S . sitcc . p_pat' inAsPat)   BangPat _ pat -> do     txt "!"-    located pat p_pat+    located pat (p_pat' inAsPat)   ListPat _ pats ->-    brackets S $ sep commaDel (located' p_pat) pats+    brackets S $ sep commaDel (located' (p_pat' inAsPat)) pats   TuplePat _ pats boxing -> do     let parens' =           case boxing of             Boxed -> parens S             Unboxed -> parensHash S-    parens' $ sep commaDel (sitcc . located' p_pat) pats-  OrPat _ pats ->-    sepSemi (located' p_pat) (NE.toList pats)+    parens' $ sep commaDel (sitcc . located' (p_pat' inAsPat)) pats+  OrPat _ pats -> do+    sepSemi' inAsPat (located' (p_pat' inAsPat)) (NE.toList pats)   SumPat _ pat tag arity ->-    p_unboxedSum S tag arity (located pat p_pat)+    p_unboxedSum S tag arity (located pat (p_pat' inAsPat))   ConPat _ pat details ->     case details of-      PrefixCon tys xs -> sitcc $ do+      PrefixCon xs -> sitcc $ do         p_rdrName pat-        unless (null tys && null xs) breakpoint-        inci . sitcc $-          sep breakpoint (sitcc . either p_hsConPatTyArg (located' p_pat)) $-            (Left <$> tys) <> (Right <$> xs)+        unless (null xs) breakpoint+        inci . sitcc $ sep breakpoint (sitcc . located' (p_pat' inAsPat)) xs       RecCon (HsRecFields _ fields dotdot) -> do         p_rdrName pat         breakpointPreRecordBrace         let f = \case               Nothing -> txt ".."               Just x -> located x p_pat_hsFieldBind-        inci . braces N . sep commaDel f $+        inci . recordBraces . sep commaDel f $           case dotdot of             Nothing -> Just <$> fields             Just (L _ (RecFieldsDotDot n)) -> (Just <$> take n fields) ++ [Nothing]       InfixCon l r -> do         switchLayout [getLocA l, getLocA r] $ do-          located l p_pat+          located l (p_pat' inAsPat)           breakpoint           inci $ do             p_rdrName pat             space-            located r p_pat+            located r (p_pat' inAsPat)   ViewPat _ expr pat -> sitcc $ do     located expr p_hsExpr     space     token'rarrow     breakpoint-    inci (located pat p_pat)+    inci (located pat (p_pat' inAsPat))   SplicePat _ splice -> p_hsUntypedSplice DollarSplice splice   LitPat _ p -> atom p   NPat _ v (isJust -> isNegated) _ -> do@@ -1318,7 +1342,7 @@       space       located k (atom . ol_val)   SigPat _ pat HsPS {..} -> do-    located pat p_pat+    located pat (p_pat' inAsPat)     p_typeAscription (lhsTypeToSigType hsps_body)   EmbTyPat _ (HsTP _ ty) -> do     txt "type"@@ -1329,9 +1353,6 @@ p_tyPat :: HsTyPat GhcPs -> R () p_tyPat (HsTP _ ty) = txt "@" *> located ty p_hsType -p_hsConPatTyArg :: HsConPatTyArg GhcPs -> R ()-p_hsConPatTyArg (HsConPatTyArg _ patSigTy) = p_tyPat patSigTy- p_pat_hsFieldBind :: HsRecField GhcPs (LPat GhcPs) -> R () p_pat_hsFieldBind HsFieldBind {..} = do   located hfbLHS p_fieldOcc@@ -1361,7 +1382,7 @@   HsUntypedSpliceExpr _ expr -> p_hsSpliceTH False expr deco   HsQuasiQuote _ quoterName str -> do     txt "["-    p_rdrName (noLocA quoterName)+    p_rdrName quoterName     txt "|"     -- QuasiQuoters often rely on precise custom strings. We cannot do any     -- formatting here without potentially breaking someone's code.@@ -1428,21 +1449,31 @@  -- | Function types in expressions, e.g. with -XRequiredTypeArguments instance FunRepr (HsExpr GhcPs) where-  renderFunItem = p_hsExpr   parseFunRepr = \case     -- `forall a. _`     L ann (HsForAll _ tele expr) ->-      ParsedFunForall (L ann tele) (parseFunRepr expr)+      ParsedFunForall+        { tele = L ann tele,+          next = parseFunRepr expr+        }     -- `HasCallStack => _`     expr@(L _ HsQual {}) ->       let (ctxs, rest) = getContexts expr-       in ParsedFunQuals ctxs (parseFunRepr rest)+       in ParsedFunQuals+            { ctxs,+              next = parseFunRepr rest+            }     -- `Int -> _`-    expr@(L _ HsFunArr {}) ->-      let (args, ret) = getArgsAndReturn expr-       in ParsedFunArgs args (parseFunRepr ret)+    L ann (HsFunArr _ multAnn arg r) ->+      ParsedFunArg+        { span = ann,+          arg,+          doc = Nothing,+          multAnn,+          next = parseFunRepr r+        }     -- `_ -> Int`-    expr -> ParsedFunReturn (expr, Nothing)+    ret -> ParsedFunReturn {ret, doc = Nothing}     where       getContexts =         let go ctxs = \case@@ -1451,14 +1482,12 @@               expr ->                 (reverse ctxs, expr)          in go []-      getArgsAndReturn =-        let go args = \case-              L ann (HsFunArr _ arrow l r) ->-                go (L ann (l, Nothing, arrow) : args) r-              expr ->-                (reverse args, expr)-         in go [] +  renderFunReprArg = p_hsExpr+  renderFunReprCtx = p_hsExpr+  renderFunReprMult = p_hsExpr+  renderFunReprRet = p_hsExpr+ ---------------------------------------------------------------------------- -- Helpers @@ -1476,9 +1505,9 @@ -- | Determine placement of a given block. blockPlacement ::   (body -> Placement) ->-  [LGRHS GhcPs (LocatedA body)] ->+  NonEmpty (LGRHS GhcPs (LocatedA body)) ->   Placement-blockPlacement placer [L _ (GRHS _ _ (L _ x))] = placer x+blockPlacement placer (L _ (GRHS _ _ (L _ x)) :| []) = placer x blockPlacement _ _ = Normal  -- | Determine placement of a given command.@@ -1522,7 +1551,7 @@   _ -> Normal  -- | Return 'True' if any of the RHS expressions has guards.-withGuards :: [LGRHS GhcPs body] -> Bool+withGuards :: NonEmpty (LGRHS GhcPs body) -> Bool withGuards = any (checkOne . unLoc)   where     checkOne (GRHS _ [] _) = False@@ -1531,10 +1560,12 @@ -- | For use before record braces. Collapse to empty if not 'poRecordBraceSpace'. breakpointPreRecordBrace :: R () breakpointPreRecordBrace = do+  recordStyle <- getPrinterOpt poRecordStyle   useSpace <- getPrinterOpt poRecordBraceSpace-  if useSpace-    then breakpoint-    else breakpoint'+  if+    | recordStyle == RecordStyleKnr -> space+    | useSpace -> breakpoint+    | otherwise -> breakpoint'  -- | For nested lists/tuples, pad with whitespace so that we always indent correctly, -- rather than sometimes indenting by 2 regardless of 'poIndentation'.
src/Ormolu/Printer/Meat/ImportExport.hs view
@@ -49,6 +49,10 @@   space   when ideclSafe (txt "safe")   space+  case ideclLevelSpec of+    LevelStylePre l -> p_declLevel l+    _ -> return ()+  space   when     (isImportDeclQualified ideclQualified && not useQualifiedPost)     (txt "qualified")@@ -59,6 +63,10 @@   space   inci $ do     located ideclName atom+    space+    case ideclLevelSpec of+      LevelStylePost l -> p_declLevel l+      _ -> return ()     when       (isImportDeclQualified ideclQualified && useQualifiedPost)       (space >> txt "qualified")@@ -84,6 +92,11 @@             (\(p, l) -> sitcc (located l (p_lie layout False p)))             (attachRelativePos xs)     newline++p_declLevel :: ImportDeclLevel -> R ()+p_declLevel = \case+  ImportDeclSplice -> txt "splice"+  ImportDeclQuote -> txt "quote"  p_lie :: Layout -> Bool -> RelativePos -> IE GhcPs -> R () p_lie encLayout isAllPrevDoc relativePos = \case
src/Ormolu/Printer/Meat/Type.hs view
@@ -1,10 +1,12 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-}+{-# OPTIONS_GHC -Wno-orphans #-}  -- | Rendering of types. module Ormolu.Printer.Meat.Type@@ -16,42 +18,40 @@     p_hsTyVarBndr,     ForAllVisibility (..),     p_forallBndrs,-    p_conDeclFields,+    p_hsConDeclRecFields,+    p_hsConDeclField,+    p_hsConDeclFieldWithDoc,     p_lhsTypeArg,     p_hsSigType,-    FunRepr (..),-    ParsedFunRepr (..),-    p_hsFun,-    hsOuterTyVarBndrsToHsType,     hsSigTypeToType,     lhsTypeToSigType,++    -- * Re-exports from Ormolu.Printer.Meat.Type.Function+    FunRepr (..),+    ParsedFunRepr,+    ParsedFunRepr' (..),+    p_hsFun,   ) where  import Control.Monad-import Control.Monad.Cont qualified as Cont-import Control.Monad.State qualified as State-import Control.Monad.Trans qualified as Trans-import Data.Choice (Choice, pattern Is, pattern Isn't, pattern With, pattern Without)-import Data.Choice qualified as Choice-import Data.Foldable (traverse_)-import Data.Functor ((<&>))-import Data.List (sortOn)-import Data.Maybe (isJust)+import Data.Choice (pattern Is, pattern With, pattern Without)+import Data.Maybe (fromMaybe) import GHC.Data.Strict qualified as Strict import GHC.Hs hiding (isPromoted) import GHC.Types.SourceText import GHC.Types.SrcLoc import GHC.Types.Var-import GHC.Utils.Outputable (Outputable) import Ormolu.Config import Ormolu.Printer.Combinators import Ormolu.Printer.Meat.Common import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.OpTree (p_tyOpTree, tyOpTree) import Ormolu.Printer.Meat.Declaration.StringLiteral import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration.Value (p_hsUntypedSplice)+import Ormolu.Printer.Meat.Type.Function import Ormolu.Printer.Operators import Ormolu.Utils+import Prelude hiding (span)  p_hsType :: HsType GhcPs -> R () p_hsType = \case@@ -137,31 +137,8 @@     --       String     --       -- | Age     --       Int-    usePipe <--      getPrinterOpt poFunctionArrows <&> \case-        TrailingArrows -> True-        LeadingArrows -> False-        LeadingArgsArrows -> False-    if usePipe-      then do-        p_hsDoc Pipe (With #endNewline) str-        located t p_hsType-      else do-        located t p_hsType-        newline-        p_hsDoc Caret (Without #endNewline) str-  HsBangTy _ (HsBang u s) t -> do-    case u of-      SrcUnpack -> txt "{-# UNPACK #-}" >> space-      SrcNoUnpack -> txt "{-# NOUNPACK #-}" >> space-      NoSrcUnpack -> return ()-    case s of-      SrcLazy -> txt "~"-      SrcStrict -> txt "!"-      NoSrcStrict -> return ()-    located t p_hsType-  HsRecTy _ fields ->-    p_conDeclFields fields+    withHaddocks (Is #end) (Just str) $ do+      located t p_hsType   HsExplicitListTy _ p xs -> do     case p of       IsPromoted -> txt "'"@@ -189,7 +166,20 @@       HsStrTy (SourceText s) _ -> p_stringLit s       a -> atom a   HsWildCardTy _ -> txt "_"-  XHsType t -> atom t+  XHsType ext -> case ext of+    HsCoreTy t -> atom @HsCoreTy t+    HsBangTy _ (HsSrcBang _ u s) t -> do+      case u of+        SrcUnpack -> txt "{-# UNPACK #-}" >> space+        SrcNoUnpack -> txt "{-# NOUNPACK #-}" >> space+        NoSrcUnpack -> return ()+      case s of+        SrcLazy -> txt "~"+        SrcStrict -> txt "!"+        NoSrcStrict -> return ()+      located t p_hsType+    HsRecTy _ fields ->+      p_hsConDeclRecFields fields   where     startsWithSingleQuote = \case       HsAppTy _ (L _ f) _ -> startsWithSingleQuote f@@ -200,7 +190,12 @@       _ -> False  p_hsTypeAnnotation :: LHsType GhcPs -> R ()-p_hsTypeAnnotation = p_hsFunParsed . ParsedFunSig . parseFunRepr+p_hsTypeAnnotation ty =+  p_hsFunParsed @(HsType GhcPs) $+    ParsedFunSig+      { sig = (),+        next = parseFunRepr ty+      }  -- | Return 'True' if at least one argument in 'HsType' has a doc string -- attached to it.@@ -215,101 +210,47 @@ p_hsContext :: HsContext GhcPs -> R () p_hsContext = p_hsContext' p_hsType -p_hsContext' ::-  (Outputable (GenLocated (Anno a) a), HasLoc (Anno a)) =>-  (a -> R ()) ->-  [XRec GhcPs a] ->-  R ()-p_hsContext' f = \case-  [] -> txt "()"-  [x] -> located x f-  xs -> do-    shouldSort <- getPrinterOpt poSortConstraints-    let sort = if shouldSort then sortOn showOutputable else id-    parens N $ sep commaDel (sitcc . located' f) (sort xs)--class IsTyVarBndrFlag flag where-  isInferred :: flag -> Bool-  p_tyVarBndrFlag :: flag -> R ()-  p_tyVarBndrFlag _ = pure ()--instance IsTyVarBndrFlag () where-  isInferred () = False--instance IsTyVarBndrFlag Specificity where-  isInferred = \case-    InferredSpec -> True-    SpecifiedSpec -> False--instance IsTyVarBndrFlag (HsBndrVis GhcPs) where-  isInferred _ = False-  p_tyVarBndrFlag = \case-    HsBndrRequired NoExtField -> pure ()-    HsBndrInvisible _ -> txt "@"--p_hsTyVarBndr :: (IsTyVarBndrFlag flag) => HsTyVarBndr flag GhcPs -> R ()-p_hsTyVarBndr HsTvb {..} = do-  p_tyVarBndrFlag tvb_flag-  let wrap-        | isInferred tvb_flag = braces N-        | otherwise = case tvb_kind of-            HsBndrKind {} -> parens N-            HsBndrNoKind {} -> id-  wrap $ do-    case tvb_var of-      HsBndrVar _ x -> p_rdrName x-      HsBndrWildCard _ -> txt "_"-    case tvb_kind of-      HsBndrKind _ k -> inci $ p_hsTypeAnnotation k-      HsBndrNoKind _ -> pure ()--data ForAllVisibility = ForAllInvis | ForAllVis---- | Render several @forall@-ed variables.-p_forallBndrs ::-  (HasLoc l) =>-  ForAllVisibility ->-  (a -> R ()) ->-  [GenLocated l a] ->-  R ()-p_forallBndrs vis p tyvars = do-  p_forallBndrsStart p tyvars-  p_forallBndrsEnd (Without #extraSpace) vis--p_forallBndrsStart :: (HasLoc l) => (a -> R ()) -> [GenLocated l a] -> R ()-p_forallBndrsStart _ [] = token'forall-p_forallBndrsStart p tyvars = do-  switchLayout (locA <$> tyvars) $ do-    token'forall-    breakpoint-    inci $ do-      sitcc $ sep breakpoint (sitcc . located' p) tyvars--p_forallBndrsEnd :: Choice "extraSpace" -> ForAllVisibility -> R ()-p_forallBndrsEnd extraSpace = \case-  ForAllInvis -> txt dot >> space-  ForAllVis -> space >> token'rarrow-  where-    dot = if Choice.isTrue extraSpace then " ." else "."--p_conDeclFields :: [LConDeclField GhcPs] -> R ()-p_conDeclFields xs =-  braces N $ sep commaDel (sitcc . located' p_conDeclField) xs+p_hsConDeclRecFields :: [LHsConDeclRecField GhcPs] -> R ()+p_hsConDeclRecFields xs =+  recordBraces $ sep commaDel (sitcc . located' p_hsConDeclRecField) xs -p_conDeclField :: ConDeclField GhcPs -> R ()-p_conDeclField ConDeclField {..} = do-  commaStyle <- getPrinterOpt poCommaStyle-  when (commaStyle == Trailing) $-    mapM_ (p_hsDoc Pipe (With #endNewline)) cd_fld_doc+p_hsConDeclRecField :: HsConDeclRecField GhcPs -> R ()+p_hsConDeclRecField field@HsConDeclRecField {..} = withFieldHaddocks $ do   sitcc $     sep       commaDel       (located' (p_rdrName . foLabel))-      cd_fld_names-  inci $ p_hsTypeAnnotation cd_fld_type-  when (commaStyle == Leading) $-    mapM_ (inciByFrac (-1) . (newline >>) . p_hsDoc Caret (Without #endNewline)) cd_fld_doc+      cdrf_names+  inci $ p_hsFun field+  where+    withFieldHaddocks action = do+      commaStyle <- getPrinterOpt poCommaStyle+      let doc = cdf_doc cdrf_spec+      when (commaStyle == Trailing) $+        mapM_ (p_hsDoc Pipe (With #endNewline)) doc+      action+      when (commaStyle == Leading) $+        mapM_ (inciByFrac (-1) . (newline >>) . p_hsDoc Caret (Without #endNewline)) doc +-- | This does not print 'cdf_doc' and 'cdf_multiplicity' as there is no single+-- strategy for where to print them (see call sites).+p_hsConDeclField :: HsConDeclField GhcPs -> R ()+p_hsConDeclField CDF {..} = do+  case cdf_unpack of+    SrcUnpack -> txt "{-# UNPACK #-}" *> space+    SrcNoUnpack -> txt "{-# NOUNPACK #-}" *> space+    NoSrcUnpack -> pure ()+  located cdf_type $ \ty -> do+    case cdf_bang of+      SrcLazy -> txt "~"+      SrcStrict -> txt "!"+      NoSrcStrict -> pure ()+    p_hsType ty++p_hsConDeclFieldWithDoc :: HsConDeclField GhcPs -> R ()+p_hsConDeclFieldWithDoc cdf = withHaddocks (Is #end) cdf.cdf_doc $ do+  p_hsConDeclField cdf+ p_lhsTypeArg :: LHsTypeArg GhcPs -> R () p_lhsTypeArg = \case   HsValArg NoExtField ty -> located ty p_hsType@@ -322,54 +263,37 @@ p_hsSigType :: HsSigType GhcPs -> R () p_hsSigType = p_hsType . hsSigTypeToType -------------------------------------------------------------------------------- Rendering function types---- | The parsed representation of a function-data ParsedFunRepr a-  = ParsedFunSig (ParsedFunRepr a)-  | ParsedFunForall-      (LocatedA (HsForAllTelescope GhcPs))-      (ParsedFunRepr a)-  | ParsedFunQuals-      [LocatedA (LocatedC [LocatedA a])]-      (ParsedFunRepr a)-  | -- | The argument, its optional docstring, and the arrow going to the next arg/return-    ParsedFunArgs-      [ LocatedA-          ( LocatedA a,-            Maybe (LHsDoc GhcPs),-            HsArrowOf (LocatedA a) GhcPs-          )-      ]-      (ParsedFunRepr a)-  | ParsedFunReturn-      ( LocatedA a,-        Maybe (LHsDoc GhcPs)-      )--class (Anno a ~ SrcSpanAnnA, Outputable a) => FunRepr a where-  renderFunItem :: a -> R ()--  parseFunRepr :: LocatedA a -> ParsedFunRepr a- instance FunRepr (HsType GhcPs) where-  renderFunItem = p_hsType   parseFunRepr = \case     -- `forall a. _`     L ann (HsForAllTy _ tele ty) ->-      ParsedFunForall (L ann tele) (parseFunRepr ty)+      ParsedFunForall+        { tele = L ann tele,+          next = parseFunRepr ty+        }     -- `HasCallStack => _`     ty@(L _ HsQualTy {}) ->       let (ctxs, rest) = getContexts ty-       in ParsedFunQuals ctxs (parseFunRepr rest)+       in ParsedFunQuals+            { ctxs,+              next = parseFunRepr rest+            }     -- `Int -> _`-    ty@(L _ HsFunTy {}) ->-      let (args, ret) = getArgsAndReturn ty-       in ParsedFunArgs args (parseFunRepr ret)+    L ann (HsFunTy _ multAnn l r) ->+      let (arg, doc) =+            case l of+              L _ (HsDocTy _ x doc_) -> (x, Just doc_)+              _ -> (l, Nothing)+       in ParsedFunArg+            { span = ann,+              arg,+              doc,+              multAnn,+              next = parseFunRepr r+            }     -- `_ -> Int`-    L _ (HsDocTy _ ty doc) -> ParsedFunReturn (ty, Just doc)-    ty -> ParsedFunReturn (ty, Nothing)+    L _ (HsDocTy _ ret doc) -> ParsedFunReturn {ret, doc = Just doc}+    ret -> ParsedFunReturn {ret, doc = Nothing}     where       getContexts =         let go ctxs = \case@@ -378,203 +302,106 @@               ty ->                 (reverse ctxs, ty)          in go []-      getArgsAndReturn =-        let go args = \case-              L ann (HsFunTy _ arrow (L _ (HsDocTy _ l doc)) r) ->-                go (L ann (l, Just doc, arrow) : args) r-              L ann (HsFunTy _ arrow l r) ->-                go (L ann (l, Nothing, arrow) : args) r-              ty ->-                (reverse args, ty)-         in go [] --- | For implementing function-arrows and related configuration, we'll collect--- all the components of the function type first, then render as a block instead--- of rendering each part independently, which will let us track local state--- within a function type.------ This function should be passed the first function-related construct we find;--- see FunRepr for more details.-p_hsFun :: (FunRepr a) => a -> R ()-p_hsFun = p_hsFunParsed . parseFunRepr . L (noAnn @SrcSpanAnnA)--p_hsFunParsed :: (FunRepr a) => ParsedFunRepr a -> R ()-p_hsFunParsed fun = do-  arrowsStyle <- getPrinterOpt poFunctionArrows-  p_hsFunParsed' arrowsStyle fun--type PrintHsFun x = State.StateT PrintHsFunState (Cont.ContT () R) x--type PrintHsFunState =-  ( Maybe (R ()), -- The leading delimiter to output at the next line-    Choice "forceMultiline"-  )--p_hsFunParsed' ::-  (FunRepr a) =>-  FunctionArrowsStyle ->-  ParsedFunRepr a ->-  R ()-p_hsFunParsed' arrowsStyle fun0 = Cont.evalContT . (`State.evalStateT` initialState) . go $ fun0-  where-    go = \case-      ParsedFunSig fun' -> do-        -- Should only happen at the very beginning, if at all-        p_parsedFunSig-        setMultilineContext fun'-        go fun'-      ParsedFunForall tele fun' -> do-        p_parsedFunForall tele-        setMultilineContext fun'-        go fun'-      ParsedFunQuals ctxs fun' -> do-        p_parsedFunQuals ctxs-        setMultilineContext fun'-        go fun'-      ParsedFunArgs args fun' -> do-        p_parsedFunArgs args-        go fun'-      ParsedFunReturn ret -> do-        p_parsedFunReturn ret--    liftR = Trans.lift . Trans.lift-    initialState = (Nothing, Without #forceMultiline)--    withApplyLeadingDelim f = do-      (leadingDelim, _) <- State.get-      a <- f leadingDelim-      State.modify $ \(_, x) -> (Nothing, x)-      pure a-    applyLeadingDelim = withApplyLeadingDelim (traverse_ liftR)--    setLeadingDelim delim =-      State.modify $ \(_, x) -> (Just delim, x)--    getIsMultiline = do-      (_, forceMultiline) <- State.get-      layout <- liftR getLayout-      pure $ Choice.toBool forceMultiline || layout == MultiLine--    setMultilineContext fun =-      State.modify $ \(x, _) -> (x, Choice.fromBool $ hasDocs fun)--    -- Make everything afterwards occur within a located block, with the-    -- magic of ContT. `item <- setLocated litem; ...` is equivalent to-    -- `located litem $ \item -> ...`.-    setLocated :: (HasLoc ann) => GenLocated ann x -> PrintHsFun x-    setLocated litem = Trans.lift $ Cont.ContT (located litem)--    p_parsedFunSig = do-      if isTrailing (Isn't #argDelim)-        then liftR $ do-          space >> token'dcolon-          if hasDocs fun0 then newline else breakpoint-        else do-          liftR breakpoint-          setLeadingDelim (token'dcolon >> space)--    p_parsedFunForall x@(L _ tele) = do-      void $ setLocated x-      applyLeadingDelim-      vis <--        case tele of-          HsForAllInvis _ bndrs -> do-            liftR $ p_forallBndrsStart p_hsTyVarBndr bndrs-            pure ForAllInvis-          HsForAllVis _ bndrs -> do-            liftR $ p_forallBndrsStart p_hsTyVarBndr bndrs-            pure ForAllVis-      isMultiline <- getIsMultiline-      if isTrailing (Isn't #argDelim) || not isMultiline-        then do-          liftR $ p_forallBndrsEnd (Without #extraSpace) vis-          interArgBreak-        else do-          interArgBreak-          let extraSpace = if isMultiline then With #extraSpace else Without #extraSpace-          setLeadingDelim (p_forallBndrsEnd extraSpace vis)--    p_parsedFunQuals ctxs = do-      -- we only want to set located on the first context-      case ctxs of-        ctx : _ -> void $ setLocated ctx-        _ -> pure ()--      forM_ ctxs $ \(L _ lctx) -> do-        applyLeadingDelim-        liftR $ located lctx (p_hsContext' renderFunItem)-        if isTrailing (Isn't #argDelim)-          then do-            liftR $ space >> token'darrow-            interArgBreak-          else do-            interArgBreak-            setLeadingDelim (token'darrow >> space)--    p_parsedFunArgs args = do-      -- we only want to set located on the first arg-      case args of-        arg : _ -> void $ setLocated arg-        _ -> pure ()+  renderFunReprCtx = p_hsType+  renderFunReprArg = p_hsType+  renderFunReprMult = p_hsType+  renderFunReprRet = p_hsType -      forM_ args $ \(L _ (larg, doc, arrow)) -> do-        let renderArrow = p_arrow (located' renderFunItem) arrow-        withHaddocks (Isn't #end) doc $ do-          withApplyLeadingDelim $ \applyLeadingDelim' ->-            liftR . located larg $ \arg -> do-              traverse_ id applyLeadingDelim'-              renderFunItem arg-          if isTrailing (Is #argDelim)-            then do-              liftR $ space >> renderArrow-              interArgBreak-            else do-              interArgBreak-              setLeadingDelim (renderArrow >> space)+----------------------------------------------------------------------------+-- FunRepr HsConDeclRecField -    p_parsedFunReturn (ret, doc) = do-      void $ setLocated ret-      withHaddocks (Is #end) doc $ do-        applyLeadingDelim-        liftR $ located ret renderFunItem+-- | FunRepr HsConDeclRecField renders a record field type annotation. If the+-- record field has a function type, we want to render it as a function+-- respecting `function-arrows`. For the most part, it behaves like HsTypeSig;+-- however, record fields can have additional syntax not in normal function+-- types, like specifying the multiplicity for the `::` or specifying+-- UNPACK/strictness, so we need to handle this specially.+instance FunRepr (HsConDeclRecField GhcPs) where+  parseFunRepr (L _ HsConDeclRecField {..}) =+    ParsedFunSig+      { sig = cdrf_spec.cdf_multiplicity,+        next = toRecFieldRepr False $ parseFunRepr cdrf_spec.cdf_type+      }+    where+      toRecFieldRepr :: Bool -> ParsedFunRepr (HsType GhcPs) -> ParsedFunRepr (HsConDeclRecField GhcPs)+      toRecFieldRepr seenArg = \case+        ParsedFunSig {} -> error "parseFunRepr @HsType unexpectedly returned ParsedFunSig"+        ParsedFunForall {..} ->+          ParsedFunForall+            { tele,+              next = toRecFieldRepr seenArg next+            }+        ParsedFunQuals {..} ->+          ParsedFunQuals+            { ctxs,+              next = toRecFieldRepr seenArg next+            }+        ParsedFunArg {..} ->+          ParsedFunArg+            { span,+              arg =+                let mUnpack =+                      if not seenArg+                        then Just cdrf_spec.cdf_unpack+                        else Nothing+                 in L (getLoc arg) (mUnpack, arg),+              doc,+              multAnn,+              next = toRecFieldRepr True next+            }+        ParsedFunReturn {..} ->+          ParsedFunReturn+            { ret =+                let mAnn =+                      if not seenArg+                        then Just (cdrf_spec.cdf_unpack, cdrf_spec.cdf_bang)+                        else Nothing+                 in L (getLoc ret) (mAnn, ret),+              doc+            } -    withHaddocks isEnd doc m = do-      isLeadingHaddock <--        liftR $-          getPrinterOpt poHaddockLocSignature <&> \case-            HaddockLocSigAuto ->-              if isTrailing (Is #argDelim) then With #pipe else Without #pipe-            HaddockLocSigLeading ->-              With #pipe-            HaddockLocSigTrailing ->-              Without #pipe+  type FunReprSig (HsConDeclRecField GhcPs) = HsMultAnnOf (LocatedA (HsType GhcPs)) GhcPs+  renderFunReprSig multAnn = do+    space+    p_hsMultAnn (located' p_hsType) multAnn+    space+    token'dcolon -      if Choice.isTrue isLeadingHaddock-        then do-          traverse (liftR . p_hsDoc Pipe (With #endNewline)) doc *> m-        else do-          let (pre, endNewline) =-                if Choice.isTrue isEnd-                  then (when (isJust doc) (liftR newline), Without #endNewline)-                  else (pure (), With #endNewline)-          m <* pre <* traverse (liftR . p_hsDoc Caret endNewline) doc+  type FunReprCtx (HsConDeclRecField GhcPs) = HsType GhcPs+  renderFunReprCtx = p_hsType -    interArgBreak = do-      isMultiline <- getIsMultiline-      liftR $ if isMultiline then newline else breakpoint+  -- Invariant: SrcUnpackedness should only be set for the first arg+  type FunReprArg (HsConDeclRecField GhcPs) = (Maybe SrcUnpackedness, LocatedA (HsType GhcPs))+  renderFunReprArg (mUnpacked, ty) =+    p_hsConDeclField+      CDF+        { cdf_ext = error "unused",+          cdf_unpack = fromMaybe NoSrcUnpack mUnpacked,+          cdf_bang = NoSrcStrict,+          cdf_multiplicity = error "unused",+          cdf_type = ty,+          cdf_doc = error "unused"+        } -    hasDocs = \case-      ParsedFunSig fun -> hasDocs fun-      ParsedFunForall _ fun -> hasDocs fun-      ParsedFunQuals _ fun -> hasDocs fun-      ParsedFunArgs args fun -> or [isJust doc | L _ (_, doc, _) <- args] || hasDocs fun-      ParsedFunReturn (_, doc) -> isJust doc+  type FunReprMult (HsConDeclRecField GhcPs) = HsType GhcPs+  renderFunReprMult = p_hsType -    isTrailing isArgDelim =-      case arrowsStyle of-        TrailingArrows -> True-        LeadingArgsArrows -> not $ Choice.isTrue isArgDelim-        LeadingArrows -> False+  -- Invariant: SrcUnpackedness/SrcStrictness should only be set if there are+  -- no args+  type FunReprRet (HsConDeclRecField GhcPs) = (Maybe (SrcUnpackedness, SrcStrictness), LocatedA (HsType GhcPs))+  renderFunReprRet (mAnn, ty) =+    p_hsConDeclField+      CDF+        { cdf_ext = error "unused",+          cdf_unpack = unpack,+          cdf_bang = strictness,+          cdf_multiplicity = error "unused",+          cdf_type = ty,+          cdf_doc = error "unused"+        }+    where+      (unpack, strictness) = fromMaybe (NoSrcUnpack, NoSrcStrict) mAnn  ---------------------------------------------------------------------------- -- Conversion functions
+ src/Ormolu/Printer/Meat/Type/Function.hs view
@@ -0,0 +1,493 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoFieldSelectors #-}+{-# OPTIONS_GHC -Wno-partial-fields #-}++-- | A Fourmolu-specific module for rendering function-like type signatures.+module Ormolu.Printer.Meat.Type.Function+  ( -- * PrintHsFun+    PrintHsFun,+    MonadR,++    -- * ParsedFunRepr+    ParsedFunRepr,+    ParsedFunRepr' (..),+    FunRepr (..),+    p_hsFun,+    p_hsFunParsed,++    -- * forall+    ForAllVisibility (..),+    p_forallBndrs,++    -- * TyVarBndr+    p_hsTyVarBndr,++    -- * Contexts+    p_hsContext',++    -- * Haddocks+    withHaddocks,+  )+where++import Control.Monad (forM_, void, when)+import Control.Monad.Cont qualified as Cont+import Control.Monad.State qualified as State+import Control.Monad.Trans qualified as Trans+import Data.Choice (Choice, pattern Is, pattern Isn't, pattern With, pattern Without)+import Data.Choice qualified as Choice+import Data.Foldable (traverse_)+import Data.Functor ((<&>))+import Data.List (sortOn)+import Data.Maybe (isJust)+import GHC.Hs+import GHC.Types.SrcLoc (GenLocated (..))+import GHC.Types.Var (Specificity (..))+import GHC.Utils.Outputable (Outputable)+import Ormolu.Config+import Ormolu.Printer.Combinators+import Ormolu.Printer.Meat.Common (p_arrow, p_hsDoc, p_rdrName)+import Ormolu.Utils (showOutputable)+import Prelude hiding (span)++{----- PrintHsFun -----}++newtype PrintHsFun a = PrintHsFun (State.StateT PrintHsFunState (Cont.ContT () R) a)+  deriving newtype (Functor, Applicative, Monad)++data PrintHsFunState = PrintHsFunState+  { -- | The leading delimiter to output at the next line+    leadingDelim :: Maybe (R ()),+    forceMultiline :: Bool,+    -- | Whether we're currently printing args+    inArgList :: Bool+  }++runPrintHsFun :: PrintHsFun () -> R ()+runPrintHsFun (PrintHsFun m) = Cont.evalContT . (`State.evalStateT` initialState) $ m+  where+    initialState =+      PrintHsFunState+        { leadingDelim = Nothing,+          forceMultiline = False,+          inArgList = False+        }++class (Monad m) => MonadR m where+  liftR :: R a -> m a++instance MonadR R where+  liftR = id++instance MonadR PrintHsFun where+  liftR = PrintHsFun . Trans.lift . Trans.lift++withApplyLeadingDelim :: (Maybe (R ()) -> PrintHsFun a) -> PrintHsFun a+withApplyLeadingDelim f = do+  state <- PrintHsFun State.get+  a <- f state.leadingDelim+  PrintHsFun . State.put $ state {leadingDelim = Nothing}+  pure a++applyLeadingDelim :: PrintHsFun ()+applyLeadingDelim = withApplyLeadingDelim (traverse_ liftR)++setLeadingDelim :: R () -> PrintHsFun ()+setLeadingDelim delim =+  PrintHsFun . State.modify $ \state -> state {leadingDelim = Just delim}++getIsMultiline :: PrintHsFun Bool+getIsMultiline = do+  state <- PrintHsFun State.get+  layout <- liftR getLayout+  pure $ state.forceMultiline || layout == MultiLine++setMultilineContext :: ParsedFunRepr' sig ctx arg mult ret -> PrintHsFun ()+setMultilineContext fun =+  PrintHsFun . State.modify $ \state -> state {forceMultiline = hasDocs fun}++getIsTrailing :: (MonadR m) => m (Choice "argDelim" -> Bool)+getIsTrailing = do+  arrowsStyle <- liftR $ getPrinterOpt poFunctionArrows+  pure $ \isArgDelim ->+    case arrowsStyle of+      TrailingArrows -> True+      LeadingArgsArrows -> not $ Choice.isTrue isArgDelim+      LeadingArrows -> False++getInArgList :: PrintHsFun Bool+getInArgList = PrintHsFun . State.gets $ (.inArgList)++setInArgList :: Bool -> PrintHsFun ()+setInArgList x = PrintHsFun . State.modify $ \state -> state {inArgList = x}++-- Make everything afterwards occur within a located block, with the+-- magic of ContT. `item <- setLocated litem; ...` is equivalent to+-- `located litem $ \item -> ...`.+setLocated :: (HasLoc ann) => GenLocated ann x -> PrintHsFun x+setLocated litem = PrintHsFun . Trans.lift $ Cont.ContT (located litem)++setLocated_ :: (HasLoc ann) => GenLocated ann x -> PrintHsFun ()+setLocated_ = void . setLocated++{----- ParsedFunRepr -----}++type ParsedFunRepr a =+  ParsedFunRepr'+    (FunReprSig a)+    (FunReprCtx a)+    (FunReprArg a)+    (FunReprMult a)+    (FunReprRet a)++-- | The parsed representation of a function+data ParsedFunRepr' sig ctx arg mult ret+  = -- | The "::" delimiter. Invariant: must be first.+    ParsedFunSig+      { sig :: sig,+        next :: ParsedFunRepr' sig ctx arg mult ret+      }+  | -- | The "forall a b." or "forall a b ->" construct.+    ParsedFunForall+      { tele :: LocatedA (HsForAllTelescope GhcPs),+        next :: ParsedFunRepr' sig ctx arg mult ret+      }+  | -- | The "(A, B) =>" construct.+    ParsedFunQuals+      { ctxs :: [LocatedA (LocatedC [LocatedA ctx])],+        next :: ParsedFunRepr' sig ctx arg mult ret+      }+  | ParsedFunArg+      { span :: SrcSpanAnnA,+        arg :: LocatedA arg,+        doc :: Maybe (LHsDoc GhcPs),+        multAnn :: HsMultAnnOf (LocatedA mult) GhcPs,+        next :: ParsedFunRepr' sig ctx arg mult ret+      }+  | ParsedFunReturn+      { ret :: LocatedA ret,+        doc :: Maybe (LHsDoc GhcPs)+      }++class+  ( Anno (FunReprCtx a) ~ SrcSpanAnnA,+    Outputable (FunReprCtx a)+  ) =>+  FunRepr a+  where+  parseFunRepr :: LocatedA a -> ParsedFunRepr a++  type FunReprSig a+  type FunReprSig a = ()+  renderFunReprSig :: FunReprSig a -> R ()+  default renderFunReprSig :: (FunReprSig a ~ ()) => FunReprSig a -> R ()+  renderFunReprSig () = space *> token'dcolon++  type FunReprCtx a+  type FunReprCtx a = a+  renderFunReprCtx :: FunReprCtx a -> R ()++  type FunReprArg a+  type FunReprArg a = a+  renderFunReprArg :: FunReprArg a -> R ()++  type FunReprMult a+  type FunReprMult a = a+  renderFunReprMult :: FunReprMult a -> R ()++  type FunReprRet a+  type FunReprRet a = a+  renderFunReprRet :: FunReprRet a -> R ()++-- | Some functions rely on HsType having a FunRepr instance, but the instance+-- can't be implemented here, since p_hsType is implemented in Type.hs, which+-- would cause a circular dependency. So we'll add it as an explicit constraint+-- and the call-site should bring the FunRepr instance for HsType in scope.+type FunReprHsType =+  ( FunRepr (HsType GhcPs),+    FunReprSig (HsType GhcPs) ~ (),+    FunReprCtx (HsType GhcPs) ~ HsType GhcPs,+    FunReprArg (HsType GhcPs) ~ HsType GhcPs,+    FunReprMult (HsType GhcPs) ~ HsType GhcPs,+    FunReprRet (HsType GhcPs) ~ HsType GhcPs+  )++hasDocs :: ParsedFunRepr' ig ctx arg mult ret -> Bool+hasDocs = \case+  ParsedFunSig {next} -> hasDocs next+  ParsedFunForall {next} -> hasDocs next+  ParsedFunQuals {next} -> hasDocs next+  ParsedFunArg {doc, next} -> isJust doc || hasDocs next+  ParsedFunReturn {doc} -> isJust doc++{----- Rendering ParsedFunRepr -----}++-- | For implementing function-arrows and related configuration, we'll collect+-- all the components of the function type first, then render as a block instead+-- of rendering each part independently, which will let us track local state+-- within a function type.+--+-- This function should be passed the first function-related construct we find;+-- see FunRepr for more details.+p_hsFun :: forall a. (FunRepr a, FunReprHsType) => a -> R ()+p_hsFun = p_hsFunParsed @a . parseFunRepr . L (noAnn @SrcSpanAnnA)++p_hsFunParsed :: forall a. (FunRepr a, FunReprHsType) => ParsedFunRepr a -> R ()+p_hsFunParsed = runPrintHsFun . p_hsFunParsed' @a++p_hsFunParsed' :: forall a. (FunRepr a, FunReprHsType) => ParsedFunRepr a -> PrintHsFun ()+p_hsFunParsed' = \case+  ParsedFunSig {sig, next} -> do+    -- Should only happen at the very beginning, if at all+    p_parsedFunSig @a sig next+    setMultilineContext next+    p_hsFunParsed' @a next+  ParsedFunForall {tele, next} -> do+    p_parsedFunForall tele+    setMultilineContext next+    p_hsFunParsed' @a next+  ParsedFunQuals {ctxs, next} -> do+    p_parsedFunQuals @a ctxs+    setMultilineContext next+    p_hsFunParsed' @a next+  ParsedFunArg {span, arg, doc, multAnn, next} -> do+    p_parsedFunArg @a span arg doc multAnn+    case next of+      ParsedFunArg {} -> pure ()+      _ -> do+        -- Don't reset multiline context within args; all args should be on+        -- one line together, or all on separate lines+        setMultilineContext next+        -- Reset inArgList, needed if there are multiple arg lists in one+        -- function type (e.g. `a -> Show a => b -> c`)+        setInArgList False+    p_hsFunParsed' @a next+  ParsedFunReturn {ret, doc} -> do+    p_parsedFunReturn @a ret doc++p_parsedFunSig :: forall a. (FunRepr a) => FunReprSig a -> ParsedFunRepr a -> PrintHsFun ()+p_parsedFunSig sig fun = do+  isTrailing <- getIsTrailing+  if isTrailing (Isn't #argDelim)+    then liftR $ do+      renderFunReprSig @a sig+      if hasDocs fun then newline else breakpoint+    else do+      liftR breakpoint+      setLeadingDelim (token'dcolon >> space)++p_parsedFunForall :: (FunReprHsType) => LocatedA (HsForAllTelescope GhcPs) -> PrintHsFun ()+p_parsedFunForall x@(L _ tele) = do+  setLocated_ x+  applyLeadingDelim+  vis <-+    case tele of+      HsForAllInvis _ bndrs -> do+        liftR $ p_forallBndrsStart p_hsTyVarBndr bndrs+        pure ForAllInvis+      HsForAllVis _ bndrs -> do+        liftR $ p_forallBndrsStart p_hsTyVarBndr bndrs+        pure ForAllVis++  isTrailing <- getIsTrailing+  isMultiline <- getIsMultiline+  if isTrailing (Isn't #argDelim) || not isMultiline+    then do+      liftR $ p_forallBndrsEnd (Without #extraSpace) vis+      interArgBreak+    else do+      interArgBreak+      let extraSpace = if isMultiline then With #extraSpace else Without #extraSpace+      setLeadingDelim (p_forallBndrsEnd extraSpace vis)++p_parsedFunQuals :: forall a. (FunRepr a) => [LocatedA (LocatedC [LocatedA (FunReprCtx a)])] -> PrintHsFun ()+p_parsedFunQuals ctxs = do+  isTrailing <- getIsTrailing+  -- we only want to set located on the first context+  case ctxs of+    ctx : _ -> setLocated_ ctx+    _ -> pure ()++  forM_ ctxs $ \(L _ lctx) -> do+    applyLeadingDelim+    liftR $ located lctx (p_hsContext' (renderFunReprCtx @a))+    if isTrailing (Isn't #argDelim)+      then do+        liftR $ space >> token'darrow+        interArgBreak+      else do+        interArgBreak+        setLeadingDelim (token'darrow >> space)++p_parsedFunArg ::+  forall a.+  (FunRepr a) =>+  SrcSpanAnnA ->+  LocatedA (FunReprArg a) ->+  Maybe (LHsDoc GhcPs) ->+  HsMultAnnOf (LocatedA (FunReprMult a)) GhcPs ->+  PrintHsFun ()+p_parsedFunArg span item doc multAnn = do+  isTrailing <- getIsTrailing++  -- We only want to set located on the first arg+  inArgList <- getInArgList+  when (not inArgList) $ do+    setLocated_ (L span item)+    setInArgList True++  let renderArrow = p_arrow (located' (renderFunReprMult @a)) multAnn+  withHaddocks (Isn't #end) doc $ do+    withApplyLeadingDelim $ \applyLeadingDelim' ->+      liftR . located item $ \arg -> do+        traverse_ id applyLeadingDelim'+        renderFunReprArg @a arg+    if isTrailing (Is #argDelim)+      then do+        liftR $ space >> renderArrow+        interArgBreak+      else do+        interArgBreak+        setLeadingDelim (renderArrow >> space)++p_parsedFunReturn ::+  forall a.+  (FunRepr a) =>+  LocatedA (FunReprRet a) ->+  Maybe (LHsDoc GhcPs) ->+  PrintHsFun ()+p_parsedFunReturn item doc = do+  setLocated_ item+  withHaddocks (Is #end) doc $ do+    applyLeadingDelim+    liftR $ located item (renderFunReprRet @a)++{----- forall -----}++data ForAllVisibility = ForAllInvis | ForAllVis++-- | Render several @forall@-ed variables.+p_forallBndrs ::+  (HasLoc l) =>+  ForAllVisibility ->+  (a -> R ()) ->+  [GenLocated l a] ->+  R ()+p_forallBndrs vis p tyvars = do+  p_forallBndrsStart p tyvars+  p_forallBndrsEnd (Without #extraSpace) vis++p_forallBndrsStart :: (HasLoc l) => (a -> R ()) -> [GenLocated l a] -> R ()+p_forallBndrsStart _ [] = token'forall+p_forallBndrsStart p tyvars = do+  switchLayout (locA <$> tyvars) $ do+    token'forall+    breakpoint+    inci $ do+      sitcc $ sep breakpoint (sitcc . located' p) tyvars++p_forallBndrsEnd :: Choice "extraSpace" -> ForAllVisibility -> R ()+p_forallBndrsEnd extraSpace = \case+  ForAllInvis -> txt dot >> space+  ForAllVis -> space >> token'rarrow+  where+    dot = if Choice.isTrue extraSpace then " ." else "."++{----- HsTyVarBndr -----}++class IsTyVarBndrFlag flag where+  isInferred :: flag -> Bool+  p_tyVarBndrFlag :: flag -> R ()+  p_tyVarBndrFlag _ = pure ()++instance IsTyVarBndrFlag () where+  isInferred () = False++instance IsTyVarBndrFlag Specificity where+  isInferred = \case+    InferredSpec -> True+    SpecifiedSpec -> False++instance IsTyVarBndrFlag (HsBndrVis GhcPs) where+  isInferred _ = False+  p_tyVarBndrFlag = \case+    HsBndrRequired NoExtField -> pure ()+    HsBndrInvisible _ -> txt "@"++p_hsTyVarBndr ::+  (IsTyVarBndrFlag flag, FunReprHsType) =>+  HsTyVarBndr flag GhcPs -> R ()+p_hsTyVarBndr HsTvb {..} = do+  p_tyVarBndrFlag tvb_flag+  let wrap+        | isInferred tvb_flag = braces N+        | otherwise = case tvb_kind of+            HsBndrKind {} -> parens N+            HsBndrNoKind {} -> id+  wrap $ do+    case tvb_var of+      HsBndrVar _ x -> p_rdrName x+      HsBndrWildCard _ -> txt "_"+    case tvb_kind of+      HsBndrKind _ k -> inci $ do+        p_hsFunParsed @(HsType GhcPs) $+          ParsedFunSig+            { sig = (),+              next = parseFunRepr k+            }+      HsBndrNoKind _ -> pure ()++{----- Contexts -----}++p_hsContext' ::+  (Outputable (GenLocated (Anno a) a), HasLoc (Anno a)) =>+  (a -> R ()) ->+  [XRec GhcPs a] ->+  R ()+p_hsContext' f = \case+  [] -> txt "()"+  [x] -> located x f+  xs -> do+    shouldSort <- getPrinterOpt poSortConstraints+    let sort = if shouldSort then sortOn showOutputable else id+    parens N $ sep commaDel (sitcc . located' f) (sort xs)++{----- Helpers -----}++withHaddocks :: (MonadR m) => Choice "end" -> Maybe (LHsDoc GhcPs) -> m a -> m a+withHaddocks isEnd doc m = do+  isTrailing <- getIsTrailing+  isLeadingHaddock <-+    liftR $+      getPrinterOpt poHaddockLocSignature <&> \case+        HaddockLocSigAuto ->+          if isTrailing (Is #argDelim) then With #pipe else Without #pipe+        HaddockLocSigLeading ->+          With #pipe+        HaddockLocSigTrailing ->+          Without #pipe++  if Choice.isTrue isLeadingHaddock+    then do+      traverse (liftR . p_hsDoc Pipe (With #endNewline)) doc *> m+    else do+      let (pre, endNewline) =+            if Choice.isTrue isEnd+              then (when (isJust doc) (liftR newline), Without #endNewline)+              else (pure (), With #endNewline)+      m <* pre <* traverse (liftR . p_hsDoc Caret endNewline) doc++interArgBreak :: PrintHsFun ()+interArgBreak = do+  isMultiline <- getIsMultiline+  liftR $ if isMultiline then newline else breakpoint
src/Ormolu/Utils.hs view
@@ -73,14 +73,14 @@  -- | Split and normalize a doc string. The result is a list of lines that -- make up the comment.-splitDocString :: Bool -> HsDocString -> [Text]-splitDocString shouldEscapeCommentBraces docStr =+splitDocString :: HsDocString -> [Text]+splitDocString docStr =   case r of     [] -> [""]     _ -> r   where     r =-      fmap (escapeLeadingDollar . escapeCommentBraces)+      fmap escapeLeadingDollar         . dropPaddingSpace'         . dropWhileEnd T.null         . fmap (T.stripEnd . T.pack)@@ -117,10 +117,6 @@            in if leadingSpace x                 then dropSpace <$> xs                 else xs-    escapeCommentBraces =-      if shouldEscapeCommentBraces-        then T.replace "{-" "{\\-" . T.replace "-}" "-\\}"-        else id  -- | Increment line number in a 'SrcSpan'. incSpanLine :: Int -> SrcSpan -> SrcSpan
src/Ormolu/Utils/Glob.hs view
@@ -1,6 +1,7 @@ module Ormolu.Utils.Glob   ( Glob,     mkGlob,+    matchAllGlob,     matchesGlob,   ) where@@ -31,6 +32,9 @@       t ->         let (m, t') = break (== '*') t          in MatchExactly m : parsePart t'++matchAllGlob :: Glob+matchAllGlob = Glob [DoubleWildcard]  matchesGlob :: String -> Glob -> Bool matchesGlob s (Glob ps) = s `matchesGlobParts` ps
tests/Ormolu/Config/PrinterOptsSpec.hs view
@@ -13,6 +13,7 @@ import Control.Monad (forM_, when) import Data.Algorithm.DiffContext qualified as Diff import Data.Char (isSpace)+import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty qualified as NonEmpty import Data.Maybe (isJust) import Data.Set qualified as S@@ -26,7 +27,7 @@ import Ormolu.Config import Ormolu.Exception (OrmoluException, printOrmoluException) import Ormolu.Terminal (runTerm)-import Ormolu.Utils.Glob (mkGlob)+import Ormolu.Utils.Glob (matchAllGlob, mkGlob) import Path   ( File,     Path,@@ -111,6 +112,24 @@           checkIdempotence = True         },       TestGroup+        { label = "record-style",+          isMulti = False,+          testCases = liftA2 (,) allOptions allOptions,+          updateConfig = \(recordStyle, commaStyle) opts ->+            opts+              { poRecordStyle = pure recordStyle,+                -- Test record style in combination with comma style+                poCommaStyle = pure commaStyle,+                -- Makes the indentation more visible+                poIndentation = pure 4+              },+          showTestCase = \(recordStyle, commaStyle) ->+            [ renderPrinterOpt recordStyle,+              renderPrinterOpt commaStyle+            ],+          checkIdempotence = True+        },+      TestGroup         { label = "import-export",           isMulti = True,           testCases = allOptions,@@ -130,62 +149,7 @@               ImportGroupByQualified,               ImportGroupByScope,               ImportGroupByScopeThenQualified,-              ImportGroupCustom . NonEmpty.fromList $-                [ ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "Data.Text"),-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = defaultImportRulePriority-                              }-                          ]-                    },-                  ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchAllModules,-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = ImportRulePriority 100-                              }-                          ]-                    },-                  ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "SomeInternal.**"),-                                igrQualifiedMatcher = MatchQualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              },-                            ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "Unknown.**"),-                                igrQualifiedMatcher = MatchUnqualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              }-                          ]-                    },-                  ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchLocalModules,-                                igrQualifiedMatcher = MatchUnqualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              },-                            ImportGroupRule-                              { igrModuleMatcher = MatchAllModules,-                                igrQualifiedMatcher = MatchQualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              }-                          ]-                    }-                ]+              ImportGroupCustom importGroupCustomRules             ],           updateConfig = \igs opts ->             opts@@ -525,3 +489,58 @@ spanEnd f xs =   let xs' = reverse xs    in (reverse $ dropWhile f xs', reverse $ takeWhile f xs')++importGroupCustomRules :: NonEmpty ImportGroup+importGroupCustomRules =+  NonEmpty.fromList+    [ defaultImportGroup . NonEmpty.fromList $+        [ defaultImportGroupRule+            { igrImportListMatcher = MatchWholeModuleImport,+              igrQualifiedMatcher = MatchUnqualifiedOnly+            }+        ],+      defaultImportGroup . NonEmpty.fromList $+        [ defaultImportGroupRule {igrGlob = mkGlob "Data.Text"}+        ],+      defaultImportGroup . NonEmpty.fromList $+        [ defaultImportGroupRule+            { igrPriority = ImportRulePriority 100+            }+        ],+      defaultImportGroup . NonEmpty.fromList $+        [ defaultImportGroupRule+            { igrGlob = mkGlob "SomeInternal.**",+              igrQualifiedMatcher = MatchQualifiedOnly+            },+          defaultImportGroupRule+            { igrGlob = mkGlob "Unknown.**",+              igrQualifiedMatcher = MatchUnqualifiedOnly+            }+        ],+      defaultImportGroup . NonEmpty.fromList $+        [ defaultImportGroupRule+            { igrQualifiedMatcher = MatchUnqualifiedOnly,+              igrScopeMatcher = MatchLocalModules+            },+          defaultImportGroupRule+            { igrQualifiedMatcher = MatchQualifiedOnly+            }+        ]+    ]+  where+    defaultImportGroup :: NonEmpty ImportGroupRule -> ImportGroup+    defaultImportGroup rules =+      ImportGroup+        { igName = Nothing,+          igRules = rules+        }++    defaultImportGroupRule :: ImportGroupRule+    defaultImportGroupRule =+      ImportGroupRule+        { igrGlob = matchAllGlob,+          igrImportListMatcher = MatchAnyImportDeclaration,+          igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,+          igrScopeMatcher = MatchAllModules,+          igrPriority = defaultImportRulePriority+        }
tests/Ormolu/ConfigSpec.hs view
@@ -3,14 +3,16 @@ -- | Tests for Fourmolu file configuration. module Ormolu.ConfigSpec (spec) where +import Control.Monad ((>=>)) import Data.ByteString.Char8 qualified as Char8 import Data.List (isInfixOf)+import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NonEmpty import Data.Map qualified as Map import Data.Yaml qualified as Yaml-import Ormolu.Config (FourmoluConfig (..), ImportGroup (..), ImportGroupRule (..), ImportGrouping (..), ImportModuleMatcher (..), ImportRulePriority (ImportRulePriority), PrinterOpts (..), QualifiedImportMatcher (MatchBothQualifiedAndUnqualified, MatchQualifiedOnly, MatchUnqualifiedOnly), defaultImportRulePriority, matchAllRulePriority, resolvePrinterOpts)+import Ormolu.Config import Ormolu.Fixity (ModuleReexports (..))-import Ormolu.Utils.Glob (mkGlob)+import Ormolu.Utils.Glob (matchAllGlob, mkGlob) import Test.Hspec  spec :: Spec@@ -71,165 +73,84 @@             [ "import-grouping:",               "  - name: Some name",               "    rules:",-              "      - match: all"+              "      - {}"             ]         let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)             expectedRules =               NonEmpty.fromList                 [ ImportGroup                     { igName = Just "Some name",-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchAllModules,-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = matchAllRulePriority-                              }-                          ]-                    }-                ]-        actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)-      it "enables the 'qualified' rule option" $ do-        config <--          Yaml.decodeThrow . Char8.pack . unlines $-            [ "import-grouping:",-              "  - rules:",-              "      - glob: \"**\"",-              "        qualified: yes"-            ]-        let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)-            expectedRules =-              NonEmpty.fromList-                [ ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "**"),-                                igrQualifiedMatcher = MatchQualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              }-                          ]-                    }-                ]-        actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)-      it "disables the 'qualified' rule option" $ do-        config <--          Yaml.decodeThrow . Char8.pack . unlines $-            [ "import-grouping:",-              "  - rules:",-              "      - glob: \"**\"",-              "        qualified: no"-            ]-        let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)-            expectedRules =-              NonEmpty.fromList-                [ ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "**"),-                                igrQualifiedMatcher = MatchUnqualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              }-                          ]-                    }-                ]-        actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)-      it "decodes the 'priority' rule option" $ do-        config <--          Yaml.decodeThrow . Char8.pack . unlines $-            [ "import-grouping:",-              "  - rules:",-              "      - match: all",-              "        priority: 55"-            ]-        let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)-            expectedRules =-              NonEmpty.fromList-                [ ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchAllModules,-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = ImportRulePriority 55-                              }-                          ]-                    }-                ]-        actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)-      it "parses a 'glob' rule" $ do-        config <--          Yaml.decodeThrow . Char8.pack . unlines $-            [ "import-grouping:",-              "  - rules:",-              "      - glob: Data.Text.**"-            ]-        let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)-            expectedRules =-              NonEmpty.fromList-                [ ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "Data.Text.**"),-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = defaultImportRulePriority-                              }-                          ]-                    }-                ]-        actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)-      it "parses a 'match' rule for local modules" $ do-        config <--          Yaml.decodeThrow . Char8.pack . unlines $-            [ "import-grouping:",-              "  - rules:",-              "      - match: local-modules"-            ]-        let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)-            expectedRules =-              NonEmpty.fromList-                [ ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchLocalModules,-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = defaultImportRulePriority-                              }-                          ]-                    }-                ]-        actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)-      it "parses a 'match' rule for all modules" $ do-        config <--          Yaml.decodeThrow . Char8.pack . unlines $-            [ "import-grouping:",-              "  - rules:",-              "      - match: all"-            ]-        let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)-            expectedRules =-              NonEmpty.fromList-                [ ImportGroup-                    { igName = Nothing,-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchAllModules,-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = matchAllRulePriority-                              }-                          ]+                      igRules = NonEmpty.fromList [defaultImportGroupRule]                     }                 ]         actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)++      let checkRule :: (HasCallStack) => String -> String -> ImportGroupRule -> SpecWith ()+          checkRule desc yamlRuleConfig expectedRuleConfig =+            it ("parses " ++ desc) $ do+              let yamlConfig =+                    unlines+                      [ "import-grouping:",+                        "  - rules:",+                        "      - " ++ yamlRuleConfig+                      ]++                  downCustomRules (ImportGroupCustom customRules) = Just customRules+                  downCustomRules _ = Nothing+                  downSingleton (a :| []) = Just a+                  downSingleton _ = Nothing+                  accessTestedRule = downCustomRules >=> downSingleton >=> downSingleton . igRules++              config <- Yaml.decodeThrow (Char8.pack yamlConfig)++              let actualStrategy = poImportGrouping (cfgFilePrinterOpts config)+              case actualStrategy >>= accessTestedRule of+                Nothing -> expectationFailure "A single tested rule change was expected"+                Just actualRule -> actualRule `shouldBe` expectedRuleConfig++          checkRuleAttribute :: (HasCallStack) => String -> String -> (ImportGroupRule -> ImportGroupRule) -> SpecWith ()+          checkRuleAttribute desc yamlRuleConfig applyExpectedChange = do+            let baseArbitraryYamlConfig = "glob: \"**\""+                yamlConfig = "{ " ++ baseArbitraryYamlConfig ++ ", " ++ yamlRuleConfig ++ "}"++                baseArbitraryConfig = defaultImportGroupRule {igrGlob = matchAllGlob}+                expectedRuleConfig = applyExpectedChange baseArbitraryConfig+            checkRule+              ("rules with the " ++ desc ++ " rule option")+              yamlConfig+              expectedRuleConfig++      checkRule "the 'glob' rule" "glob: Data.Text.**" defaultImportGroupRule {igrGlob = mkGlob "Data.Text.**"}+      checkRule "the default 'glob' rule (match all modules)" "{}" defaultImportGroupRule {igrGlob = mkGlob "**"}++      checkRuleAttribute "'any' import list" "import-list: any" $ \config -> config {igrImportListMatcher = MatchAnyImportDeclaration}+      checkRuleAttribute "'explicit' import list" "import-list: explicit" $ \config -> config {igrImportListMatcher = MatchExplicitImportList}+      checkRuleAttribute "'hiding' import list" "import-list: hiding" $ \config -> config {igrImportListMatcher = MatchHidingImportClause}+      checkRuleAttribute "'none' import list" "import-list: none" $ \config -> config {igrImportListMatcher = MatchWholeModuleImport}+      checkRuleAttribute "default import list" "" $ \config -> config {igrImportListMatcher = MatchAnyImportDeclaration}++      checkRuleAttribute "'qualified: yes'" "qualified: yes" $ \config -> config {igrQualifiedMatcher = MatchQualifiedOnly}+      checkRuleAttribute "'qualified: no'" "qualified: no" $ \config -> config {igrQualifiedMatcher = MatchUnqualifiedOnly}+      checkRuleAttribute "default 'qualified' (match any)" "" $ \config -> config {igrQualifiedMatcher = MatchBothQualifiedAndUnqualified}++      checkRuleAttribute "'scope: any'" "scope: any" $ \config -> config {igrScopeMatcher = MatchAllModules}+      checkRuleAttribute "'scope: local'" "scope: local" $ \config -> config {igrScopeMatcher = MatchLocalModules}+      checkRuleAttribute "'scope: external'" "scope: external" $ \config -> config {igrScopeMatcher = MatchExternalModules}+      checkRuleAttribute "default 'scope' (match all)" "" $ \config -> config {igrScopeMatcher = MatchAllModules}++      checkRuleAttribute "'priority' (55 priority example)" "priority: 55" $ \config -> config {igrPriority = ImportRulePriority 55}+      checkRuleAttribute "'priority' (80 priority example)" "priority: 80" $ \config -> config {igrPriority = ImportRulePriority 80}+      checkRuleAttribute "default 'priority' (50)" "" $ \config -> config {igrPriority = ImportRulePriority 50}++      checkRule+        "the legacy 'match' rule for all modules (with a 100 priority)"+        "match: all"+        defaultImportGroupRule {igrGlob = mkGlob "**", igrScopeMatcher = MatchAllModules, igrPriority = ImportRulePriority 100}+      checkRule+        "the legacy 'match' rule for local modules"+        "match: local-modules"+        defaultImportGroupRule {igrGlob = mkGlob "**", igrScopeMatcher = MatchLocalModules, igrPriority = ImportRulePriority 60}+       it "parses multiple group configurations and rules in their order of appearance in the configuration" $ do         config <-           Yaml.decodeThrow . Char8.pack . unlines $@@ -240,7 +161,6 @@               "  - name: The rest",               "    rules:",               "      - match: all",-              "        priority: 100",               "  - name: My internals and monads unqualified",               "    rules:",               "      - match: local-modules",@@ -262,68 +182,31 @@               NonEmpty.fromList                 [ ImportGroup                     { igName = Just "Text modules",-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "Data.Text"),-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = defaultImportRulePriority-                              }-                          ]+                      igRules = NonEmpty.fromList [defaultImportGroupRule {igrGlob = mkGlob "Data.Text"}]                     },                   ImportGroup                     { igName = Just "The rest",-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchAllModules,-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = ImportRulePriority 100-                              }-                          ]+                      igRules = NonEmpty.fromList [defaultImportGroupRule {igrGlob = matchAllGlob, igrPriority = ImportRulePriority 100}]                     },                   ImportGroup                     { igName = Just "My internals and monads unqualified",                       igRules =                         NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchLocalModules,-                                igrQualifiedMatcher = MatchUnqualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              },-                            ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "Control.Monad"),-                                igrQualifiedMatcher = MatchUnqualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              }+                          [ defaultImportGroupRule {igrQualifiedMatcher = MatchUnqualifiedOnly, igrScopeMatcher = MatchLocalModules, igrPriority = ImportRulePriority 60},+                            defaultImportGroupRule {igrGlob = mkGlob "Control.Monad", igrQualifiedMatcher = MatchUnqualifiedOnly}                           ]                     },                   ImportGroup                     { igName = Just "My internals and monads qualified",                       igRules =                         NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchLocalModules,-                                igrQualifiedMatcher = MatchQualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              },-                            ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "Control.Monad"),-                                igrQualifiedMatcher = MatchQualifiedOnly,-                                igrPriority = defaultImportRulePriority-                              }+                          [ defaultImportGroupRule {igrQualifiedMatcher = MatchQualifiedOnly, igrScopeMatcher = MatchLocalModules, igrPriority = ImportRulePriority 60},+                            defaultImportGroupRule {igrGlob = mkGlob "Control.Monad", igrQualifiedMatcher = MatchQualifiedOnly}                           ]                     },                   ImportGroup                     { igName = Just "Specific monads",-                      igRules =-                        NonEmpty.fromList-                          [ ImportGroupRule-                              { igrModuleMatcher = MatchGlob (mkGlob "Control.Monad.**"),-                                igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,-                                igrPriority = defaultImportRulePriority-                              }-                          ]+                      igRules = NonEmpty.fromList [defaultImportGroupRule {igrGlob = mkGlob "Control.Monad.**"}]                     }                 ]         actualStrategy `shouldBe` Just (ImportGroupCustom expectedRules)@@ -332,9 +215,19 @@               Yaml.decodeEither' @FourmoluConfig . Char8.pack . unlines $                 [ "import-grouping:",                   "  - rules:",-                  "      - some-unknown-rule-type: whatever"+                  "      - match: whatever"                 ]             isAnUnknownModuleMatcher e = case e of-              Left (Yaml.AesonException msg) -> "Unknown or invalid module matcher" `isInfixOf` msg+              Left (Yaml.AesonException msg) -> "Unknown legacy match value" `isInfixOf` msg               _ -> False         decodeResult `shouldSatisfy` isAnUnknownModuleMatcher++defaultImportGroupRule :: ImportGroupRule+defaultImportGroupRule =+  ImportGroupRule+    { igrGlob = matchAllGlob,+      igrImportListMatcher = MatchAnyImportDeclaration,+      igrQualifiedMatcher = MatchBothQualifiedAndUnqualified,+      igrScopeMatcher = MatchAllModules,+      igrPriority = defaultImportRulePriority+    }
tests/Ormolu/Fixity/ParserSpec.hs view
@@ -214,7 +214,7 @@                 elabel "module name"               ]           )-    it "fails with correct parse error (typo: export intead exports)" $+    it "fails with correct parse error (typo: export instead exports)" $       parseModuleReexportDeclaration "module Control.Lens export Control.Lens.Lens"         `shouldFailWith` err           20
tests/Ormolu/Integration/Utils.hs view
@@ -10,15 +10,16 @@ where  import Control.Monad (when)-import System.Directory (findExecutable)+import System.Directory (findExecutable, makeAbsolute) import System.Environment (getEnvironment) import System.Exit (ExitCode (..)) import System.Process (CreateProcess (..), readCreateProcessWithExitCode) import System.Process qualified as Process  -- | Find a `fourmolu` executable on PATH.+-- Needs to be absolute to work around https://github.com/haskell/cabal/issues/11598 getFourmoluExe :: IO FilePath-getFourmoluExe = findExecutable "fourmolu" >>= maybe (fail "Could not find fourmolu executable") return+getFourmoluExe = findExecutable "fourmolu" >>= maybe (fail "Could not find fourmolu executable") makeAbsolute  -- | Like 'System.Process.readProcess', except without specifying stdin and -- with better failure messages.