diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,28 @@
+## Ormolu 0.5.3.0
+
+* Stop making empty `let`s move comments. [Issue
+  917](https://github.com/tweag/ormolu/issues/917).
+
+* Now `.ormolu` fixity override files can use both LF and CRLF line endings.
+  [PR 969](https://github.com/tweag/ormolu/pull/969).
+
+* Normalize parentheses around constraints. [Issue
+  264](https://github.com/tweag/ormolu/issues/264).
+
+* The `ormolu` function now consumes `Text` instead of `String` due to an
+  internal refactoring.
+
+* Exposed a more complete public API in the `Ormolu` module. The API is
+  supposed to be stable and change according to
+  [PVP](https://pvp.haskell.org/).
+
+* Now warnings regarding Ormolu not being able to find `.cabal` files or
+  finding such files but them not mentioning the source file in question are
+  only displayed when `--debug` is used. Printing the warnings by default
+  seems to have been confusing, see e.g. [Issue
+  971](https://github.com/tweag/ormolu/issues/971) and [issue
+  924](https://github.com/tweag/ormolu/issues/924).
+
 ## Ormolu 0.5.2.0
 
 * Eliminated the `fixity-th` Cabal flag because it caused issues on GHC 9.4 as
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -40,7 +40,7 @@
 
 ## Formatting
 
-Use `format.sh` script to format Ormolu with the current version of Ormolu.
-If Ormolu is not formatted like this, the CI will fail.
+Use `nix run .#format` script to format Ormolu with the current version of
+Ormolu. If Ormolu is not formatted like this, the CI will fail.
 
 [issues]: https://github.com/tweag/ormolu/issues
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
 [![Hackage](https://img.shields.io/hackage/v/ormolu.svg?style=flat)](https://hackage.haskell.org/package/ormolu)
 [![Stackage Nightly](http://stackage.org/package/ormolu/badge/nightly)](http://stackage.org/nightly/package/ormolu)
 [![Stackage LTS](http://stackage.org/package/ormolu/badge/lts)](http://stackage.org/lts/package/ormolu)
-[![Build status](https://badge.buildkite.com/8e3b0951f3652b77e1c422b361904136a539b0522029156354.svg?branch=master)](https://buildkite.com/tweag-1/ormolu)
+[![CI](https://github.com/tweag/ormolu/actions/workflows/ci.yml/badge.svg)](https://github.com/tweag/ormolu/actions/workflows/ci.yml)
 
 * [Installation](#installation)
 * [Building from source](#building-from-source)
@@ -17,6 +17,7 @@
     * [Magic comments](#magic-comments)
     * [Regions](#regions)
     * [Exit codes](#exit-codes)
+    * [Using as a library](#using-as-a-library)
 * [Limitations](#limitations)
 * [Running on Hackage](#running-on-hackage)
 * [Forks and modifications](#forks-and-modifications)
@@ -69,12 +70,11 @@
 The easiest way to build the project is with Nix:
 
 ```console
-$ nix-build -A ormolu
+$ nix build
 ```
 
-Note that you will need to add [IOHK Hydra binary
-cache][iohk-hydra-binary-cache], otherwise building may take a very long
-time.
+Make sure to accept the offered Nix caches (in particular the IOG cache),
+otherwise building may take a very long time.
 
 Alternatively, `stack` could be used as follows:
 
@@ -83,19 +83,15 @@
 $ stack install # to install
 ```
 
-To use Ormolu directly from GitHub with Nix, this snippet may come in handy:
+To use Ormolu directly from GitHub with Nix flakes, this snippet may come in handy:
 
 ```nix
-let
-  pkgs = import <nixpkgs> { };
-  source = pkgs.fetchFromGitHub {
-      owner = "tweag";
-      repo = "ormolu";
-      rev = "c1d8a8083cf1492545b8deed342c6399fe9873ea"; # update as necessary
-      # do not forget to update the hash:
-      sha256 = "sha256-3XxKuWqZnFa9s3mY7OBD+uEn/fGxPmC8jdevx7exy9o=";
-    };
-in (import source {  }).ormoluExe # this is e.g. the executable derivation
+{
+  inputs.ormolu.url = "github:tweag/ormolu";
+  outputs = { ormolu, ... }: {
+    # use ormolu.packages.${system}.default here
+  };
+}
 ```
 
 ## Usage
@@ -248,6 +244,13 @@
 101       | Inplace mode does not work with stdin
 102       | Other issue (with multiple input files)
 
+### Using as a library
+
+The `ormolu` package can also be depended upon from other Haskell programs.
+For these purposes only the top `Ormolu` module should be considered stable.
+It follows [PVP](https://pvp.haskell.org/) starting from the version
+0.5.3.0. Rely on other modules at your own risk.
+
 ## Limitations
 
 * CPP support is experimental. CPP is virtually impossible to handle
@@ -264,7 +267,7 @@
 execute (from the root of the cloned repo):
 
 ```console
-$ nix-build -A hackage.<package>
+$ nix build .#hackage.<package>
 ```
 
 Then inspect `result/log.txt` for possible problems. The derivation will
@@ -293,7 +296,6 @@
 [design-cpp]: https://github.com/tweag/ormolu/blob/master/DESIGN.md#cpp
 [emacs-package]: https://github.com/vyorkin/ormolu.el
 [haskell-src-exts]: https://hackage.haskell.org/package/haskell-src-exts
-[iohk-hydra-binary-cache]: https://input-output-hk.github.io/haskell.nix/tutorials/getting-started.html#setting-up-the-binary-cache
 [neoformat]: https://github.com/sbdchd/neoformat
 [releases]: https://github.com/tweag/ormolu/releases
 [ormolu-action]: https://github.com/marketplace/actions/ormolu-action
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -14,27 +14,23 @@
 import Control.Monad
 import Data.Bool (bool)
 import Data.List (intercalate, sort)
-import qualified Data.Map as Map
-import Data.Maybe (fromMaybe, mapMaybe)
+import qualified Data.Map.Strict as Map
+import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
 import qualified Data.Set as Set
-import qualified Data.Text as T
 import qualified Data.Text.IO as TIO
 import Data.Version (showVersion)
-import Development.GitRev
+import Language.Haskell.TH.Env (envQ)
 import Options.Applicative
 import Ormolu
 import Ormolu.Diff.Text (diffText, printTextDiff)
-import Ormolu.Fixity (FixityInfo)
+import Ormolu.Fixity (FixityInfo, OpName)
 import Ormolu.Parser (manualExts)
 import Ormolu.Terminal
 import Ormolu.Utils (showOutputable)
-import Ormolu.Utils.Cabal
-import Ormolu.Utils.Fixity
-  ( getFixityOverridesForSourceFile,
-    parseFixityDeclarationStr,
-  )
+import Ormolu.Utils.Fixity (parseFixityDeclarationStr)
 import Ormolu.Utils.IO
 import Paths_ormolu (version)
+import System.Directory
 import System.Exit (ExitCode (..), exitWith)
 import qualified System.FilePath as FP
 import System.IO (hPutStrLn, stderr)
@@ -84,15 +80,35 @@
   IO ExitCode
 formatOne CabalOpts {..} mode reqSourceType rawConfig mpath =
   withPrettyOrmoluExceptions (cfgColorMode rawConfig) $ do
+    let getCabalInfoForSourceFile' sourceFile = do
+          cabalSearchResult <- getCabalInfoForSourceFile sourceFile
+          let debugEnabled = cfgDebug rawConfig
+          case cabalSearchResult of
+            CabalNotFound -> do
+              when debugEnabled $
+                hPutStrLn stderr $
+                  "Could not find a .cabal file for " <> sourceFile
+              return Nothing
+            CabalDidNotMention cabalInfo -> do
+              when debugEnabled $ do
+                relativeCabalFile <-
+                  makeRelativeToCurrentDirectory (ciCabalFilePath cabalInfo)
+                hPutStrLn stderr $
+                  "Found .cabal file "
+                    <> relativeCabalFile
+                    <> ", but it did not mention "
+                    <> sourceFile
+              return (Just cabalInfo)
+            CabalFound cabalInfo -> return (Just cabalInfo)
     case FP.normalise <$> mpath of
       -- input source = STDIN
       Nothing -> do
         resultConfig <-
           ( if optDoNotUseCabal
-              then pure defaultCabalInfo
+              then pure Nothing
               else case optStdinInputFile of
                 Just stdinInputFile ->
-                  getCabalInfoForSourceFile stdinInputFile
+                  getCabalInfoForSourceFile' stdinInputFile
                 Nothing -> throwIO OrmoluMissingStdinInputFile
             )
             >>= patchConfig Nothing
@@ -111,14 +127,14 @@
             originalInput <- getContentsUtf8
             let stdinRepr = "<stdin>"
             formattedInput <-
-              ormolu resultConfig stdinRepr (T.unpack originalInput)
+              ormolu resultConfig stdinRepr originalInput
             handleDiff originalInput formattedInput stdinRepr
       -- input source = a file
       Just inputFile -> do
         resultConfig <-
           ( if optDoNotUseCabal
-              then pure defaultCabalInfo
-              else getCabalInfoForSourceFile inputFile
+              then pure Nothing
+              else getCabalInfoForSourceFile' inputFile
             )
             >>= patchConfig (Just (detectSourceType inputFile))
         case mode of
@@ -129,7 +145,7 @@
             -- ormoluFile is not used because we need originalInput
             originalInput <- readFileUtf8 inputFile
             formattedInput <-
-              ormolu resultConfig inputFile (T.unpack originalInput)
+              ormolu resultConfig inputFile originalInput
             when (formattedInput /= originalInput) $
               writeFileUtf8 inputFile formattedInput
             return ExitSuccess
@@ -137,30 +153,16 @@
             -- ormoluFile is not used because we need originalInput
             originalInput <- readFileUtf8 inputFile
             formattedInput <-
-              ormolu resultConfig inputFile (T.unpack originalInput)
+              ormolu resultConfig inputFile originalInput
             handleDiff originalInput formattedInput inputFile
   where
-    patchConfig mdetectedSourceType cabalInfo@CabalInfo {..} = do
-      let depsFromCabal =
-            -- It makes sense to take into account the operator info for the
-            -- package itself if we know it, as if it were its own
-            -- dependency.
-            case ciPackageName of
-              Nothing -> ciDependencies
-              Just p -> Set.insert p ciDependencies
-      fixityOverrides <- getFixityOverridesForSourceFile cabalInfo
-      return
-        rawConfig
-          { cfgDynOptions = cfgDynOptions rawConfig ++ ciDynOpts,
-            cfgFixityOverrides =
-              Map.unionWith (<>) (cfgFixityOverrides rawConfig) fixityOverrides,
-            cfgDependencies =
-              Set.union (cfgDependencies rawConfig) depsFromCabal,
-            cfgSourceType =
-              fromMaybe
-                ModuleSource
-                (reqSourceType <|> mdetectedSourceType)
-          }
+    patchConfig mdetectedSourceType mcabalInfo = do
+      let sourceType =
+            fromMaybe
+              ModuleSource
+              (reqSourceType <|> mdetectedSourceType)
+      mfixityOverrides <- traverse getFixityOverridesForSourceFile mcabalInfo
+      return (refineConfig sourceType mcabalInfo mfixityOverrides rawConfig)
     handleDiff originalInput formattedInput fileRepr =
       case diffText originalInput formattedInput fileRepr of
         Nothing -> return ExitSuccess
@@ -224,12 +226,9 @@
     verStr =
       intercalate
         "\n"
-        [ unwords
-            [ "ormolu",
-              showVersion version,
-              $gitBranch,
-              $gitHash
-            ],
+        [ unwords $
+            ["ormolu", showVersion version]
+              <> maybeToList $$(envQ @String "ORMOLU_REV"),
           "using ghc-lib-parser " ++ VERSION_ghc_lib_parser
         ]
     exts :: Parser (a -> a)
@@ -362,7 +361,7 @@
   s -> Left $ "unknown mode: " ++ s
 
 -- | Parse a fixity declaration.
-parseFixityDeclaration :: ReadM [(String, FixityInfo)]
+parseFixityDeclaration :: ReadM [(OpName, FixityInfo)]
 parseFixityDeclaration = eitherReader parseFixityDeclarationStr
 
 -- | Parse 'ColorMode'.
diff --git a/data/examples/declaration/class/default-signatures-simple-out.hs b/data/examples/declaration/class/default-signatures-simple-out.hs
--- a/data/examples/declaration/class/default-signatures-simple-out.hs
+++ b/data/examples/declaration/class/default-signatures-simple-out.hs
@@ -4,5 +4,5 @@
 class Foo a where
   -- | Foo
   foo :: a -> String
-  default foo :: Show a => a -> String
+  default foo :: (Show a) => a -> String
   foo = show
diff --git a/data/examples/declaration/class/super-classes-out.hs b/data/examples/declaration/class/super-classes-out.hs
--- a/data/examples/declaration/class/super-classes-out.hs
+++ b/data/examples/declaration/class/super-classes-out.hs
@@ -1,6 +1,6 @@
 class Foo a
 
-class Foo a => Bar a
+class (Foo a) => Bar a
 
 class
   (Foo a, Bar a) =>
diff --git a/data/examples/declaration/data/existential-multiline-out.hs b/data/examples/declaration/data/existential-multiline-out.hs
--- a/data/examples/declaration/data/existential-multiline-out.hs
+++ b/data/examples/declaration/data/existential-multiline-out.hs
@@ -2,7 +2,7 @@
 
 data Foo
   = forall a. MkFoo a (a -> Bool)
-  | forall a. Eq a => MkBar a
+  | forall a. (Eq a) => MkBar a
 
 data Bar
   = forall x y.
diff --git a/data/examples/declaration/data/existential-out.hs b/data/examples/declaration/data/existential-out.hs
--- a/data/examples/declaration/data/existential-out.hs
+++ b/data/examples/declaration/data/existential-out.hs
@@ -2,4 +2,4 @@
 
 data Foo = forall a. MkFoo a (a -> Bool)
 
-data Bar = forall a b. a + b => Bar a b
+data Bar = forall a b. (a + b) => Bar a b
diff --git a/data/examples/declaration/instance/contexts-out.hs b/data/examples/declaration/instance/contexts-out.hs
--- a/data/examples/declaration/instance/contexts-out.hs
+++ b/data/examples/declaration/instance/contexts-out.hs
@@ -1,4 +1,4 @@
-instance Eq a => Eq [a] where (==) _ _ = False
+instance (Eq a) => Eq [a] where (==) _ _ = False
 
 instance
   ( Ord a,
diff --git a/data/examples/declaration/instance/newlines-between-methods-out.hs b/data/examples/declaration/instance/newlines-between-methods-out.hs
--- a/data/examples/declaration/instance/newlines-between-methods-out.hs
+++ b/data/examples/declaration/instance/newlines-between-methods-out.hs
@@ -1,4 +1,4 @@
-instance Num a => Num (Diff a) where
+instance (Num a) => Num (Diff a) where
   D u dudx + D v dvdx = D (u + v) (dudx + dvdx)
   D u dudx - D v dvdx = D (u - v) (dudx - dvdx)
   D u dudx * D v dvdx = D (u * v) (u * dvdx + v * dudx)
diff --git a/data/examples/declaration/signature/specialize/specialize-instance-out.hs b/data/examples/declaration/signature/specialize/specialize-instance-out.hs
--- a/data/examples/declaration/signature/specialize/specialize-instance-out.hs
+++ b/data/examples/declaration/signature/specialize/specialize-instance-out.hs
@@ -8,7 +8,7 @@
 
 instance (Num r, V.Vector v r, Factored m r) => Num (VT v m r) where
   {-# SPECIALIZE instance
-    ( Factored m Int => Num (VT U.Vector m Int)
+    ( (Factored m Int) => Num (VT U.Vector m Int)
     )
     #-}
   VT x + VT y = VT $ V.zipWith (+) x y
diff --git a/data/examples/declaration/signature/specialize/specialize-out.hs b/data/examples/declaration/signature/specialize/specialize-out.hs
--- a/data/examples/declaration/signature/specialize/specialize-out.hs
+++ b/data/examples/declaration/signature/specialize/specialize-out.hs
@@ -1,13 +1,13 @@
-foo :: Num a => a -> a
+foo :: (Num a) => a -> a
 foo = id
 {-# SPECIALIZE foo :: Int -> Int #-}
 {-# SPECIALIZE INLINE foo :: Float -> Float #-}
 
 {-# SPECIALIZE NOINLINE [2] bar :: Int -> Int #-}
-bar :: Num a => a -> a
+bar :: (Num a) => a -> a
 bar = id
 
-baz :: Num a => a -> a
+baz :: (Num a) => a -> a
 baz = id
 {-# SPECIALIZE [~2] baz ::
   Int ->
@@ -20,5 +20,5 @@
   Bool,
   Integer -> Bool
   #-}
-fits13Bits :: Integral a => a -> Bool
+fits13Bits :: (Integral a) => a -> Bool
 fits13Bits x = x >= -4096 && x < 4096
diff --git a/data/examples/declaration/signature/type/unicode-out.hs b/data/examples/declaration/signature/type/unicode-out.hs
--- a/data/examples/declaration/signature/type/unicode-out.hs
+++ b/data/examples/declaration/signature/type/unicode-out.hs
@@ -1,4 +1,4 @@
 {-# LANGUAGE UnicodeSyntax #-}
 
-foo :: forall a. Show a => a -> String
+foo :: forall a. (Show a) => a -> String
 foo = const ()
diff --git a/data/examples/declaration/value/function/fancy-forall-0-out.hs b/data/examples/declaration/value/function/fancy-forall-0-out.hs
--- a/data/examples/declaration/value/function/fancy-forall-0-out.hs
+++ b/data/examples/declaration/value/function/fancy-forall-0-out.hs
@@ -2,9 +2,9 @@
   forall outertag innertag t outer inner m a.
   ( forall x. Coercible (t m x) (m x),
     forall m'.
-    HasCatch outertag outer m' =>
+    (HasCatch outertag outer m') =>
     HasCatch innertag inner (t m'),
     HasCatch outertag outer m
   ) =>
-  (forall m'. HasCatch innertag inner m' => m' a) ->
+  (forall m'. (HasCatch innertag inner m') => m' a) ->
   m a
diff --git a/data/examples/declaration/value/function/fancy-forall-1-out.hs b/data/examples/declaration/value/function/fancy-forall-1-out.hs
--- a/data/examples/declaration/value/function/fancy-forall-1-out.hs
+++ b/data/examples/declaration/value/function/fancy-forall-1-out.hs
@@ -2,9 +2,9 @@
   forall outertag innertag t outer inner m a.
   ( forall x. Coercible (t m x) (m x),
     forall m'.
-    HasReader outertag outer m' =>
+    (HasReader outertag outer m') =>
     HasReader innertag inner (t m'),
     HasReader outertag outer m
   ) =>
-  (forall m'. HasReader innertag inner m' => m' a) ->
+  (forall m'. (HasReader innertag inner m') => m' a) ->
   m a
diff --git a/data/examples/declaration/value/function/let-multi-line-out.hs b/data/examples/declaration/value/function/let-multi-line-out.hs
--- a/data/examples/declaration/value/function/let-multi-line-out.hs
+++ b/data/examples/declaration/value/function/let-multi-line-out.hs
@@ -17,7 +17,7 @@
                    () -> undefined
    in go
 
-implicitParams :: HasCallStack => Int
+implicitParams :: (HasCallStack) => Int
 implicitParams =
   let ?cs = ?callstack
    in foo cs
diff --git a/data/examples/other/argument-comment-out.hs b/data/examples/other/argument-comment-out.hs
--- a/data/examples/other/argument-comment-out.hs
+++ b/data/examples/other/argument-comment-out.hs
@@ -5,12 +5,12 @@
 foo _ = True
 
 foo ::
-  Foo a =>
+  (Foo a) =>
   -- | Foo
   Int ->
   Int
 foo ::
-  Foo a =>
+  (Foo a) =>
   -- | Foo
   Int ->
   Int
diff --git a/data/examples/other/comment-empty-let-out.hs b/data/examples/other/comment-empty-let-out.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/other/comment-empty-let-out.hs
@@ -0,0 +1,6 @@
+foo =
+  let
+   in 10
+
+-- a comment
+bar = 20
diff --git a/data/examples/other/comment-empty-let.hs b/data/examples/other/comment-empty-let.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/other/comment-empty-let.hs
@@ -0,0 +1,6 @@
+foo =
+  let
+   in 10
+
+-- a comment
+bar = 20
diff --git a/data/examples/other/cpp/separation-0a-out.hs b/data/examples/other/cpp/separation-0a-out.hs
--- a/data/examples/other/cpp/separation-0a-out.hs
+++ b/data/examples/other/cpp/separation-0a-out.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE CPP #-}
 
-instance Stream s => Monad (ParsecT e s m) where
+instance (Stream s) => Monad (ParsecT e s m) where
   return = pure
   (>>=) = pBind
 #if !(MIN_VERSION_base(4,13,0))
diff --git a/data/examples/other/cpp/separation-0b-out.hs b/data/examples/other/cpp/separation-0b-out.hs
--- a/data/examples/other/cpp/separation-0b-out.hs
+++ b/data/examples/other/cpp/separation-0b-out.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE CPP #-}
 
-instance Stream s => Monad (ParsecT e s m) where
+instance (Stream s) => Monad (ParsecT e s m) where
   return = pure
   (>>=) = pBind
 #if !(MIN_VERSION_base(4,13,0))
diff --git a/extract-hackage-info/hackage-info.bin b/extract-hackage-info/hackage-info.bin
Binary files a/extract-hackage-info/hackage-info.bin and b/extract-hackage-info/hackage-info.bin differ
diff --git a/ormolu.cabal b/ormolu.cabal
--- a/ormolu.cabal
+++ b/ormolu.cabal
@@ -1,10 +1,10 @@
 cabal-version:      2.4
 name:               ormolu
-version:            0.5.2.0
+version:            0.5.3.0
 license:            BSD-3-Clause
 license-file:       LICENSE.md
 maintainer:         Mark Karpov <mark.karpov@tweag.io>
-tested-with:        ghc ==9.0.2 ghc ==9.2.4
+tested-with:        ghc ==9.0.2 ghc ==9.2.5
 homepage:           https://github.com/tweag/ormolu
 bug-reports:        https://github.com/tweag/ormolu/issues
 synopsis:           A formatter for Haskell source code
@@ -32,6 +32,13 @@
     default:     False
     manual:      True
 
+flag internal-bundle-fixities
+    description:
+        An internal ad-hoc flag that is enabled by default, Ormolu Live disables
+        it due to missing WASM TH support.
+
+    manual:      True
+
 library
     exposed-modules:
         Ormolu
@@ -105,7 +112,7 @@
         megaparsec >=9.0,
         mtl >=2.0 && <3.0,
         syb >=0.7 && <0.8,
-        text >=0.2 && <3.0
+        text >=2.0 && <3.0
 
     if flag(dev)
         ghc-options:
@@ -116,6 +123,9 @@
     else
         ghc-options: -O2 -Wall
 
+    if flag(internal-bundle-fixities)
+        cpp-options: -DBUNDLE_FIXITIES
+
 executable ormolu
     main-is:          Main.hs
     hs-source-dirs:   app
@@ -125,12 +135,13 @@
     build-depends:
         base >=4.12 && <5.0,
         containers >=0.5 && <0.7,
+        directory ^>=1.3,
         filepath >=1.2 && <1.5,
         ghc-lib-parser >=9.4 && <9.5,
-        gitrev >=1.3 && <1.4,
+        th-env >=0.1.1 && <0.2,
         optparse-applicative >=0.14 && <0.18,
         ormolu,
-        text >=0.2 && <3.0
+        text >=2.0 && <3.0
 
     if flag(dev)
         ghc-options:
@@ -161,6 +172,7 @@
 
     default-language:   Haskell2010
     build-depends:
+        Cabal-syntax >=3.8 && <3.9,
         QuickCheck >=2.14,
         base >=4.14 && <5.0,
         containers >=0.5 && <0.7,
@@ -173,7 +185,7 @@
         path >=0.6 && <0.10,
         path-io >=1.4.2 && <2.0,
         temporary ^>=1.3,
-        text >=0.2 && <3.0
+        text >=2.0 && <3.0
 
     if flag(dev)
         ghc-options: -Wall -Werror -Wunused-packages
diff --git a/src/Ormolu.hs b/src/Ormolu.hs
--- a/src/Ormolu.hs
+++ b/src/Ormolu.hs
@@ -1,18 +1,35 @@
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE RecordWildCards #-}
 
--- | A formatter for Haskell source code.
+-- | A formatter for Haskell source code. This module exposes the official
+-- stable API, other modules may be not as reliable.
 module Ormolu
-  ( ormolu,
+  ( -- * Top-level formatting functions
+    ormolu,
     ormoluFile,
     ormoluStdin,
+
+    -- * Configuration
     Config (..),
     ColorMode (..),
     RegionIndices (..),
     SourceType (..),
     defaultConfig,
     detectSourceType,
+    refineConfig,
     DynOption (..),
+
+    -- * Cabal info
+    CabalUtils.CabalSearchResult (..),
+    CabalUtils.CabalInfo (..),
+    CabalUtils.getCabalInfoForSourceFile,
+
+    -- * Fixity overrides
+    FixityMap,
+    getFixityOverridesForSourceFile,
+
+    -- * Working with exceptions
     OrmoluException (..),
     withPrettyOrmoluExceptions,
   )
@@ -21,6 +38,8 @@
 import Control.Exception
 import Control.Monad
 import Control.Monad.IO.Class (MonadIO (..))
+import qualified Data.Map.Strict as Map
+import qualified Data.Set as Set
 import Data.Text (Text)
 import qualified Data.Text as T
 import Debug.Trace
@@ -36,14 +55,15 @@
 import Ormolu.Parser.Result
 import Ormolu.Printer
 import Ormolu.Utils (showOutputable)
+import qualified Ormolu.Utils.Cabal as CabalUtils
+import Ormolu.Utils.Fixity (getFixityOverridesForSourceFile)
 import Ormolu.Utils.IO
 import System.FilePath
 
--- | Format a 'String', return formatted version as 'Text'.
+-- | Format a 'Text'.
 --
 -- The function
 --
---     * Takes 'String' because that's what GHC parser accepts.
 --     * Needs 'IO' because some functions from GHC that are necessary to
 --       setup parsing context require 'IO'. There should be no visible
 --       side-effects though.
@@ -54,16 +74,16 @@
 -- the 'cfgSourceType' field. Autodetection of source type won't happen
 -- here, see 'detectSourceType'.
 ormolu ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Ormolu configuration
   Config RegionIndices ->
   -- | Location of source file
   FilePath ->
   -- | Input to format
-  String ->
+  Text ->
   m Text
 ormolu cfgWithIndices path originalInput = do
-  let totalLines = length (lines originalInput)
+  let totalLines = length (T.lines originalInput)
       cfg = regionIndicesToDeltas totalLines <$> cfgWithIndices
       fixityMap =
         -- It is important to keep all arguments (but last) of
@@ -95,9 +115,9 @@
         fixityMap
         OrmoluOutputParsingFailed
         path
-        (T.unpack formattedText)
+        formattedText
     unless (cfgUnsafe cfg) . liftIO $ do
-      let diff = case diffText (T.pack originalInput) formattedText path of
+      let diff = case diffText originalInput formattedText path of
             Nothing -> error "AST differs, yet no changes have been introduced"
             Just x -> x
       when (length result0 /= length result1) $
@@ -124,7 +144,7 @@
 -- the 'cfgSourceType' field. Autodetection of source type won't happen
 -- here, see 'detectSourceType'.
 ormoluFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Ormolu configuration
   Config RegionIndices ->
   -- | Location of source file
@@ -132,7 +152,7 @@
   -- | Resulting rendition
   m Text
 ormoluFile cfg path =
-  readFileUtf8 path >>= ormolu cfg path . T.unpack
+  readFileUtf8 path >>= ormolu cfg path
 
 -- | Read input from stdin and format it.
 --
@@ -140,20 +160,61 @@
 -- the 'cfgSourceType' field. Autodetection of source type won't happen
 -- here, see 'detectSourceType'.
 ormoluStdin ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Ormolu configuration
   Config RegionIndices ->
   -- | Resulting rendition
   m Text
 ormoluStdin cfg =
-  getContentsUtf8 >>= ormolu cfg "<stdin>" . T.unpack
+  getContentsUtf8 >>= ormolu cfg "<stdin>"
 
+-- | Refine a 'Config' by incorporating given 'SourceType', 'CabalInfo', and
+-- fixity overrides 'FixityMap'. You can use 'detectSourceType' to deduce
+-- 'SourceType' based on the file extension,
+-- 'CabalUtils.getCabalInfoForSourceFile' to obtain 'CabalInfo' and
+-- 'getFixityOverridesForSourceFile' for 'FixityMap'.
+--
+-- @since 0.5.3.0
+refineConfig ::
+  -- | Source type to use
+  SourceType ->
+  -- | Cabal info for the file, if available
+  Maybe CabalUtils.CabalInfo ->
+  -- | Fixity overrides, if available
+  Maybe FixityMap ->
+  -- | 'Config' to refine
+  Config region ->
+  -- | Refined 'Config'
+  Config region
+refineConfig sourceType mcabalInfo mfixityOverrides rawConfig =
+  rawConfig
+    { cfgDynOptions = cfgDynOptions rawConfig ++ dynOptsFromCabal,
+      cfgFixityOverrides =
+        Map.unionWith (<>) (cfgFixityOverrides rawConfig) fixityOverrides,
+      cfgDependencies =
+        Set.union (cfgDependencies rawConfig) depsFromCabal,
+      cfgSourceType = sourceType
+    }
+  where
+    fixityOverrides =
+      case mfixityOverrides of
+        Nothing -> Map.empty
+        Just x -> x
+    (dynOptsFromCabal, depsFromCabal) =
+      case mcabalInfo of
+        Nothing -> ([], Set.empty)
+        Just CabalUtils.CabalInfo {..} ->
+          -- It makes sense to take into account the operator info for the
+          -- package itself if we know it, as if it were its own
+          -- dependency.
+          (ciDynOpts, Set.insert ciPackageName ciDependencies)
+
 ----------------------------------------------------------------------------
 -- Helpers
 
 -- | A wrapper around 'parseModule'.
 parseModule' ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Ormolu configuration
   Config RegionDeltas ->
   -- | Fixity Map for operators
@@ -163,7 +224,7 @@
   -- | File name to use in errors
   FilePath ->
   -- | Actual input for the parser
-  String ->
+  Text ->
   m ([GHC.Warn], [SourceSnippet])
 parseModule' cfg fixityMap mkException path str = do
   (warnings, r) <- parseModule cfg fixityMap path str
diff --git a/src/Ormolu/Config.hs b/src/Ormolu/Config.hs
--- a/src/Ormolu/Config.hs
+++ b/src/Ormolu/Config.hs
@@ -19,6 +19,7 @@
 import qualified Data.Map.Strict as Map
 import Data.Set (Set)
 import qualified Data.Set as Set
+import Distribution.Types.PackageName (PackageName)
 import GHC.Generics (Generic)
 import qualified GHC.Types.SrcLoc as GHC
 import Ormolu.Fixity (FixityMap)
@@ -39,7 +40,7 @@
     -- | Fixity overrides
     cfgFixityOverrides :: FixityMap,
     -- | Known dependencies, if any
-    cfgDependencies :: !(Set String),
+    cfgDependencies :: !(Set PackageName),
     -- | Do formatting faster but without automatic detection of defects
     cfgUnsafe :: !Bool,
     -- | Output information useful for debugging
diff --git a/src/Ormolu/Diff/ParseResult.hs b/src/Ormolu/Diff/ParseResult.hs
--- a/src/Ormolu/Diff/ParseResult.hs
+++ b/src/Ormolu/Diff/ParseResult.hs
@@ -74,7 +74,7 @@
 --     * LayoutInfo (brace style) in extension fields
 --     * Empty contexts in type classes
 --     * Parens around derived type classes
-matchIgnoringSrcSpans :: Data a => a -> a -> ParseResultDiff
+matchIgnoringSrcSpans :: (Data a) => a -> a -> ParseResultDiff
 matchIgnoringSrcSpans a = genericQuery a
   where
     genericQuery :: GenericQ (GenericQ ParseResultDiff)
@@ -150,7 +150,7 @@
     unicodeArrowStyleEq (HsLinearArrow _) (castArrow -> Just (HsLinearArrow _)) = Same
     unicodeArrowStyleEq (HsExplicitMult _ _ t) (castArrow -> Just (HsExplicitMult _ _ t')) = genericQuery t t'
     unicodeArrowStyleEq _ _ = Different []
-    castArrow :: Typeable a => a -> Maybe (HsArrow GhcPs)
+    castArrow :: (Typeable a) => a -> Maybe (HsArrow GhcPs)
     castArrow = cast
     -- LayoutInfo ~ XClassDecl GhcPs tracks brace information
     layoutInfoEq :: LayoutInfo -> GenericQ ParseResultDiff
diff --git a/src/Ormolu/Exception.hs b/src/Ormolu/Exception.hs
--- a/src/Ormolu/Exception.hs
+++ b/src/Ormolu/Exception.hs
@@ -4,6 +4,7 @@
 -- | 'OrmoluException' type and surrounding definitions.
 module Ormolu.Exception
   ( OrmoluException (..),
+    printOrmoluException,
     withPrettyOrmoluExceptions,
   )
 where
diff --git a/src/Ormolu/Fixity.hs b/src/Ormolu/Fixity.hs
--- a/src/Ormolu/Fixity.hs
+++ b/src/Ormolu/Fixity.hs
@@ -1,10 +1,17 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE TemplateHaskell #-}
 
 -- | Definitions for fixity analysis.
 module Ormolu.Fixity
-  ( FixityDirection (..),
+  ( OpName,
+    pattern OpName,
+    unOpName,
+    occOpName,
+    FixityDirection (..),
     FixityInfo (..),
     FixityMap,
     LazyFixityMap,
@@ -23,31 +30,51 @@
 import qualified Data.Binary as Binary
 import qualified Data.Binary.Get as Binary
 import qualified Data.ByteString.Lazy as BL
-import Data.FileEmbed (embedFile)
 import Data.Foldable (foldl')
 import Data.List.NonEmpty (NonEmpty ((:|)))
 import qualified Data.List.NonEmpty as NE
 import Data.Map.Strict (Map)
 import qualified Data.Map.Strict as Map
 import Data.Maybe (fromMaybe)
-import Data.MemoTrie (HasTrie, memo)
+import Data.MemoTrie (memo)
 import Data.Semigroup (sconcat)
 import Data.Set (Set)
 import qualified Data.Set as Set
+import Distribution.Types.PackageName (PackageName, mkPackageName, unPackageName)
 import Ormolu.Fixity.Internal
+#if BUNDLE_FIXITIES
+import Data.FileEmbed (embedFile)
+#else
+import qualified Data.ByteString.Unsafe as BU
+import Foreign.Ptr
+import System.Environment (getEnv)
+import System.IO.Unsafe (unsafePerformIO)
+#endif
 
-packageToOps :: Map String FixityMap
-packageToPopularity :: Map String Int
+packageToOps :: Map PackageName FixityMap
+packageToPopularity :: Map PackageName Int
+#if BUNDLE_FIXITIES
 HackageInfo packageToOps packageToPopularity =
   Binary.runGet Binary.get $
     BL.fromStrict $(embedFile "extract-hackage-info/hackage-info.bin")
+#else
+-- The GHC WASM backend does not yet support Template Haskell, so we instead
+-- pass in the encoded fixity DB at runtime by storing the pointer and length of
+-- the bytes in an environment variable.
+HackageInfo packageToOps packageToPopularity = unsafePerformIO $ do
+  (ptr, len) <- read <$> getEnv "ORMOLU_HACKAGE_INFO"
+  Binary.runGet Binary.get . BL.fromStrict
+    <$> BU.unsafePackMallocCStringLen (intPtrToPtr $ IntPtr ptr, len)
+{-# NOINLINE packageToOps #-}
+{-# NOINLINE packageToPopularity #-}
+#endif
 
 -- | List of packages shipped with GHC, for which the download count from
 -- Hackage does not reflect their high popularity.
 -- See https://github.com/tweag/ormolu/pull/830#issuecomment-986609572.
 -- "base" is not is this list, because it is already whitelisted
 -- by buildFixityMap'.
-bootPackages :: Set String
+bootPackages :: Set PackageName
 bootPackages =
   Set.fromList
     [ "array",
@@ -87,7 +114,7 @@
   -- instead of being merged with them
   Float ->
   -- | Explicitly known dependencies
-  Set String ->
+  Set PackageName ->
   -- | Resulting map
   LazyFixityMap
 buildFixityMap = buildFixityMap' packageToOps packageToPopularity bootPackages
@@ -98,17 +125,17 @@
 -- specify the package databases used to build the final fixity map.
 buildFixityMap' ::
   -- | Map from package to fixity map for operators defined in this package
-  Map String FixityMap ->
+  Map PackageName FixityMap ->
   -- | Map from package to popularity
-  Map String Int ->
+  Map PackageName Int ->
   -- | Higher priority packages
-  Set String ->
+  Set PackageName ->
   -- | Popularity ratio threshold, after which a very popular package will
   -- completely rule out conflicting definitions coming from other packages
   -- instead of being merged with them
   Float ->
   -- | Explicitly known dependencies
-  Set String ->
+  Set PackageName ->
   -- | Resulting map
   LazyFixityMap
 buildFixityMap'
@@ -147,8 +174,8 @@
             remainingFixityMap
           ]
 
-memoSet :: (HasTrie a, Eq a) => (Set a -> v) -> Set a -> v
-memoSet f = memo (f . Set.fromAscList) . Set.toAscList
+memoSet :: (Set PackageName -> v) -> Set PackageName -> v
+memoSet f = memo (f . Set.fromAscList . fmap mkPackageName) . fmap unPackageName . Set.toAscList
 
 -- | Merge a list of individual fixity maps, coming from different packages.
 -- Package popularities and the given threshold are used to choose between
@@ -156,11 +183,11 @@
 -- strategies when conflicting definitions are encountered for an operator.
 mergeFixityMaps ::
   -- | Map from package name to 30-days download count
-  Map String Int ->
+  Map PackageName Int ->
   -- | Popularity ratio threshold
   Float ->
   -- | List of (package name, package fixity map) to merge
-  [(String, FixityMap)] ->
+  [(PackageName, FixityMap)] ->
   -- | Resulting fixity map
   FixityMap
 mergeFixityMaps popularityMap threshold packageMaps =
@@ -174,7 +201,7 @@
     --   op1 -map-> {definitions1 -map-> originPackages}
     --   op1 -map-> {definitions2 -map-> originPackages}
     -- so we merge the keys (which have the type:
-    -- Map FixityInfo (NonEmpty String))
+    -- Map FixityInfo (NonEmpty PackageName))
     -- using 'Map.unionWith (<>)', to "concatenate" the list of
     -- definitions for this operator, and to also "concatenate" origin
     -- packages if a same definition is found in both maps
@@ -202,7 +229,7 @@
     getScores ::
       -- Map for a given operator associating each of its conflicting
       -- definitions with the packages that define it
-      Map FixityInfo (NonEmpty String) ->
+      Map FixityInfo (NonEmpty PackageName) ->
       -- Map for a given operator associating each of its conflicting
       -- definitions with their score (= sum of the popularity of the
       -- packages that define it)
@@ -212,18 +239,18 @@
         (sum . fmap (fromMaybe 0 . flip Map.lookup popularityMap))
     opFixityMapFrom ::
       -- (packageName, package fixity map)
-      (String, FixityMap) ->
+      (PackageName, FixityMap) ->
       -- Map associating each operator of the package with a
       -- {map for a given operator associating each of its definitions with
       -- the list of packages that define it}
       -- (this list can only be == [packageName] in the context of this
       -- function)
-      Map String (Map FixityInfo (NonEmpty String))
+      Map OpName (Map FixityInfo (NonEmpty PackageName))
     opFixityMapFrom (packageName, opsMap) =
       Map.map
         (flip Map.singleton (packageName :| []))
         opsMap
-    maxWith :: Ord b => (a -> b) -> NonEmpty a -> NonEmpty a
+    maxWith :: (Ord b) => (a -> b) -> NonEmpty a -> NonEmpty a
     maxWith f xs = snd $ foldl' comp (f h, h :| []) t
       where
         h :| t = xs
diff --git a/src/Ormolu/Fixity/Internal.hs b/src/Ormolu/Fixity/Internal.hs
--- a/src/Ormolu/Fixity/Internal.hs
+++ b/src/Ormolu/Fixity/Internal.hs
@@ -1,12 +1,19 @@
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ViewPatterns #-}
 
 module Ormolu.Fixity.Internal
-  ( FixityDirection (..),
+  ( OpName,
+    pattern OpName,
+    unOpName,
+    occOpName,
+    FixityDirection (..),
     FixityInfo (..),
     defaultFixityInfo,
     colonFixityInfo,
@@ -18,10 +25,19 @@
 where
 
 import Data.Binary (Binary)
+import Data.ByteString.Short (ShortByteString)
+import qualified Data.ByteString.Short as SBS
 import Data.Foldable (asum)
 import Data.Map.Strict (Map)
 import qualified Data.Map.Strict as Map
+import Data.String (IsString (..))
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import Distribution.Types.PackageName (PackageName)
+import GHC.Data.FastString (fs_sbs)
 import GHC.Generics (Generic)
+import GHC.Types.Name (OccName (occNameFS))
 
 -- | Fixity direction.
 data FixityDirection
@@ -79,8 +95,36 @@
           (Just a, Just b) | a == b -> Just a
           _ -> Nothing
 
+-- | An operator name.
+newtype OpName = MkOpName
+  { -- | Invariant: UTF-8 encoded
+    getOpName :: ShortByteString
+  }
+  deriving newtype (Eq, Ord, Binary)
+
+-- | Convert an 'OpName' to 'Text'.
+unOpName :: OpName -> Text
+unOpName = T.decodeUtf8 . SBS.fromShort . getOpName
+
+pattern OpName :: Text -> OpName
+pattern OpName opName <- (unOpName -> opName)
+  where
+    OpName = MkOpName . SBS.toShort . T.encodeUtf8
+
+{-# COMPLETE OpName #-}
+
+-- | Convert an 'OccName to an 'OpName'.
+occOpName :: OccName -> OpName
+occOpName = MkOpName . fs_sbs . occNameFS
+
+instance Show OpName where
+  show = T.unpack . unOpName
+
+instance IsString OpName where
+  fromString = OpName . T.pack
+
 -- | Map from the operator name to its 'FixityInfo'.
-type FixityMap = Map String FixityInfo
+type FixityMap = Map OpName FixityInfo
 
 -- | A variant of 'FixityMap', represented as a lazy union of several
 -- 'FixityMap's.
@@ -89,16 +133,16 @@
 
 -- | Lookup a 'FixityInfo' of an operator. This might have drastically
 -- different performance depending on whether this is an "unusual" operator.
-lookupFixity :: String -> LazyFixityMap -> Maybe FixityInfo
+lookupFixity :: OpName -> LazyFixityMap -> Maybe FixityInfo
 lookupFixity op (LazyFixityMap maps) = asum (Map.lookup op <$> maps)
 
 -- | The map of operators declared by each package and the popularity of
 -- each package, if available.
 data HackageInfo
   = HackageInfo
-      (Map String FixityMap)
+      (Map PackageName FixityMap)
       -- ^ Map from package name to a map from operator name to its fixity
-      (Map String Int)
+      (Map PackageName Int)
       -- ^ Map from package name to its 30-days download count from Hackage
   deriving stock (Generic)
   deriving anyclass (Binary)
diff --git a/src/Ormolu/Fixity/Parser.hs b/src/Ormolu/Fixity/Parser.hs
--- a/src/Ormolu/Fixity/Parser.hs
+++ b/src/Ormolu/Fixity/Parser.hs
@@ -16,8 +16,9 @@
 import qualified Data.Char as Char
 import qualified Data.Map.Strict as Map
 import Data.Text (Text)
+import qualified Data.Text as T
 import Data.Void (Void)
-import Ormolu.Fixity.Internal
+import Ormolu.Fixity
 import Text.Megaparsec
 import Text.Megaparsec.Char
 import qualified Text.Megaparsec.Char.Lexer as L
@@ -39,19 +40,19 @@
   -- | Expression to parse
   Text ->
   -- | Parse result
-  Either (ParseErrorBundle Text Void) [(String, FixityInfo)]
+  Either (ParseErrorBundle Text Void) [(OpName, FixityInfo)]
 parseFixityDeclaration = runParser (pFixity <* eof) ""
 
 pFixityMap :: Parser FixityMap
 pFixityMap =
   Map.fromListWith (<>) . mconcat
-    <$> many (pFixity <* newline <* hidden space)
+    <$> many (pFixity <* eol <* hidden space)
     <* eof
 
 -- | Parse a single fixity declaration, such as
 --
 -- > infixr 4 +++, >>>
-pFixity :: Parser [(String, FixityInfo)]
+pFixity :: Parser [(OpName, FixityInfo)]
 pFixity = do
   fiDirection <- Just <$> pFixityDirection
   hidden hspace1
@@ -72,19 +73,16 @@
     ]
 
 -- | See <https://www.haskell.org/onlinereport/haskell2010/haskellch2.html>
-pOperator :: Parser String
-pOperator = tickedOperator <|> normalOperator
+pOperator :: Parser OpName
+pOperator = OpName <$> (tickedOperator <|> normalOperator)
   where
     tickedOperator = between tick tick haskellIdentifier
     tick = char '`'
-    haskellIdentifier = do
-      x <- letterChar
-      xs <- many (alphaNumChar <|> char '_' <|> char '\'')
-      return (x : xs)
-    normalOperator = some operatorChar
-    operatorChar =
-      satisfy
-        (\x -> (Char.isSymbol x || Char.isPunctuation x) && isNotExcluded x)
-        <?> "operator character"
-      where
-        isNotExcluded x = x /= ',' && x /= '`' && x /= '(' && x /= ')'
+    haskellIdentifier =
+      T.cons
+        <$> letterChar
+        <*> takeWhileP Nothing (\x -> Char.isAlphaNum x || x == '_' || x == '\'')
+    normalOperator =
+      takeWhile1P (Just "operator character") $ \x ->
+        (Char.isSymbol x || Char.isPunctuation x)
+          && (x /= ',' && x /= '`' && x /= '(' && x /= ')')
diff --git a/src/Ormolu/Fixity/Printer.hs b/src/Ormolu/Fixity/Printer.hs
--- a/src/Ormolu/Fixity/Printer.hs
+++ b/src/Ormolu/Fixity/Printer.hs
@@ -10,11 +10,12 @@
 import qualified Data.Char as Char
 import qualified Data.Map.Strict as Map
 import Data.Text (Text)
+import qualified Data.Text as T
 import qualified Data.Text.Lazy as TL
 import Data.Text.Lazy.Builder (Builder)
 import qualified Data.Text.Lazy.Builder as B
 import qualified Data.Text.Lazy.Builder.Int as B
-import Ormolu.Fixity.Internal
+import Ormolu.Fixity
 
 -- | Print out a textual representation of a 'FixityMap'.
 printFixityMap :: FixityMap -> Text
@@ -26,7 +27,7 @@
     . concatMap decompose
     . Map.toList
   where
-    decompose :: (String, FixityInfo) -> [(FixityDirection, Int, String)]
+    decompose :: (OpName, FixityInfo) -> [(FixityDirection, Int, OpName)]
     decompose (operator, FixityInfo {..}) =
       let forDirection dir =
             (dir, fiMinPrecedence, operator)
@@ -36,8 +37,8 @@
        in case fiDirection of
             Nothing -> concatMap forDirection [InfixL, InfixR]
             Just dir -> forDirection dir
-    renderOne :: (FixityDirection, Int, String) -> Builder
-    renderOne (fixityDirection, n, operator) =
+    renderOne :: (FixityDirection, Int, OpName) -> Builder
+    renderOne (fixityDirection, n, OpName operator) =
       mconcat
         [ case fixityDirection of
             InfixL -> "infixl"
@@ -47,9 +48,8 @@
           B.decimal n,
           " ",
           if isTickedOperator operator
-            then "`" <> B.fromString operator <> "`"
-            else B.fromString operator,
+            then "`" <> B.fromText operator <> "`"
+            else B.fromText operator,
           "\n"
         ]
-    isTickedOperator [] = True
-    isTickedOperator (x : _) = Char.isLetter x
+    isTickedOperator = maybe True (Char.isLetter . fst) . T.uncons
diff --git a/src/Ormolu/Parser.hs b/src/Ormolu/Parser.hs
--- a/src/Ormolu/Parser.hs
+++ b/src/Ormolu/Parser.hs
@@ -13,16 +13,18 @@
 where
 
 import Control.Exception
-import Control.Monad.Except
+import Control.Monad
+import Control.Monad.Except (ExceptT (..), runExceptT)
+import Control.Monad.IO.Class
 import Data.Char (isSpace)
 import Data.Functor
 import Data.Generics
 import qualified Data.List as L
 import qualified Data.List.NonEmpty as NE
+import Data.Text (Text)
 import GHC.Data.Bag (bagToList)
 import qualified GHC.Data.EnumSet as EnumSet
 import qualified GHC.Data.FastString as GHC
-import qualified GHC.Data.StringBuffer as GHC
 import qualified GHC.Driver.CmdLine as GHC
 import GHC.Driver.Config.Parser (initParserOpts)
 import GHC.Driver.Session as GHC
@@ -46,11 +48,11 @@
 import Ormolu.Parser.Result
 import Ormolu.Processing.Common
 import Ormolu.Processing.Preprocess
-import Ormolu.Utils (incSpanLine, showOutputable)
+import Ormolu.Utils (incSpanLine, showOutputable, textToStringBuffer)
 
 -- | Parse a complete module from string.
 parseModule ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Ormolu configuration
   Config RegionDeltas ->
   -- | Fixity map to include in the resulting 'ParseResult's
@@ -58,7 +60,7 @@
   -- | File name (only for source location annotations)
   FilePath ->
   -- | Input for parser
-  String ->
+  Text ->
   m
     ( [GHC.Warn],
       Either (SrcSpan, String) [SourceSnippet]
@@ -90,12 +92,12 @@
   pure (warnings, snippets)
 
 parseModuleSnippet ::
-  MonadIO m =>
+  (MonadIO m) =>
   Config RegionDeltas ->
   LazyFixityMap ->
   DynFlags ->
   FilePath ->
-  String ->
+  Text ->
   m (Either (SrcSpan, String) ParseResult)
 parseModuleSnippet Config {..} fixityMap dynFlags path rawInput = liftIO $ do
   let (input, indent) = removeIndentation . linesInRegion cfgRegion $ rawInput
@@ -153,7 +155,7 @@
 normalizeModule :: HsModule -> HsModule
 normalizeModule hsmod =
   everywhere
-    (mkT dropBlankTypeHaddocks)
+    (extT (mkT dropBlankTypeHaddocks) patchContext)
     hsmod
       { hsmodImports =
           normalizeImports (hsmodImports hsmod),
@@ -173,11 +175,15 @@
       IEGroup _ _ s -> isBlankDocString s
       IEDoc _ s -> isBlankDocString s
       _ -> False
-
     dropBlankTypeHaddocks = \case
       L _ (HsDocTy _ ty s) :: LHsType GhcPs
         | isBlankDocString s -> ty
       a -> a
+    patchContext :: LHsContext GhcPs -> LHsContext GhcPs
+    patchContext = mapLoc $ \case
+      [x@(L _ (HsParTy _ _))] -> [x]
+      [x@(L lx _)] -> [L lx (HsParTy EpAnnNotUsed x)]
+      xs -> xs
 
 -- | Enable all language extensions that we think should be enabled by
 -- default for ease of use.
@@ -227,13 +233,13 @@
   -- | Module path
   FilePath ->
   -- | Module contents
-  String ->
+  Text ->
   -- | Parse result
   GHC.ParseResult a
 runParser parser flags filename input = GHC.unP parser parseState
   where
     location = mkRealSrcLoc (GHC.mkFastString filename) 1 1
-    buffer = GHC.stringToStringBuffer input
+    buffer = textToStringBuffer input
     parseState = GHC.initParserState (initParserOpts flags) buffer location
 
 ----------------------------------------------------------------------------
@@ -247,14 +253,14 @@
   -- | File name (only for source location annotations)
   FilePath ->
   -- | Input for parser
-  String ->
+  Text ->
   IO (Either String ([GHC.Warn], DynFlags))
 parsePragmasIntoDynFlags flags extraOpts filepath str =
   catchErrors $ do
     let (_warnings, fileOpts) =
           GHC.getOptions
             (initParserOpts flags)
-            (GHC.stringToStringBuffer str)
+            (textToStringBuffer str)
             filepath
     (flags', leftovers, warnings) <-
       parseDynamicFilePragma flags (extraOpts <> fileOpts)
diff --git a/src/Ormolu/Parser/CommentStream.hs b/src/Ormolu/Parser/CommentStream.hs
--- a/src/Ormolu/Parser/CommentStream.hs
+++ b/src/Ormolu/Parser/CommentStream.hs
@@ -1,8 +1,10 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE ViewPatterns #-}
 
 -- | Functions for working with comment stream.
 module Ormolu.Parser.CommentStream
@@ -23,12 +25,13 @@
 import Data.Char (isSpace)
 import Data.Data (Data)
 import Data.Generics.Schemes
-import qualified Data.List as L
 import Data.List.NonEmpty (NonEmpty (..))
 import qualified Data.List.NonEmpty as NE
 import qualified Data.Map.Lazy as M
 import Data.Maybe
 import qualified Data.Set as S
+import Data.Text (Text)
+import qualified Data.Text as T
 import qualified GHC.Data.Strict as Strict
 import GHC.Hs (HsModule)
 import GHC.Hs.Doc
@@ -52,7 +55,7 @@
 -- removed from the 'CommentStream'.
 mkCommentStream ::
   -- | Original input
-  String ->
+  Text ->
   -- | Module to use for comment extraction
   HsModule ->
   -- | Stack header, pragmas, and comment stream
@@ -120,7 +123,7 @@
 -- atoms before beginning of the comment in the original input. The
 -- 'NonEmpty' list inside contains lines of multiline comment @{- … -}@ or
 -- just single item\/line otherwise.
-data Comment = Comment Bool (NonEmpty String)
+data Comment = Comment Bool (NonEmpty Text)
   deriving (Eq, Show, Data)
 
 -- | Normalize comment string. Sometimes one multi-line comment is turned
@@ -128,37 +131,36 @@
 -- each line.
 mkComment ::
   -- | Lines of original input with their indices
-  [(Int, String)] ->
+  [(Int, Text)] ->
   -- | Raw comment string
-  RealLocated String ->
+  RealLocated Text ->
   -- | Remaining lines of original input and the constructed 'Comment'
-  ([(Int, String)], RealLocated Comment)
+  ([(Int, Text)], RealLocated Comment)
 mkComment ls (L l s) = (ls', comment)
   where
     comment =
-      L l . Comment atomsBefore . removeConseqBlanks . fmap dropTrailing $
-        case NE.nonEmpty (lines s) of
+      L l . Comment atomsBefore . removeConseqBlanks . fmap T.stripEnd $
+        case NE.nonEmpty (T.lines s) of
           Nothing -> s :| []
           Just (x :| xs) ->
             let getIndent y =
-                  if all isSpace y
+                  if T.all isSpace y
                     then startIndent
-                    else length (takeWhile isSpace y)
+                    else T.length (T.takeWhile isSpace y)
                 n = minimum (startIndent : fmap getIndent xs)
-                commentPrefix = if "{-" `L.isPrefixOf` s then "" else "-- "
-             in x :| ((commentPrefix <>) . escapeHaddockTriggers . drop n <$> xs)
+                commentPrefix = if "{-" `T.isPrefixOf` s then "" else "-- "
+             in x :| ((commentPrefix <>) . escapeHaddockTriggers . T.drop n <$> xs)
     (atomsBefore, ls') =
       case dropWhile ((< commentLine) . fst) ls of
         [] -> (False, [])
         ((_, i) : ls'') ->
-          case take 2 (dropWhile isSpace i) of
+          case T.take 2 (T.stripStart i) of
             "--" -> (False, ls'')
             "{-" -> (False, ls'')
             _ -> (True, ls'')
-    dropTrailing = L.dropWhileEnd isSpace
     startIndent
       -- srcSpanStartCol counts columns starting from 1, so we subtract 1
-      | "{-" `L.isPrefixOf` s = srcSpanStartCol l - 1
+      | "{-" `T.isPrefixOf` s = srcSpanStartCol l - 1
       -- For single-line comments, the only case where xs != [] is when an
       -- invalid haddock comment composed of several single-line comments is
       -- encountered. In that case, each line of xs is prefixed with an
@@ -168,7 +170,7 @@
     commentLine = srcSpanStartLine l
 
 -- | Get a collection of lines from a 'Comment'.
-unComment :: Comment -> NonEmpty String
+unComment :: Comment -> NonEmpty Text
 unComment (Comment _ xs) = xs
 
 -- | Check whether the 'Comment' had some non-whitespace atoms in front of
@@ -178,7 +180,7 @@
 
 -- | Is this comment multiline-style?
 isMultilineComment :: Comment -> Bool
-isMultilineComment (Comment _ (x :| _)) = "{-" `L.isPrefixOf` x
+isMultilineComment (Comment _ (x :| _)) = "{-" `T.isPrefixOf` x
 
 ----------------------------------------------------------------------------
 -- Helpers
@@ -186,8 +188,8 @@
 -- | Detect and extract stack header if it is present.
 extractStackHeader ::
   -- | Comment stream to analyze
-  [RealLocated String] ->
-  ([RealLocated String], Maybe (RealLocated Comment))
+  [RealLocated Text] ->
+  ([RealLocated Text], Maybe (RealLocated Comment))
 extractStackHeader = \case
   [] -> ([], Nothing)
   (x : xs) ->
@@ -197,18 +199,18 @@
           else (x : xs, Nothing)
   where
     isStackHeader (Comment _ (x :| _)) =
-      "stack" `L.isPrefixOf` dropWhile isSpace (drop 2 x)
+      "stack" `T.isPrefixOf` T.stripStart (T.drop 2 x)
 
 -- | Extract pragmas and their associated comments.
 extractPragmas ::
   -- | Input
-  String ->
+  Text ->
   -- | Comment stream to analyze
-  [RealLocated String] ->
+  [RealLocated Text] ->
   ([RealLocated Comment], [([RealLocated Comment], Pragma)])
 extractPragmas input = go initialLs id id
   where
-    initialLs = zip [1 ..] (lines input)
+    initialLs = zip [1 ..] (T.lines input)
     go ls csSoFar pragmasSoFar = \case
       [] -> (csSoFar [], pragmasSoFar [])
       (x : xs) ->
@@ -229,8 +231,8 @@
                           then go' ls' [y'] ys
                           else go' ls [] xs
 
--- | Extract @'RealLocated' 'String'@ from 'GHC.LEpaComment'.
-unAnnotationComment :: GHC.LEpaComment -> Maybe (RealLocated String)
+-- | Extract @'RealLocated' 'Text'@ from 'GHC.LEpaComment'.
+unAnnotationComment :: GHC.LEpaComment -> Maybe (RealLocated Text)
 unAnnotationComment (L (GHC.Anchor anchor _) (GHC.EpaComment eck _)) =
   case eck of
     GHC.EpaDocComment s ->
@@ -239,48 +241,48 @@
             NestedDocString t _ -> Just t
             -- should not occur
             GeneratedDocString _ -> Nothing
-       in haddock trigger (renderHsDocString s)
-    GHC.EpaDocOptions s -> mkL s
-    GHC.EpaLineComment s -> mkL $
-      case take 3 s of
+       in haddock trigger (T.pack $ renderHsDocString s)
+    GHC.EpaDocOptions s -> mkL (T.pack s)
+    GHC.EpaLineComment (T.pack -> s) -> mkL $
+      case T.take 3 s of
         "-- " -> s
         "---" -> s
-        _ -> let s' = insertAt " " s 3 in s'
-    GHC.EpaBlockComment s -> mkL s
+        _ -> insertAt " " s 3
+    GHC.EpaBlockComment s -> mkL (T.pack s)
     GHC.EpaEofComment -> Nothing
   where
     mkL = Just . L anchor
-    insertAt x xs n = take (n - 1) xs ++ x ++ drop (n - 1) xs
+    insertAt x xs n = T.take (n - 1) xs <> x <> T.drop (n - 1) xs
     haddock mtrigger =
       mkL . dashPrefix . escapeHaddockTriggers . (trigger <>) <=< dropBlank
       where
         trigger = case mtrigger of
           Just HsDocStringNext -> "|"
           Just HsDocStringPrevious -> "^"
-          Just (HsDocStringNamed n) -> "$" <> n
-          Just (HsDocStringGroup k) -> replicate k '*'
+          Just (HsDocStringNamed n) -> "$" <> T.pack n
+          Just (HsDocStringGroup k) -> T.replicate k "*"
           Nothing -> ""
         dashPrefix s = "--" <> spaceIfNecessary <> s
           where
-            spaceIfNecessary = case s of
-              c : _ | c /= ' ' -> " "
+            spaceIfNecessary = case T.uncons s of
+              Just (c, _) | c /= ' ' -> " "
               _ -> ""
-        dropBlank :: String -> Maybe String
-        dropBlank s = if all isSpace s then Nothing else Just s
+        dropBlank :: Text -> Maybe Text
+        dropBlank s = if T.all isSpace s then Nothing else Just s
 
 -- | Remove consecutive blank lines.
-removeConseqBlanks :: NonEmpty String -> NonEmpty String
-removeConseqBlanks (x :| xs) = x :| go (null x) id xs
+removeConseqBlanks :: NonEmpty Text -> NonEmpty Text
+removeConseqBlanks (x :| xs) = x :| go (T.null x) id xs
   where
     go seenBlank acc = \case
       [] -> acc []
       (y : ys) ->
-        if seenBlank && null y
+        if seenBlank && T.null y
           then go True acc ys
-          else go (null y) (acc . (y :)) ys
+          else go (T.null y) (acc . (y :)) ys
 
 -- | Escape characters that can turn a line into a Haddock.
-escapeHaddockTriggers :: String -> String
+escapeHaddockTriggers :: Text -> Text
 escapeHaddockTriggers string
-  | h : _ <- string, h `elem` "|^*$" = '\\' : string
+  | Just (h, _) <- T.uncons string, h `elem` ("|^*$" :: [Char]) = T.cons '\\' string
   | otherwise = string
diff --git a/src/Ormolu/Parser/Pragma.hs b/src/Ormolu/Parser/Pragma.hs
--- a/src/Ormolu/Parser/Pragma.hs
+++ b/src/Ormolu/Parser/Pragma.hs
@@ -8,65 +8,62 @@
   )
 where
 
-import Control.Monad
-import Data.Char (isSpace, toLower)
-import qualified Data.List as L
-import GHC.Data.FastString (mkFastString, unpackFS)
-import GHC.Data.StringBuffer
+import Data.Char (isSpace)
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import GHC.Data.FastString (bytesFS, mkFastString)
 import GHC.Driver.Config.Parser (initParserOpts)
 import GHC.DynFlags (baseDynFlags)
 import qualified GHC.Parser.Lexer as L
 import GHC.Types.SrcLoc
+import Ormolu.Utils (textToStringBuffer)
 
 -- | Ormolu's representation of pragmas.
 data Pragma
   = -- | Language pragma
-    PragmaLanguage [String]
+    PragmaLanguage [Text]
   | -- | GHC options pragma
-    PragmaOptionsGHC String
+    PragmaOptionsGHC Text
   | -- | Haddock options pragma
-    PragmaOptionsHaddock String
+    PragmaOptionsHaddock Text
   deriving (Show, Eq)
 
 -- | Extract a pragma from a comment if possible, or return 'Nothing'
 -- otherwise.
 parsePragma ::
   -- | Comment to try to parse
-  String ->
+  Text ->
   Maybe Pragma
 parsePragma input = do
-  inputNoPrefix <- L.stripPrefix "{-#" input
-  guard ("#-}" `L.isSuffixOf` input)
-  let contents = take (length inputNoPrefix - 3) inputNoPrefix
-      (pragmaName, cs) = (break isSpace . dropWhile isSpace) contents
-  case toLower <$> pragmaName of
+  contents <- T.stripSuffix "#-}" =<< T.stripPrefix "{-#" input
+  let (pragmaName, cs) = (T.break isSpace . T.dropWhile isSpace) contents
+  case T.toLower pragmaName of
     "language" -> PragmaLanguage <$> parseExtensions cs
-    "options_ghc" -> Just $ PragmaOptionsGHC (trimSpaces cs)
-    "options_haddock" -> Just $ PragmaOptionsHaddock (trimSpaces cs)
+    "options_ghc" -> Just $ PragmaOptionsGHC (T.strip cs)
+    "options_haddock" -> Just $ PragmaOptionsHaddock (T.strip cs)
     _ -> Nothing
-  where
-    trimSpaces :: String -> String
-    trimSpaces = L.dropWhileEnd isSpace . dropWhile isSpace
 
 -- | Assuming the input consists of a series of tokens from a language
 -- pragma, return the set of enabled extensions.
-parseExtensions :: String -> Maybe [String]
+parseExtensions :: Text -> Maybe [Text]
 parseExtensions str = tokenize str >>= go
   where
     go = \case
-      [L.ITconid ext] -> return [unpackFS ext]
-      (L.ITconid ext : L.ITcomma : xs) -> (unpackFS ext :) <$> go xs
+      [L.ITconid ext] -> return [fsToText ext]
+      (L.ITconid ext : L.ITcomma : xs) -> (fsToText ext :) <$> go xs
       _ -> Nothing
+    fsToText = T.decodeUtf8 . bytesFS
 
 -- | Tokenize a given input using GHC's lexer.
-tokenize :: String -> Maybe [L.Token]
+tokenize :: Text -> Maybe [L.Token]
 tokenize input =
   case L.unP pLexer parseState of
     L.PFailed {} -> Nothing
     L.POk _ x -> Just x
   where
     location = mkRealSrcLoc (mkFastString "") 1 1
-    buffer = stringToStringBuffer input
+    buffer = textToStringBuffer input
     parseState = L.initParserState parserOpts buffer location
     parserOpts = initParserOpts baseDynFlags
 
diff --git a/src/Ormolu/Printer/Combinators.hs b/src/Ormolu/Printer/Combinators.hs
--- a/src/Ormolu/Printer/Combinators.hs
+++ b/src/Ormolu/Printer/Combinators.hs
@@ -99,7 +99,7 @@
 -- 'Located' wrapper, it should be “discharged” with a corresponding
 -- 'located' invocation.
 located ::
-  HasSrcSpan l =>
+  (HasSrcSpan l) =>
   -- | Thing to enter
   GenLocated l a ->
   -- | How to render inner value
@@ -115,7 +115,7 @@
 
 -- | A version of 'located' with arguments flipped.
 located' ::
-  HasSrcSpan l =>
+  (HasSrcSpan l) =>
   -- | How to render inner value
   (a -> R ()) ->
   -- | Thing to enter
diff --git a/src/Ormolu/Printer/Comments.hs b/src/Ormolu/Printer/Comments.hs
--- a/src/Ormolu/Printer/Comments.hs
+++ b/src/Ormolu/Printer/Comments.hs
@@ -15,7 +15,6 @@
 import Control.Monad
 import qualified Data.List.NonEmpty as NE
 import Data.Maybe (listToMaybe)
-import qualified Data.Text as T
 import GHC.Types.SrcLoc
 import Ormolu.Parser.CommentStream
 import Ormolu.Printer.Internal
@@ -264,7 +263,7 @@
   sitcc
     . sequence_
     . NE.intersperse newline
-    . fmap (txt . T.pack)
+    . fmap txt
     . unComment
     $ comment
   setSpanMark (CommentSpan spn)
@@ -280,7 +279,7 @@
   wrapper
     . sequence_
     . NE.toList
-    . fmap (registerPendingCommentLine position . T.pack)
+    . fmap (registerPendingCommentLine position)
     . unComment
     $ comment
   setSpanMark (CommentSpan spn)
diff --git a/src/Ormolu/Printer/Internal.hs b/src/Ormolu/Printer/Internal.hs
--- a/src/Ormolu/Printer/Internal.hs
+++ b/src/Ormolu/Printer/Internal.hs
@@ -56,6 +56,7 @@
   )
 where
 
+import Control.Monad
 import Control.Monad.Reader
 import Control.Monad.State.Strict
 import Data.Bool (bool)
@@ -251,7 +252,7 @@
 -- literals and similar. Everything that doesn't have inner structure but
 -- does have an 'Outputable' instance.
 atom ::
-  Outputable a =>
+  (Outputable a) =>
   a ->
   R ()
 atom = spit Atom . T.pack . showOutputable
diff --git a/src/Ormolu/Printer/Meat/Declaration.hs b/src/Ormolu/Printer/Meat/Declaration.hs
--- a/src/Ormolu/Printer/Meat/Declaration.hs
+++ b/src/Ormolu/Printer/Meat/Declaration.hs
@@ -209,10 +209,10 @@
       ) -> True
     _ -> False
 
-intersects :: Ord a => [a] -> [a] -> Bool
+intersects :: (Ord a) => [a] -> [a] -> Bool
 intersects a b = go (sort a) (sort b)
   where
-    go :: Ord a => [a] -> [a] -> Bool
+    go :: (Ord a) => [a] -> [a] -> Bool
     go _ [] = False
     go [] _ = False
     go (x : xs) (y : ys)
diff --git a/src/Ormolu/Printer/Meat/Declaration/OpTree.hs b/src/Ormolu/Printer/Meat/Declaration/OpTree.hs
--- a/src/Ormolu/Printer/Meat/Declaration/OpTree.hs
+++ b/src/Ormolu/Printer/Meat/Declaration/OpTree.hs
@@ -47,7 +47,7 @@
 
 -- | Decide if the operands of an operator chain should be hanging.
 opBranchPlacement ::
-  HasSrcSpan l =>
+  (HasSrcSpan l) =>
   -- | Placer function for nodes
   (ty -> Placement) ->
   -- | first expression of the chain
diff --git a/src/Ormolu/Printer/Meat/Declaration/Value.hs b/src/Ormolu/Printer/Meat/Declaration/Value.hs
--- a/src/Ormolu/Printer/Meat/Declaration/Value.hs
+++ b/src/Ormolu/Printer/Meat/Declaration/Value.hs
@@ -537,8 +537,11 @@
     -- 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}}} ->
-        located (L (RealSrcSpan anchor Strict.Nothing) ()) . const
+      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
       _ -> id
 
 p_ldotFieldOcc :: XRec GhcPs (DotFieldOcc GhcPs) -> R ()
@@ -1170,7 +1173,7 @@
     -- of an (*|) operator.
     -- The detection is a bit overcautious, as it adds the spaces as soon as
     -- HsStarTy is anywhere in the type/declaration.
-    handleStarIsType :: Data a => a -> R () -> R ()
+    handleStarIsType :: (Data a) => a -> R () -> R ()
     handleStarIsType a p
       | containsHsStarTy a = space *> p <* space
       | otherwise = p
@@ -1236,7 +1239,7 @@
 
 -- | Append each element in both lists with semigroups. If one list is shorter
 -- than the other, return the rest of the longer list unchanged.
-liftAppend :: Semigroup a => [a] -> [a] -> [a]
+liftAppend :: (Semigroup a) => [a] -> [a] -> [a]
 liftAppend [] [] = []
 liftAppend [] (y : ys) = y : ys
 liftAppend (x : xs) [] = x : xs
diff --git a/src/Ormolu/Printer/Meat/Pragma.hs b/src/Ormolu/Printer/Meat/Pragma.hs
--- a/src/Ormolu/Printer/Meat/Pragma.hs
+++ b/src/Ormolu/Printer/Meat/Pragma.hs
@@ -10,9 +10,9 @@
 import Control.Monad
 import Data.Char (isUpper)
 import qualified Data.List as L
-import Data.Maybe (listToMaybe)
 import Data.Set (Set)
 import qualified Data.Set as Set
+import Data.Text (Text)
 import qualified Data.Text as T
 import GHC.Driver.Flags (Language)
 import GHC.Types.SrcLoc
@@ -63,7 +63,7 @@
   forM_ (prepare ps) $ \(cs, (pragmaTy, x)) ->
     p_pragma cs pragmaTy x
 
-p_pragma :: [RealLocated Comment] -> PragmaTy -> String -> R ()
+p_pragma :: [RealLocated Comment] -> PragmaTy -> Text -> R ()
 p_pragma comments ty x = do
   forM_ comments $ \(L l comment) -> do
     spitCommentNow l comment
@@ -74,28 +74,28 @@
     OptionsGHC -> "OPTIONS_GHC"
     OptionsHaddock -> "OPTIONS_HADDOCK"
   space
-  txt (T.pack x)
+  txt x
   txt " #-}"
   newline
 
 -- | Classify a 'LanguagePragma'.
-classifyLanguagePragma :: String -> LanguagePragmaClass
+classifyLanguagePragma :: Text -> LanguagePragmaClass
 classifyLanguagePragma = \case
   str | str `Set.member` extensionPacks -> ExtensionPack
   "ImplicitPrelude" -> Final
   "CUSKs" -> Final
   str ->
-    case splitAt 2 str of
+    case T.splitAt 2 str of
       ("No", rest) ->
-        case listToMaybe rest of
+        case T.uncons rest of
           Nothing -> Normal
-          Just x ->
+          Just (x, _) ->
             if isUpper x
               then Disabling
               else Normal
       _ -> Normal
 
 -- | Extension packs, like @GHC2021@ and @Haskell2010@.
-extensionPacks :: Set String
+extensionPacks :: Set Text
 extensionPacks =
-  Set.fromList $ show <$> [minBound :: Language .. maxBound]
+  Set.fromList $ T.pack . show <$> [minBound :: Language .. maxBound]
diff --git a/src/Ormolu/Printer/Meat/Type.hs b/src/Ormolu/Printer/Meat/Type.hs
--- a/src/Ormolu/Printer/Meat/Type.hs
+++ b/src/Ormolu/Printer/Meat/Type.hs
@@ -227,7 +227,7 @@
     InferredSpec -> True
     SpecifiedSpec -> False
 
-p_hsTyVarBndr :: IsInferredTyVarBndr flag => HsTyVarBndr flag GhcPs -> R ()
+p_hsTyVarBndr :: (IsInferredTyVarBndr flag) => HsTyVarBndr flag GhcPs -> R ()
 p_hsTyVarBndr = \case
   UserTyVar _ flag x ->
     (if isInferred flag then braces N else id) $ p_rdrName x
@@ -242,7 +242,7 @@
 
 -- | Render several @forall@-ed variables.
 p_forallBndrs ::
-  HasSrcSpan l =>
+  (HasSrcSpan l) =>
   ForAllVisibility ->
   (a -> R ()) ->
   [GenLocated l a] ->
diff --git a/src/Ormolu/Printer/Operators.hs b/src/Ormolu/Printer/Operators.hs
--- a/src/Ormolu/Printer/Operators.hs
+++ b/src/Ormolu/Printer/Operators.hs
@@ -16,7 +16,6 @@
 import qualified Data.List.NonEmpty as NE
 import qualified Data.Map.Strict as Map
 import Data.Maybe (fromMaybe)
-import GHC.Types.Name.Occurrence (occNameString)
 import GHC.Types.Name.Reader
 import GHC.Types.SrcLoc
 import Ormolu.Fixity
@@ -44,10 +43,10 @@
 data OpInfo op = OpInfo
   { -- | The actual operator
     opiOp :: op,
-    -- | Its name, if available. We use 'Maybe String' here instead of
-    -- 'String' because the name-fetching function received by
-    -- 'reassociateOpTree' returns a 'Maybe'
-    opiName :: Maybe String,
+    -- | Its name, if available. We use 'Maybe OpName' here instead of 'OpName'
+    -- because the name-fetching function received by 'reassociateOpTree'
+    -- returns a 'Maybe'
+    opiName :: Maybe OpName,
     -- | Information about the fixity direction and precedence level of the
     -- operator
     opiFix :: FixityInfo
@@ -80,7 +79,7 @@
         _ -> False
 
 -- | Return combined 'SrcSpan's of all elements in this 'OpTree'.
-opTreeLoc :: HasSrcSpan l => OpTree (GenLocated l a) b -> SrcSpan
+opTreeLoc :: (HasSrcSpan l) => OpTree (GenLocated l a) b -> SrcSpan
 opTreeLoc (OpNode n) = getLoc' n
 opTreeLoc (OpBranches exprs _) =
   combineSrcSpans' . NE.fromList . fmap opTreeLoc $ exprs
@@ -125,7 +124,7 @@
   where
     toOpInfo o = OpInfo o mName fixityInfo
       where
-        mName = occNameString . rdrNameOcc <$> getOpName o
+        mName = occOpName . rdrNameOcc <$> getOpName o
         fixityInfo =
           fromMaybe
             defaultFixityInfo
diff --git a/src/Ormolu/Printer/SpanStream.hs b/src/Ormolu/Printer/SpanStream.hs
--- a/src/Ormolu/Printer/SpanStream.hs
+++ b/src/Ormolu/Printer/SpanStream.hs
@@ -27,7 +27,7 @@
 -- | Create 'SpanStream' from a data structure containing \"located\"
 -- elements.
 mkSpanStream ::
-  Data a =>
+  (Data a) =>
   -- | Data structure to inspect (AST)
   a ->
   SpanStream
diff --git a/src/Ormolu/Processing/Common.hs b/src/Ormolu/Processing/Common.hs
--- a/src/Ormolu/Processing/Common.hs
+++ b/src/Ormolu/Processing/Common.hs
@@ -18,24 +18,24 @@
 import qualified Data.Text as T
 import Ormolu.Config
 
--- | Remove indentation from a given 'String'. Return the input with
--- indentation removed and the detected indentation level.
-removeIndentation :: String -> (String, Int)
-removeIndentation (lines -> xs) = (unlines (drop n <$> xs), n)
+-- | Remove indentation from a given 'Text'. Return the input with indentation
+-- removed and the detected indentation level.
+removeIndentation :: Text -> (Text, Int)
+removeIndentation (T.lines -> xs) = (T.unlines (T.drop n <$> xs), n)
   where
     n = minimum (getIndent <$> xs)
     getIndent y =
-      if all isSpace y
+      if T.all isSpace y
         then 0
-        else length (takeWhile isSpace y)
+        else T.length (T.takeWhile isSpace y)
 
 -- | Add indentation to a 'Text'.
 reindent :: Int -> Text -> Text
 reindent i = T.unlines . fmap (T.replicate i " " <>) . T.lines
 
 -- | All lines in the region specified by 'RegionDeltas'.
-linesInRegion :: RegionDeltas -> String -> String
-linesInRegion RegionDeltas {..} (lines -> ls) = unlines middle
+linesInRegion :: RegionDeltas -> Text -> Text
+linesInRegion RegionDeltas {..} (T.lines -> ls) = T.unlines middle
   where
     (_, nonPrefix) = splitAt regionPrefixLength ls
     middle = take (length nonPrefix - regionSuffixLength) nonPrefix
diff --git a/src/Ormolu/Processing/Cpp.hs b/src/Ormolu/Processing/Cpp.hs
--- a/src/Ormolu/Processing/Cpp.hs
+++ b/src/Ormolu/Processing/Cpp.hs
@@ -6,11 +6,11 @@
   )
 where
 
-import Data.Char (isSpace)
 import Data.IntSet (IntSet)
 import qualified Data.IntSet as IntSet
-import qualified Data.List as L
 import Data.Maybe (isJust)
+import Data.Text (Text)
+import qualified Data.Text as T
 
 -- | State of the CPP processor.
 data State
@@ -23,8 +23,8 @@
   deriving (Eq, Show)
 
 -- | Return an 'IntSet' containing all lines which are affected by CPP.
-cppLines :: String -> IntSet
-cppLines input = IntSet.fromAscList $ go Outside (lines input `zip` [1 ..])
+cppLines :: Text -> IntSet
+cppLines input = IntSet.fromAscList $ go Outside (T.lines input `zip` [1 ..])
   where
     go _ [] = []
     go state ((line, i) : ls)
@@ -50,10 +50,10 @@
            in is <> go state' ls
       where
         for directive = isJust $ do
-          s <- dropWhile isSpace <$> L.stripPrefix "#" line
-          L.stripPrefix directive s
+          s <- T.stripStart <$> T.stripPrefix "#" line
+          T.stripPrefix directive s
         contState =
-          if "\\" `L.isSuffixOf` line && not inConditional
+          if "\\" `T.isSuffixOf` line && not inConditional
             then InContinuation
             else Outside
           where
diff --git a/src/Ormolu/Processing/Preprocess.hs b/src/Ormolu/Processing/Preprocess.hs
--- a/src/Ormolu/Processing/Preprocess.hs
+++ b/src/Ormolu/Processing/Preprocess.hs
@@ -32,7 +32,7 @@
   -- | Whether CPP is enabled
   Bool ->
   RegionDeltas ->
-  String ->
+  Text ->
   [Either Text RegionDeltas]
 preprocess cppEnabled region rawInput = rawSnippetsAndRegionsToFormat
   where
@@ -48,9 +48,9 @@
     interleave' = case regionsNotToFormat of
       r : _ | regionPrefixLength r == 0 -> interleave
       _ -> flip interleave
-    rawSnippets = T.pack . flip linesInRegion updatedInput <$> regionsNotToFormat
+    rawSnippets = flip linesInRegion updatedInput <$> regionsNotToFormat
       where
-        updatedInput = unlines . fmap updateLine . zip [1 ..] . lines $ rawInput
+        updatedInput = T.unlines . fmap updateLine . zip [1 ..] . T.lines $ rawInput
         updateLine (i, line) = IntMap.findWithDefault line i replacementLines
     rawSnippetsAndRegionsToFormat =
       interleave' (Left <$> rawSnippets) (Right <$> regionsToFormat)
@@ -64,7 +64,7 @@
     -- Extraneous raw snippets at the start/end are dropped afterwards.
     patchSeparatingBlankLines = \case
       Right r@RegionDeltas {..} ->
-        if all isSpace (linesInRegion r rawInput)
+        if T.all isSpace (linesInRegion r rawInput)
           then [blankRawSnippet]
           else
             [blankRawSnippet | isBlankLine regionPrefixLength]
@@ -73,14 +73,14 @@
       Left r -> [Left r]
       where
         blankRawSnippet = Left "\n"
-        isBlankLine i = isJust . mfilter (all isSpace) $ rawLines !!? i
+        isBlankLine i = isJust . mfilter (T.all isSpace) $ rawLines !!? i
     isBlankRawSnippet = \case
       Left r | T.all isSpace r -> True
       _ -> False
 
     rawLines = A.listArray (0, length rawLines' - 1) rawLines'
       where
-        rawLines' = lines rawInput
+        rawLines' = T.lines rawInput
     rawLineLength = length rawLines
 
     interleave [] bs = bs
@@ -94,15 +94,15 @@
   -- | Whether CPP is enabled
   Bool ->
   RegionDeltas ->
-  String ->
-  (IntSet, IntMap String)
+  Text ->
+  (IntSet, IntMap Text)
 linesNotToFormat cppEnabled region@RegionDeltas {..} input =
   (unconsidered <> magicDisabled <> otherDisabled, lineUpdates)
   where
     unconsidered =
       IntSet.fromAscList $
         [1 .. regionPrefixLength] <> [totalLines - regionSuffixLength + 1 .. totalLines]
-    totalLines = length (lines input)
+    totalLines = length (T.lines input)
     regionLines = linesInRegion region input
     (magicDisabled, lineUpdates) = magicDisabledLines regionLines
     otherDisabled = (mconcat allLines) regionLines
@@ -119,10 +119,10 @@
 
 -- | All lines which are disabled by Ormolu's magic comments,
 -- as well as normalizing replacements.
-magicDisabledLines :: String -> (IntSet, IntMap String)
+magicDisabledLines :: Text -> (IntSet, IntMap Text)
 magicDisabledLines input =
   bimap IntSet.fromAscList IntMap.fromAscList . mconcat $
-    go OrmoluEnabled (lines input `zip` [1 ..])
+    go OrmoluEnabled (T.lines input `zip` [1 ..])
   where
     go _ [] = []
     go state ((line, i) : ls)
@@ -139,40 +139,39 @@
           OrmoluEnabled -> ([], [])
 
 -- | All lines which satisfy a predicate.
-linesFiltered :: (String -> Bool) -> String -> IntSet
+linesFiltered :: (Text -> Bool) -> Text -> IntSet
 linesFiltered p =
-  IntSet.fromAscList . fmap snd . filter (p . fst) . (`zip` [1 ..]) . lines
+  IntSet.fromAscList . fmap snd . filter (p . fst) . (`zip` [1 ..]) . T.lines
 
 -- | Lines which contain a shebang.
-shebangLines :: String -> IntSet
-shebangLines = linesFiltered ("#!" `L.isPrefixOf`)
+shebangLines :: Text -> IntSet
+shebangLines = linesFiltered ("#!" `T.isPrefixOf`)
 
 -- | Lines which contain a LINE pragma.
-linePragmaLines :: String -> IntSet
-linePragmaLines = linesFiltered ("{-# LINE" `L.isPrefixOf`)
+linePragmaLines :: Text -> IntSet
+linePragmaLines = linesFiltered ("{-# LINE" `T.isPrefixOf`)
 
 -- | Inner text of a magic enabling marker.
-ormoluEnable :: String
+ormoluEnable :: Text
 ormoluEnable = "ORMOLU_ENABLE"
 
 -- | Inner text of a magic disabling marker.
-ormoluDisable :: String
+ormoluDisable :: Text
 ormoluDisable = "ORMOLU_DISABLE"
 
 -- | Creates a magic comment with the given inner text.
-magicComment :: String -> String
+magicComment :: Text -> Text
 magicComment t = "{- " <> t <> " -}"
 
 -- | Construct a function for whitespace-insensitive matching of string.
 isMagicComment ::
   -- | What to expect
-  String ->
+  Text ->
   -- | String to test
-  String ->
+  Text ->
   -- | If the two strings match, we return the rest of the line.
-  Maybe String
+  Maybe Text
 isMagicComment expected s0 = do
-  let trim = dropWhile isSpace
-  s1 <- trim <$> L.stripPrefix "{-" (trim s0)
-  s2 <- trim <$> L.stripPrefix expected s1
-  L.stripPrefix "-}" s2
+  s1 <- T.stripStart <$> T.stripPrefix "{-" (T.stripStart s0)
+  s2 <- T.stripStart <$> T.stripPrefix expected s1
+  T.stripPrefix "-}" s2
diff --git a/src/Ormolu/Utils.hs b/src/Ormolu/Utils.hs
--- a/src/Ormolu/Utils.hs
+++ b/src/Ormolu/Utils.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ViewPatterns #-}
 
@@ -17,6 +18,7 @@
     HasSrcSpan (..),
     getLoc',
     matchAddEpAnn,
+    textToStringBuffer,
   )
 where
 
@@ -26,10 +28,15 @@
 import Data.Maybe (fromMaybe)
 import Data.Text (Text)
 import qualified Data.Text as T
+import qualified Data.Text.Foreign as TFFI
+import Foreign (pokeElemOff, withForeignPtr)
 import qualified GHC.Data.Strict as Strict
+import GHC.Data.StringBuffer (StringBuffer (..))
 import GHC.Driver.Ppr
 import GHC.DynFlags (baseDynFlags)
+import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
 import GHC.Hs
+import GHC.IO.Unsafe (unsafePerformIO)
 import GHC.Types.SrcLoc
 import GHC.Utils.Outputable
 
@@ -61,7 +68,7 @@
 notImplemented msg = error $ "not implemented yet: " ++ msg
 
 -- | Pretty-print an 'GHC.Outputable' thing.
-showOutputable :: Outputable o => o -> String
+showOutputable :: (Outputable o) => o -> String
 showOutputable = showSDoc baseDynFlags . ppr
 
 -- | Split and normalize a doc string. The result is a list of lines that
@@ -141,7 +148,7 @@
 instance HasSrcSpan (SrcSpanAnn' ann) where
   loc' = locA
 
-getLoc' :: HasSrcSpan l => GenLocated l a -> SrcSpan
+getLoc' :: (HasSrcSpan l) => GenLocated l a -> SrcSpan
 getLoc' = loc' . getLoc
 
 -- | Check whether the given 'AnnKeywordId' or its Unicode variant is in an
@@ -150,3 +157,17 @@
 matchAddEpAnn annId (AddEpAnn annId' loc)
   | annId == annId' || unicodeAnn annId == annId' = Just loc
   | otherwise = Nothing
+
+-- | Convert 'Text' to a 'StringBuffer' by making a copy.
+textToStringBuffer :: Text -> StringBuffer
+textToStringBuffer txt = unsafePerformIO $ do
+  buf <- mallocPlainForeignPtrBytes (len + 3)
+  withForeignPtr buf $ \ptr -> do
+    TFFI.unsafeCopyToPtr txt ptr
+    -- last three bytes have to be zero for easier decoding
+    pokeElemOff ptr len 0
+    pokeElemOff ptr (len + 1) 0
+    pokeElemOff ptr (len + 2) 0
+  pure StringBuffer {buf, len, cur = 0}
+  where
+    len = TFFI.lengthWord8 txt
diff --git a/src/Ormolu/Utils/Cabal.hs b/src/Ormolu/Utils/Cabal.hs
--- a/src/Ormolu/Utils/Cabal.hs
+++ b/src/Ormolu/Utils/Cabal.hs
@@ -4,10 +4,8 @@
 {-# LANGUAGE ViewPatterns #-}
 
 module Ormolu.Utils.Cabal
-  ( CabalInfo (..),
-    defaultCabalInfo,
-    PackageName,
-    unPackageName,
+  ( CabalSearchResult (..),
+    CabalInfo (..),
     Extension (..),
     getCabalInfoForSourceFile,
     findCabalFile,
@@ -34,52 +32,58 @@
 import Ormolu.Exception
 import System.Directory
 import System.FilePath
-import System.IO (hPutStrLn, stderr)
 import System.IO.Error (isDoesNotExistError)
 import System.IO.Unsafe (unsafePerformIO)
 
+-- | The result of searching for a @.cabal@ file.
+--
+-- @since 0.5.3.0
+data CabalSearchResult
+  = -- | Cabal file could not be found
+    CabalNotFound
+  | -- | Cabal file was found, but it did not mention the source file in
+    -- question
+    CabalDidNotMention CabalInfo
+  | -- | Cabal file was found and it mentions the source file in question
+    CabalFound CabalInfo
+  deriving (Eq, Show)
+
 -- | Cabal information of interest to Ormolu.
 data CabalInfo = CabalInfo
   { -- | Package name
-    ciPackageName :: !(Maybe String),
+    ciPackageName :: !PackageName,
     -- | Extension and language settings in the form of 'DynOption's
     ciDynOpts :: ![DynOption],
     -- | Direct dependencies
-    ciDependencies :: !(Set String),
-    -- | Absolute path to the cabal file, if it was found
-    ciCabalFilePath :: !(Maybe FilePath)
+    ciDependencies :: !(Set PackageName),
+    -- | Absolute path to the cabal file
+    ciCabalFilePath :: !FilePath
   }
   deriving (Eq, Show)
 
--- | Cabal info that is used by default when no .cabal file can be found.
-defaultCabalInfo :: CabalInfo
-defaultCabalInfo =
-  CabalInfo
-    { ciPackageName = Nothing,
-      ciDynOpts = [],
-      ciDependencies = Set.empty,
-      ciCabalFilePath = Nothing
-    }
-
--- | Locate .cabal file corresponding to the given Haskell source file and
--- obtain 'CabalInfo' from it.
+-- | Locate a @.cabal@ file corresponding to the given Haskell source file
+-- and obtain 'CabalInfo' from it.
 getCabalInfoForSourceFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Haskell source file
   FilePath ->
-  -- | Extracted cabal info
-  m CabalInfo
+  -- | Extracted cabal info, if any
+  m CabalSearchResult
 getCabalInfoForSourceFile sourceFile = liftIO $ do
   findCabalFile sourceFile >>= \case
-    Just cabalFile -> parseCabalInfo cabalFile sourceFile
-    Nothing -> do
-      hPutStrLn stderr $ "Could not find a .cabal file for " <> sourceFile
-      return defaultCabalInfo
+    Just cabalFile -> do
+      (mentioned, cabalInfo) <- parseCabalInfo cabalFile sourceFile
+      return
+        ( if mentioned
+            then CabalFound cabalInfo
+            else CabalDidNotMention cabalInfo
+        )
+    Nothing -> return CabalNotFound
 
 -- | Find the path to an appropriate .cabal file for a Haskell source file,
 -- if available.
 findCabalFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Path to a Haskell source file in a project with a .cabal file
   FilePath ->
   -- | Absolute path to the .cabal file if available
@@ -111,7 +115,7 @@
     genericPackageDescription :: GenericPackageDescription,
     -- | Map from Haskell source file paths (without any extensions) to the
     -- corresponding 'DynOption's and dependencies.
-    extensionsAndDeps :: Map FilePath ([DynOption], [String])
+    extensionsAndDeps :: Map FilePath ([DynOption], [PackageName])
   }
   deriving (Show)
 
@@ -122,13 +126,14 @@
 
 -- | Parse 'CabalInfo' from a .cabal file at the given 'FilePath'.
 parseCabalInfo ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | Location of the .cabal file
   FilePath ->
   -- | Location of the source file we are formatting
   FilePath ->
-  -- | Extracted cabal info
-  m CabalInfo
+  -- | Indication if the source file was mentioned in the Cabal file and the
+  -- extracted 'CabalInfo'
+  m (Bool, CabalInfo)
 parseCabalInfo cabalFileAsGiven sourceFileAsGiven = liftIO $ do
   cabalFile <- makeAbsolute cabalFileAsGiven
   sourceFileAbs <- makeAbsolute sourceFileAsGiven
@@ -143,26 +148,22 @@
         cachedCabalFile = CachedCabalFile {..}
     atomicModifyIORef cabalCacheRef $
       (,cachedCabalFile) . M.insert cabalFile cachedCabalFile
-  (dynOpts, dependencies) <-
-    whenNothing (M.lookup (dropExtensions sourceFileAbs) extensionsAndDeps) $ do
-      relativeCabalFile <- makeRelativeToCurrentDirectory cabalFile
-      hPutStrLn stderr $
-        "Found .cabal file "
-          <> relativeCabalFile
-          <> ", but it did not mention "
-          <> sourceFileAsGiven
-      return ([], [])
-  let pdesc = packageDescription genericPackageDescription
-      packageName = (unPackageName . pkgName . package) pdesc
+  let (dynOpts, dependencies, mentioned) =
+        case M.lookup (dropExtensions sourceFileAbs) extensionsAndDeps of
+          Nothing -> ([], [], False)
+          Just (dynOpts', dependencies') -> (dynOpts', dependencies', True)
+      pdesc = packageDescription genericPackageDescription
   return
-    CabalInfo
-      { ciPackageName = Just packageName,
-        ciDynOpts = dynOpts,
-        ciDependencies = Set.fromList dependencies,
-        ciCabalFilePath = Just cabalFile
-      }
+    ( mentioned,
+      CabalInfo
+        { ciPackageName = pkgName (package pdesc),
+          ciDynOpts = dynOpts,
+          ciDependencies = Set.fromList dependencies,
+          ciCabalFilePath = cabalFile
+        }
+    )
   where
-    whenNothing :: Monad m => Maybe a -> m a -> m a
+    whenNothing :: (Monad m) => Maybe a -> m a -> m a
     whenNothing maya ma = maybe ma pure maya
 
 -- | Get a map from Haskell source file paths (without any extensions) to
@@ -172,7 +173,7 @@
   FilePath ->
   -- | Parsed generic package description
   GenericPackageDescription ->
-  Map FilePath ([DynOption], [String])
+  Map FilePath ([DynOption], [PackageName])
 getExtensionAndDepsMap cabalFile GenericPackageDescription {..} =
   M.unions . concat $
     [ buildMap extractFromLibrary <$> lib ++ sublibs,
@@ -196,7 +197,7 @@
         prependSrcDirs f
           | null hsSourceDirs = [f]
           | otherwise = (</> f) . getSymbolicPath <$> hsSourceDirs
-        deps = unPackageName . depPkgName <$> targetBuildDepends
+        deps = depPkgName <$> targetBuildDepends
         exts = maybe [] langExt defaultLanguage ++ fmap extToDynOption defaultExtensions
         langExt =
           pure . DynOption . ("-X" <>) . \case
diff --git a/src/Ormolu/Utils/Fixity.hs b/src/Ormolu/Utils/Fixity.hs
--- a/src/Ormolu/Utils/Fixity.hs
+++ b/src/Ormolu/Utils/Fixity.hs
@@ -28,35 +28,32 @@
 cacheRef = unsafePerformIO (newIORef Map.empty)
 {-# NOINLINE cacheRef #-}
 
--- | Attempt to locate and parse a @.ormolu@ file. If it does not exist,
+-- | Attempt to locate and parse an @.ormolu@ file. If it does not exist,
 -- empty fixity map is returned. This function maintains a cache of fixity
 -- overrides where cabal file paths act as keys.
 getFixityOverridesForSourceFile ::
-  MonadIO m =>
+  (MonadIO m) =>
   -- | 'CabalInfo' already obtained for this source file
   CabalInfo ->
   m FixityMap
 getFixityOverridesForSourceFile CabalInfo {..} = liftIO $ do
-  case ciCabalFilePath of
-    Nothing -> return Map.empty
-    Just cabalPath -> do
-      cache <- readIORef cacheRef
-      case Map.lookup cabalPath cache of
-        Nothing -> do
-          let dotOrmolu = replaceFileName cabalPath ".ormolu"
-          exists <- doesFileExist dotOrmolu
-          if exists
-            then do
-              dotOrmoluRelative <- makeRelativeToCurrentDirectory dotOrmolu
-              contents <- readFileUtf8 dotOrmolu
-              case parseFixityMap dotOrmoluRelative contents of
-                Left errorBundle ->
-                  throwIO (OrmoluFixityOverridesParseError errorBundle)
-                Right x -> do
-                  modifyIORef' cacheRef (Map.insert cabalPath x)
-                  return x
-            else return Map.empty
-        Just x -> return x
+  cache <- readIORef cacheRef
+  case Map.lookup ciCabalFilePath cache of
+    Nothing -> do
+      let dotOrmolu = replaceFileName ciCabalFilePath ".ormolu"
+      exists <- doesFileExist dotOrmolu
+      if exists
+        then do
+          dotOrmoluRelative <- makeRelativeToCurrentDirectory dotOrmolu
+          contents <- readFileUtf8 dotOrmolu
+          case parseFixityMap dotOrmoluRelative contents of
+            Left errorBundle ->
+              throwIO (OrmoluFixityOverridesParseError errorBundle)
+            Right x -> do
+              modifyIORef' cacheRef (Map.insert ciCabalFilePath x)
+              return x
+        else return Map.empty
+    Just x -> return x
 
 -- | A wrapper around 'parseFixityDeclaration' for parsing individual fixity
 -- definitions.
@@ -64,6 +61,6 @@
   -- | Input to parse
   String ->
   -- | Parse result
-  Either String [(String, FixityInfo)]
+  Either String [(OpName, FixityInfo)]
 parseFixityDeclarationStr =
   first errorBundlePretty . parseFixityDeclaration . T.pack
diff --git a/src/Ormolu/Utils/IO.hs b/src/Ormolu/Utils/IO.hs
--- a/src/Ormolu/Utils/IO.hs
+++ b/src/Ormolu/Utils/IO.hs
@@ -16,19 +16,19 @@
 
 -- | Write a 'Text' to a file using UTF8 and ignoring native
 -- line ending conventions.
-writeFileUtf8 :: MonadIO m => FilePath -> Text -> m ()
+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 :: (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 :: (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 :: (MonadIO m) => ByteString -> m Text
 decodeUtf8 = liftIO . either throwIO pure . TE.decodeUtf8'
diff --git a/tests/Ormolu/CabalInfoSpec.hs b/tests/Ormolu/CabalInfoSpec.hs
--- a/tests/Ormolu/CabalInfoSpec.hs
+++ b/tests/Ormolu/CabalInfoSpec.hs
@@ -3,6 +3,7 @@
 module Ormolu.CabalInfoSpec (spec) where
 
 import qualified Data.Set as Set
+import Distribution.Types.PackageName (unPackageName)
 import Ormolu.Config (DynOption (..))
 import Ormolu.Utils.Cabal
 import System.Directory
@@ -30,24 +31,35 @@
           cabalFile <- findCabalFile (dir </> "foo/bar.hs")
           cabalFile `shouldBe` Nothing
   describe "parseCabalInfo" $ do
-    it "extracts correct package name from ormolu.cabal" $ do
-      CabalInfo {..} <- parseCabalInfo "ormolu.cabal" "src/Ormolu/Config.hs"
-      ciPackageName `shouldBe` Just "ormolu"
-    it "extracts correct dyn opts from ormolu.cabal" $ do
-      CabalInfo {..} <- parseCabalInfo "ormolu.cabal" "src/Ormolu/Config.hs"
+    it "extracts correct cabal info from ormolu.cabal for src/Ormolu/Config.hs" $ do
+      (mentioned, CabalInfo {..}) <- parseCabalInfo "ormolu.cabal" "src/Ormolu/Config.hs"
+      mentioned `shouldBe` True
+      unPackageName ciPackageName `shouldBe` "ormolu"
       ciDynOpts `shouldBe` [DynOption "-XHaskell2010"]
-    it "extracts correct dependencies from ormolu.cabal (src/Ormolu/Config.hs)" $ do
-      CabalInfo {..} <- parseCabalInfo "ormolu.cabal" "src/Ormolu/Config.hs"
-      ciDependencies `shouldBe` Set.fromList ["Cabal-syntax", "Diff", "MemoTrie", "ansi-terminal", "array", "base", "binary", "bytestring", "containers", "directory", "dlist", "file-embed", "filepath", "ghc-lib-parser", "megaparsec", "mtl", "syb", "text"]
-    it "extracts correct dependencies from ormolu.cabal (tests/Ormolu/PrinterSpec.hs)" $ do
-      CabalInfo {..} <- parseCabalInfo "ormolu.cabal" "tests/Ormolu/PrinterSpec.hs"
-      ciDependencies `shouldBe` Set.fromList ["QuickCheck", "base", "containers", "directory", "filepath", "ghc-lib-parser", "hspec", "hspec-megaparsec", "ormolu", "path", "path-io", "temporary", "text"]
-
+      Set.map unPackageName ciDependencies `shouldBe` Set.fromList ["Cabal-syntax", "Diff", "MemoTrie", "ansi-terminal", "array", "base", "binary", "bytestring", "containers", "directory", "dlist", "file-embed", "filepath", "ghc-lib-parser", "megaparsec", "mtl", "syb", "text"]
+      ciCabalFilePath `shouldSatisfy` isAbsolute
+      makeRelativeToCurrentDirectory ciCabalFilePath `shouldReturn` "ormolu.cabal"
+    it "extracts correct cabal info from ormolu.cabal for tests/Ormolu/PrinterSpec.hs" $ do
+      (mentioned, CabalInfo {..}) <- parseCabalInfo "ormolu.cabal" "tests/Ormolu/PrinterSpec.hs"
+      mentioned `shouldBe` True
+      unPackageName ciPackageName `shouldBe` "ormolu"
+      ciDynOpts `shouldBe` [DynOption "-XHaskell2010"]
+      Set.map unPackageName ciDependencies `shouldBe` Set.fromList ["Cabal-syntax", "QuickCheck", "base", "containers", "directory", "filepath", "ghc-lib-parser", "hspec", "hspec-megaparsec", "ormolu", "path", "path-io", "temporary", "text"]
+      ciCabalFilePath `shouldSatisfy` isAbsolute
+      makeRelativeToCurrentDirectory ciCabalFilePath `shouldReturn` "ormolu.cabal"
+    it "handles correctly files that are not mentioned in ormolu.cabal" $ do
+      (mentioned, CabalInfo {..}) <- parseCabalInfo "ormolu.cabal" "src/FooBob.hs"
+      mentioned `shouldBe` False
+      unPackageName ciPackageName `shouldBe` "ormolu"
+      ciDynOpts `shouldBe` []
+      Set.map unPackageName ciDependencies `shouldBe` Set.empty
+      ciCabalFilePath `shouldSatisfy` isAbsolute
+      makeRelativeToCurrentDirectory ciCabalFilePath `shouldReturn` "ormolu.cabal"
     it "handles `hs-source-dirs: .`" $ do
-      CabalInfo {..} <- parseTestCabalInfo "Foo.hs"
+      (_, CabalInfo {..}) <- parseTestCabalInfo "Foo.hs"
       ciDynOpts `shouldContain` [DynOption "-XImportQualifiedPost"]
     it "handles empty hs-source-dirs" $ do
-      CabalInfo {..} <- parseTestCabalInfo "Bar.hs"
+      (_, CabalInfo {..}) <- parseTestCabalInfo "Bar.hs"
       ciDynOpts `shouldContain` [DynOption "-XImportQualifiedPost"]
   where
     parseTestCabalInfo f =
diff --git a/tests/Ormolu/Fixity/ParserSpec.hs b/tests/Ormolu/Fixity/ParserSpec.hs
--- a/tests/Ormolu/Fixity/ParserSpec.hs
+++ b/tests/Ormolu/Fixity/ParserSpec.hs
@@ -3,6 +3,7 @@
 module Ormolu.Fixity.ParserSpec (spec) where
 
 import qualified Data.Map.Strict as Map
+import Data.Text (Text)
 import qualified Data.Text as T
 import Ormolu.Fixity
 import Ormolu.Fixity.Parser
@@ -115,6 +116,13 @@
             (".", FixityInfo (Just InfixR) 7 9),
             ("^", FixityInfo (Just InfixR) 9 9)
           ]
+    it "handles CRLF line endings correctly" $
+      parseFixityMap ""
+        `shouldSucceedOn` ( unlinesCrlf
+                              [ "infixr 9  .",
+                                "infixr 5  ++"
+                              ]
+                          )
     it "fails with correct parse error (keyword wrong second line)" $
       parseFixityMap "" "infixr 5 .\nfoobar 5 $"
         `shouldFailWith` err
@@ -127,3 +135,6 @@
                 eeof
               ]
           )
+
+unlinesCrlf :: [Text] -> Text
+unlinesCrlf = T.concat . fmap (<> "\r\n")
diff --git a/tests/Ormolu/Fixity/PrinterSpec.hs b/tests/Ormolu/Fixity/PrinterSpec.hs
--- a/tests/Ormolu/Fixity/PrinterSpec.hs
+++ b/tests/Ormolu/Fixity/PrinterSpec.hs
@@ -4,6 +4,7 @@
 
 import qualified Data.Char as Char
 import qualified Data.Map.Strict as Map
+import qualified Data.Text as T
 import Ormolu.Fixity
 import Ormolu.Fixity.Parser
 import Ormolu.Fixity.Printer
@@ -20,7 +21,8 @@
       <$> listOf ((,) <$> genOperator <*> genFixityInfo)
     where
       scaleDown = scale (`div` 4)
-      genOperator = oneof [genNormalOperator, genIdentifier]
+      genOperator =
+        OpName . T.pack <$> oneof [genNormalOperator, genIdentifier]
       genNormalOperator =
         listOf1 (scaleDown arbitrary `suchThat` isOperatorConstituent)
       isOperatorConstituent x =
diff --git a/tests/Ormolu/HackageInfoSpec.hs b/tests/Ormolu/HackageInfoSpec.hs
--- a/tests/Ormolu/HackageInfoSpec.hs
+++ b/tests/Ormolu/HackageInfoSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TupleSections #-}
 
 module Ormolu.HackageInfoSpec (spec) where
@@ -5,6 +6,7 @@
 import qualified Data.Map.Strict as Map
 import Data.Maybe (mapMaybe)
 import qualified Data.Set as Set
+import Distribution.Types.PackageName (PackageName)
 import Ormolu.Fixity
 import Test.Hspec
 
@@ -13,12 +15,12 @@
 -- operators.
 checkFixityMap ::
   -- | List of dependencies
-  [String] ->
+  [PackageName] ->
   -- | Threshold to choose the conflict resolution strategy
   Float ->
   -- | Associative list representing a subset of the resulting fixity map
   -- that should be checked.
-  [(String, FixityInfo)] ->
+  [(OpName, FixityInfo)] ->
   Expectation
 checkFixityMap
   dependencies
@@ -43,20 +45,20 @@
 checkFixityMap' ::
   -- | Associative list for packageToOps:
   -- package name -map-> (operator -map-> fixity)
-  [(String, [(String, FixityInfo)])] ->
+  [(PackageName, [(OpName, FixityInfo)])] ->
   -- | Associative list for packageToPopularity:
   -- package name -map-> download count
-  [(String, Int)] ->
+  [(PackageName, Int)] ->
   -- | List of packages that should have a higher priority than
   -- unspecified packages (boot packages)
-  [String] ->
+  [PackageName] ->
   -- | List of dependencies
-  [String] ->
+  [PackageName] ->
   -- | Threshold to choose the conflict resolution strategy
   Float ->
   -- | Associative list representing a subset of the resulting fixity map
   -- that should be checked.
-  [(String, FixityInfo)] ->
+  [(OpName, FixityInfo)] ->
   Expectation
 checkFixityMap'
   lPackageToOps
diff --git a/tests/Ormolu/OpTreeSpec.hs b/tests/Ormolu/OpTreeSpec.hs
--- a/tests/Ormolu/OpTreeSpec.hs
+++ b/tests/Ormolu/OpTreeSpec.hs
@@ -1,7 +1,11 @@
+{-# LANGUAGE OverloadedStrings #-}
+
 module Ormolu.OpTreeSpec (spec) where
 
 import qualified Data.Map.Strict as Map
 import Data.Maybe (fromJust)
+import Data.Text (Text)
+import qualified Data.Text as T
 import GHC.Types.Name (mkOccName, varName)
 import GHC.Types.Name.Reader (mkRdrUnqual)
 import Ormolu.Fixity
@@ -9,17 +13,17 @@
 import Ormolu.Printer.Operators
 import Test.Hspec
 
-n :: String -> OpTree String String
+n :: Text -> OpTree Text OpName
 n = OpNode
 
 -- | Check that the input tree is actually reassociated as expected.
 checkReassociate ::
   -- | Fixity map used for the reassociation
-  [(String, FixityInfo)] ->
+  [(OpName, FixityInfo)] ->
   -- | Input tree
-  OpTree String String ->
+  OpTree Text OpName ->
   -- | Expected output tree
-  OpTree String String ->
+  OpTree Text OpName ->
   Expectation
 checkReassociate lFixities inputTree expectedOutputTree =
   removeOpInfo actualOutputTree `shouldBe` expectedOutputTree
@@ -29,10 +33,10 @@
       OpBranches (removeOpInfo <$> exprs) (opiOp <$> ops)
     actualOutputTree = reassociateOpTree convertName Map.empty fixityMap inputTree
     fixityMap = LazyFixityMap [Map.fromList lFixities]
-    convertName = Just . mkRdrUnqual . mkOccName varName
+    convertName = Just . mkRdrUnqual . mkOccName varName . T.unpack . unOpName
 
 -- | Associative list of fixities for operators from "base"
-baseFixities :: [(String, FixityInfo)]
+baseFixities :: [(OpName, FixityInfo)]
 baseFixities = Map.toList . fromJust $ Map.lookup "base" packageToOps
 
 spec :: Spec
diff --git a/tests/Ormolu/Parser/OptionsSpec.hs b/tests/Ormolu/Parser/OptionsSpec.hs
--- a/tests/Ormolu/Parser/OptionsSpec.hs
+++ b/tests/Ormolu/Parser/OptionsSpec.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
 
 module Ormolu.Parser.OptionsSpec (spec) where
 
@@ -10,7 +11,7 @@
 spec = describe "GHC options in source files take priority" $ do
   it "default extensions can be disabled locally" $ do
     let src =
-          unlines
+          T.unlines
             [ "{-# LANGUAGE NoBlockArguments #-}",
               "",
               "test = test do test"
@@ -20,7 +21,7 @@
       _ -> False
   it "extensions disabled via CLI can be enabled locally" $ do
     let src =
-          unlines
+          T.unlines
             [ "{-# LANGUAGE BlockArguments #-}",
               "",
               "test = test do test"
@@ -29,4 +30,4 @@
   where
     fixedPoint opts input = do
       output <- ormolu defaultConfig {cfgDynOptions = DynOption <$> opts} "<input>" input
-      T.unpack output `shouldBe` input
+      output `shouldBe` input
diff --git a/tests/Ormolu/Parser/PragmaSpec.hs b/tests/Ormolu/Parser/PragmaSpec.hs
--- a/tests/Ormolu/Parser/PragmaSpec.hs
+++ b/tests/Ormolu/Parser/PragmaSpec.hs
@@ -1,5 +1,9 @@
+{-# LANGUAGE OverloadedStrings #-}
+
 module Ormolu.Parser.PragmaSpec (spec) where
 
+import Data.Text (Text)
+import qualified Data.Text as T
 import Ormolu.Parser.Pragma
 import Test.Hspec
 
@@ -20,7 +24,7 @@
     stdTest "{-# OPTIONS_GHC foo bar baz  #-}" (Just $ PragmaOptionsGHC "foo bar baz")
     stdTest "{-#OPTIONS_HADDOCK foo, bar, baz  #-}" (Just $ PragmaOptionsHaddock "foo, bar, baz")
 
-stdTest :: String -> Maybe Pragma -> Spec
+stdTest :: Text -> Maybe Pragma -> Spec
 stdTest input result =
-  it input $
+  it (T.unpack input) $
     parsePragma input `shouldBe` result
diff --git a/tests/Ormolu/PrinterSpec.hs b/tests/Ormolu/PrinterSpec.hs
--- a/tests/Ormolu/PrinterSpec.hs
+++ b/tests/Ormolu/PrinterSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell #-}
 
 module Ormolu.PrinterSpec (spec) where
@@ -56,7 +57,7 @@
   shouldMatch False formatted0 expected
   -- 4. Check that running the formatter on the output produces the same
   -- output again (the transformation is idempotent).
-  formatted1 <- ormolu config "<formatted>" (T.unpack formatted0)
+  formatted1 <- ormolu config "<formatted>" formatted0
   shouldMatch True formatted1 formatted0
 
 -- | Build list of examples for testing.
