diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,14 +1,18 @@
-## Fourmolu 0.0.6.0
+## Fourmolu 0.1.0.0
 
-* The project was fourked to provide four space indent.
+* Allow configuration of indentation size via `fourmolu.yaml` config files.
 
+* An operator on a new line is no longer indented when its left operand is a do-block. This prevents the AST from potentially changing when indenting by more than two spaces.
+
+Upstream changes:
+
 * Fixed rendering of type signatures concerning several identifiers. [Issue
   566](https://github.com/tweag/ormolu/issues/566).
 
 * Fixed an idempotence issue with inline comments in tuples and parentheses.
   [Issue 450](https://github.com/tweag/ormolu/issues/450).
 
-* Fixed an idempotence issue when certain comments where picked up as
+* Fixed an idempotence issue when certain comments were picked up as
   “continuation” of a series of comments [Issue
   449](https://github.com/tweag/ormolu/issues/449).
 
@@ -27,7 +31,7 @@
   multiple blank lines in a row. [Issue
   518](https://github.com/tweag/ormolu/issues/518).
 
-* Fixed rendering of comment around if expressions. [Issue
+* Fixed rendering of comments around if expressions. [Issue
   458](https://github.com/tweag/ormolu/issues/458).
 
 * Unnamed fields of data constructors are now documented using the `-- ^`
@@ -37,8 +41,15 @@
 * Fixed non-idempotent transformation of partly documented data definition.
   [Issue 590](https://github.com/tweag/ormolu/issues/590).
 
+* Fixed an idempotence issue related to operators. [Issue
+  522](https://github.com/tweag/ormolu/issues/522).
+
 * Renamed the `--check-idempotency` flag to `--check-idempotence`.
   Apparently only the latter is correct.
+
+## Fourmolu 0.0.6.0
+
+* The project was fourked to provide four space indent.
 
 ## Ormolu 0.0.5.0
 
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -25,7 +25,10 @@
 main :: IO ()
 main = withPrettyOrmoluExceptions $ do
   Opts {..} <- execParser optsParserInfo
-  let formatOne' = formatOne optMode optConfig
+  let formatOne' path = do
+        printerOpts <-
+          loadConfigFile (cfgDebug optConfig) path $ cfgPrinterOpts optConfig
+        formatOne optMode optConfig {cfgPrinterOpts = printerOpts} path
   case optInputFiles of
     [] -> formatOne' Nothing
     ["-"] -> formatOne' Nothing
@@ -183,6 +186,7 @@
                 help "End line of the region to format (inclusive)"
               ]
         )
+    <*> pure defaultPrinterOpts
 
 ----------------------------------------------------------------------------
 -- Helpers
diff --git a/data/examples/declaration/value/function/infix/do-out.hs b/data/examples/declaration/value/function/infix/do-out.hs
--- a/data/examples/declaration/value/function/infix/do-out.hs
+++ b/data/examples/declaration/value/function/infix/do-out.hs
@@ -1,7 +1,7 @@
 main =
   do stuff
-    `finally` do
-      recover
+  `finally` do
+    recover
 
 main = do stuff `finally` recover
 
diff --git a/data/examples/declaration/value/function/operators-0-out.hs b/data/examples/declaration/value/function/operators-0-out.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-0-out.hs
@@ -0,0 +1,5 @@
+a =
+  b & c .~ d
+    & e
+      %~ f
+        g
diff --git a/data/examples/declaration/value/function/operators-0.hs b/data/examples/declaration/value/function/operators-0.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-0.hs
@@ -0,0 +1,4 @@
+a =
+  b & c .~ d
+    & e %~ f
+            g
diff --git a/data/examples/declaration/value/function/operators-1-out.hs b/data/examples/declaration/value/function/operators-1-out.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-1-out.hs
@@ -0,0 +1,4 @@
+foo =
+  f
+    . g
+    =<< h . i
diff --git a/data/examples/declaration/value/function/operators-1.hs b/data/examples/declaration/value/function/operators-1.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-1.hs
@@ -0,0 +1,3 @@
+foo = f
+    . g
+  =<< h . i
diff --git a/data/examples/declaration/value/function/operators-2-out.hs b/data/examples/declaration/value/function/operators-2-out.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-2-out.hs
@@ -0,0 +1,4 @@
+foo n
+  | x || y && z || n ** x
+      || x && n =
+    42
diff --git a/data/examples/declaration/value/function/operators-2.hs b/data/examples/declaration/value/function/operators-2.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-2.hs
@@ -0,0 +1,4 @@
+foo n
+  | x || y && z || n ** x
+      || x && n =
+        42
diff --git a/data/examples/declaration/value/function/operators-3-out.hs b/data/examples/declaration/value/function/operators-3-out.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-3-out.hs
@@ -0,0 +1,3 @@
+foo =
+  op <> n <+> colon <+> prettySe <+> text "="
+    <+> prettySe <> text sc
diff --git a/data/examples/declaration/value/function/operators-3.hs b/data/examples/declaration/value/function/operators-3.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-3.hs
@@ -0,0 +1,3 @@
+foo =
+  op <> n <+> colon <+> prettySe <+> text "=" <+>
+    prettySe <> text sc
diff --git a/data/examples/declaration/value/function/operators-4-out.hs b/data/examples/declaration/value/function/operators-4-out.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-4-out.hs
@@ -0,0 +1,3 @@
+foo =
+  line <> bindingOf <+> text "=" <+> tPretty <+> colon
+    <+> align <> prettyPs
diff --git a/data/examples/declaration/value/function/operators-4.hs b/data/examples/declaration/value/function/operators-4.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-4.hs
@@ -0,0 +1,3 @@
+foo =
+  line <> bindingOf <+> text "=" <+> tPretty <+> colon <+>
+    align <> prettyPs
diff --git a/data/examples/declaration/value/function/operators-5-out.hs b/data/examples/declaration/value/function/operators-5-out.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-5-out.hs
@@ -0,0 +1,5 @@
+foo =
+  map bar $
+    [ baz
+    ]
+      ++ quux
diff --git a/data/examples/declaration/value/function/operators-5.hs b/data/examples/declaration/value/function/operators-5.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-5.hs
@@ -0,0 +1,4 @@
+foo =
+  map bar $
+    [ baz
+    ] ++ quux
diff --git a/data/examples/declaration/value/function/operators-6-out.hs b/data/examples/declaration/value/function/operators-6-out.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-6-out.hs
@@ -0,0 +1,9 @@
+type PermuteRef =
+  "a"
+    :> ( "b" :> "c" :> End
+           :<|> "c" :> "b" :> End
+       )
+    :<|> "b"
+      :> ( "a" :> "c" :> End
+             :<|> "c" :> "a" :> End
+         )
diff --git a/data/examples/declaration/value/function/operators-6.hs b/data/examples/declaration/value/function/operators-6.hs
new file mode 100644
--- /dev/null
+++ b/data/examples/declaration/value/function/operators-6.hs
@@ -0,0 +1,7 @@
+type PermuteRef =
+       "a" :> (    "b" :> "c" :> End
+              :<|> "c" :> "b" :> End
+              )
+  :<|> "b" :> (    "a" :> "c" :> End
+              :<|> "c" :> "a" :> End
+              )
diff --git a/data/examples/declaration/value/function/operators-out.hs b/data/examples/declaration/value/function/operators-out.hs
deleted file mode 100644
--- a/data/examples/declaration/value/function/operators-out.hs
+++ /dev/null
@@ -1,5 +0,0 @@
-a =
-  b & c .~ d
-    & e
-      %~ f
-        g
diff --git a/data/examples/declaration/value/function/operators.hs b/data/examples/declaration/value/function/operators.hs
deleted file mode 100644
--- a/data/examples/declaration/value/function/operators.hs
+++ /dev/null
@@ -1,4 +0,0 @@
-a =
-  b & c .~ d
-    & e %~ f
-            g
diff --git a/fourmolu.cabal b/fourmolu.cabal
--- a/fourmolu.cabal
+++ b/fourmolu.cabal
@@ -1,9 +1,9 @@
 cabal-version:   1.18
 name:            fourmolu
-version:         0.0.6.0
+version:         0.1.0.0
 license:         BSD3
 license-file:    LICENSE.md
-maintainer:      Matt Parsons <parsonsmatt@gmail.com>
+maintainer:      Matt Parsons <parsonsmatt@gmail.com>, George Thomas <georgefsthomas@gmail.com>
 tested-with:     ghc ==8.6.5 ghc ==8.8.3 ghc ==8.10.1
 homepage:        https://github.com/parsonsmatt/fourmolu
 bug-reports:     https://github.com/parsonsmatt/fourmolu/issues
@@ -116,15 +116,19 @@
 
     default-language: Haskell2010
     build-depends:
+        aeson >=1.5.2 && <1.6,
         base >=4.12 && <5.0,
         bytestring >=0.2 && <0.11,
         containers >=0.5 && <0.7,
+        directory >= 1.3.5 && <1.4,
         dlist >=0.8 && <0.9,
         exceptions >=0.6 && <0.11,
+        filepath >=1.4.2.1 && <1.5,
         ghc-lib-parser >=8.10 && <8.11,
         mtl >=2.0 && <3.0,
         syb >=0.7 && <0.8,
-        text >=0.2 && <1.3
+        text >=0.2 && <1.3,
+        yaml >=0.11.2 && <0.12
 
     if flag(dev)
         ghc-options:
diff --git a/src/Ormolu.hs b/src/Ormolu.hs
--- a/src/Ormolu.hs
+++ b/src/Ormolu.hs
@@ -9,6 +9,9 @@
     RegionIndices (..),
     defaultConfig,
     DynOption (..),
+    PrinterOpts (..),
+    defaultPrinterOpts,
+    loadConfigFile,
     OrmoluException (..),
     withPrettyOrmoluExceptions,
   )
@@ -62,7 +65,7 @@
   -- about not-yet-supported functionality) will be thrown later when we try
   -- to parse the rendered code back, inside of GHC monad wrapper which will
   -- lead to error messages presenting the exceptions as GHC bugs.
-  let !txt = printModule result0
+  let !txt = printModule result0 $ cfgPrinterOpts cfgWithIndices
   when (not (cfgUnsafe cfg) || cfgCheckIdempotence cfg) $ do
     let pathRendered = path ++ "<rendered>"
     -- Parse the result of pretty-printing again and make sure that AST
@@ -80,7 +83,7 @@
     -- Try re-formatting the formatted result to check if we get exactly
     -- the same output.
     when (cfgCheckIdempotence cfg) $
-      let txt2 = printModule result1
+      let txt2 = printModule result1 $ cfgPrinterOpts cfgWithIndices
        in case diffText txt txt2 pathRendered of
             Nothing -> return ()
             Just (loc, l, r) ->
diff --git a/src/Ormolu/Config.hs b/src/Ormolu/Config.hs
--- a/src/Ormolu/Config.hs
+++ b/src/Ormolu/Config.hs
@@ -1,4 +1,6 @@
 {-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE RecordWildCards #-}
 
 -- | Configuration options used by the tool.
@@ -7,13 +9,38 @@
     RegionIndices (..),
     RegionDeltas (..),
     defaultConfig,
+    PrinterOpts (..),
+    defaultPrinterOpts,
+    loadConfigFile,
     regionIndicesToDeltas,
     DynOption (..),
     dynOptionToLocatedStr,
   )
 where
 
+import Control.Monad (when)
+import Data.Aeson
+  ( FromJSON (..),
+    camelTo2,
+    defaultOptions,
+    fieldLabelModifier,
+    genericParseJSON,
+    rejectUnknownFields,
+  )
+import Data.List (stripPrefix)
+import Data.Maybe (fromMaybe)
+import Data.Yaml (decodeFileEither, prettyPrintParseException)
+import GHC.Generics (Generic)
 import qualified SrcLoc as GHC
+import System.Directory
+  ( XdgDirectory (XdgConfig),
+    findFile,
+    getCurrentDirectory,
+    getXdgDirectory,
+    makeAbsolute,
+  )
+import System.FilePath ((</>), splitPath)
+import System.IO (hPutStrLn, stderr)
 
 -- | Ormolu configuration.
 data Config region = Config
@@ -26,7 +53,8 @@
     -- | Checks if re-formatting the result is idempotent
     cfgCheckIdempotence :: !Bool,
     -- | Region selection
-    cfgRegion :: !region
+    cfgRegion :: !region,
+    cfgPrinterOpts :: PrinterOpts
   }
   deriving (Eq, Show, Functor)
 
@@ -61,9 +89,20 @@
         RegionIndices
           { regionStartLine = Nothing,
             regionEndLine = Nothing
-          }
+          },
+      cfgPrinterOpts = defaultPrinterOpts
     }
 
+-- | Options controlling formatting output
+data PrinterOpts = PrinterOpts
+  { -- | Number of spaces to use for indentation
+    poIndentStep :: Int
+  }
+  deriving (Eq, Show)
+
+defaultPrinterOpts :: PrinterOpts
+defaultPrinterOpts = PrinterOpts {poIndentStep = 4}
+
 -- | Convert 'RegionIndices' into 'RegionDeltas'.
 regionIndicesToDeltas ::
   -- | Total number of lines in the input
@@ -87,3 +126,55 @@
 -- | Convert 'DynOption' to @'GHC.Located' 'String'@.
 dynOptionToLocatedStr :: DynOption -> GHC.Located String
 dynOptionToLocatedStr (DynOption o) = GHC.L GHC.noSrcSpan o
+
+-- | A version of 'PrinterOpts' where any field can be empty.
+-- This corresponds to the information in a config file.
+data PrinterOptsPartial = PrinterOptsPartial
+  { popIndentation :: Maybe Int
+  }
+  deriving (Eq, Show, Generic)
+
+instance FromJSON PrinterOptsPartial where
+  parseJSON =
+    genericParseJSON
+      defaultOptions
+        { rejectUnknownFields = True,
+          fieldLabelModifier = camelTo2 '_' . fromMaybe "" . stripPrefix "pop"
+        }
+
+-- | Replace fields with those from a config file, if found.
+-- Looks recursively in parent folders, then in 'XdgConfig',
+-- for a file matching /fourmolu.yaml/'.
+loadConfigFile :: Bool -> Maybe FilePath -> PrinterOpts -> IO PrinterOpts
+loadConfigFile debug maybePath PrinterOpts {..} = do
+  root <- maybe getCurrentDirectory makeAbsolute maybePath
+  xdg <- getXdgDirectory XdgConfig ""
+  PrinterOptsPartial {..} <-
+    optsFromFile debug $ reverse $ xdg : scanl1 (</>) (splitPath root)
+  return $
+    PrinterOpts
+      { poIndentStep = fromMaybe poIndentStep popIndentation
+      }
+
+-- | Search the directories, in order, for a config file.
+optsFromFile :: Bool -> [FilePath] -> IO PrinterOptsPartial
+optsFromFile debug dirs =
+  findFile dirs configFileName >>= \case
+    Nothing -> do
+      printDebug $
+        "No " ++ show configFileName ++ " found in any of:\n"
+          ++ unlines (map ("  " ++) dirs)
+      return def
+    Just file -> do
+      printDebug $ "Found " ++ show file ++ ""
+      decodeFileEither file >>= \case
+        Left e -> do
+          printDebug $ prettyPrintParseException e
+          return def
+        Right x -> return x
+  where
+    def = PrinterOptsPartial Nothing
+    printDebug = when debug . hPutStrLn stderr
+
+configFileName :: FilePath
+configFileName = "fourmolu.yaml"
diff --git a/src/Ormolu/Printer.hs b/src/Ormolu/Printer.hs
--- a/src/Ormolu/Printer.hs
+++ b/src/Ormolu/Printer.hs
@@ -3,10 +3,12 @@
 -- | Pretty-printer for Haskell AST.
 module Ormolu.Printer
   ( printModule,
+    PrinterOpts (..),
   )
 where
 
 import Data.Text (Text)
+import Ormolu.Config
 import Ormolu.Parser.Result
 import Ormolu.Printer.Combinators
 import Ormolu.Printer.Meat.Module
@@ -17,9 +19,10 @@
 printModule ::
   -- | Result of parsing
   ParseResult ->
+  PrinterOpts ->
   -- | Resulting rendition
   Text
-printModule ParseResult {..} =
+printModule ParseResult {..} printerOpts =
   prLiteralPrefix <> region <> prLiteralSuffix
   where
     region =
@@ -35,4 +38,5 @@
           (mkSpanStream prParsedSource)
           prCommentStream
           prAnns
+          printerOpts
           prUseRecordDot
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
@@ -63,6 +63,7 @@
 import qualified Data.Text.Lazy as TL
 import Data.Text.Lazy.Builder
 import GHC
+import Ormolu.Config
 import Ormolu.Parser.Anns
 import Ormolu.Parser.CommentStream
 import Ormolu.Printer.SpanStream
@@ -92,7 +93,8 @@
     -- | Whether the last expression in the layout can use braces
     rcCanUseBraces :: Bool,
     -- | Whether the source could have used the record dot preprocessor
-    rcUseRecDot :: Bool
+    rcUseRecDot :: Bool,
+    rcPrinterOpts :: PrinterOpts
   }
 
 -- | State context of 'R'.
@@ -158,11 +160,12 @@
   CommentStream ->
   -- | Annotations
   Anns ->
+  PrinterOpts ->
   -- | Use Record Dot Syntax
   Bool ->
   -- | Resulting rendition
   Text
-runR (R m) sstream cstream anns recDot =
+runR (R m) sstream cstream anns printerOpts recDot =
   TL.toStrict . toLazyText . scBuilder $ execState (runReaderT m rc) sc
   where
     rc =
@@ -172,7 +175,8 @@
           rcEnclosingSpans = [],
           rcAnns = anns,
           rcCanUseBraces = False,
-          rcUseRecDot = recDot
+          rcUseRecDot = recDot,
+          rcPrinterOpts = printerOpts
         }
     sc =
       SC
@@ -373,12 +377,13 @@
 -- to be valid Haskell. When layout is single-line there is no obvious
 -- effect, but with multi-line layout correct indentation levels matter.
 inci :: R () -> R ()
-inci (R m) = R (local modRC m)
-  where
-    modRC rc =
-      rc
-        { rcIndent = rcIndent rc + indentStep
-        }
+inci (R m) = do
+  indentStep <- R (asks (poIndentStep . rcPrinterOpts))
+  let modRC rc =
+        rc
+          { rcIndent = rcIndent rc + indentStep
+          }
+  R (local modRC m)
 
 -- | Set indentation level for the inner computation equal to current
 -- column. This makes sure that the entire inner block is uniformly
@@ -566,10 +571,3 @@
 -- | Return 'True' if we can use braces in this context.
 canUseBraces :: R Bool
 canUseBraces = R (asks rcCanUseBraces)
-
-----------------------------------------------------------------------------
--- Constants
-
--- | Indentation step.
-indentStep :: Int
-indentStep = 4
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
@@ -2,6 +2,7 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE ViewPatterns #-}
 
 module Ormolu.Printer.Meat.Declaration.Value
   ( p_valDecl,
@@ -1324,10 +1325,17 @@
           inci p_y
         else do
           ub lhs
-          placeHanging placement $ do
-            p_op
-            space
-            p_y
+          let opAndRhs = do
+                p_op
+                space
+                p_y
+          case x of
+            -- This case prevents an operator from being indented past the start of a `do` block
+            -- constituting its left operand, thus altering the AST.
+            -- This is only relevant when the `do` block is on one line, as otherwise we will
+            -- insert a newline after `do` anyway.
+            OpNode (unLoc -> HsDo _ _ _) | isOneLineSpan (opTreeLoc x) -> breakpoint >> opAndRhs
+            _ -> placeHanging placement opAndRhs
 
 -- | Return 'True' if given expression is a record-dot operator expression.
 isRecordDot ::
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
@@ -12,8 +12,9 @@
 
 import Data.Function (on)
 import qualified Data.List as L
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
 import Data.Maybe (fromMaybe, mapMaybe)
-import Data.Ord (Down (Down), comparing)
 import GHC
 import OccName (mkVarOcc)
 import Ormolu.Utils (unSrcSpan)
@@ -56,7 +57,7 @@
 reassociateOpTreeWith ::
   forall ty op.
   -- | Fixity map for operators
-  [(RdrName, Fixity)] ->
+  Map RdrName Fixity ->
   -- | How to get the name of an operator
   (op -> Maybe RdrName) ->
   -- | Original 'OpTree'
@@ -68,12 +69,12 @@
     fixityOf :: op -> Fixity
     fixityOf op = fromMaybe defaultFixity $ do
       opName <- getOpName op
-      lookup opName fixityMap
+      M.lookup opName fixityMap
     -- Here, left branch is already associated and the root alongside with
     -- the right branch is right-associated. This function picks up one item
     -- from the right and inserts it correctly to the left.
     --
-    -- Also, we are using the 'compareFixity' function which returns if the
+    -- Also, we are using the 'compareFixity' function which tells if the
     -- expression should associate to right.
     go :: OpTree ty op -> OpTree ty op
     -- base cases
@@ -93,6 +94,16 @@
         then go $ OpBranch (OpBranch l op (go $ OpBranch r op' l')) op'' r'
         else go $ OpBranch (OpBranch (OpBranch l op r) op' l') op'' r'
 
+-- | A score assigned to an operator.
+data Score
+  = -- | The operator was placed at the beginning of a line
+    AtBeginning Int
+  | -- | The operator was placed at the end of a line
+    AtEnd
+  | -- | The operator was placed in between arguments on a single line
+    InBetween
+  deriving (Eq, Ord)
+
 -- | Build a map of inferred 'Fixity's from an 'OpTree'.
 buildFixityMap ::
   forall ty op.
@@ -101,23 +112,26 @@
   -- | Operator tree
   OpTree (Located ty) (Located op) ->
   -- | Fixity map
-  [(RdrName, Fixity)]
+  Map RdrName Fixity
 buildFixityMap getOpName opTree =
-  concatMap (\(i, ns) -> map (\(n, _) -> (n, fixity i InfixL)) ns)
-    . zip [0 ..]
-    . L.groupBy (doubleWithinEps 0.00001 `on` snd)
-    . (overrides ++)
-    . modeScores
+  addOverrides
+    . M.fromList
+    . concatMap (\(i, ns) -> map (\(n, _) -> (n, fixity i InfixL)) ns)
+    . zip [1 ..]
+    . L.groupBy ((==) `on` snd)
+    . selectScores
     $ score opTree
   where
-    -- Add a special case for ($), since it is pretty unlikely for someone
-    -- to override it.
-    overrides :: [(RdrName, Double)]
-    overrides =
-      [ (mkRdrUnqual $ mkVarOcc "$", -1)
-      ]
-    -- Assign scores to operators based on their location in the source.
-    score :: OpTree (Located ty) (Located op) -> [(RdrName, Double)]
+    addOverrides :: Map RdrName Fixity -> Map RdrName Fixity
+    addOverrides m =
+      let mk k v = (mkRdrUnqual (mkVarOcc k), fixity v InfixL)
+       in M.fromList
+            [ mk "$" 0,
+              mk "." 9
+            ]
+            `M.union` m
+    fixity = Fixity NoSourceText
+    score :: OpTree (Located ty) (Located op) -> [(RdrName, Score)]
     score (OpNode _) = []
     score (OpBranch l o r) = fromMaybe (score r) $ do
       -- If we fail to get any of these, 'defaultFixity' will be used by
@@ -129,46 +143,25 @@
       oc <- srcSpanStartCol <$> unSrcSpan (getLoc o) -- operator column
       opName <- getOpName (unLoc o)
       let s
-            | le < ob =
-              -- if the operator is in the beginning of a line, assign
-              -- a score relative to its column within range [0, 1).
-              fromIntegral oc / fromIntegral (maxCol + 1)
-            | oe < rb =
-              -- if the operator is in the end of the line, assign the
-              -- score 1.
-              1
-            | otherwise =
-              2 -- otherwise, assign a high score.
+            | le < ob = AtBeginning oc
+            | oe < rb = AtEnd
+            | otherwise = InBetween
       return $ (opName, s) : score r
-    -- Pick the most common score per 'RdrName'.
-    modeScores :: [(RdrName, Double)] -> [(RdrName, Double)]
-    modeScores =
+    selectScores :: [(RdrName, Score)] -> [(RdrName, Score)]
+    selectScores =
       L.sortOn snd
         . mapMaybe
           ( \case
               [] -> Nothing
-              xs@((n, _) : _) -> Just (n, mode $ map snd xs)
+              xs@((n, _) : _) -> Just (n, selectScore $ map snd xs)
           )
         . L.groupBy ((==) `on` fst)
         . L.sort
-    -- Return the most common number, leaning to the smaller
-    -- one in case of a tie.
-    mode :: [Double] -> Double
-    mode =
-      head
-        . L.minimumBy (comparing (Down . length))
-        . L.groupBy (doubleWithinEps 0.0001)
-        . L.sort
-    -- The start column of the rightmost operator.
-    maxCol = go opTree
-      where
-        go (OpNode (L _ _)) = 0
-        go (OpBranch l (L o _) r) =
-          maximum
-            [ go l,
-              maybe 0 srcSpanStartCol (unSrcSpan o),
-              go r
-            ]
+    selectScore :: [Score] -> Score
+    selectScore xs =
+      case filter (/= InBetween) xs of
+        [] -> InBetween
+        xs' -> maximum xs'
 
 ----------------------------------------------------------------------------
 -- Helpers
@@ -182,9 +175,3 @@
   OpBranch (OpNode l) lop (normalizeOpTree r)
 normalizeOpTree (OpBranch (OpBranch l' lop' r') lop r) =
   normalizeOpTree (OpBranch l' lop' (OpBranch r' lop r))
-
-fixity :: Int -> FixityDirection -> Fixity
-fixity = Fixity NoSourceText
-
-doubleWithinEps :: Double -> Double -> Double -> Bool
-doubleWithinEps eps a b = abs (a - b) < eps
diff --git a/tests/Ormolu/PrinterSpec.hs b/tests/Ormolu/PrinterSpec.hs
--- a/tests/Ormolu/PrinterSpec.hs
+++ b/tests/Ormolu/PrinterSpec.hs
@@ -24,12 +24,13 @@
 checkExample :: Path Rel File -> Spec
 checkExample srcPath' = it (fromRelFile srcPath' ++ " works") . withNiceExceptions $ do
   let srcPath = examplesDir </> srcPath'
+      cfg = defaultConfig {cfgPrinterOpts = PrinterOpts {poIndentStep = 2}}
   expectedOutputPath <- deriveOutput srcPath
   -- 1. Given input snippet of source code parse it and pretty print it.
   -- 2. Parse the result of pretty-printing again and make sure that AST
   -- is the same as AST of the original snippet. (This happens in
   -- 'ormoluFile' automatically.)
-  formatted0 <- ormoluFile defaultConfig (fromRelFile srcPath)
+  formatted0 <- ormoluFile cfg (fromRelFile srcPath)
   -- 3. Check the output against expected output. Thus all tests should
   -- include two files: input and expected output.
   -- T.writeFile (fromRelFile expectedOutputPath) formatted0
@@ -37,7 +38,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 defaultConfig "<formatted>" (T.unpack formatted0)
+  formatted1 <- ormolu cfg "<formatted>" (T.unpack formatted0)
   shouldMatch True formatted1 formatted0
 
 -- | Build list of examples for testing.
