diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,22 @@
+## Ormolu 0.5.0.1
+
+* Fixed a bug in the diff printing functionality. [Issue
+  886](https://github.com/tweag/ormolu/issues/886).
+
+* Indent closing bracket for list comprehensions in `do` blocks.
+  [Issue 893](https://github.com/tweag/ormolu/issues/893).
+
+* Fix `hs-source-dirs: .` resulting in failing to find a `.cabal` file for a
+  Haskell source file. [Issue 909](https://github.com/tweag/ormolu/issues/909).
+
+* Comments in closed type family declarations are now indented correctly.
+  [Issue 913](https://github.com/tweag/ormolu/issues/913).
+
+* Cache `.cabal` file parsing and processing when given multiple input files in
+  the same project. This results in dramatic speedups on projects which have
+  both huge `.cabal` files and a large number of individual modules. [Issue
+  897](https://github.com/tweag/ormolu/issues/897).
+
 ## Ormolu 0.5.0.0
 
 * Changed the way operator fixities and precedences are inferred.
diff --git a/data/cabal-tests/Bar.hs b/data/cabal-tests/Bar.hs
new file mode 100644
--- /dev/null
+++ b/data/cabal-tests/Bar.hs
@@ -0,0 +1,3 @@
+module Foo where
+
+import Data.List qualified as List
diff --git a/data/cabal-tests/Foo.hs b/data/cabal-tests/Foo.hs
new file mode 100644
--- /dev/null
+++ b/data/cabal-tests/Foo.hs
@@ -0,0 +1,3 @@
+module Foo where
+
+import Data.List qualified as List
diff --git a/data/cabal-tests/test.cabal b/data/cabal-tests/test.cabal
new file mode 100644
--- /dev/null
+++ b/data/cabal-tests/test.cabal
@@ -0,0 +1,13 @@
+cabal-version: 2.4
+name: test
+version: 0
+
+library
+  exposed-modules: Foo
+  hs-source-dirs: .
+  default-extensions: ImportQualifiedPost
+
+executable app
+  main-is: Main
+  other-modules: Bar
+  default-extensions: ImportQualifiedPost
diff --git a/data/diff-tests/inputs/applicative-after.hs b/data/diff-tests/inputs/applicative-after.hs
new file mode 100644
--- /dev/null
+++ b/data/diff-tests/inputs/applicative-after.hs
@@ -0,0 +1,8 @@
+testPermParser :: Permutation Parser String
+testPermParser =
+  f
+    <$> toPermutationWithDefault 'x' (char 'a')
+    <*> toPermutationWithDefault 'y' (char 'b')
+    <*> toPermutationWithDefault 'z' (char 'c')
+  where
+    f a b c = [a, b, c]
diff --git a/data/diff-tests/inputs/applicative-before.hs b/data/diff-tests/inputs/applicative-before.hs
new file mode 100644
--- /dev/null
+++ b/data/diff-tests/inputs/applicative-before.hs
@@ -0,0 +1,7 @@
+testPermParser :: Permutation Parser String
+testPermParser =
+  f <$> toPermutationWithDefault 'x' (char 'a')
+    <*> toPermutationWithDefault 'y' (char 'b')
+    <*> toPermutationWithDefault 'z' (char 'c')
+  where
+    f a b c = [a, b, c]
diff --git a/data/diff-tests/inputs/longer-v2.hs b/data/diff-tests/inputs/longer-v2.hs
new file mode 100644
--- /dev/null
+++ b/data/diff-tests/inputs/longer-v2.hs
@@ -0,0 +1,16 @@
+module Main (foo) where
+
+a
+b
+c
+
+main :: IO ()
+main = return ()
+
+d
+e
+f
+g
+
+foo :: Int
+foo = 6
diff --git a/data/diff-tests/inputs/longer.hs b/data/diff-tests/inputs/longer.hs
new file mode 100644
--- /dev/null
+++ b/data/diff-tests/inputs/longer.hs
@@ -0,0 +1,16 @@
+module Main (main) where
+
+a
+b
+c
+
+main :: IO ()
+main = return ()
+
+d
+e
+f
+g
+
+foo :: Int
+foo = 5
diff --git a/data/diff-tests/outputs/no-preceding.txt b/data/diff-tests/outputs/no-preceding.txt
--- a/data/diff-tests/outputs/no-preceding.txt
+++ b/data/diff-tests/outputs/no-preceding.txt
@@ -1,6 +1,7 @@
 TEST
-@@ -1,3 +1,3 @@
+@@ -1,4 +1,4 @@
 - module Main (foo) where
 + module Main (main) where
+
   main :: IO ()
   main = return ()
diff --git a/data/diff-tests/outputs/simple-hunk.txt b/data/diff-tests/outputs/simple-hunk.txt
--- a/data/diff-tests/outputs/simple-hunk.txt
+++ b/data/diff-tests/outputs/simple-hunk.txt
@@ -1,9 +1,10 @@
 TEST
-@@ -1,6 +1,6 @@
+@@ -1,7 +1,7 @@
   module Main (main) where
 
   main :: IO ()
 - main = return ()
 + main = pure ()
+
   foo :: Int
   foo = 5
diff --git a/data/diff-tests/outputs/trimming-trailing-both-eof.txt b/data/diff-tests/outputs/trimming-trailing-both-eof.txt
new file mode 100644
--- /dev/null
+++ b/data/diff-tests/outputs/trimming-trailing-both-eof.txt
@@ -0,0 +1,10 @@
+TEST
+@@ -1,6 +1,7 @@
+  testPermParser :: Permutation Parser String
+  testPermParser =
+-   f <$> toPermutationWithDefault 'x' (char 'a')
++   f
++     <$> toPermutationWithDefault 'x' (char 'a')
+      <*> toPermutationWithDefault 'y' (char 'b')
+      <*> toPermutationWithDefault 'z' (char 'c')
+    where
diff --git a/data/diff-tests/outputs/trimming-trailing-both-out-of-margin.txt b/data/diff-tests/outputs/trimming-trailing-both-out-of-margin.txt
new file mode 100644
--- /dev/null
+++ b/data/diff-tests/outputs/trimming-trailing-both-out-of-margin.txt
@@ -0,0 +1,13 @@
+TEST
+@@ -1,4 +1,4 @@
+- module Main (main) where
++ module Main (foo) where
+
+  a
+  b
+@@ -13,4 +13,4 @@
+  g
+
+  foo :: Int
+- foo = 5
++ foo = 6
diff --git a/data/examples/declaration/type-families/closed-type-family/with-comments-out.hs b/data/examples/declaration/type-families/closed-type-family/with-comments-out.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/type-families/closed-type-family/with-comments-out.hs
@@ -0,0 +1,10 @@
+type family LT a b where
+  -- 0
+  LT 0 _ = True
+  -- 1
+  LT 1 0 = False
+  LT 1 _ = True
+  -- 2
+  LT 2 0 = False
+  LT 2 1 = False
+  LT 2 _ = True
diff --git a/data/examples/declaration/type-families/closed-type-family/with-comments.hs b/data/examples/declaration/type-families/closed-type-family/with-comments.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/type-families/closed-type-family/with-comments.hs
@@ -0,0 +1,12 @@
+type family LT a b where
+  -- 0
+  LT 0 _ = True
+
+  -- 1
+  LT 1 0 = False
+  LT 1 _ = True
+
+  -- 2
+  LT 2 0 = False
+  LT 2 1 = False
+  LT 2 _ = True
diff --git a/data/examples/declaration/value/function/list-comprehensions-out.hs b/data/examples/declaration/value/function/list-comprehensions-out.hs
--- a/data/examples/declaration/value/function/list-comprehensions-out.hs
+++ b/data/examples/declaration/value/function/list-comprehensions-out.hs
@@ -22,3 +22,8 @@
           d
         ]
   ]
+
+a = do
+  [ c
+    | c <- d
+    ]
diff --git a/data/examples/declaration/value/function/list-comprehensions.hs b/data/examples/declaration/value/function/list-comprehensions.hs
--- a/data/examples/declaration/value/function/list-comprehensions.hs
+++ b/data/examples/declaration/value/function/list-comprehensions.hs
@@ -20,3 +20,7 @@
         c, d
       ]
   ]
+
+a = do
+  [ c
+      | c <- d ]
diff --git a/ormolu.cabal b/ormolu.cabal
--- a/ormolu.cabal
+++ b/ormolu.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               ormolu
-version:            0.5.0.0
+version:            0.5.0.1
 license:            BSD-3-Clause
 license-file:       LICENSE.md
 maintainer:         Mark Karpov <mark.karpov@tweag.io>
@@ -14,6 +14,7 @@
 extra-source-files:
     data/**/*.hs
     data/**/*.txt
+    data/**/*.cabal
     extract-hackage-info/hackage-info.json
 
 extra-doc-files:
diff --git a/src/Ormolu/Diff/Text.hs b/src/Ormolu/Diff/Text.hs
--- a/src/Ormolu/Diff/Text.hs
+++ b/src/Ormolu/Diff/Text.hs
@@ -38,10 +38,7 @@
     -- whole diff will be displayed.
     textDiffSelectedLines :: IntSet
   }
-  deriving (Eq)
-
-instance Show TextDiff where
-  show (TextDiff path _ _) = "TextDiff " ++ show path ++ " _ _"
+  deriving (Eq, Show)
 
 -- | List of lines tagged by 'D.Both', 'D.First', or 'D.Second'.
 type DiffList = [D.Diff [Text]]
@@ -57,6 +54,7 @@
     hunkSecondLength :: Int,
     hunkDiff :: DiffList
   }
+  deriving (Show)
 
 ----------------------------------------------------------------------------
 -- API
@@ -211,8 +209,8 @@
       [] ->
         if gotChanges
           then
-            let p = reverse (take margin bothHistory)
-                currentAcc' = addBothAfter p currentAcc
+            let currentAcc' = addBothAfter p currentAcc
+                p = take margin (reverse bothHistory)
              in case formHunk (currentAcc' []) of
                   Nothing -> hunksAcc []
                   Just hunk -> hunksAcc [hunk]
@@ -235,12 +233,12 @@
           piece ->
             if gotChanges
               then
-                let p = reverse bothHistory
-                    currentAcc' = currentAcc . addBothBefore p (piece :)
+                let currentAcc' = currentAcc . addBothBefore p (piece :)
+                    p = reverse bothHistory
                  in go 0 True hunksAcc currentAcc' [] xs
               else
-                let p = reverse (take margin bothHistory)
-                    currentAcc' = addBothBefore p (piece :)
+                let currentAcc' = addBothBefore p (piece :)
+                    p = reverse (take margin bothHistory)
                  in go 0 True hunksAcc currentAcc' [] xs
     addBothBefore [] acc = acc
     addBothBefore p acc = (D.Both p p :) . acc
diff --git a/src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs b/src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs
--- a/src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs
+++ b/src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs
@@ -55,7 +55,7 @@
           txt ".."
         Just eqs -> do
           newline
-          sep newline (located' (inci . p_tyFamInstEqn)) eqs
+          inci (sep newline (located' p_tyFamInstEqn) eqs)
 
 p_familyResultSigL ::
   Located (FamilyResultSig GhcPs) ->
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
@@ -744,7 +744,7 @@
           forM_ moduleName $ \m -> atom m *> txt "."
           txt header
           p_stmts exprPlacement (p_hsExpr' S) es
-        compBody = brackets N . located es $ \xs -> do
+        compBody = brackets s . located es $ \xs -> do
           let p_parBody =
                 sep
                   (breakpoint >> txt "|" >> space)
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
@@ -18,6 +18,7 @@
 import Control.Exception
 import Control.Monad.IO.Class
 import qualified Data.ByteString as B
+import Data.IORef
 import Data.Map.Lazy (Map)
 import qualified Data.Map.Lazy as M
 import Data.Maybe (maybeToList)
@@ -35,6 +36,7 @@
 import System.FilePath
 import System.IO (hPutStrLn, stderr)
 import System.IO.Error (isDoesNotExistError)
+import System.IO.Unsafe (unsafePerformIO)
 
 -- | Cabal information of interest to Ormolu.
 data CabalInfo = CabalInfo
@@ -103,6 +105,21 @@
         then pure Nothing
         else findCabalFile parentDir
 
+-- | Parsed cabal file information to be shared across multiple source files.
+data CachedCabalFile = CachedCabalFile
+  { -- | Parsed generic package description.
+    genericPackageDescription :: GenericPackageDescription,
+    -- | Map from Haskell source file paths (without any extensions) to the
+    -- corresponding 'DynOption's and dependencies.
+    extensionsAndDeps :: Map FilePath ([DynOption], [String])
+  }
+  deriving (Show)
+
+-- | Cache ref that stores 'CachedCabalFile' per cabal file.
+cabalCacheRef :: IORef (Map FilePath CachedCabalFile)
+cabalCacheRef = unsafePerformIO $ newIORef M.empty
+{-# NOINLINE cabalCacheRef #-}
+
 -- | Parse 'CabalInfo' from a .cabal file at the given 'FilePath'.
 parseCabalInfo ::
   MonadIO m =>
@@ -115,23 +132,26 @@
 parseCabalInfo cabalFileAsGiven sourceFileAsGiven = liftIO $ do
   cabalFile <- makeAbsolute cabalFileAsGiven
   sourceFileAbs <- makeAbsolute sourceFileAsGiven
-  cabalFileBs <- B.readFile cabalFile
-  genericPackageDescription <-
-    case parseGenericPackageDescriptionMaybe cabalFileBs of
-      Just gpd -> pure gpd
-      Nothing -> throwIO (OrmoluCabalFileParsingFailed cabalFile)
-  (dynOpts, dependencies) <- do
-    let extMap = getExtensionAndDepsMap cabalFile genericPackageDescription
-    case M.lookup (dropExtensions sourceFileAbs) extMap of
-      Just exts -> pure exts
-      Nothing -> do
-        relativeCabalFile <- makeRelativeToCurrentDirectory cabalFile
-        hPutStrLn stderr $
-          "Found .cabal file "
-            <> relativeCabalFile
-            <> ", but it did not mention "
-            <> sourceFileAsGiven
-        return ([], [])
+  cabalCache <- readIORef cabalCacheRef
+  CachedCabalFile {..} <- whenNothing (M.lookup cabalFile cabalCache) $ do
+    cabalFileBs <- B.readFile cabalFile
+    genericPackageDescription <-
+      whenNothing (parseGenericPackageDescriptionMaybe cabalFileBs) $
+        throwIO (OrmoluCabalFileParsingFailed cabalFile)
+    let extensionsAndDeps =
+          getExtensionAndDepsMap cabalFile genericPackageDescription
+        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
   return
@@ -141,6 +161,9 @@
         ciDependencies = Set.fromList dependencies,
         ciCabalFilePath = Just cabalFile
       }
+  where
+    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
 -- the corresponding 'DynOption's and dependencies.
@@ -168,7 +191,7 @@
 
     extractFromBuildInfo extraModules BuildInfo {..} = (,(exts, deps)) $ do
       m <- extraModules ++ (ModuleName.toFilePath <$> otherModules)
-      (takeDirectory cabalFile </>) <$> prependSrcDirs (dropExtensions m)
+      normalise . (takeDirectory cabalFile </>) <$> prependSrcDirs (dropExtensions m)
       where
         prependSrcDirs f
           | null hsSourceDirs = [f]
diff --git a/tests/Ormolu/CabalInfoSpec.hs b/tests/Ormolu/CabalInfoSpec.hs
--- a/tests/Ormolu/CabalInfoSpec.hs
+++ b/tests/Ormolu/CabalInfoSpec.hs
@@ -42,3 +42,13 @@
     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", "megaparsec", "ormolu", "path", "path-io", "temporary", "text"]
+
+    it "handles `hs-source-dirs: .`" $ do
+      CabalInfo {..} <- parseTestCabalInfo "Foo.hs"
+      ciDynOpts `shouldContain` [DynOption "-XImportQualifiedPost"]
+    it "handles empty hs-source-dirs" $ do
+      CabalInfo {..} <- parseTestCabalInfo "Bar.hs"
+      ciDynOpts `shouldContain` [DynOption "-XImportQualifiedPost"]
+  where
+    parseTestCabalInfo f =
+      parseCabalInfo "data/cabal-tests/test.cabal" ("data/cabal-tests" </> f)
diff --git a/tests/Ormolu/Diff/TextSpec.hs b/tests/Ormolu/Diff/TextSpec.hs
--- a/tests/Ormolu/Diff/TextSpec.hs
+++ b/tests/Ormolu/Diff/TextSpec.hs
@@ -24,6 +24,8 @@
     stdTest "two-hunks" "main-and-baz" "main-and-baz-v2"
     stdTest "trimming" "spaced" "spaced-v2"
     stdTest "trailing-blank-line" "no-trailing-blank-line" "with-trailing-blank-line"
+    stdTest "trimming-trailing-both-eof" "applicative-before" "applicative-after"
+    stdTest "trimming-trailing-both-out-of-margin" "longer" "longer-v2"
 
 -- | Test diff printing.
 stdTest ::
