packages feed

fourmolu 0.15.0.0 → 0.16.0.0

raw patch · 70 files changed

+834/−388 lines, 70 filesdep +terminal-sizedep ~Cabal-syntaxdep ~containersdep ~filepathPVP ok

version bump matches the API change (PVP)

Dependencies added: terminal-size

Dependency ranges changed: Cabal-syntax, containers, filepath, ghc-lib-parser, optparse-applicative, text

API changes (from Hackage documentation)

- Ormolu: ConfigLoaded :: FilePath -> FourmoluConfig -> ConfigFileLoadResult
- Ormolu: ConfigParseError :: FilePath -> ParseException -> ConfigFileLoadResult
- Ormolu: data ConfigFileLoadResult
- Ormolu: loadConfigFile :: FilePath -> IO ConfigFileLoadResult
- Ormolu.Config: ConfigLoaded :: FilePath -> FourmoluConfig -> ConfigFileLoadResult
- Ormolu.Config: ConfigParseError :: FilePath -> ParseException -> ConfigFileLoadResult
- Ormolu.Config: data ConfigFileLoadResult
- Ormolu.Config: instance GHC.Show.Show Ormolu.Config.ConfigFileLoadResult
- Ormolu.Config: loadConfigFile :: FilePath -> IO ConfigFileLoadResult
- Ormolu.Utils: class HasSrcSpan l
- Ormolu.Utils: getLoc' :: HasSrcSpan l => GenLocated l a -> SrcSpan
- Ormolu.Utils: instance Ormolu.Utils.HasSrcSpan (GHC.Parser.Annotation.SrcSpanAnn' ann)
- Ormolu.Utils: instance Ormolu.Utils.HasSrcSpan GHC.Types.SrcLoc.RealSrcSpan
- Ormolu.Utils: instance Ormolu.Utils.HasSrcSpan GHC.Types.SrcLoc.SrcSpan
- Ormolu.Utils: loc' :: HasSrcSpan l => l -> SrcSpan
- Ormolu.Utils.IO: getContentsUtf8 :: MonadIO m => m Text
- Ormolu.Utils.IO: readFileUtf8 :: MonadIO m => FilePath -> m Text
- Ormolu.Utils.IO: writeFileUtf8 :: MonadIO m => FilePath -> Text -> m ()
+ Ormolu: [searchDirs] :: ConfigNotFound -> [FilePath]
+ Ormolu: data ConfigNotFound
+ Ormolu: findConfigFile :: FilePath -> IO (Either ConfigNotFound FilePath)
+ Ormolu.Config: [searchDirs] :: ConfigNotFound -> [FilePath]
+ Ormolu.Config: data ConfigNotFound
+ Ormolu.Config: findConfigFile :: FilePath -> IO (Either ConfigNotFound FilePath)
+ Ormolu.Config: instance GHC.Show.Show Ormolu.Config.ConfigNotFound
+ Ormolu.Printer.Combinators: getEnclosingComments :: R [LComment]
+ Ormolu.Printer.Internal: getEnclosingComments :: R [LComment]
+ Ormolu.Printer.Meat.Common: p_namespaceSpec :: NamespaceSpecifier -> R ()
- Ormolu: ConfigNotFound :: [FilePath] -> ConfigFileLoadResult
+ Ormolu: ConfigNotFound :: [FilePath] -> ConfigNotFound
- Ormolu.Config: ConfigNotFound :: [FilePath] -> ConfigFileLoadResult
+ Ormolu.Config: ConfigNotFound :: [FilePath] -> ConfigNotFound
- Ormolu.Printer.Combinators: encloseLocated :: HasSrcSpan l => GenLocated l [a] -> ([a] -> R ()) -> R ()
+ Ormolu.Printer.Combinators: encloseLocated :: HasLoc l => GenLocated l [a] -> ([a] -> R ()) -> R ()
- Ormolu.Printer.Combinators: located :: HasSrcSpan l => GenLocated l a -> (a -> R ()) -> R ()
+ Ormolu.Printer.Combinators: located :: HasLoc l => GenLocated l a -> (a -> R ()) -> R ()
- Ormolu.Printer.Combinators: located' :: HasSrcSpan l => (a -> R ()) -> GenLocated l a -> R ()
+ Ormolu.Printer.Combinators: located' :: HasLoc l => (a -> R ()) -> GenLocated l a -> R ()
- Ormolu.Printer.Meat.Type: p_forallBndrs :: HasSrcSpan l => ForAllVisibility -> (a -> R ()) -> [GenLocated l a] -> R ()
+ Ormolu.Printer.Meat.Type: p_forallBndrs :: HasLoc l => ForAllVisibility -> (a -> R ()) -> [GenLocated l a] -> R ()
- Ormolu.Printer.Meat.Type: startTypeAnnotation :: HasSrcSpan l => GenLocated l a -> (a -> R ()) -> R ()
+ Ormolu.Printer.Meat.Type: startTypeAnnotation :: HasLoc l => GenLocated l a -> (a -> R ()) -> R ()
- Ormolu.Printer.Meat.Type: startTypeAnnotationDecl :: HasSrcSpan l => GenLocated l a -> (a -> HsType GhcPs) -> (a -> R ()) -> R ()
+ Ormolu.Printer.Meat.Type: startTypeAnnotationDecl :: HasLoc l => GenLocated l a -> (a -> HsType GhcPs) -> (a -> R ()) -> R ()
- Ormolu.Printer.Operators: opTreeLoc :: HasSrcSpan l => OpTree (GenLocated l a) b -> SrcSpan
+ Ormolu.Printer.Operators: opTreeLoc :: HasLoc l => OpTree (GenLocated l a) b -> SrcSpan

Files

CHANGELOG.md view
@@ -1,3 +1,23 @@+## Fourmolu 0.16.0.0++* Allow specifying path to configuration file with `--config` ([#396](https://github.com/fourmolu/fourmolu/issues/396))++### Upstream changes:++#### Ormolu 0.7.5.0++* Switched to `ghc-lib-parser-9.10`, with the following new syntactic features/behaviors:+  * GHC proposal [#575](https://github.com/ghc-proposals/ghc-proposals/blob/10290a668608d608c3f6c6010be265cf7a02e1fc/proposals/0575-deprecated-instances.rst): deprecated instances.+  * GHC proposal [#281](https://github.com/ghc-proposals/ghc-proposals/blob/10290a668608d608c3f6c6010be265cf7a02e1fc/proposals/0281-visible-forall.rst): visible forall in types of terms.+    Enabled by `RequiredTypeArguments` (enabled by default).+  * `LinearTypes`: `let` and `where` bindings can now be linear, in particular have multiplicity annotations.+  * Using `forall` as an identifier is now a parse error.+  * GHC proposal [#65](https://github.com/ghc-proposals/ghc-proposals/blob/10290a668608d608c3f6c6010be265cf7a02e1fc/proposals/0065-type-infix.rst): namespacing fixity declarations for type names and WARNING/DEPRECATED pragmas.+  * `TypeAbstractions` now supports `@`-binders in lambdas and function equations.+  * Support for the `GHC2024` language.++* Updated to `Cabal-syntax-3.12`.+ ## Fourmolu 0.15.0.0  * Add `single-deriving-parens` configuration option to determine if `deriving` clauses of a single type should be parenthesized ([#386](https://github.com/fourmolu/fourmolu/pull/386))
README.md view
@@ -205,6 +205,8 @@ `--end-line` command line options. `--start-line` defaults to the beginning of the file, while `--end-line` defaults to the end. +Note that the selected region needs to be parseable Haskell code on its own.+ ### Exit codes  Exit code | Meaning
app/Main.hs view
@@ -15,7 +15,7 @@ import Data.Map.Strict qualified as Map import Data.Maybe (fromMaybe, mapMaybe, maybeToList) import Data.Set qualified as Set-import Data.Text.IO qualified as TIO+import Data.Text.IO.Utf8 qualified as T.Utf8 import Data.Version (showVersion) import Data.Yaml qualified as Yaml import Distribution.ModuleName (ModuleName)@@ -30,8 +30,8 @@ import Ormolu.Terminal import Ormolu.Utils (showOutputable) import Ormolu.Utils.Fixity-import Ormolu.Utils.IO import Paths_fourmolu (version)+import System.Console.Terminal.Size qualified as Terminal import System.Directory import System.Exit (ExitCode (..), exitWith) import System.FilePath qualified as FP@@ -40,13 +40,8 @@ -- | Entry point of the program. main :: IO () main = do-  opts@Opts {..} <- execParser optsParserInfo--  cwd <- getCurrentDirectory-  cfg <- case optInputFiles of-    [] -> mkConfig cwd opts-    ["-"] -> mkConfig cwd opts-    file : _ -> mkConfig file opts+  opts@Opts {..} <- runParser optsParserInfo+  cfg <- resolveConfig opts    let formatOne' =         formatOne@@ -75,27 +70,34 @@                 else 102    exitWith exitCode+  where+    runParser p = do+      termWidth <- maybe 100 Terminal.width <$> Terminal.size+      customExecParser (prefs $ helpIndent 35 <> columns termWidth) p  -- | Build the full config, by adding 'PrinterOpts' from a file, if found.-mkConfig :: FilePath -> Opts -> IO (Config RegionIndices)-mkConfig path Opts {optQuiet, optConfig = cliConfig, optPrinterOpts = cliPrinterOpts} = do+resolveConfig :: Opts -> IO (Config RegionIndices)+resolveConfig opts@(Opts {optConfig = cliConfig, optPrinterOpts = cliPrinterOpts}) = do   fourmoluConfig <--    loadConfigFile path >>= \case-      ConfigLoaded f cfg -> do-        outputInfo $ "Loaded config from: " <> f-        outputDebug $ unwords ["*** CONFIG FILE ***", show cfg]-        pure cfg-      ConfigParseError f e -> do-        outputError . unlines $-          [ "Failed to load " <> f <> ":",-            Yaml.prettyPrintParseException e-          ]-        exitWith $ ExitFailure 400-      ConfigNotFound searchDirs -> do+    findConfigFile' >>= \case+      Left ConfigNotFound {searchDirs} -> do         outputDebug . unlines $           ("No " ++ show configFileName ++ " found in any of:")             : map ("  " ++) searchDirs         pure emptyConfig+      Right configPath ->+        Yaml.decodeFileEither configPath >>= \case+          Left e -> do+            outputError . unlines $+              [ "Failed to load " <> configPath <> ":",+                Yaml.prettyPrintParseException e+              ]+            exitWith $ ExitFailure 400+          Right cfg -> do+            outputInfo $ "Loaded config from: " <> configPath+            outputDebug $ unwords ["*** CONFIG FILE ***", show cfg]+            pure cfg+   return $     cliConfig       { cfgPrinterOpts =@@ -115,9 +117,17 @@             ]       }   where+    findConfigFile' = do+      cwd <- getCurrentDirectory+      case optInputFiles opts of+        _ | Just configPath <- optConfigFilePath opts -> pure $ Right configPath+        [] -> findConfigFile cwd+        ["-"] -> findConfigFile cwd+        file : _ -> findConfigFile $ FP.takeDirectory file+     output = hPutStrLn stderr     outputError = output-    outputInfo = unless optQuiet . output+    outputInfo = unless (optQuiet opts) . output     outputDebug = when (cfgDebug cliConfig) . output  getHaskellFiles :: FilePath -> IO [FilePath]@@ -191,7 +201,7 @@         config <- patchConfig Nothing mcabalInfo mdotOrmolu         case mode of           Stdout -> do-            ormoluStdin config >>= TIO.putStr+            ormoluStdin config >>= T.Utf8.putStr             return ExitSuccess           InPlace -> do             hPutStrLn@@ -201,7 +211,7 @@             return (ExitFailure 101)           Check -> do             -- ormoluStdin is not used because we need the originalInput-            originalInput <- getContentsUtf8+            originalInput <- T.Utf8.getContents             let stdinRepr = "<stdin>"             formattedInput <-               ormolu config stdinRepr originalInput@@ -220,19 +230,19 @@             mdotOrmolu         case mode of           Stdout -> do-            ormoluFile config inputFile >>= TIO.putStr+            ormoluFile config inputFile >>= T.Utf8.putStr             return ExitSuccess           InPlace -> do             -- ormoluFile is not used because we need originalInput-            originalInput <- readFileUtf8 inputFile+            originalInput <- T.Utf8.readFile inputFile             formattedInput <-               ormolu config inputFile originalInput             when (formattedInput /= originalInput) $-              writeFileUtf8 inputFile formattedInput+              T.Utf8.writeFile inputFile formattedInput             return ExitSuccess           Check -> do             -- ormoluFile is not used because we need originalInput-            originalInput <- readFileUtf8 inputFile+            originalInput <- T.Utf8.readFile inputFile             formattedInput <-               ormolu config inputFile originalInput             handleDiff originalInput formattedInput inputFile@@ -274,6 +284,8 @@     optQuiet :: !Bool,     -- | Ormolu 'Config'     optConfig :: !(Config RegionIndices),+    -- | Ormolu 'Config'+    optConfigFilePath :: !(Maybe FilePath),     -- | Fourmolu 'PrinterOpts',     optPrinterOpts :: PrinterOptsPartial,     -- | Options related to info extracted from files@@ -363,6 +375,11 @@         help "Make output quieter"       ]     <*> configParser+    <*> (optional . strOption . mconcat)+      [ metavar "CONFIG_FILE",+        long "config",+        help "Path to the config file to use. If not specified, tries to discover one automatically."+      ]     <*> printerOptsParser     <*> configFileOptsParser     <*> sourceTypeParser@@ -456,7 +473,7 @@                 help "End line of the region to format (inclusive)"               ]         )-    <*> pure defaultPrinterOpts -- unused; overwritten in mkConfig+    <*> pure defaultPrinterOpts -- unused; overwritten in resolveConfig  sourceTypeParser :: Parser (Maybe SourceType) sourceTypeParser =
data/examples/declaration/signature/fixity/infix-four-out.hs view
@@ -2,3 +2,5 @@ infix 9 <^-^>  infix 2 ->++infix 0 type <!>
data/examples/declaration/signature/fixity/infix-out.hs view
@@ -3,3 +3,5 @@ infix 9 <^-^>  infix 2 ->++infix 0 type <!>
data/examples/declaration/signature/fixity/infix.hs view
@@ -2,3 +2,5 @@ infix 9 <^-^>  infix 2 ->++infix 0 type <!>
data/examples/declaration/signature/fixity/infixl-four-out.hs view
@@ -1,2 +1,4 @@ infixl 8 *** infixl 0 $, *, +, &&, **++infixl 9 type $
data/examples/declaration/signature/fixity/infixl-out.hs view
@@ -1,3 +1,5 @@ infixl 8 ***  infixl 0 $, *, +, &&, **++infixl 9 type $
data/examples/declaration/signature/fixity/infixl.hs view
@@ -1,2 +1,4 @@ infixl 8 *** infixl 0 $, *, +, &&, **++infixl 9 type $
data/examples/declaration/signature/fixity/infixr-four-out.hs view
@@ -1,2 +1,4 @@ infixr 8 `Foo` infixr 0 ***, &&&++infixr 0 data $
data/examples/declaration/signature/fixity/infixr-out.hs view
@@ -1,3 +1,5 @@ infixr 8 `Foo`  infixr 0 ***, &&&++infixr 0 data $
data/examples/declaration/signature/fixity/infixr.hs view
@@ -1,2 +1,4 @@ infixr 8 `Foo` infixr 0 ***, &&&++infixr 0 data $
data/examples/declaration/value/function/pragmas-four-out.hs view
@@ -1,4 +1,4 @@-sccfoo = {-# SCC foo #-} 1+sccfoo = {-# SCC "foo" #-} 1 sccbar =     {-# SCC "barbaz" #-}     "hello"
data/examples/declaration/value/function/pragmas-out.hs view
@@ -1,4 +1,4 @@-sccfoo = {-# SCC foo #-} 1+sccfoo = {-# SCC "foo" #-} 1  sccbar =   {-# SCC "barbaz" #-}
+ data/examples/declaration/value/function/required-type-arguments-four-out.hs view
@@ -0,0 +1,17 @@+vshow :: forall a -> (Show a) => a -> String+vshow t x = show (x :: t)++s1 = vshow Int 42+s2 = vshow Double 42++a1 = f (type (Int -> Bool))+a2 = f (type ((Read T) => T))+a3 = f (type (forall a. a))+a4 = f (type (forall a. (Read a) => String -> a))++foo =+    f+        ( type ( Maybe+                    Int+               )+        )
+ data/examples/declaration/value/function/required-type-arguments-out.hs view
@@ -0,0 +1,21 @@+vshow :: forall a -> (Show a) => a -> String+vshow t x = show (x :: t)++s1 = vshow Int 42++s2 = vshow Double 42++a1 = f (type (Int -> Bool))++a2 = f (type ((Read T) => T))++a3 = f (type (forall a. a))++a4 = f (type (forall a. (Read a) => String -> a))++foo =+  f+    ( type ( Maybe+              Int+           )+    )
+ data/examples/declaration/value/function/required-type-arguments.hs view
@@ -0,0 +1,13 @@+vshow :: forall a -> Show a => a -> String+vshow t x = show (x :: t)++s1 = vshow Int    42+s2 = vshow Double 42++a1 = f (type (Int -> Bool))+a2 = f (type (Read T => T))+a3 = f (type (forall a. a))+a4 = f (type (forall a. Read a => String -> a))++foo = f (type (Maybe+            Int))
+ data/examples/declaration/value/function/type-abstractions-four-out.hs view
@@ -0,0 +1,9 @@+id :: forall a. a -> a+id @t x = x :: t++f1 :: forall a. a -> forall b. b -> (a, b)+f1 @a x @b y = (x :: a, y :: b)++f2 =+    (\ @a x @b y -> (x :: a, y :: b)) ::+        forall a. a -> forall b. b -> (a, b)
+ data/examples/declaration/value/function/type-abstractions-out.hs view
@@ -0,0 +1,9 @@+id :: forall a. a -> a+id @t x = x :: t++f1 :: forall a. a -> forall b. b -> (a, b)+f1 @a x @b y = (x :: a, y :: b)++f2 =+  (\ @a x @b y -> (x :: a, y :: b)) ::+    forall a. a -> forall b. b -> (a, b)
+ data/examples/declaration/value/function/type-abstractions.hs view
@@ -0,0 +1,8 @@+id :: forall a. a -> a+id @t x = x :: t++f1 :: forall a. a -> forall b. b -> (a, b)+f1 @a x @b y = (x :: a, y :: b)++f2 = (\ @a x @b y -> (x :: a, y :: b) )+    :: forall a. a -> forall b. b -> (a, b)
data/examples/declaration/warning/warning-multiline-four-out.hs view
@@ -7,3 +7,12 @@     #-} test :: IO () test = pure ()++instance+    {-# WARNING "Don't use" #-}+    Show G1 where+    show = "G1"++deriving instance+        {-# WARNING "to be removed" #-}+    Eq G2
data/examples/declaration/warning/warning-multiline-out.hs view
@@ -7,3 +7,12 @@   #-} test :: IO () test = pure ()++instance+  {-# WARNING "Don't use" #-}+  Show G1 where+  show = "G1"++deriving instance+    {-# WARNING "to be removed" #-}+  Eq G2
data/examples/declaration/warning/warning-multiline.hs view
@@ -2,3 +2,11 @@   foo ["These are bad functions", "Really bad!"] #-} test :: IO () test = pure ()++instance+  {-# WARNING "Don't use" #-}+  Show G1 where+  show = "G1"++deriving instance+  {-# WARNING "to be removed" #-} Eq G2
data/examples/declaration/warning/warning-single-line-four-out.hs view
@@ -6,11 +6,17 @@ bar = 3 {-# DEPRECATED bar "Bar is deprecated" #-} -{-# DEPRECATED baz "Baz is also deprecated" #-}+{-# DEPRECATED data baz "Baz is also deprecated" #-} baz = 5  data Number = Number Dobule-{-# DEPRECATED Number "Use Scientific instead." #-}+{-# DEPRECATED type Number "Use Scientific instead." #-}  head (a : _) = a {-# WARNING in "x-partial" head "This function is partial..." #-}++instance {-# DEPRECATED "Don't use" #-} Show T1+instance {-# WARNING "Don't use either" #-} Show G1++deriving instance {-# DEPRECATED "to be removed" #-} Eq T2+deriving instance {-# WARNING "to be removed as well" #-} Eq G2
data/examples/declaration/warning/warning-single-line-out.hs view
@@ -6,11 +6,19 @@ bar = 3 {-# DEPRECATED bar "Bar is deprecated" #-} -{-# DEPRECATED baz "Baz is also deprecated" #-}+{-# DEPRECATED data baz "Baz is also deprecated" #-} baz = 5  data Number = Number Dobule-{-# DEPRECATED Number "Use Scientific instead." #-}+{-# DEPRECATED type Number "Use Scientific instead." #-}  head (a : _) = a {-# WARNING in "x-partial" head "This function is partial..." #-}++instance {-# DEPRECATED "Don't use" #-} Show T1++instance {-# WARNING "Don't use either" #-} Show G1++deriving instance {-# DEPRECATED "to be removed" #-} Eq T2++deriving instance {-# WARNING "to be removed as well" #-} Eq G2
data/examples/declaration/warning/warning-single-line.hs view
@@ -8,11 +8,17 @@  {-# Deprecated bar "Bar is deprecated" #-} -{-# DEPRECATED baz "Baz is also deprecated" #-}+{-# DEPRECATED data baz "Baz is also deprecated" #-} baz = 5  data Number = Number Dobule-{-# DEPRECATED Number "Use Scientific instead." #-}+{-# DEPRECATED type Number "Use Scientific instead." #-}  head (a:_) = a {-# WARNING in "x-partial" head "This function is partial..." #-}++instance {-# DEPRECATED "Don't use" #-}     Show T1 where+instance {-# WARNING "Don't use either" #-} Show G1 where++deriving instance {-# DEPRECATED "to be removed" #-}      Eq T2+deriving instance {-# WARNING "to be removed as well" #-} Eq G2
+ data/examples/import/docstrings-after-exports-four-out.hs view
@@ -0,0 +1,11 @@+module Test (+    since1, -- ^ @since 1.0+    since2, -- ^ @since 2.0+    since3, -- ^ @since 3.0+    SinceType (..), -- ^ @since 4.0+    SinceClass (..), -- ^ @since 5.0+    Multi (..),+    -- ^ since 6.0+    -- multi+    -- line+) where
+ data/examples/import/docstrings-after-exports-out.hs view
@@ -0,0 +1,12 @@+module Test+  ( since1, -- ^ @since 1.0+    since2, -- ^ @since 2.0+    since3, -- ^ @since 3.0+    SinceType (..), -- ^ @since 4.0+    SinceClass (..), -- ^ @since 5.0+    Multi (..),+    -- ^ since 6.0+    -- multi+    -- line+  )+where
+ data/examples/import/docstrings-after-exports.hs view
@@ -0,0 +1,11 @@+module Test (+        since1, -- ^ @since 1.0+        since2  -- ^ @since 2.0+      , since3  -- ^ @since 3.0+      , SinceType(..) -- ^ @since 4.0+      , SinceClass(..) -- ^ @since 5.0+      , Multi(..)+        -- ^ since 6.0+        -- multi+        -- line+      ) where
data/fourmolu/import-export/input-multi.hs view
@@ -50,3 +50,18 @@     -- * Some other thing     Foo, ) where++{- // -}++-- See data/examples/import/docstrings-after-exports.hs+module Test (+        since1, -- ^ @since 1.0+        since2  -- ^ @since 2.0+      , since3  -- ^ @since 3.0+      , SinceType(..) -- ^ @since 4.0+      , SinceClass(..) -- ^ @since 5.0+      , Multi(..)+        -- ^ since 6.0+        -- multi+        -- line+      ) where
data/fourmolu/import-export/output-ImportExportDiffFriendly.hs view
@@ -50,3 +50,18 @@     -- * Some other thing     Foo, ) where++{- // -}++-- See data/examples/import/docstrings-after-exports.hs+module Test (+    since1, -- ^ @since 1.0+    since2, -- ^ @since 2.0+    since3, -- ^ @since 3.0+    SinceType (..), -- ^ @since 4.0+    SinceClass (..), -- ^ @since 5.0+    Multi (..),+    -- ^ since 6.0+    -- multi+    -- line+) where
data/fourmolu/import-export/output-ImportExportLeading.hs view
@@ -50,3 +50,18 @@       -- * Some other thing     , Foo     ) where++{- // -}++-- See data/examples/import/docstrings-after-exports.hs+module Test+    ( since1 -- ^ @since 1.0+    , since2 -- ^ @since 2.0+    , since3 -- ^ @since 3.0+    , SinceType (..) -- ^ @since 4.0+    , SinceClass (..) -- ^ @since 5.0+    , Multi (..)+    -- ^ since 6.0+    -- multi+    -- line+    ) where
data/fourmolu/import-export/output-ImportExportTrailing.hs view
@@ -50,3 +50,18 @@       -- * Some other thing       Foo,     ) where++{- // -}++-- See data/examples/import/docstrings-after-exports.hs+module Test+    ( since1, -- ^ @since 1.0+      since2, -- ^ @since 2.0+      since3, -- ^ @since 3.0+      SinceType (..), -- ^ @since 4.0+      SinceClass (..), -- ^ @since 5.0+      Multi (..),+      -- ^ since 6.0+      -- multi+      -- line+    ) where
extract-hackage-info/hackage-info.bin view

binary file changed (952236 → 989821 bytes)

fourmolu.cabal view
@@ -1,13 +1,13 @@ cabal-version:      2.4 name:               fourmolu-version:            0.15.0.0+version:            0.16.0.0 license:            BSD-3-Clause license-file:       LICENSE.md maintainer:     Matt Parsons <parsonsmatt@gmail.com>     George Thomas <georgefsthomas@gmail.com>     Brandon Chinn <brandonchinn178@gmail.com>-tested-with:        ghc ==9.4.7 ghc ==9.6.3 ghc ==9.8.1+tested-with:        ghc ==9.6.5 ghc ==9.8.2 ghc ==9.10.1 homepage:           https://github.com/fourmolu/fourmolu bug-reports:        https://github.com/fourmolu/fourmolu/issues synopsis:           A formatter for Haskell source code@@ -105,10 +105,10 @@     other-modules:    GHC.DynFlags     default-language: GHC2021     build-depends:-        Cabal-syntax >=3.10 && <3.11,+        Cabal-syntax >=3.12 && <3.13,         Diff >=0.4 && <1,         MemoTrie >=0.6 && <0.7,-        ansi-terminal >=0.10 && <1.1,+        ansi-terminal >=0.10 && <1.2,         array >=0.5 && <0.6,         base >=4.14 && <5,         binary >=0.8 && <0.9,@@ -117,16 +117,15 @@         deepseq >=1.4 && <1.6,         directory ^>=1.3,         file-embed >=0.0.15 && <0.1,-        filepath >=1.2 && <1.5,-        ghc-lib-parser >=9.8 && <9.9,+        filepath >=1.2 && <1.6,+        ghc-lib-parser >=9.10 && <9.11,         megaparsec >=9,         mtl >=2 && <3,         syb >=0.7 && <0.8,-        text >=2 && <3,+        text >=2.1 && <3,         -- fourmolu-only deps         aeson >=1.0 && <3.0,         scientific >=0.3.2 && <1,-        yaml >=0.11.6.0 && <1      if flag(dev)         ghc-options:@@ -146,17 +145,19 @@     autogen-modules:  Paths_fourmolu     default-language: GHC2021     build-depends:-        Cabal-syntax >=3.10 && <3.11,+        Cabal-syntax >=3.12 && <3.13,         base >=4.12 && <5,-        containers >=0.5 && <0.7,+        containers >=0.5 && <0.8,         directory ^>=1.3,-        filepath >=1.2 && <1.5,-        ghc-lib-parser >=9.8 && <9.9,+        filepath >=1.2 && <1.6,+        ghc-lib-parser >=9.10 && <9.11,         optparse-applicative >=0.14 && <0.19,-        text >=2 && <3,+        text >=2.1 && <3,         th-env >=0.1.1 && <0.2,         -- fourmolu-only deps         directory >=1.3.3 && <1.4,+        optparse-applicative >=0.17,+        terminal-size >=0.1 && <0.4,         yaml >=0.11.6.0 && <1,         fourmolu @@ -187,20 +188,20 @@      default-language:   GHC2021     build-depends:-        Cabal-syntax >=3.10 && <3.11,+        Cabal-syntax >=3.12 && <3.13,         QuickCheck >=2.14,         base >=4.14 && <5,-        containers >=0.5 && <0.7,+        containers >=0.5 && <0.8,         directory ^>=1.3,-        filepath >=1.2 && <1.5,-        ghc-lib-parser >=9.8 && <9.9,+        filepath >=1.2 && <1.6,+        ghc-lib-parser >=9.10 && <9.11,         hspec >=2 && <3,         hspec-megaparsec >=2.2,         megaparsec >=9,         path >=0.6 && <0.10,         path-io >=1.4.2 && <2,         temporary ^>=1.3,-        text >=2 && <3+        text >=2.1 && <3      -- specific to fourmolu tests     other-modules:
src/Ormolu.hs view
@@ -22,8 +22,8 @@     PrinterOptsPartial,     PrinterOptsTotal,     defaultPrinterOpts,-    loadConfigFile,-    ConfigFileLoadResult (..),+    findConfigFile,+    ConfigNotFound (..),     configFileName,     resolvePrinterOpts, @@ -52,6 +52,7 @@ import Data.Set qualified as Set import Data.Text (Text) import Data.Text qualified as T+import Data.Text.IO.Utf8 qualified as T.Utf8 import Debug.Trace import GHC.Driver.Errors.Types import GHC.Types.Error@@ -68,7 +69,6 @@ import Ormolu.Printer import Ormolu.Utils (showOutputable) import Ormolu.Utils.Cabal qualified as CabalUtils-import Ormolu.Utils.IO import System.FilePath  -- | Format a 'Text'.@@ -165,7 +165,7 @@   -- | Resulting rendition   m Text ormoluFile cfg path =-  readFileUtf8 path >>= ormolu cfg path+  liftIO (T.Utf8.readFile path) >>= ormolu cfg path  -- | Read input from stdin and format it. --@@ -179,7 +179,7 @@   -- | Resulting rendition   m Text ormoluStdin cfg =-  getContentsUtf8 >>= ormolu cfg "<stdin>"+  liftIO T.Utf8.getContents >>= ormolu cfg "<stdin>"  -- | Refine a 'Config' by incorporating given 'SourceType', 'CabalInfo', and -- fixity overrides 'FixityMap'. You can use 'detectSourceType' to deduce
src/Ormolu/Config.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}@@ -47,11 +48,11 @@     parsePrinterOptType,      -- ** Loading Fourmolu configuration-    loadConfigFile,+    ConfigNotFound (..),+    findConfigFile,     configFileName,     FourmoluConfig (..),     emptyConfig,-    ConfigFileLoadResult (..),   ) where @@ -59,13 +60,11 @@ import Data.Aeson ((.!=), (.:?)) import Data.Aeson qualified as Aeson import Data.Aeson.Types qualified as Aeson-import Data.Foldable (foldl') import Data.Functor.Identity (Identity (..)) import Data.Map.Strict qualified as Map import Data.Set (Set) import Data.Set qualified as Set import Data.String (fromString)-import Data.Yaml qualified as Yaml import Distribution.Types.PackageName (PackageName) import GHC.Generics (Generic) import GHC.Types.SrcLoc qualified as GHC@@ -79,7 +78,10 @@     getXdgDirectory,     makeAbsolute,   )-import System.FilePath (splitPath, (</>))+import System.FilePath (takeDirectory)+#if !MIN_VERSION_base(4,20,0)+import Data.List (foldl')+#endif  -- | Type of sources that can be formatted by Ormolu. data SourceType@@ -267,25 +269,25 @@       cfgFileReexports = ModuleReexports mempty     } --- | Read options from a config file, if found.--- Looks recursively in parent folders, then in 'XdgConfig',--- for a file named /fourmolu.yaml/.-loadConfigFile :: FilePath -> IO ConfigFileLoadResult-loadConfigFile path = do-  root <- makeAbsolute path+-- | Find a fourmolu configuration file.+--+-- Looks for a file named /fourmolu.yaml/, first in the given path and+-- its parents, and then in the XDG config directory.+findConfigFile :: FilePath -> IO (Either ConfigNotFound FilePath)+findConfigFile rootDir = do+  rootDirAbs <- makeAbsolute rootDir   xdg <- getXdgDirectory XdgConfig ""-  let dirs = reverse $ xdg : scanl1 (</>) (splitPath root)-  findFile dirs configFileName >>= \case-    Nothing -> return $ ConfigNotFound dirs-    Just file ->-      either (ConfigParseError file) (ConfigLoaded file)-        <$> Yaml.decodeFileEither file+  let dirs = getParents rootDirAbs ++ [xdg]+  maybe (Left $ ConfigNotFound dirs) Right <$> findFile dirs configFileName+  where+    -- getParents "/a/b/c/" == ["/a/b/c/", "/a/b/", "/a/", "/"]+    getParents dir =+      let parentDir = takeDirectory dir+       in dir : if parentDir == dir then [] else getParents parentDir --- | The result of calling 'loadConfigFile'.-data ConfigFileLoadResult-  = ConfigLoaded FilePath FourmoluConfig-  | ConfigParseError FilePath Yaml.ParseException-  | ConfigNotFound [FilePath]+data ConfigNotFound = ConfigNotFound+  { searchDirs :: [FilePath]+  }   deriving (Show)  -- | Expected file name for YAML config.
src/Ormolu/Diff/ParseResult.hs view
@@ -97,17 +97,21 @@                   `extQ` considerEqual @SourceText                   `extQ` hsDocStringEq                   `extQ` importDeclQualifiedStyleEq-                  `extQ` considerEqual @(LayoutInfo GhcPs)                   `extQ` classDeclCtxEq                   `extQ` derivedTyClsParensEq                   `extQ` considerEqual @EpAnnComments -- ~ XCGRHSs GhcPs                   `extQ` considerEqual @TokenLocation -- in LHs(Uni)Token                   `extQ` considerEqual @EpaLocation+                  `extQ` considerEqual @EpLayout+                  `extQ` considerEqual @[AddEpAnn]+                  `extQ` considerEqual @AnnSig+                  `extQ` considerEqual @HsRuleAnn                   `ext2Q` forLocated                   -- unicode-related-                  `extQ` considerEqual @(HsUniToken "->" "→")-                  `extQ` considerEqual @(HsUniToken "::" "∷")-                  `extQ` considerEqual @(HsLinearArrowTokens GhcPs)+                  `extQ` considerEqual @(EpUniToken "->" "→")+                  `extQ` considerEqual @(EpUniToken "::" "∷")+                  `extQ` considerEqual @EpLinearArrow+                  `extQ` considerEqualVia' compareAnnKeywordId               )               x               y@@ -145,7 +149,10 @@       GenLocated e0 e1 ->       GenericQ ParseResultDiff     forLocated x@(L mspn _) y =-      maybe id appendSpan (cast `ext1Q` (Just . locA) $ mspn) (genericQuery x y)+      maybe id appendSpan (cast `ext1Q` (Just . epAnnLoc) $ mspn) (genericQuery x y)+      where+        epAnnLoc :: EpAnn ann -> SrcSpan+        epAnnLoc = locA     appendSpan :: SrcSpan -> ParseResultDiff -> ParseResultDiff     appendSpan s' d@(Different ss) =       case s' of@@ -165,3 +172,21 @@       (DctSingle _ ty, DctMulti _ [ty']) -> genericQuery ty ty'       (DctMulti _ [ty], DctSingle _ ty') -> genericQuery ty ty'       (x, y) -> genericQuery x y++    compareAnnKeywordId x y =+      let go = curry $ \case+            (AnnCloseB, AnnCloseBU) -> True+            (AnnCloseQ, AnnCloseQU) -> True+            (AnnDarrow, AnnDarrowU) -> True+            (AnnDcolon, AnnDcolonU) -> True+            (AnnForall, AnnForallU) -> True+            (AnnLarrow, AnnLarrowU) -> True+            (AnnOpenB, AnnOpenBU) -> True+            (AnnOpenEQ, AnnOpenEQU) -> True+            (AnnRarrow, AnnRarrowU) -> True+            (Annlarrowtail, AnnlarrowtailU) -> True+            (Annrarrowtail, AnnrarrowtailU) -> True+            (AnnLarrowtail, AnnLarrowtailU) -> True+            (AnnRarrowtail, AnnRarrowtailU) -> True+            (_, _) -> False+       in go x y || go y x || x == y
src/Ormolu/Diff/Text.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QualifiedDo #-}@@ -17,13 +18,15 @@ import Data.Foldable (for_) import Data.IntSet (IntSet) import Data.IntSet qualified as IntSet-import Data.List (foldl') import Data.Maybe (listToMaybe) import Data.Text (Text) import Data.Text qualified as T 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/Fixity/Imports.hs view
@@ -69,10 +69,10 @@  ieToOccNames :: IE GhcPs -> [OccName] ieToOccNames = \case-  IEVar _ (L _ x) -> [occName x]-  IEThingAbs _ (L _ x) -> [occName x]-  IEThingAll _ (L _ x) -> [occName x] -- TODO not quite correct, but how to do better?-  IEThingWith _ (L _ x) _ xs -> occName x : fmap (occName . unLoc) xs+  IEVar _ (L _ x) _ -> [occName x]+  IEThingAbs _ (L _ x) _ -> [occName x]+  IEThingAll _ (L _ x) _ -> [occName x] -- TODO not quite correct, but how to do better?+  IEThingWith _ (L _ x) _ xs _ -> occName x : fmap (occName . unLoc) xs   _ -> []  -- | Apply given module re-exports.
src/Ormolu/Fixity/Parser.hs view
@@ -45,6 +45,17 @@  type Parser = Parsec Void Text +-- TODO Support fixity namespacing?+-- https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0065-type-infix.rst+--+-- Note that currently, our fixity machinery does *not* do any namespacing:+--+--  - https://github.com/tweag/ormolu/pull/994#pullrequestreview-1396958951+--    brought this up in the past+--+--  - https://github.com/tweag/ormolu/pull/1029#issue-1718217029+--    has a concrete example (morley-prelude) where namespacing would matter+ -- | Parse textual representation of 'FixityOverrides'. parseDotOrmolu ::   -- | Location of the file we are parsing (only for parse errors)
src/Ormolu/Imports.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-}@@ -14,7 +15,7 @@ import Data.Char (isAlphaNum) import Data.Foldable (toList) import Data.Function (on)-import Data.List (foldl', nubBy, sortBy, sortOn)+import Data.List (nubBy, sortBy, sortOn) import Data.Map.Strict (Map) import Data.Map.Strict qualified as M import GHC.Data.FastString@@ -25,6 +26,9 @@ import GHC.Types.SourceText import GHC.Types.SrcLoc import Ormolu.Utils (groupBy', notImplemented, separatedByBlank, showOutputable)+#if !MIN_VERSION_base(4,20,0)+import Data.List (foldl')+#endif  -- | Sort, group and normalize imports. --@@ -148,33 +152,34 @@           alter = \case             Nothing -> Just . L new_l $               case new of-                IEThingWith _ n wildcard g ->-                  IEThingWith (Nothing, EpAnnNotUsed) n wildcard (normalizeWNames g)+                IEThingWith x n wildcard g _ ->+                  IEThingWith x n wildcard (normalizeWNames g) Nothing                 other -> other             Just old ->               let f = \case-                    IEVar _ n -> IEVar Nothing n-                    IEThingAbs _ _ -> new-                    IEThingAll _ n -> IEThingAll (Nothing, EpAnnNotUsed) n-                    IEThingWith _ n wildcard g ->+                    IEVar _ n _ -> IEVar Nothing n Nothing+                    IEThingAbs _ _ _ -> new+                    IEThingAll x n _ -> IEThingAll x n Nothing+                    IEThingWith _ n wildcard g _ ->                       case new of-                        IEVar _ _ ->+                        IEVar _ _ _ ->                           error "Ormolu.Imports broken presupposition"-                        IEThingAbs _ _ ->-                          IEThingWith (Nothing, EpAnnNotUsed) n wildcard g-                        IEThingAll _ n' ->-                          IEThingAll (Nothing, EpAnnNotUsed) n'-                        IEThingWith _ n' wildcard' g' ->+                        IEThingAbs x _ _ ->+                          IEThingWith x n wildcard g Nothing+                        IEThingAll x n' _ ->+                          IEThingAll x n' Nothing+                        IEThingWith x n' wildcard' g' _ ->                           let combinedWildcard =                                 case (wildcard, wildcard') of                                   (IEWildcard _, _) -> IEWildcard 0                                   (_, IEWildcard _) -> IEWildcard 0                                   _ -> NoIEWildcard                            in IEThingWith-                                (Nothing, EpAnnNotUsed)+                                x                                 n'                                 combinedWildcard                                 (normalizeWNames (g <> g'))+                                Nothing                         IEModuleContents _ _ -> notImplemented "IEModuleContents"                         IEGroup NoExtField _ _ -> notImplemented "IEGroup"                         IEDoc NoExtField _ -> notImplemented "IEDoc"@@ -197,10 +202,10 @@ -- | Project @'IEWrappedName' 'GhcPs'@ from @'IE' 'GhcPs'@. getIewn :: IE GhcPs -> IEWrappedNameOrd getIewn = \case-  IEVar _ x -> IEWrappedNameOrd (unLoc x)-  IEThingAbs _ x -> IEWrappedNameOrd (unLoc x)-  IEThingAll _ x -> IEWrappedNameOrd (unLoc x)-  IEThingWith _ x _ _ -> IEWrappedNameOrd (unLoc x)+  IEVar _ x _ -> IEWrappedNameOrd (unLoc x)+  IEThingAbs _ x _ -> IEWrappedNameOrd (unLoc x)+  IEThingAll _ x _ -> IEWrappedNameOrd (unLoc x)+  IEThingWith _ x _ _ _ -> IEWrappedNameOrd (unLoc x)   IEModuleContents _ _ -> notImplemented "IEModuleContents"   IEGroup NoExtField _ _ -> notImplemented "IEGroup"   IEDoc NoExtField _ -> notImplemented "IEDoc"
src/Ormolu/Parser.hs view
@@ -219,7 +219,7 @@         | ConstraintNever <- constraintParens -> [inner]         | otherwise -> [x]       [x@(L lx _)]-        | ConstraintAlways <- constraintParens -> [L lx (HsParTy EpAnnNotUsed x)]+        | ConstraintAlways <- constraintParens -> [L lx (HsParTy noAnn x)]         | otherwise -> [x]       xs -> xs     constraintParens = runIdentity (poSingleConstraintParens cfgPrinterOpts)
src/Ormolu/Parser/CommentStream.hs view
@@ -223,7 +223,7 @@  -- | Extract @'RealLocated' 'Text'@ from 'GHC.LEpaComment'. unAnnotationComment :: GHC.LEpaComment -> Maybe (RealLocated Text)-unAnnotationComment (L (GHC.Anchor anchor _) (GHC.EpaComment eck _)) =+unAnnotationComment (L epaLoc (GHC.EpaComment eck _)) =   case eck of     GHC.EpaDocComment s ->       let trigger = case s of@@ -239,9 +239,10 @@         "---" -> s         _ -> insertAt " " s 3     GHC.EpaBlockComment s -> mkL (T.pack s)-    GHC.EpaEofComment -> Nothing   where-    mkL = Just . L anchor+    mkL = case epaLoc of+      GHC.EpaSpan (RealSrcSpan s _) -> Just . L s+      _ -> const Nothing     insertAt x xs n = T.take (n - 1) xs <> x <> T.drop (n - 1) xs     haddock mtrigger =       mkL . dashPrefix . escapeHaddockTriggers . (trigger <>) <=< dropBlank
src/Ormolu/Printer/Combinators.hs view
@@ -11,6 +11,7 @@     runR,     getEnclosingSpan,     getEnclosingSpanWhere,+    getEnclosingComments,     isExtensionEnabled,      -- * Combinators@@ -99,11 +100,11 @@ import Data.Text (Text) import GHC.Data.Strict qualified as Strict import GHC.LanguageExtensions.Type+import GHC.Parser.Annotation hiding (IsUnicodeSyntax (..)) import GHC.Types.SrcLoc hiding (spans) import Ormolu.Config import Ormolu.Printer.Comments import Ormolu.Printer.Internal-import Ormolu.Utils (HasSrcSpan (..), getLoc')  ---------------------------------------------------------------------------- -- Basic@@ -123,13 +124,13 @@ -- 'Located' wrapper, it should be “discharged” with a corresponding -- 'located' invocation. located ::-  (HasSrcSpan l) =>+  (HasLoc l) =>   -- | Thing to enter   GenLocated l a ->   -- | How to render inner value   (a -> R ()) ->   R ()-located (L l' a) f = case loc' l' of+located (L l' a) f = case locA l' of   UnhelpfulSpan _ -> f a   RealSrcSpan l _ -> do     spitPrecedingComments l@@ -141,7 +142,7 @@ -- virtual elements at the start and end of the source span to prevent comments -- from "floating out". encloseLocated ::-  (HasSrcSpan l) =>+  (HasLoc l) =>   GenLocated l [a] ->   ([a] -> R ()) ->   R ()@@ -150,13 +151,13 @@   f a   when (null a) $ located (L endSpan ()) pure   where-    l = getLoc' la+    l = locA la     (startLoc, endLoc) = (srcSpanStart l, srcSpanEnd l)     (startSpan, endSpan) = (mkSrcSpan startLoc startLoc, mkSrcSpan endLoc endLoc)  -- | A version of 'located' with arguments flipped. located' ::-  (HasSrcSpan l) =>+  (HasLoc l) =>   -- | How to render inner value   (a -> R ()) ->   -- | Thing to enter
src/Ormolu/Printer/Internal.hs view
@@ -42,6 +42,7 @@     trimSpanStream,     nextEltSpan,     popComment,+    getEnclosingComments,     getEnclosingSpan,     getEnclosingSpanWhere,     withEnclosingSpan,@@ -64,6 +65,7 @@ import Control.Monad.State.Strict import Data.Bool (bool) import Data.Coerce+import Data.Functor ((<&>)) import Data.Functor.Identity (runIdentity) import Data.List (find) import Data.Maybe (listToMaybe)@@ -537,6 +539,16 @@       modify $ \sc -> sc {scCommentStream = CommentStream xs}       return $ Just x     _ -> return Nothing++-- | Get the comments contained in the enclosing span.+getEnclosingComments :: R [LComment]+getEnclosingComments = do+  isEnclosed <-+    getEnclosingSpan <&> \case+      Just enclSpan -> containsSpan enclSpan+      Nothing -> const False+  CommentStream cstream <- R $ gets scCommentStream+  pure $ takeWhile (isEnclosed . getLoc) cstream  -- | Get the immediately enclosing 'RealSrcSpan'. getEnclosingSpan :: R (Maybe RealSrcSpan)
src/Ormolu/Printer/Meat/Common.hs view
@@ -13,6 +13,7 @@     p_hsDoc,     p_hsDoc',     p_sourceText,+    p_namespaceSpec,   ) where @@ -20,6 +21,7 @@ import Data.Foldable (traverse_) import Data.Text qualified as T import GHC.Data.FastString+import GHC.Hs.Binds import GHC.Hs.Doc import GHC.Hs.Extension (GhcPs) import GHC.Hs.ImpExp@@ -67,18 +69,16 @@ p_rdrName :: LocatedN RdrName -> R () p_rdrName l = located l $ \x -> do   unboxedSums <- isExtensionEnabled UnboxedSums-  let wrapper = \case-        EpAnn {anns} -> case anns of-          NameAnnQuote {nann_quoted} -> tickPrefix . wrapper (ann nann_quoted)-          NameAnn {nann_adornment = NameParens} ->-            parens N . handleUnboxedSumsAndHashInteraction-          NameAnn {nann_adornment = NameBackquotes} -> backticks-          -- whether the `->` identifier is parenthesized-          NameAnnRArrow {nann_mopen = Just _} -> parens N-          -- special case for unboxed unit tuples-          NameAnnOnly {nann_adornment = NameParensHash} -> const $ txt "(# #)"-          _ -> id-        EpAnnNotUsed -> id+  let wrapper EpAnn {anns} = case anns of+        NameAnnQuote {nann_quoted} -> tickPrefix . wrapper nann_quoted+        NameAnn {nann_adornment = NameParens} ->+          parens N . handleUnboxedSumsAndHashInteraction+        NameAnn {nann_adornment = NameBackquotes} -> backticks+        -- whether the `->` identifier is parenthesized+        NameAnnRArrow {nann_mopen = Just _} -> parens N+        -- special case for unboxed unit tuples+        NameAnnOnly {nann_adornment = NameParensHash} -> const $ txt "(# #)"+        _ -> id        -- When UnboxedSums is enabled, `(#` is a single lexeme, so we have to       -- insert spaces when we have a parenthesized operator starting with `#`.@@ -89,7 +89,7 @@             \y -> space *> y <* space         | otherwise = id -  wrapper (ann . getLoc $ l) $ case x of+  wrapper (getLoc l) $ case x of     Unqual occName ->       atom occName     Qual mname occName ->@@ -252,3 +252,9 @@ p_sourceText = \case   NoSourceText -> pure ()   SourceText s -> atom @FastString s++p_namespaceSpec :: NamespaceSpecifier -> R ()+p_namespaceSpec = \case+  NoNamespaceSpecifier -> pure ()+  TypeNamespaceSpecifier _ -> txt "type" *> space+  DataNamespaceSpecifier _ -> txt "data" *> space
src/Ormolu/Printer/Meat/Declaration.hs view
@@ -260,7 +260,7 @@ pattern AnnValuePragma n <- AnnD _ (HsAnnotation _ (ValueAnnProvenance (L _ n)) _) pattern Pattern n <- ValD _ (PatSynBind _ (PSB _ (L _ n) _ _ _)) pattern DataDeclaration n <- TyClD _ (DataDecl _ (L _ n) _ _ _)-pattern ClassDeclaration n <- TyClD _ (ClassDecl _ _ _ (L _ n) _ _ _ _ _ _ _ _)+pattern ClassDeclaration n <- TyClD _ (ClassDecl _ _ (L _ n) _ _ _ _ _ _ _ _) pattern KindSignature n <- KindSigD _ (StandaloneKindSig _ (L _ n) _) pattern FamilyDeclaration n <- TyClD _ (FamDecl _ (FamilyDecl _ _ _ (L _ n) _ _ _ _)) pattern TypeSynonym n <- TyClD _ (SynDecl _ (L _ n) _ _ _)@@ -296,7 +296,7 @@  funRdrNames :: HsDecl GhcPs -> Maybe [RdrName] funRdrNames (ValD _ (FunBind _ (L _ n) _)) = Just [n]-funRdrNames (ValD _ (PatBind _ (L _ n) _)) = Just $ patBindNames n+funRdrNames (ValD _ (PatBind _ (L _ n) _ _)) = Just $ patBindNames n funRdrNames _ = Nothing  patSigRdrNames :: HsDecl GhcPs -> Maybe [RdrName]@@ -315,9 +315,9 @@ patBindNames (WildPat _) = [] patBindNames (LazyPat _ (L _ p)) = patBindNames p patBindNames (BangPat _ (L _ p)) = patBindNames p-patBindNames (ParPat _ _ (L _ p) _) = patBindNames p+patBindNames (ParPat _ (L _ p)) = patBindNames p patBindNames (ListPat _ ps) = concatMap (patBindNames . unLoc) ps-patBindNames (AsPat _ (L _ n) _ (L _ p)) = n : patBindNames p+patBindNames (AsPat _ (L _ n) (L _ p)) = n : patBindNames p patBindNames (SumPat _ (L _ p) _ _) = patBindNames p patBindNames (ViewPat _ _ (L _ p)) = patBindNames p patBindNames (SplicePat _ _) = []@@ -326,3 +326,5 @@ patBindNames (NPat _ _ _ _) = [] patBindNames (NPlusKPat _ (L _ n) _ _ _ _) = [n] patBindNames (ConPat _ _ d) = concatMap (patBindNames . unLoc) (hsConPatArgs d)+patBindNames (EmbTyPat _ _) = []+patBindNames (InvisPat _ _) = []
src/Ormolu/Printer/Meat/Declaration/Data.hs view
@@ -13,7 +13,7 @@ import Control.Monad import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE-import Data.Maybe (isJust, maybeToList)+import Data.Maybe (isJust, mapMaybe, maybeToList) import Data.Text qualified as Text import Data.Void import GHC.Data.Strict qualified as Strict@@ -141,8 +141,8 @@             <> conArgsSpans           where             conArgsSpans = case con_g_args of-              PrefixConGADT xs -> getLocA . hsScaledThing <$> xs-              RecConGADT x _ -> [getLocA x]+              PrefixConGADT NoExtField xs -> getLocA . hsScaledThing <$> xs+              RecConGADT _ x -> [getLocA x]     switchLayout conDeclSpn $ do       let c :| cs = con_names       p_rdrName c@@ -151,23 +151,24 @@         sep commaDel p_rdrName cs       inci $ do         let conTy = case con_g_args of-              PrefixConGADT xs ->-                let go (HsScaled a b) t = addCLocAA t b (HsFunTy EpAnnNotUsed a b t)+              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 _ ->-                addCLocAA r con_res_ty $+              RecConGADT _ r ->+                addCLocA r con_res_ty $                   HsFunTy-                    EpAnnNotUsed-                    (HsUnrestrictedArrow noHsUniTok)-                    (la2la $ HsRecTy EpAnnNotUsed <$> r)+                    NoExtField+                    (HsUnrestrictedArrow noAnn)+                    (la2la $ HsRecTy noAnn <$> r)                     con_res_ty             qualTy = case con_mb_cxt of               Nothing -> conTy               Just qs ->-                addCLocAA qs conTy $+                addCLocA qs conTy $                   HsQualTy NoExtField qs conTy+            quantifiedTy :: LHsType GhcPs             quantifiedTy =-              addCLocAA con_bndrs qualTy $+              addCLocA con_bndrs qualTy $                 hsOuterTyVarBndrsToHsType (unLoc con_bndrs) qualTy         startTypeAnnotationDecl quantifiedTy id p_hsType   ConDeclH98 {..} -> do@@ -175,7 +176,8 @@     let conNameSpn = getLocA con_name         conNameWithContextSpn =           [ RealSrcSpan real Strict.Nothing-            | Just (EpaSpan real _) <- matchAddEpAnn AnnForall <$> epAnnAnns con_ext+            | EpaSpan (RealSrcSpan real _) <-+                mapMaybe (matchAddEpAnn AnnForall) con_ext           ]             <> fmap getLocA con_ex_tvs             <> maybeToList (fmap getLocA con_mb_cxt)
src/Ormolu/Printer/Meat/Declaration/Foreign.hs view
@@ -55,7 +55,7 @@   space   located cCallConv atom   -- Need to check for 'noLoc' for the 'safe' annotation-  when (isGoodSrcSpan $ getLoc safety) (space >> atom safety)+  when (isGoodSrcSpan $ getLocA safety) (space >> atom safety)   space   located sourceText p_sourceText 
src/Ormolu/Printer/Meat/Declaration/Instance.hs view
@@ -15,6 +15,7 @@ import Data.Foldable import Data.Function (on) import Data.List (sortBy)+import Data.Maybe (maybeToList) import GHC.Hs import GHC.Types.Basic import GHC.Types.SrcLoc@@ -23,13 +24,17 @@ import {-# SOURCE #-} Ormolu.Printer.Meat.Declaration import Ormolu.Printer.Meat.Declaration.Data import Ormolu.Printer.Meat.Declaration.TypeFamily+import Ormolu.Printer.Meat.Declaration.Warning import Ormolu.Printer.Meat.Type  p_standaloneDerivDecl :: DerivDecl GhcPs -> R ()-p_standaloneDerivDecl DerivDecl {..} = do+p_standaloneDerivDecl DerivDecl {deriv_ext = (mWarnTxt, _), ..} = do   let typesAfterInstance = located (hswc_body deriv_type) p_hsSigType       instTypes toIndent = inci $ do         txt "instance"+        for_ mWarnTxt $ \warnTxt -> do+          breakpoint+          located warnTxt p_warningTxt         breakpoint         match_overlap_mode deriv_overlap_mode breakpoint         inciIf toIndent typesAfterInstance@@ -56,7 +61,7 @@         instTypes True  p_clsInstDecl :: ClsInstDecl GhcPs -> R ()-p_clsInstDecl ClsInstDecl {..} = do+p_clsInstDecl ClsInstDecl {cid_ext = (mWarnTxt, _, _), ..} = do   txt "instance"   -- GHC's AST does not necessarily store each kind of element in source   -- location order. This happens because different declarations are stored in@@ -74,9 +79,12 @@           <$> cid_datafam_insts       allDecls =         snd <$> sortBy (leftmost_smallest `on` fst) (sigs <> vals <> tyFamInsts <> dataFamInsts)-  located cid_poly_ty $ \sigTy -> do+  switchLayout (maybeToList (getLocA <$> mWarnTxt) <> [getLocA cid_poly_ty]) $ do+    for_ mWarnTxt $ \warnTxt -> do+      breakpoint+      located warnTxt p_warningTxt     breakpoint-    inci $ do+    located cid_poly_ty $ \sigTy -> inci $ do       match_overlap_mode cid_overlap_mode breakpoint       p_hsSigType sigTy       unless (null allDecls) $ do
src/Ormolu/Printer/Meat/Declaration/OpTree.hs view
@@ -35,7 +35,6 @@   ) import Ormolu.Printer.Meat.Type (p_hsType) import Ormolu.Printer.Operators-import Ormolu.Utils (HasSrcSpan)  -- | Extract the operator name of the specified 'HsExpr' if this expression -- corresponds to an operator.@@ -50,7 +49,7 @@  -- | Decide if the operands of an operator chain should be hanging. opBranchPlacement ::-  (HasSrcSpan l) =>+  (HasLoc l) =>   -- | Placer function for nodes   (ty -> Placement) ->   -- | first expression of the chain
src/Ormolu/Printer/Meat/Declaration/Signature.hs view
@@ -88,7 +88,7 @@   FixitySig GhcPs ->   R () p_fixSig = \case-  FixitySig NoExtField names (Fixity _ n dir) -> do+  FixitySig namespace names (Fixity _ n dir) -> do     txt $ case dir of       InfixL -> "infixl"       InfixR -> "infixr"@@ -96,6 +96,7 @@     space     atom n     space+    p_namespaceSpec namespace     sitcc $ sep commaDel p_rdrName names  p_inlineSig ::@@ -193,12 +194,12 @@  p_completeSig ::   -- | Constructors\/patterns-  Located [LocatedN RdrName] ->+  [LIdP GhcPs] ->   -- | Type   Maybe (LocatedN RdrName) ->   R ()-p_completeSig cs' mty =-  located cs' $ \cs ->+p_completeSig cs mty =+  switchLayout (getLocA <$> cs) $     pragma "COMPLETE" . inci $ do       sep commaDel p_rdrName cs       forM_ mty $ \ty -> do
src/Ormolu/Printer/Meat/Declaration/Value.hs view
@@ -72,7 +72,8 @@ p_valDecl :: HsBind GhcPs -> R () p_valDecl = \case   FunBind _ funId funMatches -> p_funBind funId funMatches-  PatBind _ pat grhss -> p_match PatternBind False NoSrcStrict [pat] grhss+  PatBind _ pat multAnn grhss ->+    p_match PatternBind False multAnn NoSrcStrict [pat] grhss   VarBind {} -> notImplemented "VarBinds" -- introduced by the type checker   PatSynBind _ psb -> p_patSynBind psb @@ -89,7 +90,7 @@ p_matchGroup = p_matchGroup' exprPlacement p_hsExpr  p_matchGroup' ::-  ( Anno (GRHS GhcPs (LocatedA body)) ~ SrcAnn NoEpAnns,+  ( Anno (GRHS GhcPs (LocatedA body)) ~ EpAnnCO,     Anno (Match GhcPs (LocatedA body)) ~ SrcSpanAnnA   ) =>   -- | How to get body placement@@ -119,6 +120,7 @@         render         (adjustMatchGroupStyle m style)         (isInfixMatch m)+        (HsNoMultAnn NoExtField)         (matchStrictness m)         m_pats         m_grhss@@ -148,6 +150,8 @@   MatchGroupStyle ->   -- | Is this an infix match?   Bool ->+  -- | Multiplicity annotation+  HsMultAnn GhcPs ->   -- | Strictness prefix (FunBind)   SrcStrictness ->   -- | Argument patterns@@ -158,7 +162,7 @@ p_match = p_match' exprPlacement p_hsExpr  p_match' ::-  (Anno (GRHS GhcPs (LocatedA body)) ~ SrcAnn NoEpAnns) =>+  (Anno (GRHS GhcPs (LocatedA body)) ~ EpAnnCO) =>   -- | How to get body placement   (body -> Placement) ->   -- | How to print body@@ -167,6 +171,8 @@   MatchGroupStyle ->   -- | Is this an infix match?   Bool ->+  -- | Multiplicity annotation+  HsMultAnn GhcPs ->   -- | Strictness prefix (FunBind)   SrcStrictness ->   -- | Argument patterns@@ -174,7 +180,7 @@   -- | Equations   GRHSs GhcPs (LocatedA body) ->   R ()-p_match' placer render style isInfix strictness m_pats GRHSs {..} = do+p_match' placer render style isInfix multAnn strictness m_pats GRHSs {..} = do   -- Normally, since patterns may be placed in a multi-line layout, it is   -- necessary to bump indentation for the pattern group so it's more   -- indented than function name. This in turn means that indentation for@@ -182,6 +188,13 @@   -- 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.+  case multAnn of+    HsNoMultAnn NoExtField -> pure ()+    HsPct1Ann _ -> txt "%1" *> space+    HsMultAnn _ ty -> do+      txt "%"+      located ty p_hsType+      space   case strictness of     NoSrcStrict -> return ()     SrcStrict -> txt "!"@@ -213,6 +226,7 @@                   LazyPat _ _ -> True                   BangPat _ _ -> True                   SplicePat _ _ -> True+                  InvisPat _ _ -> True                   _ -> False             txt "\\"             when needsSpace space@@ -363,15 +377,13 @@     located cmd (p_hsCmd' Applicand s)     breakpoint     inci $ located expr p_hsExpr-  HsCmdLam _ mgroup -> p_matchGroup' cmdPlacement p_hsCmd Lambda mgroup-  HsCmdPar _ _ c _ -> parens N $ sitcc $ located c p_hsCmd+  HsCmdLam _ variant mgroup -> p_lam isApp 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-  HsCmdLamCase _ variant mgroup ->-    p_lamcase isApp variant cmdPlacement p_hsCmd mgroup   HsCmdIf anns _ if' then' else' ->     p_if cmdPlacement p_hsCmd anns if' then' else'-  HsCmdLet _ letToken localBinds _ c ->+  HsCmdLet (letToken, _) localBinds c ->     p_let (s == S) p_hsCmd letToken localBinds c   HsCmdDo _ es -> do     txt "do"@@ -442,7 +454,6 @@     let letLoc =           fmap (\(AddEpAnn _ loc) -> loc)             . find (\(AddEpAnn ann _) -> ann == AnnLet)-            . epAnnAnns             $ epAnnLet     p_let' True letLoc binds Nothing   ParStmt {} ->@@ -559,11 +570,10 @@     -- of p_hsLocalBinds). Hence, we introduce a manual Located as we     -- depend on the layout being correctly set.     pseudoLocated = \case-      EpAnn {anns = AnnList {al_anchor = Just Anchor {anchor}}}-        | let sp = RealSrcSpan anchor Strict.Nothing,-          -- excluding cases where there are no bindings-          not $ isZeroWidthSpan sp ->-            located (L sp ()) . const+      EpAnn {anns = AnnList {al_anchor}}+        | -- excluding cases where there are no bindings+          not $ isZeroWidthSpan (locA al_anchor) ->+            located (L al_anchor ()) . const       _ -> id  p_ldotFieldOcc :: XRec GhcPs (DotFieldOcc GhcPs) -> R ()@@ -577,7 +587,7 @@ p_fieldOcc FieldOcc {..} = p_rdrName foLabel  p_hsFieldBind ::-  (lhs ~ GenLocated l a, HasSrcSpan l) =>+  (lhs ~ GenLocated l a, HasLoc l) =>   (lhs -> R ()) ->   HsFieldBind lhs (LHsExpr GhcPs) ->   R ()@@ -587,7 +597,7 @@     space     equals     let placement =-          if onTheSameLine (getLoc' hfbLHS) (getLocA hfbRHS)+          if onTheSameLine (getLocA hfbLHS) (getLocA hfbRHS)             then exprPlacement (unLoc hfbRHS)             else Normal     placeHanging placement (located hfbRHS p_hsExpr)@@ -626,10 +636,8 @@       HsString (SourceText stxt) _ -> p_stringLit stxt       HsStringPrim (SourceText stxt) _ -> p_stringLit stxt       r -> atom r-  HsLam _ mgroup ->-    p_matchGroup Lambda mgroup-  HsLamCase _ variant mgroup ->-    p_lamcase isApp variant exprPlacement p_hsExpr mgroup+  HsLam _ variant mgroup ->+    p_lam isApp 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@@ -687,7 +695,7 @@           sep breakpoint (located' p_hsExpr) initp         placeHanging placement $           located lastp p_hsExpr-  HsAppType _ e _ a -> do+  HsAppType _ e a -> do     located e p_hsExpr     breakpoint     inci $ do@@ -716,7 +724,7 @@     -- negated literals, as `- 1` and `-1` have differing AST.     when (negativeLiterals && isLiteral) space     located e p_hsExpr-  HsPar _ _ e _ ->+  HsPar _ e ->     parens s $ sitcc (located e (dontUseBraces . p_hsExpr))   SectionL _ x op -> do     located x p_hsExpr@@ -759,7 +767,7 @@     txt "if"     breakpoint     inciApplicand isApp $ sep newline (located' (p_grhs RightArrow)) guards-  HsLet _ letToken localBinds _ e ->+  HsLet (letToken, _) localBinds e ->     p_let (s == S) p_hsExpr letToken localBinds e   HsDo _ doFlavor es -> do     let doBody moduleName header = do@@ -857,7 +865,7 @@     located expr p_hsExpr     breakpoint'     txt "||]"-  HsUntypedBracket epAnn x -> p_hsQuote epAnn x+  HsUntypedBracket anns x -> p_hsQuote anns x   HsTypedSplice _ expr -> p_hsSpliceTH True expr DollarSplice   HsUntypedSplice _ untySplice -> p_hsUntypedSplice DollarSplice untySplice   HsProc _ p e -> do@@ -881,6 +889,10 @@       breakpoint       let inciIfS = case s of N -> id; S -> inci       inciIfS $ located x p_hsExpr+  HsEmbTy _ HsWC {hswc_body} -> do+    txt "type"+    space+    located hswc_body p_hsType  p_patSynBind :: PatSynBind GhcPs GhcPs -> R () p_patSynBind PSB {..} = do@@ -942,7 +954,7 @@       inci (rhs conSpans)  p_case ::-  ( Anno (GRHS GhcPs (LocatedA body)) ~ SrcAnn NoEpAnns,+  ( Anno (GRHS GhcPs (LocatedA body)) ~ EpAnnCO,     Anno (Match GhcPs (LocatedA body)) ~ SrcSpanAnnA   ) =>   IsApplicand ->@@ -964,13 +976,13 @@   breakpoint   inciApplicand isApp (p_matchGroup' placer render Case mgroup) -p_lamcase ::-  ( Anno (GRHS GhcPs (LocatedA body)) ~ SrcAnn NoEpAnns,+p_lam ::+  ( Anno (GRHS GhcPs (LocatedA body)) ~ EpAnnCO,     Anno (Match GhcPs (LocatedA body)) ~ SrcSpanAnnA   ) =>   IsApplicand ->-  -- | Variant (@\\case@ or @\\cases@)-  LamCaseVariant ->+  -- | Variant (@\\@ or @\\case@ or @\\cases@)+  HsLamVariant ->   -- | Placer   (body -> Placement) ->   -- | Render@@ -978,12 +990,19 @@   -- | Expression   MatchGroup GhcPs (LocatedA body) ->   R ()-p_lamcase isApp variant placer render mgroup = do-  txt $ case variant of-    LamCase -> "\\case"-    LamCases -> "\\cases"-  breakpoint-  inciApplicand isApp (p_matchGroup' placer render LambdaCase mgroup)+p_lam isApp variant placer render mgroup = do+  let mCaseTxt = case variant of+        LamSingle -> Nothing+        LamCase -> Just "\\case"+        LamCases -> Just "\\cases"+      mgs = if isJust mCaseTxt then LambdaCase else Lambda+      pMatchGroup = p_matchGroup' placer render mgs mgroup+  case mCaseTxt of+    Nothing -> pMatchGroup+    Just caseTxt -> do+      txt caseTxt+      breakpoint+      inciApplicand isApp pMatchGroup  p_if ::   -- | Placer@@ -991,7 +1010,7 @@   -- | Render   (body -> R ()) ->   -- | Annotations-  EpAnn AnnsIf ->+  AnnsIf ->   -- | If   LHsExpr GhcPs ->   -- | Then@@ -999,11 +1018,30 @@   -- | Else   LocatedA body ->   R ()-p_if placer render epAnn if' then' else' = do+p_if placer render anns if' then' else' = do   txt "if"   space   located if' p_hsExpr   breakpoint+  commentSpans <- fmap getLoc <$> getEnclosingComments+  let (thenSpan, elseSpan) = (locA aiThen, locA aiElse)+        where+          AnnsIf {aiThen, aiElse} = anns++      locatedToken tokenSpan token =+        located (L tokenSpan ()) $ \_ -> txt token++      betweenSpans spanA spanB s = spanA < s && s < spanB++      placeHangingLocated tokenSpan bodyLoc@(L _ body) = do+        let bodySpan = getLocA bodyLoc+            hasComments = fromMaybe False $ do+              tokenRealSpan <- srcSpanToRealSrcSpan tokenSpan+              bodyRealSpan <- srcSpanToRealSrcSpan bodySpan+              pure $ any (betweenSpans tokenRealSpan bodyRealSpan) commentSpans+            placement = if hasComments then Normal else placer body+        switchLayout [tokenSpan, bodySpan] $+          placeHanging placement (located bodyLoc render)   inci $ do     locatedToken thenSpan "then"     space@@ -1012,51 +1050,23 @@     locatedToken elseSpan "else"     space     placeHangingLocated elseSpan else'-  where-    (thenSpan, elseSpan, commentSpans) =-      case epAnn of-        EpAnn {anns = AnnsIf {aiThen, aiElse}, comments} ->-          ( loc' $ epaLocationRealSrcSpan aiThen,-            loc' $ epaLocationRealSrcSpan aiElse,-            map (anchor . getLoc) $-              case comments of-                EpaComments cs -> cs-                EpaCommentsBalanced pre post -> pre <> post-          )-        EpAnnNotUsed ->-          (noSrcSpan, noSrcSpan, []) -    locatedToken tokenSpan token =-      located (L tokenSpan ()) $ \_ -> txt token--    betweenSpans spanA spanB s = spanA < s && s < spanB--    placeHangingLocated tokenSpan bodyLoc@(L _ body) = do-      let bodySpan = getLoc' bodyLoc-          hasComments = fromMaybe False $ do-            tokenRealSpan <- srcSpanToRealSrcSpan tokenSpan-            bodyRealSpan <- srcSpanToRealSrcSpan bodySpan-            pure $ any (betweenSpans tokenRealSpan bodyRealSpan) commentSpans-          placement = if hasComments then Normal else placer body-      switchLayout [tokenSpan, bodySpan] $-        placeHanging placement (located bodyLoc render)- p_let ::   -- | True if in do-block   Bool ->   -- | Render   (body -> R ()) ->   -- | Annotation for the `let` block-  LHsToken "let" GhcPs ->+  EpToken "let" ->   HsLocalBinds GhcPs ->   LocatedA body ->   R () p_let inDo render letToken localBinds e = p_let' inDo letLoc localBinds $ Just (located e render)   where     letLoc =-      case getLoc letToken of-        TokenLoc loc -> Just loc-        NoTokenLoc -> Nothing+      case letToken of+        EpTok loc -> Just loc+        NoEpTok -> Nothing  p_let' ::   -- | True if in do-block@@ -1132,9 +1142,10 @@       HsValBinds epanns _ -> Just epanns       HsIPBinds epanns _ -> Just epanns       EmptyLocalBinds _ -> Nothing-    epAnnsStartLine = \case-      EpAnn {entry} -> Just (srcSpanStartLine . anchor $ entry)-      EpAnnNotUsed -> Nothing+    epAnnsStartLine epAnn =+      case entry epAnn of+        EpaSpan (RealSrcSpan r _) -> Just $ srcSpanStartLine r+        _ -> Nothing  p_pat :: Pat GhcPs -> R () p_pat = \case@@ -1143,11 +1154,11 @@   LazyPat _ pat -> do     txt "~"     located pat p_pat-  AsPat _ name _ pat -> do+  AsPat _ name pat -> do     p_rdrName name     txt "@"     located pat p_pat-  ParPat _ _ pat _ ->+  ParPat _ pat ->     located pat (parens S . sitcc . p_pat)   BangPat _ pat -> do     txt "!"@@ -1212,12 +1223,17 @@   SigPat _ pat HsPS {..} -> do     located pat p_pat     p_typeAscription (lhsTypeToSigType hsps_body)+  EmbTyPat _ (HsTP _ ty) -> do+    txt "type"+    space+    located ty p_hsType+  InvisPat _ tyPat -> p_tyPat tyPat -p_hsPatSigType :: HsPatSigType GhcPs -> R ()-p_hsPatSigType (HsPS _ ty) = txt "@" *> located ty p_hsType+p_tyPat :: HsTyPat GhcPs -> R ()+p_tyPat (HsTP _ ty) = txt "@" *> located ty p_hsType  p_hsConPatTyArg :: HsConPatTyArg GhcPs -> R ()-p_hsConPatTyArg (HsConPatTyArg _ patSigTy) = p_hsPatSigType patSigTy+p_hsConPatTyArg (HsConPatTyArg _ patSigTy) = p_tyPat patSigTy  p_pat_hsFieldBind :: HsRecField GhcPs (LPat GhcPs) -> R () p_pat_hsFieldBind HsFieldBind {..} = do@@ -1272,11 +1288,11 @@   where     decoSymbol = if isTyped then "$$" else "$" -p_hsQuote :: EpAnn [AddEpAnn] -> HsQuote GhcPs -> R ()-p_hsQuote epAnn = \case+p_hsQuote :: [AddEpAnn] -> HsQuote GhcPs -> R ()+p_hsQuote anns = \case   ExpBr _ expr -> do     let name-          | any isJust (matchAddEpAnn AnnOpenEQ <$> epAnnAnns epAnn) = ""+          | any (isJust . matchAddEpAnn AnnOpenEQ) anns = ""           | otherwise = "e"     quote name (located expr p_hsExpr)   PatBr _ pat -> located pat (quote "p" . p_pat)@@ -1387,10 +1403,9 @@ -- | Determine placement of a given command. cmdPlacement :: HsCmd GhcPs -> Placement cmdPlacement = \case-  HsCmdLam _ _ -> Hanging-  HsCmdCase _ _ _ -> Hanging-  HsCmdLamCase _ _ _ -> Hanging-  HsCmdDo _ _ -> Hanging+  HsCmdLam {} -> Hanging+  HsCmdCase {} -> Hanging+  HsCmdDo {} -> Hanging   _ -> Normal  -- | Determine placement of a top level command.@@ -1401,12 +1416,14 @@ exprPlacement :: HsExpr GhcPs -> Placement exprPlacement = \case   -- Only hang lambdas with single line parameter lists-  HsLam _ mg -> case mg of-    MG _ (L _ [L _ (Match _ _ (x : xs) _)])-      | isOneLineSpan (combineSrcSpans' $ fmap getLocA (x :| xs)) ->-          Hanging-    _ -> Normal-  HsLamCase _ _ _ -> Hanging+  HsLam _ variant mg -> case variant of+    LamSingle -> case mg of+      MG _ (L _ [L _ (Match _ _ (x : xs) _)])+        | isOneLineSpan (combineSrcSpans' $ fmap getLocA (x :| xs)) ->+            Hanging+      _ -> Normal+    LamCase -> Hanging+    LamCases -> Hanging   HsCase _ _ _ -> Hanging   HsDo _ (DoExpr _) _ -> Hanging   HsDo _ (MDoExpr _) _ -> Hanging
src/Ormolu/Printer/Meat/Declaration/Warning.hs view
@@ -12,7 +12,6 @@ import Data.Text (Text) import Data.Text qualified as T import GHC.Hs-import GHC.Types.Name.Reader import GHC.Types.SourceText import GHC.Types.SrcLoc import GHC.Unit.Module.Warnings@@ -25,24 +24,21 @@   traverse_ (located' p_warnDecl) warnings  p_warnDecl :: WarnDecl GhcPs -> R ()-p_warnDecl (Warning _ functions warningTxt) =-  p_topLevelWarning functions warningTxt--p_warningTxt :: WarningTxt GhcPs -> R ()-p_warningTxt wtxt = do-  let (pragmaText, lits) = warningText wtxt-  inci $ pragma pragmaText $ inci $ p_lits lits--p_topLevelWarning :: [LocatedN RdrName] -> WarningTxt GhcPs -> R ()-p_topLevelWarning fnames wtxt = do+p_warnDecl (Warning (namespace, _) fnames wtxt) = do   let (pragmaText, lits) = warningText wtxt-  switchLayout (fmap getLocA fnames ++ fmap getLoc lits) $+  switchLayout (fmap getLocA fnames ++ fmap getLocA lits) $     pragma pragmaText . inci $ do+      p_namespaceSpec namespace       sep commaDel p_rdrName fnames       breakpoint       p_lits lits -warningText :: WarningTxt GhcPs -> (Text, [Located StringLiteral])+p_warningTxt :: WarningTxt GhcPs -> R ()+p_warningTxt wtxt = do+  let (pragmaText, lits) = warningText wtxt+  inci $ pragma pragmaText $ inci $ p_lits lits++warningText :: WarningTxt GhcPs -> (Text, [LocatedE StringLiteral]) warningText = \case   WarningTxt mcat _ lits -> ("WARNING" <> T.pack cat, fmap hsDocString <$> lits)     where@@ -52,7 +48,7 @@         Nothing -> ""   DeprecatedTxt _ lits -> ("DEPRECATED", fmap hsDocString <$> lits) -p_lits :: [Located StringLiteral] -> R ()+p_lits :: [LocatedE StringLiteral] -> R () p_lits = \case   [l] -> atom l   ls -> brackets N $ sep commaDel atom ls
src/Ormolu/Printer/Meat/ImportExport.hs view
@@ -10,7 +10,7 @@ where  import Control.Monad-import Data.Foldable (for_)+import Data.Foldable (for_, traverse_) import Data.List (inits) import Data.Text qualified as T import GHC.Hs@@ -29,8 +29,13 @@     layout <- getLayout     sep       breakpoint-      (\(isAllPrevDoc, p, l) -> sitcc (located l (p_lie layout isAllPrevDoc p)))+      (\(isAllPrevDoc, p, l) -> sitcc (located (addDocSrcSpan l) (p_lie layout isAllPrevDoc p)))       (withAllPrevDoc $ attachRelativePos xs)+  where+    -- In order to correctly set the layout when a doc comment is present.+    addDocSrcSpan lie@(L l ie) = case ieExportDoc ie of+      Nothing -> lie+      Just (L l' _) -> L (l <> noAnnSrcSpan l') ie  p_hsmodImport :: ImportDecl GhcPs -> R () p_hsmodImport ImportDecl {..} = do@@ -79,31 +84,37 @@  p_lie :: Layout -> Bool -> RelativePos -> IE GhcPs -> R () p_lie encLayout isAllPrevDoc relativePos = \case-  IEVar mwarn l1 -> do+  IEVar mwarn l1 exportDoc -> do     for_ mwarn $ \warnTxt -> do       located warnTxt p_warningTxt       breakpoint     withComma $       located l1 p_ieWrappedName-  IEThingAbs _ l1 ->+    p_exportDoc exportDoc+  IEThingAbs _ l1 exportDoc -> do     withComma $       located l1 p_ieWrappedName-  IEThingAll _ l1 -> withComma $ do-    located l1 p_ieWrappedName-    space-    txt "(..)"-  IEThingWith _ l1 w xs -> sitcc . withComma $ do-    located l1 p_ieWrappedName-    breakIfNotDiffFriendly-    inci $ do-      let names :: [R ()]-          names = located' p_ieWrappedName <$> xs-      parens' False . sep commaDelImportExport sitcc $-        case w of-          NoIEWildcard -> names-          IEWildcard n ->-            let (before, after) = splitAt n names-             in before ++ [txt ".."] ++ after+    p_exportDoc exportDoc+  IEThingAll _ l1 exportDoc -> do+    withComma $ do+      located l1 p_ieWrappedName+      space+      txt "(..)"+    p_exportDoc exportDoc+  IEThingWith _ l1 w xs exportDoc -> sitcc $ do+    withComma $ do+      located l1 p_ieWrappedName+      breakIfNotDiffFriendly+      inci $ do+        let names :: [R ()]+            names = located' p_ieWrappedName <$> xs+        parens' False . sep commaDelImportExport sitcc $+          case w of+            NoIEWildcard -> names+            IEWildcard n ->+              let (before, after) = splitAt n names+               in before ++ [txt ".."] ++ after+    p_exportDoc exportDoc   IEModuleContents _ l1 ->     withComma $       located l1 p_hsmodName@@ -138,6 +149,16 @@                 SinglePos -> m                 _ -> comma >> space >> m             Trailing -> m >> comma++    -- This is used to support `@since` annotations for (re)exported items. It+    -- /must/ use caret style comments, see+    -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12098 and+    -- https://github.com/haskell/haddock/issues/1629#issuecomment-1931354411.+    p_exportDoc :: Maybe (ExportDoc GhcPs) -> R ()+    p_exportDoc = traverse_ $ \exportDoc -> do+      breakpoint+      p_hsDoc Caret False exportDoc+     indentDoc m = do       commaStyle <- getCommaStyle       case commaStyle of@@ -147,6 +168,17 @@             SinglePos -> m             FirstPos -> m             _ -> inciBy 2 m++ieExportDoc :: IE GhcPs -> Maybe (ExportDoc GhcPs)+ieExportDoc = \case+  IEVar _ _ doc -> doc+  IEThingAbs _ _ doc -> doc+  IEThingAll _ _ doc -> doc+  IEThingWith _ _ _ _ doc -> doc+  IEModuleContents {} -> Nothing+  IEGroup {} -> Nothing+  IEDoc {} -> Nothing+  IEDocNamed {} -> Nothing  ---------------------------------------------------------------------------- 
src/Ormolu/Printer/Meat/Module.hs view
@@ -104,6 +104,6 @@           (epaLocationRealSrcSpan moduleLoc, epaLocationRealSrcSpan whereLoc)         anns -> error $ "Module had unexpected annotations: " ++ showSDocUnsafe (ppr anns)     exportClosePSpan = do-      AddEpAnn AnnCloseP loc <- al_close . anns . ann . getLoc =<< hsmodExports+      AddEpAnn AnnCloseP loc <- al_close . anns . getLoc =<< hsmodExports       Just $ epaLocationRealSrcSpan loc     isOnSameLine token1 token2 = srcSpanEndLine token1 == srcSpanStartLine token2
src/Ormolu/Printer/Meat/Type.hs view
@@ -84,7 +84,7 @@       breakpoint       inci $         sep breakpoint (located' p_hsType) args-  HsAppKindTy _ ty _ kd -> sitcc $ do+  HsAppKindTy _ ty kd -> sitcc $ do     -- The first argument is the location of the "@..." part. Not 100% sure,     -- but I think we can ignore it as long as we use 'located' on both the     -- type and the kind.@@ -98,7 +98,7 @@           case arrow of             HsUnrestrictedArrow _ -> token'rarrow             HsLinearArrow _ -> token'lolly-            HsExplicitMult _ mult _ -> do+            HsExplicitMult _ mult -> do               txt "%"               p_hsTypeR (unLoc mult)               space@@ -200,14 +200,14 @@     p_hsTypeR m = p_hsType' multilineArgs m  startTypeAnnotation ::-  (HasSrcSpan l) =>+  (HasLoc l) =>   GenLocated l a ->   (a -> R ()) ->   R () startTypeAnnotation = startTypeAnnotation' breakpoint breakpoint  startTypeAnnotationDecl ::-  (HasSrcSpan l) =>+  (HasLoc l) =>   GenLocated l a ->   (a -> HsType GhcPs) ->   (a -> R ()) ->@@ -222,7 +222,7 @@     lItem  startTypeAnnotation' ::-  (HasSrcSpan l) =>+  (HasLoc l) =>   R () ->   R () ->   GenLocated l a ->@@ -279,7 +279,7 @@ instance IsTyVarBndrFlag (HsBndrVis GhcPs) where   isInferred _ = False   p_tyVarBndrFlag = \case-    HsBndrRequired -> pure ()+    HsBndrRequired NoExtField -> pure ()     HsBndrInvisible _ -> txt "@"  p_hsTyVarBndr :: (IsTyVarBndrFlag flag) => HsTyVarBndr flag GhcPs -> R ()@@ -297,7 +297,7 @@  -- | Render several @forall@-ed variables. p_forallBndrs ::-  (HasSrcSpan l) =>+  (HasLoc l) =>   ForAllVisibility ->   (a -> R ()) ->   [GenLocated l a] ->@@ -306,10 +306,10 @@   p_forallBndrsStart p tyvars   p_forallBndrsEnd vis -p_forallBndrsStart :: (HasSrcSpan l) => (a -> R ()) -> [GenLocated l a] -> R ()+p_forallBndrsStart :: (HasLoc l) => (a -> R ()) -> [GenLocated l a] -> R () p_forallBndrsStart _ [] = token'forall p_forallBndrsStart p tyvars = do-  switchLayout (getLoc' <$> tyvars) $ do+  switchLayout (locA <$> tyvars) $ do     token'forall     breakpoint     inci $ do@@ -354,7 +354,7 @@  p_lhsTypeArg :: LHsTypeArg GhcPs -> R () p_lhsTypeArg = \case-  HsValArg ty -> located ty p_hsType+  HsValArg NoExtField ty -> located ty p_hsType   -- first argument is the SrcSpan of the @,   -- but the @ always has to be directly before the type argument   HsTypeArg _ ty -> txt "@" *> located ty p_hsType@@ -376,8 +376,8 @@ hsOuterTyVarBndrsToHsType obndrs ty = case obndrs of   HsOuterImplicit NoExtField -> unLoc ty   HsOuterExplicit _ bndrs ->-    HsForAllTy NoExtField (mkHsForAllInvisTele EpAnnNotUsed bndrs) ty+    HsForAllTy NoExtField (mkHsForAllInvisTele noAnn bndrs) ty  lhsTypeToSigType :: LHsType GhcPs -> LHsSigType GhcPs lhsTypeToSigType ty =-  reLocA . L (getLocA ty) . HsSig NoExtField (HsOuterImplicit NoExtField) $ ty+  L (getLoc ty) . HsSig NoExtField (HsOuterImplicit NoExtField) $ ty
src/Ormolu/Printer/Operators.hs view
@@ -15,6 +15,7 @@  import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE+import GHC.Parser.Annotation import GHC.Types.Name.Reader import GHC.Types.SrcLoc import Ormolu.Fixity@@ -81,8 +82,8 @@         _ -> False  -- | Return combined 'SrcSpan's of all elements in this 'OpTree'.-opTreeLoc :: (HasSrcSpan l) => OpTree (GenLocated l a) b -> SrcSpan-opTreeLoc (OpNode n) = getLoc' n+opTreeLoc :: (HasLoc l) => OpTree (GenLocated l a) b -> SrcSpan+opTreeLoc (OpNode n) = getHasLoc n opTreeLoc (OpBranches exprs _) =   combineSrcSpans' . fmap opTreeLoc $ exprs 
src/Ormolu/Printer/SpanStream.hs view
@@ -33,7 +33,7 @@   SpanStream     . sortOn realSrcSpanStart     . toList-    $ everything mappend (const mempty `ext2Q` queryLocated `ext1Q` querySrcSpanAnn) a+    $ everything mappend (const mempty `ext2Q` queryLocated `ext1Q` queryEpAnn) a   where     queryLocated ::       (Data e0) =>@@ -41,7 +41,9 @@       Seq RealSrcSpan     queryLocated (L mspn _) =       maybe mempty srcSpanToRealSrcSpanSeq (cast mspn :: Maybe SrcSpan)-    querySrcSpanAnn :: SrcSpanAnn' a -> Seq RealSrcSpan-    querySrcSpanAnn = srcSpanToRealSrcSpanSeq . locA++    queryEpAnn :: EpAnn ann -> Seq RealSrcSpan+    queryEpAnn = srcSpanToRealSrcSpanSeq . locA+     srcSpanToRealSrcSpanSeq =       Seq.fromList . maybeToList . srcSpanToRealSrcSpan
src/Ormolu/Terminal.hs view
@@ -30,7 +30,7 @@ import Data.Sequence qualified as Seq import Data.Text (Text) import Data.Text qualified as T-import Data.Text.IO qualified as T+import Data.Text.IO.Utf8 qualified as T.Utf8 import GHC.Utils.Outputable (Outputable) import Ormolu.Utils (showOutputable) import System.Console.ANSI@@ -76,7 +76,7 @@       where         go (TermOutput (Const nodes)) =           forM_ nodes $ \case-            OutputText s -> T.hPutStr handle s+            OutputText s -> T.Utf8.hPutStr handle s             WithColor color term -> withSGR [SetColor Foreground Dull color] (go term)             WithBold term -> withSGR [SetConsoleIntensity BoldIntensity] (go term) 
src/Ormolu/Utils.hs view
@@ -14,8 +14,6 @@     separatedByBlankNE,     onTheSameLine,     groupBy',-    HasSrcSpan (..),-    getLoc',     matchAddEpAnn,     textToStringBuffer,     ghcModuleNameToCabal,@@ -165,21 +163,6 @@     if x `eq` y       then (x :| y : ys) : zs       else pure x : (y :| ys) : zs--class HasSrcSpan l where-  loc' :: l -> SrcSpan--instance HasSrcSpan SrcSpan where-  loc' = id--instance HasSrcSpan RealSrcSpan where-  loc' l = RealSrcSpan l Strict.Nothing--instance HasSrcSpan (SrcSpanAnn' ann) where-  loc' = locA--getLoc' :: (HasSrcSpan l) => GenLocated l a -> SrcSpan-getLoc' = loc' . getLoc  -- | Check whether the given 'AnnKeywordId' or its Unicode variant is in an -- 'AddEpAnn', and return the 'EpaLocation' if so.
src/Ormolu/Utils/Fixity.hs view
@@ -15,12 +15,13 @@ import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map import Data.Text qualified as T+import Data.Text.IO.Utf8 qualified as T.Utf8 import Distribution.ModuleName (ModuleName) import Distribution.Types.PackageName (PackageName) import Ormolu.Exception import Ormolu.Fixity import Ormolu.Fixity.Parser-import Ormolu.Utils.IO (findClosestFileSatisfying, readFileUtf8, withIORefCache)+import Ormolu.Utils.IO (findClosestFileSatisfying, withIORefCache) import System.Directory import System.IO.Unsafe (unsafePerformIO) import Text.Megaparsec (errorBundlePretty)@@ -38,7 +39,7 @@   liftIO (findDotOrmoluFile sourceFile) >>= \case     Just dotOrmoluFile -> liftIO $ withIORefCache cacheRef dotOrmoluFile $ do       dotOrmoluRelative <- makeRelativeToCurrentDirectory dotOrmoluFile-      contents <- readFileUtf8 dotOrmoluFile+      contents <- T.Utf8.readFile dotOrmoluFile       case parseDotOrmolu dotOrmoluRelative contents of         Left errorBundle ->           throwIO (OrmoluFixityOverridesParseError errorBundle)
src/Ormolu/Utils/IO.hs view
@@ -1,48 +1,20 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ViewPatterns #-} --- | Write 'Text' to files using UTF8 and ignoring native--- line ending conventions. module Ormolu.Utils.IO-  ( writeFileUtf8,-    readFileUtf8,-    getContentsUtf8,-    findClosestFileSatisfying,+  ( findClosestFileSatisfying,     withIORefCache,   ) where  import Control.Exception (catch, throwIO) import Control.Monad.IO.Class-import Data.ByteString (ByteString)-import Data.ByteString qualified as B import Data.IORef import Data.Map.Lazy (Map) import Data.Map.Lazy qualified as M-import Data.Text (Text)-import Data.Text.Encoding qualified as TE import System.Directory import System.FilePath import System.IO.Error (isDoesNotExistError)---- | Write a 'Text' to a file using UTF8 and ignoring native--- line ending conventions.-writeFileUtf8 :: (MonadIO m) => FilePath -> Text -> m ()-writeFileUtf8 p = liftIO . B.writeFile p . TE.encodeUtf8---- | Read an entire file strictly into a 'Text' using UTF8 and--- ignoring native line ending conventions.-readFileUtf8 :: (MonadIO m) => FilePath -> m Text-readFileUtf8 p = liftIO (B.readFile p) >>= decodeUtf8---- | Read stdin as UTF8-encoded 'Text' value.-getContentsUtf8 :: (MonadIO m) => m Text-getContentsUtf8 = liftIO B.getContents >>= decodeUtf8---- | A helper function for decoding a strict 'ByteString' into 'Text'. It is--- strict and fails immediately if decoding encounters a problem.-decodeUtf8 :: (MonadIO m) => ByteString -> m Text-decodeUtf8 = liftIO . either throwIO pure . TE.decodeUtf8'  -- | Find the path to the closest file higher in the file hierarchy that -- satisfies a given predicate.
tests/Ormolu/CabalInfoSpec.hs view
@@ -36,7 +36,7 @@       mentioned `shouldBe` True       unPackageName ciPackageName `shouldBe` "fourmolu"       ciDynOpts `shouldBe` [DynOption "-XGHC2021"]-      Set.map unPackageName ciDependencies `shouldBe` Set.fromList ["Cabal-syntax", "Diff", "MemoTrie", "aeson", "ansi-terminal", "array", "base", "binary", "bytestring", "containers", "deepseq", "directory", "file-embed", "filepath", "ghc-lib-parser", "megaparsec", "mtl", "scientific", "syb", "text", "yaml"]+      Set.map unPackageName ciDependencies `shouldBe` Set.fromList ["Cabal-syntax", "Diff", "MemoTrie", "aeson", "ansi-terminal", "array", "base", "binary", "bytestring", "containers", "deepseq", "directory", "file-embed", "filepath", "ghc-lib-parser", "megaparsec", "mtl", "scientific", "syb", "text"]       ciCabalFilePath `shouldSatisfy` isAbsolute       makeRelativeToCurrentDirectory ciCabalFilePath `shouldReturn` "fourmolu.cabal"     it "extracts correct cabal info from fourmolu.cabal for tests/Ormolu/PrinterSpec.hs" $ do
tests/Ormolu/Config/PrinterOptsSpec.hs view
@@ -17,6 +17,7 @@ import Data.Maybe (isJust) import Data.Text (Text) import Data.Text qualified as T+import Data.Text.IO.Utf8 qualified as T.Utf8 import GHC.Stack (withFrozenCallStack) import Ormolu   ( Config (..),@@ -30,7 +31,6 @@ import Ormolu.Config (ColumnLimit (..), HaddockPrintStyleModule (..)) import Ormolu.Exception (OrmoluException, printOrmoluException) import Ormolu.Terminal (ColorMode (..), runTerm)-import Ormolu.Utils.IO (readFileUtf8, writeFileUtf8) import Path   ( File,     Path,@@ -254,7 +254,7 @@             outputFile = testDir </> toRelFile ("output" ++ testCaseSuffix testCase ++ ".hs")             opts = updateConfig testCase defaultPrinterOpts -        input <- readFileUtf8 inputPath+        input <- T.Utf8.readFile inputPath         actual <-           if isMulti             then overSectionsM (T.pack "{- // -}") (runOrmolu opts checkIdempotence inputPath) input@@ -286,7 +286,7 @@  checkResult :: Path Rel File -> Text -> Expectation checkResult outputFile actual-  | shouldRegenerateOutput = writeFileUtf8 (fromRelFile outputFile) actual+  | shouldRegenerateOutput = T.Utf8.writeFile (fromRelFile outputFile) actual   | otherwise =       getFileContents outputFile >>= \case         Nothing ->@@ -326,7 +326,7 @@ getFileContents path = do   fileExists <- doesFileExist path   if fileExists-    then Just <$> readFileUtf8 (toFilePath path)+    then Just <$> T.Utf8.readFile (toFilePath path)     else pure Nothing  getDiff :: (String, Text) -> (String, Text) -> Text
tests/Ormolu/Diff/TextSpec.hs view
@@ -2,9 +2,9 @@  module Ormolu.Diff.TextSpec (spec) where +import Data.Text.IO.Utf8 qualified as T.Utf8 import Ormolu.Diff.Text import Ormolu.Terminal-import Ormolu.Utils.IO import Path import System.FilePath qualified as FP import Test.Hspec@@ -36,14 +36,14 @@ stdTest name pathA pathB = it name $ do   inputA <-     parseRelFile (FP.addExtension pathA "hs")-      >>= readFileUtf8 . toFilePath . (diffInputsDir </>)+      >>= T.Utf8.readFile . toFilePath . (diffInputsDir </>)   inputB <-     parseRelFile (FP.addExtension pathB "hs")-      >>= readFileUtf8 . toFilePath . (diffInputsDir </>)+      >>= T.Utf8.readFile . toFilePath . (diffInputsDir </>)   let expectedDiffPath = FP.addExtension name "txt"   expectedDiffText <-     parseRelFile expectedDiffPath-      >>= readFileUtf8 . toFilePath . (diffOutputsDir </>)+      >>= T.Utf8.readFile . toFilePath . (diffOutputsDir </>)   Just actualDiff <- pure $ diffText inputA inputB "TEST"   runTermPure (printTextDiff actualDiff) `shouldBe` expectedDiffText 
tests/Ormolu/Integration/CLISpec.hs view
@@ -1,7 +1,7 @@ module Ormolu.Integration.CLISpec (spec) where  import Control.Monad (forM_)-import Ormolu.Integration.Utils (getFourmoluExe, readProcess, readProcess')+import Ormolu.Integration.Utils (ProcessSpec (..), getFourmoluExe, proc, readFrom, readProcess) import System.Directory (createDirectoryIfMissing) import System.FilePath (takeDirectory, (</>)) import System.IO.Temp (withSystemTempDirectory)@@ -11,17 +11,78 @@ spec =   describe "Fourmolu CLI functionality" . beforeAll getFourmoluExe $ do     it "formats stdin when no files specified" $ \fourmoluExe -> do-      stdout <- readProcess' fourmoluExe ["--no-cabal"] unformattedCode+      stdout <- readFrom $ (proc fourmoluExe ["--no-cabal"]) {procStdin = unformattedCode}       stdout `shouldBe` formattedCode      it "formats stdin when stdin specified" $ \fourmoluExe -> do-      stdout <- readProcess' fourmoluExe ["--no-cabal", "-"] unformattedCode+      stdout <- readFrom $ (proc fourmoluExe ["--no-cabal", "-"]) {procStdin = unformattedCode}       stdout `shouldBe` formattedCode +    it "finds config file in current directory when formatting stdin" $ \fourmoluExe ->+      withTempDir $ \tmpdir -> do+        writeFile' (tmpdir </> "fourmolu.yaml") "single-constraint-parens: never"+        stdout <-+          readFrom $+            (proc fourmoluExe ["--no-cabal", "-"])+              { procStdin = "f :: (Show a) => a -> String",+                procCwd = Just tmpdir+              }+        stdout `shouldBe` "f :: Show a => a -> String\n"++    it "finds config file in ancestor directory when formatting stdin" $ \fourmoluExe ->+      withTempDir $ \tmpdir -> do+        writeFile' (tmpdir </> "fourmolu.yaml") "single-constraint-parens: never"+        let cwd = tmpdir </> "a" </> "b" </> "c"+        createDirectoryIfMissing True cwd+        stdout <-+          readFrom $+            (proc fourmoluExe ["--no-cabal", "-"])+              { procStdin = "f :: (Show a) => a -> String",+                procCwd = Just cwd+              }+        stdout `shouldBe` "f :: Show a => a -> String\n"++    it "finds config file in directory of input file" $ \fourmoluExe ->+      withTempDir $ \tmpdir -> do+        writeFile' (tmpdir </> "fourmolu.yaml") "single-constraint-parens: never"+        writeFile' (tmpdir </> "input.hs") "f :: (Show a) => a -> String"+        stdout <-+          readFrom $+            (proc fourmoluExe ["--no-cabal", "input.hs"])+              { procCwd = Just tmpdir+              }+        stdout `shouldBe` "f :: Show a => a -> String\n"++    it "finds config file in XDG directory" $ \fourmoluExe ->+      withTempDir $ \tmpdir -> do+        let configDir = tmpdir </> "config"+        writeFile' (configDir </> "fourmolu.yaml") "single-constraint-parens: never"+        stdout <-+          readFrom $+            (proc fourmoluExe ["--no-cabal", "-"])+              { procStdin = "f :: (Show a) => a -> String",+                procCwd = Just tmpdir,+                procExtraEnv = [("XDG_CONFIG_HOME", configDir)]+              }+        stdout `shouldBe` "f :: Show a => a -> String\n"++    it "uses specified config file" $ \fourmoluExe ->+      withTempDir $ \tmpdir -> do+        let configFile = tmpdir </> "foo" </> "my-config.yaml"+        let inputFile = tmpdir </> "bar" </> "input.hs"+        writeFile' configFile "single-constraint-parens: never"+        writeFile' inputFile "f :: (Show a) => a -> String"+        stdout <-+          readFrom $+            (proc fourmoluExe ["--no-cabal", "--config", configFile, inputFile])+              { procCwd = Just tmpdir+              }+        stdout `shouldBe` "f :: Show a => a -> String\n"+     it "recursively finds files in directories" $ \fourmoluExe -> do       withTempDir $ \tmpdir -> do         let hsFile = tmpdir </> "test.hs"-        writeFile hsFile unformattedCode+        writeFile' hsFile unformattedCode          output <- readProcess fourmoluExe [tmpdir]         output `shouldBe` formattedCode@@ -37,8 +98,7 @@                 tmpdir </> "dist-newstyle/test.hs"               ]         forM_ (testFiles ++ ignoredFiles) $ \fp -> do-          createDirectoryIfMissing True (takeDirectory fp)-          writeFile fp unformattedCode+          writeFile' fp unformattedCode          _ <- readProcess fourmoluExe ["-i", tmpdir]         forM_ testFiles $ \fp -> do@@ -49,5 +109,8 @@           output `shouldBe` unformattedCode   where     withTempDir = withSystemTempDirectory "fourmolu-cli-test"+    writeFile' fp s = do+      createDirectoryIfMissing True (takeDirectory fp)+      writeFile fp s     unformattedCode = "main=print(1+1)\n"     formattedCode = "main = print (1 + 1)\n"
tests/Ormolu/Integration/Utils.hs view
@@ -1,14 +1,20 @@+{-# LANGUAGE RecordWildCards #-}+ module Ormolu.Integration.Utils   ( getFourmoluExe,     readProcess,-    readProcess',+    ProcessSpec (..),+    proc,+    readFrom,   ) where  import Control.Monad (when) import System.Directory (findExecutable)+import System.Environment (getEnvironment) import System.Exit (ExitCode (..))-import System.Process (readProcessWithExitCode)+import System.Process (CreateProcess (..), readCreateProcessWithExitCode)+import System.Process qualified as Process  -- | Find a `fourmolu` executable on PATH. getFourmoluExe :: IO FilePath@@ -17,15 +23,39 @@ -- | Like 'System.Process.readProcess', except without specifying stdin and -- with better failure messages. readProcess :: FilePath -> [String] -> IO String-readProcess cmd args = readProcess' cmd args ""+readProcess cmd args = readFrom $ proc cmd args --- | Like 'System.Process.readProcess', except with better failure messages.-readProcess' :: FilePath -> [String] -> String -> IO String-readProcess' cmd args stdin = do-  (code, stdout, stderr) <- readProcessWithExitCode cmd args stdin+data ProcessSpec = ProcessSpec+  { procCmd :: FilePath,+    procArgs :: [String],+    procStdin :: String,+    procCwd :: Maybe FilePath,+    procExtraEnv :: [(String, String)]+  } +proc :: FilePath -> [String] -> ProcessSpec+proc cmd args =+  ProcessSpec+    { procCmd = cmd,+      procArgs = args,+      procStdin = "",+      procCwd = Nothing,+      procExtraEnv = []+    }++readFrom :: ProcessSpec -> IO String+readFrom ProcessSpec {..} = do+  currentEnv <- getEnvironment+  (code, stdout, stderr) <-+    readCreateProcessWithExitCode+      (Process.proc procCmd procArgs)+        { cwd = procCwd,+          env = Just $ procExtraEnv <> currentEnv+        }+      procStdin+   when (code /= ExitSuccess) $ do-    putStrLn $ "Command failed: " ++ (unwords . map (\s -> "\"" ++ s ++ "\"")) (cmd : args)+    putStrLn $ "Command failed: " ++ (unwords . map (\s -> "\"" ++ s ++ "\"")) (procCmd : procArgs)     putStrLn "========== stdout =========="     putStrLn stdout     putStrLn "========== stderr =========="
tests/Ormolu/PrinterSpec.hs view
@@ -12,11 +12,11 @@ import Data.Set qualified as Set import Data.Text (Text) import Data.Text qualified as T-import Data.Text.IO qualified as T+import Data.Text.IO.Utf8 qualified as T.Utf8+import Data.Yaml qualified as Yaml import Ormolu import Ormolu.Config import Ormolu.Fixity-import Ormolu.Utils.IO import Path import Path.IO import System.Environment (lookupEnv)@@ -26,11 +26,7 @@ spec :: Spec spec = do   -- Config for normal Ormolu output + default Fourmolu output-  ormoluConfig <--    runIO $-      loadConfigFile "fourmolu.yaml" >>= \case-        ConfigLoaded _ cfg -> pure cfg-        result -> error $ "Could not load config file: " ++ show result+  ormoluConfig <- runIO $ Yaml.decodeFileEither "fourmolu.yaml" >>= either (error . show) pure   let ormoluPrinterOpts = resolvePrinterOpts [cfgFilePrinterOpts ormoluConfig]    es <- runIO locateExamples@@ -77,8 +73,8 @@   -- 3. Check the output against expected output. Thus all tests should   -- include two files: input and expected output.   whenShouldRegenerateOutput $-    T.writeFile (fromRelFile expectedOutputPath) formatted0-  expected <- readFileUtf8 $ fromRelFile expectedOutputPath+    T.Utf8.writeFile (fromRelFile expectedOutputPath) formatted0+  expected <- T.Utf8.readFile $ fromRelFile expectedOutputPath   shouldMatch False formatted0 expected   -- 4. Check that running the formatter on the output produces the same   -- output again (the transformation is idempotent).