diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,11 @@
-5.1.1:
+5.2.2:
+
+    * Parallel list comprehensions
+    * Leave do, lambda, lambda-case on previous line of $
+    * Misc fixes
+
+5.2.1:
+
     * Fix hanging on large constraints
     * Render multi-line comments
     * Rename --tab-size to --indent-size
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -47,6 +47,8 @@
 force-trailing-newline: true
 ```
 
+By default, HIndent preserves the newline or lack of newline in your input. With `force-trailing-newline`, it will make sure there is always a trailing newline.
+
 ## Emacs
 
 In
@@ -82,6 +84,14 @@
 much trouble you can try
 [vim-textobj-haskell](https://github.com/gilligan/vim-textobj-haskell)
 which provides a text object for top level bindings.
+
+In order to format an entire source file execute:
+
+    :%!hindent
+
+Alternatively you could use the
+[vim-hindent](https://github.com/alx741/vim-hindent) plugin which runs hindent
+automatically when a Haskell file is saved.
 
 ## Atom
 
diff --git a/TESTS.md b/TESTS.md
--- a/TESTS.md
+++ b/TESTS.md
@@ -28,6 +28,17 @@
   ) where
 ```
 
+Exports, indentation 4
+
+``` haskell 4
+module X
+    ( x
+    , y
+    , Z
+    , P(x, z)
+    ) where
+```
+
 # Imports
 
 Import lists
@@ -41,6 +52,18 @@
 import Data.Text hiding (a, b, c)
 ```
 
+Sorted
+
+```haskell given
+import B
+import A
+```
+
+```haskell expect
+import A
+import B
+```
+
 # Declarations
 
 Type declaration
@@ -49,6 +72,16 @@
 type EventSource a = (AddHandler a, a -> IO ())
 ```
 
+Type declaration with infix promoted type constructor
+
+```haskell
+fun1 :: Def ('[ Ref s (Stored Uint32), IBool] 'T.:-> IBool)
+fun1 = undefined
+
+fun2 :: Def ('[ Ref s (Stored Uint32), IBool] ':-> IBool)
+fun2 = undefined
+```
+
 Instance declaration without decls
 
 ``` haskell
@@ -80,32 +113,76 @@
 -- \!a yields parse error on input ‘\!’
 ```
 
-List comprehensions
+List comprehensions, short
 
 ``` haskell
+map f xs = [f x | x <- xs]
+```
+
+List comprehensions, long
+
+``` haskell
 defaultExtensions =
   [ e
-  | e@EnableExtension {} <- knownExtensions ] \\
+  | EnableExtension {extensionField1 = extensionField1} <-
+      knownExtensions knownExtensions
+  , let a = b
+    -- comment
+  , let c = d
+    -- comment
+  ]
+```
+
+List comprehensions with operators
+
+```haskell
+defaultExtensions =
+  [e | e@EnableExtension {} <- knownExtensions] \\
   map EnableExtension badExtensions
 ```
 
-Record indentation
+Parallel list comprehension, short
 
+```haskell
+zip xs ys = [(x, y) | x <- xs | y <- ys]
+```
+
+Parallel list comprehension, long
+
+```haskell
+fun xs ys =
+  [ (alphaBetaGamma, deltaEpsilonZeta)
+  | x <- xs
+  , z <- zs
+  | y <- ys
+  , cond
+  , let t = t
+  ]
+```
+
+Record, short
+
 ``` haskell
 getGitProvider :: EventProvider GitRecord ()
 getGitProvider =
-  EventProvider
-  { getModuleName = "Git"
-  , getEvents = getRepoCommits
-  }
+  EventProvider {getModuleName = "Git", getEvents = getRepoCommits}
 ```
 
-Records again
+Record, medium
 
 ``` haskell
 commitToEvent :: FolderPath -> TimeZone -> Commit -> Event.Event
 commitToEvent gitFolderPath timezone commit =
   Event.Event
+  {pluginName = getModuleName getGitProvider, eventIcon = "glyphicon-cog"}
+```
+
+Record, long
+
+``` haskell
+commitToEvent :: FolderPath -> TimeZone -> Commit -> Event.Event
+commitToEvent gitFolderPath timezone commit =
+  Event.Event
   { pluginName = getModuleName getGitProvider
   , eventIcon = "glyphicon-cog"
   , eventDate = localTimeToUTC timezone (commitDate commit)
@@ -123,16 +200,156 @@
     _ -> error $ "Unknown month " ++ month
 ```
 
-Operators
+Operators, bad
 
 ``` haskell
 x =
-  Value <$> thing <*> secondThing <*> thirdThing <*> fourthThing <*> Just thisissolong <*>
-  Just stilllonger
+  Value <$> thing <*> secondThing <*> thirdThing <*> fourthThing <*>
+  Just thisissolong <*>
+  Just stilllonger <*>
+  evenlonger
 ```
 
+Operators, good
+
+```haskell pending
+x =
+  Value <$> thing <*> secondThing <*> thirdThing <*> fourthThing <*>
+  Just thisissolong <*> Just stilllonger <*> evenlonger
+```
+
+Operator with `do`
+
+```haskell
+for xs $ do
+  left x
+  right x
+```
+
+Operator with lambda
+
+```haskell
+for xs $ \x -> do
+  left x
+  right x
+```
+
+Operator with lambda-case
+
+```haskell
+for xs $ \case
+  Left x -> x
+```
+
+Type application
+
+```haskell
+fun @Int 12
+```
+
+Transform list comprehensions
+
+```haskell
+list =
+  [ (x, y, map the v)
+  | x <- [1 .. 10]
+  , y <- [1 .. 10]
+  , let v = x + y
+  , then group by v using groupWith
+  , then take 10
+  , then group using permutations
+  , t <- concat v
+  , then takeWhile by t < 3
+  ]
+```
+
+Type families
+
+```haskell
+type family Id a
+```
+
+Type family annotations
+
+``` haskell
+type family Id a :: *
+```
+
+Type family instances
+
+```haskell
+type instance Id Int = Int
+```
+
+Type family dependencies
+
+```haskell
+type family Id a = r | r -> a
+```
+
+Binding implicit parameters
+
+```haskell
+f =
+  let ?x = 42
+  in f
+```
+
+Closed type families
+
+```haskell
+type family Closed (a :: k) :: Bool where
+  Closed x = 'True
+```
+
+# Template Haskell
+
+Expression brackets
+
+```haskell
+add1 x = [|x + 1|]
+```
+
+Pattern brackets
+
+```haskell
+mkPat = [p|(x, y)|]
+```
+
+Type brackets
+
+```haskell
+foo :: $([t|Bool|]) -> a
+```
+
 # Type signatures
 
+Long arguments list
+
+```haskell
+longLongFunction :: ReaderT r (WriterT w (StateT s m)) a
+                 -> StateT s (WriterT w (ReaderT r m)) a
+```
+
+Long argument list should line break
+
+```haskell pending
+longLongFunction ::
+     ReaderT r (WriterT w (StateT s m)) a
+  -> StateT s (WriterT w (ReaderT r m)) a
+```
+
+Class constraints should leave :: on same line
+
+``` haskell pending
+-- see https://github.com/chrisdone/hindent/pull/266#issuecomment-244182805
+fun ::
+  (Class a, Class b)
+  => foo bar mu zot
+  -> foo bar mu zot
+  -> c
+```
+
 Class constraints
 
 ``` haskell
@@ -147,8 +364,64 @@
 fun :: (a, b, c) -> (a, b)
 ```
 
+Quasiquotes in types
+
+```haskell
+fun :: [a|bc|]
+```
+
+Default signatures
+
+```haskell
+-- https://github.com/chrisdone/hindent/issues/283
+class Foo a where
+  bar :: a -> a -> a
+  default bar :: Monoid a =>
+    a -> a -> a
+  bar = mappend
+```
+
+Implicit parameters
+
+```haskell
+f
+  :: (?x :: Int)
+  => Int
+```
+
+Promoted list (issue #348)
+
+```haskell
+a :: A '[ 'True]
+a = undefined
+
+-- nested promoted list with multiple elements.
+b :: A '[ '[ 'True, 'False], '[ 'False, 'True]]
+b = undefined
+```
+
+Promoted list with a tuple (issue #348)
+
+```haskell
+a :: A '[ '( a, b, c, d)]
+a = undefined
+
+-- nested promoted tuples.
+b :: A '[ '( 'True, 'False, '[], '( 'False, 'True))]
+b = undefined
+```
+
 # Function declarations
 
+Prefix notation for operators
+
+``` haskell
+(+)
+  :: Num a
+  => a -> a -> a
+(+) a b = a
+```
+
 Where clause
 
 ``` haskell
@@ -227,6 +500,73 @@
   ]
 ```
 
+Long line, function application
+
+```haskell
+test = do
+  alphaBetaGamma deltaEpsilonZeta etaThetaIota kappaLambdaMu nuXiOmicron piRh79
+  alphaBetaGamma deltaEpsilonZeta etaThetaIota kappaLambdaMu nuXiOmicron piRho80
+  alphaBetaGamma
+    deltaEpsilonZeta
+    etaThetaIota
+    kappaLambdaMu
+    nuXiOmicron
+    piRhoS81
+```
+
+Long line, tuple
+
+```haskell
+test
+  (alphaBetaGamma, deltaEpsilonZeta, etaThetaIota, kappaLambdaMu, nuXiOmicro79)
+  (alphaBetaGamma, deltaEpsilonZeta, etaThetaIota, kappaLambdaMu, nuXiOmicron80)
+  ( alphaBetaGamma
+  , deltaEpsilonZeta
+  , etaThetaIota
+  , kappaLambdaMu
+  , nuXiOmicronP81)
+```
+
+Long line, tuple section
+
+```haskell
+test
+  (, alphaBetaGamma, , deltaEpsilonZeta, , etaThetaIota, kappaLambdaMu, nu79, )
+  (, alphaBetaGamma, , deltaEpsilonZeta, , etaThetaIota, kappaLambdaMu, , n80, )
+  (
+  , alphaBetaGamma
+  ,
+  , deltaEpsilonZeta
+  ,
+  , etaThetaIota
+  , kappaLambdaMu
+  ,
+  , nu81
+  ,)
+```
+
+# Record syntax
+
+Pattern matching, short
+
+```haskell
+fun Rec {alpha = beta, gamma = delta, epsilon = zeta, eta = theta, iota = kappa} = do
+  beta + delta + zeta + theta + kappa
+```
+
+Pattern matching, long
+
+```haskell
+fun Rec { alpha = beta
+        , gamma = delta
+        , epsilon = zeta
+        , eta = theta
+        , iota = kappa
+        , lambda = mu
+        } =
+  beta + delta + zeta + theta + kappa + mu + beta + delta + zeta + theta + kappa
+```
+
 # Johan Tibell compatibility checks
 
 Basic example from Tibbe's style
@@ -282,17 +622,15 @@
 ``` haskell
 bar :: IO ()
 bar =
-  forM_ [1, 2, 3] $
-  \n -> do
+  forM_ [1, 2, 3] $ \n -> do
     putStrLn "Here comes a number!"
     print n
 
 foo :: IO ()
 foo =
-  alloca 10 $
-  \a ->
-     alloca 20 $
-     \b -> cFunction fooo barrr muuu (fooo barrr muuu) (fooo barrr muuu)
+  alloca 10 $ \a ->
+    alloca 20 $ \b ->
+      cFunction fooo barrr muuu (fooo barrr muuu) (fooo barrr muuu)
 ```
 
 # Comments
@@ -338,10 +676,37 @@
        )
     , 3
     ]
+    -- in the end of the function
+  where
+    alpha = alpha
+    -- between alpha and beta
+    beta = beta
+    -- after beta
 
 foo = 1 -- after foo
+
+gamma = do
+  delta
+  epsilon
+  -- in the end of a do-block 1
+
+gamma = do
+  delta
+  epsilon
+  -- the very last block is detected differently
 ```
 
+Doesn't work yet (wrong comment position detection)
+
+```haskell pending
+gamma = do
+  -- in the beginning of a do-block
+  delta
+  where
+    -- before alpha
+    alpha = alpha
+```
+
 Haddock comments
 
 ``` haskell
@@ -455,14 +820,16 @@
 ``` haskell
 c
   :: forall new.
-     (Settable "pitch" Pitch (Map.AsMap (new Map.:\ "pitch")) new
-     ,Default (Book' (Map.AsMap (new Map.:\ "pitch"))))
+     ( Settable "pitch" Pitch (Map.AsMap (new Map.:\ "pitch")) new
+     , Default (Book' (Map.AsMap (new Map.:\ "pitch")))
+     )
   => Book' new
 c = set #pitch C (def :: Book' (Map.AsMap (new Map.:\ "pitch")))
 
 foo
-  :: (Foooooooooooooooooooooooooooooooooooooooooo
-     ,Foooooooooooooooooooooooooooooooooooooooooo)
+  :: ( Foooooooooooooooooooooooooooooooooooooooooo
+     , Foooooooooooooooooooooooooooooooooooooooooo
+     )
   => A
 ```
 
@@ -487,9 +854,7 @@
 trans One e n =
   M.singleton
     (Query Unmarked (Mark NonExistent)) -- The goal of this is to fail always
-    (emptyImage
-     { notPresent = S.singleton (TransitionResult Two (Just A) n)
-     })
+    (emptyImage {notPresent = S.singleton (TransitionResult Two (Just A) n)})
 ```
 
 sheyll explicit forall in instances #218
@@ -504,9 +869,17 @@
 
 tfausak support shebangs #208
 
-``` haskell
+``` haskell given
 #!/usr/bin/env stack
 -- stack runghc
+main =
+ pure ()
+-- https://github.com/chrisdone/hindent/issues/208
+```
+
+``` haskell expect
+#!/usr/bin/env stack
+-- stack runghc
 main = pure ()
 -- https://github.com/chrisdone/hindent/issues/208
 ```
@@ -518,8 +891,8 @@
 import Data.List
 import Data.Maybe
 
-import MyProject
 import FooBar
+import MyProject
 
 import GHC.Monad
 
@@ -542,9 +915,9 @@
 Wrapped import list shouldn't add newline
 
 ```haskell
-import TooLongList
+import ATooLongList
        (alpha, beta, gamma, delta, epsilon, zeta, eta, theta)
-import Test
+import B
 ```
 
 radupopescu `deriving` keyword not aligned with pipe symbol for type declarations
@@ -561,6 +934,117 @@
   deriving (Show)
 ```
 
+sgraf812 top-level pragmas should not add an additional newline #255
+
+``` haskell
+-- https://github.com/chrisdone/hindent/issues/255
+{-# INLINE f #-}
+f :: Int -> Int
+f n = n
+```
+
+ivan-timokhin breaks code with type operators #277
+
+```haskell
+-- https://github.com/chrisdone/hindent/issues/277
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+type m ~> n = ()
+
+class (a :< b) c
+```
+
+ivan-timokhin variables swapped around in constraints #278
+
+```haskell
+-- https://github.com/chrisdone/hindent/issues/278
+data Link c1 c2 a c =
+  forall b. (c1 a b, c2 b c) =>
+            Link (Proxy b)
+```
+
+ttuegel qualified infix sections get mangled #273
+
+```haskell
+-- https://github.com/chrisdone/hindent/issues/273
+import qualified Data.Vector as V
+
+main :: IO ()
+main = do
+  let _ = foldr1 (V.++) [V.empty, V.empty]
+  pure ()
+
+-- more corner cases.
+xs = V.empty V.++ V.empty
+
+ys = (++) [] []
+
+cons :: V.Vector a -> V.Vector a -> V.Vector a
+cons = (V.++)
+```
+
+ivan-timokhin breaks operators type signatures #301
+
+```haskell
+-- https://github.com/chrisdone/hindent/issues/301
+(+) :: ()
+```
+
+cdepillabout Long deriving clauses are not reformatted #289
+
+```haskell
+newtype Foo =
+  Foo Proxy
+  deriving ( Functor
+           , Applicative
+           , Monad
+           , Semigroup
+           , Monoid
+           , Alternative
+           , MonadPlus
+           , Foldable
+           , Traversable
+           )
+```
+
+ivan-timokhin Breaks instances with type operators #342
+
+```haskell
+-- https://github.com/chrisdone/hindent/issues/342
+instance Foo (->)
+
+instance Foo (^>)
+
+instance Foo (T.<^)
+```
+
+# MINIMAL pragma
+
+Monad example
+
+```haskell
+class A where
+  {-# MINIMAL return, ((>>=) | (join, fmap)) #-}
+```
+
+Very long names #310
+
+```haskell
+class A where
+  {-# MINIMAL averylongnamewithnoparticularmeaning
+            | ananotherverylongnamewithnomoremeaning #-}
+```
+
+NorfairKing Do as left-hand side of an infix operation #296
+
+```haskell
+block =
+  do ds <- inBraces $ inWhiteSpace declarations
+     return $ Block ds
+     <?> "block"
+```
+
 # Behaviour checks
 
 Unicode
@@ -591,36 +1075,36 @@
 quasiQuotes =
   [ ( ''[]
     , \(typeVariable:_) _automaticPrinter ->
-         (let presentVar = varE (presentVarName typeVariable)
-          in lamE
-               [varP (presentVarName typeVariable)]
-               [|(let typeString = "[" ++ fst $(presentVar) ++ "]"
-                  in ( typeString
-                     , \xs ->
-                          case fst $(presentVar) of
-                            "GHC.Types.Char" ->
-                              ChoicePresentation
-                                "String"
-                                [ ( "String"
-                                  , StringPresentation
-                                      "String"
-                                      (concatMap
-                                         getCh
-                                         (map (snd $(presentVar)) xs)))
-                                , ( "List of characters"
-                                  , ListPresentation
-                                      typeString
-                                      (map (snd $(presentVar)) xs))
-                                ]
-                              where getCh (CharPresentation "GHC.Types.Char" ch) =
-                                      ch
-                                    getCh (ChoicePresentation _ ((_, CharPresentation _ ch):_)) =
-                                      ch
-                                    getCh _ = ""
-                            _ ->
-                              ListPresentation
-                                typeString
-                                (map (snd $(presentVar)) xs)))|]))
+        (let presentVar = varE (presentVarName typeVariable)
+         in lamE
+              [varP (presentVarName typeVariable)]
+              [|(let typeString = "[" ++ fst $(presentVar) ++ "]"
+                 in ( typeString
+                    , \xs ->
+                        case fst $(presentVar) of
+                          "GHC.Types.Char" ->
+                            ChoicePresentation
+                              "String"
+                              [ ( "String"
+                                , StringPresentation
+                                    "String"
+                                    (concatMap
+                                       getCh
+                                       (map (snd $(presentVar)) xs)))
+                              , ( "List of characters"
+                                , ListPresentation
+                                    typeString
+                                    (map (snd $(presentVar)) xs))
+                              ]
+                            where getCh (CharPresentation "GHC.Types.Char" ch) =
+                                    ch
+                                  getCh (ChoicePresentation _ ((_, CharPresentation _ ch):_)) =
+                                    ch
+                                  getCh _ = ""
+                          _ ->
+                            ListPresentation
+                              typeString
+                              (map (snd $(presentVar)) xs)))|]))
   ]
 ```
 
diff --git a/elisp/hindent.el b/elisp/hindent.el
--- a/elisp/hindent.el
+++ b/elisp/hindent.el
@@ -36,26 +36,34 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Customization properties
 
+(defgroup hindent nil
+  "Integration with the \"hindent\" reformatting program."
+  :prefix "hindent-"
+  :group 'haskell)
+
 (defcustom hindent-style
   nil
   "The style to use for formatting.
 
-This customization is deprecated and ignored."
-  :group 'haskell
+For hindent versions lower than 5, you must set this to a non-nil string."
+  :group 'hindent
   :type 'string
   :safe #'stringp)
 
+(make-obsolete-variable 'hindent-style nil "hindent 5")
+
+
 (defcustom hindent-process-path
   "hindent"
   "Location where the hindent executable is located."
-  :group 'haskell
+  :group 'hindent
   :type 'string
   :safe #'stringp)
 
 (defcustom hindent-line-length
   80
   "Optionally override the line length."
-  :group 'haskell
+  :group 'hindent
   :type '(choice (const :tag "Default: 80" 80)
                  (integer :tag "Override" 120))
   :safe (lambda (val) (or (integerp val) (not val))))
@@ -63,15 +71,15 @@
 (defcustom hindent-indent-size
   2
   "Optionally override the indent size."
-  :group 'haskell
+  :group 'hindent
   :type '(choice (const :tag "Default: 2" 2)
                  (integer :tag "Override" 4))
   :safe (lambda (val) (or (integerp val) (not val))))
 
 (defcustom hindent-reformat-buffer-on-save nil
-  "Set to t to run `hindent-reformat-buffer' when a buffer in
-`hindent-mode' is saved."
-  :group 'haskell
+  "Set to t to run `hindent-reformat-buffer' when a buffer in `hindent-mode' is saved."
+  :group 'hindent
+  :type 'boolean
   :safe #'booleanp)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -94,7 +102,7 @@
   :init-value nil
   :keymap hindent-mode-map
   :lighter " HI"
-  :group 'haskell
+  :group 'hindent
   :require 'hindent
   (if hindent-mode
       (add-hook 'before-save-hook 'hindent--before-save nil t)
@@ -110,9 +118,10 @@
 
 ;;;###autoload
 (defun hindent-reformat-decl ()
-  "Re-format the current declaration by parsing and pretty
-  printing it. Comments are preserved, although placement may be
-  funky."
+  "Re-format the current declaration.
+
+The declaration is parsed and pretty printed.  Comments are
+preserved, although placement may be funky."
   (interactive)
   (let ((start-end (hindent-decl-points)))
     (when start-end
@@ -132,18 +141,22 @@
   "Re-format current declaration, or fill paragraph.
 
 Fill paragraph if in a comment, otherwise reformat the current
-declaration."
+declaration.  When filling, the prefix argument JUSTIFY will
+cause the text to be justified, as per `fill-paragraph'."
   (interactive (progn
                  ;; Copied from `fill-paragraph'
                  (barf-if-buffer-read-only)
                  (list (if current-prefix-arg 'full))))
   (if (hindent-in-comment)
       (fill-paragraph justify t)
-    (hindent/reformat-decl)))
+    (hindent-reformat-decl)))
 
 ;;;###autoload
 (defun hindent-reformat-region (beg end &optional drop-newline)
-  "Reformat the given region, accounting for indentation."
+  "Reformat the region from BEG to END, accounting for indentation.
+
+If DROP-NEWLINE is non-nil, don't require a newline at the end of
+the file."
   (interactive "r")
   (if (= (save-excursion (goto-char beg)
                          (line-beginning-position))
@@ -165,17 +178,19 @@
         (insert new-string)))))
 
 ;;;###autoload
-(defun hindent/reformat-decl ()
-  "See `hindent-reformat-decl'."
-  (hindent-reformat-decl))
+(define-obsolete-function-alias 'hindent/reformat-decl 'hindent-reformat-decl)
 
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Internal library
 
 (defun hindent-reformat-region-as-is (beg end &optional drop-newline)
-  "Reformat the given region as-is.
+  "Reformat the given region from BEG to END as-is.
 
-This is the place where hindent is actually called."
+This is the place where hindent is actually called.
+
+If DROP-NEWLINE is non-nil, don't require a newline at the end of
+the file."
   (let* ((original (current-buffer))
          (orig-str (buffer-substring-no-properties beg end)))
     (with-temp-buffer
@@ -205,8 +220,8 @@
                      (new-str (with-current-buffer temp
                                 (when (and drop-newline (not last-decl))
                                   (goto-char (point-max))
-                                  (when (looking-back "\n")
-                                    (delete-backward-char 1)))
+                                  (when (looking-back "\n" (1- (point)))
+                                    (delete-char -1)))
                                 (buffer-string))))
                 (if (not (string= new-str orig-str))
                     (let ((line (line-number-at-pos))
@@ -219,25 +234,25 @@
                           (goto-char (point-min))
                           (forward-line (1- line))
                           (goto-char (+ (line-beginning-position) col))
-                          (when (looking-back "^[ ]+")
+                          (when (looking-back "^[ ]+" (line-beginning-position))
                             (back-to-indentation))
                           (delete-trailing-whitespace new-start new-end)))
                       (message "Formatted."))
                   (message "Already formatted.")))))))))))
 
-(defun hindent-decl-points (&optional use-line-comments)
-  "Get the start and end position of the current
-declaration. This assumes that declarations start at column zero
-and that the rest is always indented by one space afterwards, so
-Template Haskell uses with it all being at column zero are not
-expected to work."
+(defun hindent-decl-points ()
+  "Get the start and end position of the current declaration.
+
+This assumes that declarations start at column zero and that the
+rest is always indented by one space afterwards, so Template
+Haskell uses with it all being at column zero are not expected to
+work."
   (cond
    ;; If we're in a block comment spanning multiple lines then let's
    ;; see if it starts at the beginning of the line (or if any comment
    ;; is at the beginning of the line, we don't care to treat it as a
    ;; proper declaration.
-   ((and (not use-line-comments)
-         (hindent-in-comment)
+   ((and (hindent-in-comment)
          (save-excursion (goto-char (line-beginning-position))
                          (hindent-in-comment)))
     nil)
@@ -290,25 +305,20 @@
                   (point))
                (/= (line-beginning-position) (point)))
       (forward-char -1))
-    (and (or (eq 'font-lock-comment-delimiter-face
-                 (get-text-property (point) 'face))
-             (eq 'font-lock-doc-face
-                 (get-text-property (point) 'face))
-             (eq 'font-lock-comment-face
-                 (get-text-property (point) 'face))
-             (save-excursion (goto-char (line-beginning-position))
-                             (looking-at "^\-\- ")))
-         ;; Pragmas {-# SPECIALIZE .. #-} etc are not to be treated as
-         ;; comments, even though they are highlighted as such
-         (not (save-excursion (goto-char (line-beginning-position))
-                              (looking-at "{-# "))))))
+    (and
+     (elt (syntax-ppss) 4)
+     ;; Pragmas {-# SPECIALIZE .. #-} etc are not to be treated as
+     ;; comments, even though they are highlighted as such
+     (not (save-excursion (goto-char (line-beginning-position))
+                          (looking-at "{-# "))))))
 
 (defun hindent-extra-arguments ()
-  "Pass in extra arguments, such as extensions and optionally
-other things later."
-  (if (boundp 'haskell-language-extensions)
-      haskell-language-extensions
-    '()))
+  "Extra command line arguments for the hindent invocation."
+  (append
+   (when (boundp 'haskell-language-extensions)
+     haskell-language-extensions)
+   (when hindent-style
+     (list "--style" hindent-style))))
 
 (provide 'hindent)
 
diff --git a/hindent.cabal b/hindent.cabal
--- a/hindent.cabal
+++ b/hindent.cabal
@@ -1,5 +1,5 @@
 name:                hindent
-version:             5.2.1
+version:             5.2.2
 synopsis:            Extensible Haskell pretty printer
 description:         Extensible Haskell pretty printer. Both a library and an executable.
                      .
diff --git a/src/HIndent.hs b/src/HIndent.hs
--- a/src/HIndent.hs
+++ b/src/HIndent.hs
@@ -153,21 +153,23 @@
 cppSplitBlocks inp =
   modifyLast (inBlock (<> trailing)) .
   map (classify . mconcat . intersperse "\n") .
-  groupBy ((==) `on` cppLine) . S8.lines $
+  groupBy ((==) `on` nonHaskellLine) . S8.lines $
   inp
   where
+    nonHaskellLine :: ByteString -> Bool
+    nonHaskellLine src = cppLine src || shebangLine src
+    shebangLine :: ByteString -> Bool
+    shebangLine = S8.isPrefixOf "#!"
     cppLine :: ByteString -> Bool
     cppLine src =
       any
         (`S8.isPrefixOf` src)
         ["#if", "#end", "#else", "#define", "#undef", "#elif"]
     classify :: ByteString -> CodeBlock
-    classify text =
-      if S8.isPrefixOf "#!" text
-         then Shebang text
-         else if cppLine text
-                then CPPDirectives text
-                else HaskellSource text
+    classify text
+      | shebangLine text = Shebang text
+      | cppLine text = CPPDirectives text
+      | otherwise = HaskellSource text
     -- Hack to work around some parser issues in haskell-src-exts: Some pragmas
     -- need to have a newline following them in order to parse properly, so we include
     -- the trailing newline in the code block if it existed.
@@ -399,7 +401,7 @@
 -- the State. This allows for a multiple pass approach.
 collectCommentsBy
   :: ([NodeComment] -> [NodeComment] -> [NodeComment])
-  -> (SomeComment -> NodeComment)
+  -> (SrcSpan -> SomeComment -> NodeComment)
   -> (SrcSpan -> SrcSpan -> Bool)
   -> NodeInfo
   -> State [Comment] NodeInfo
@@ -412,6 +414,7 @@
                  if predicate nodeSpan (setFilename commentString commentSpan)
                    then Right
                           (cons
+                             commentSpan
                              ((if multiLine
                                  then MultiLine
                                  else EndOfLine)
diff --git a/src/HIndent/Pretty.hs b/src/HIndent/Pretty.hs
--- a/src/HIndent/Pretty.hs
+++ b/src/HIndent/Pretty.hs
@@ -15,8 +15,7 @@
 import           Control.Applicative
 import           Control.Monad.State.Strict hiding (state)
 import qualified Data.ByteString.Builder as S
-import qualified Data.Foldable
-import           Data.Foldable (traverse_)
+import           Data.Foldable (for_, traverse_)
 import           Data.Int
 import           Data.List
 import           Data.Maybe
@@ -42,7 +41,7 @@
   mapM_
     (\c' -> do
        case c' of
-         CommentBeforeLine c -> do
+         CommentBeforeLine _ c -> do
            case c of
              EndOfLine s -> write ("--" ++ s)
              MultiLine s -> write ("{-" ++ s ++ "-}")
@@ -53,13 +52,21 @@
   mapM_
     (\(i, c') -> do
        case c' of
-         CommentSameLine c -> do
+         CommentSameLine spn c -> do
            col <- gets psColumn
-           unless (col == 0) space
-           writeComment c
-         CommentAfterLine c -> do
+           if col == 0
+             then do
+               -- write comment keeping original indentation
+               let col' = fromIntegral $ srcSpanStartColumn spn - 1
+               column col' $ writeComment c
+             else do
+               space
+               writeComment c
+         CommentAfterLine spn c -> do
            when (i == 0) newline
-           writeComment c
+           -- write comment keeping original indentation
+           let col = fromIntegral $ srcSpanStartColumn spn - 1
+           column col $ writeComment c
          _ -> return ())
     (zip [0 :: Int ..] comments)
   where
@@ -110,7 +117,7 @@
 
 -- | Print all the printers separated by commas.
 commas :: [Printer ()] -> Printer ()
-commas = inter (do comma; space)
+commas = inter (write ", ")
 
 -- | Print all the printers separated by sep.
 inter :: Printer () -> [Printer ()] -> Printer ()
@@ -191,29 +198,21 @@
         then column col dependent
         else dependent
 
+-- | Wrap.
+wrap :: String -> String -> Printer a -> Printer a
+wrap open close p = depend (write open) $ p <* write close
+
 -- | Wrap in parens.
 parens :: Printer a -> Printer a
-parens p =
-  depend (write "(")
-         (do v <- p
-             write ")"
-             return v)
+parens = wrap "(" ")"
 
 -- | Wrap in braces.
 braces :: Printer a -> Printer a
-braces p =
-  depend (write "{")
-         (do v <- p
-             write "}"
-             return v)
+braces = wrap "{" "}"
 
 -- | Wrap in brackets.
 brackets :: Printer a -> Printer a
-brackets p =
-  depend (write "[")
-         (do v <- p
-             write "]"
-             return v)
+brackets = wrap "[" "]"
 
 -- | Write a space.
 space :: Printer ()
@@ -235,11 +234,6 @@
      let addingNewline = eol && x /= "\n"
      when addingNewline newline
      state <- get
-     when
-       hardFail
-       (guard
-          (additionalLines == 0 &&
-           (psColumn state < configMaxColumns (psConfig state))))
      let writingNewline = x == "\n"
          out :: String
          out =
@@ -248,15 +242,21 @@
                                ' ') <>
                    x
               else x
+         psColumn' =
+            if additionalLines > 0
+               then fromIntegral (length (concat (take 1 (reverse srclines))))
+               else psColumn state + fromIntegral (length out)
+     when
+       hardFail
+       (guard
+          (additionalLines == 0 &&
+           (psColumn' <= configMaxColumns (psConfig state))))
      modify (\s ->
                s {psOutput = psOutput state <> S.stringUtf8 out
                  ,psNewline = False
                  ,psLine = psLine state + fromIntegral additionalLines
                  ,psEolComment= False
-                 ,psColumn =
-                    if additionalLines > 0
-                       then fromIntegral (length (concat (take 1 (reverse srclines))))
-                       else psColumn state + fromIntegral (length out)})
+                 ,psColumn = psColumn'})
   where srclines = lines x
         additionalLines =
           length (filter (== '\n') x)
@@ -367,13 +367,22 @@
       PList _ ps ->
         brackets (commas (map pretty ps))
       PParen _ e -> parens (pretty e)
-      PRec _ qname fields ->
-        do indentSpaces <- getIndentSpaces
-           depend (do pretty qname
-                      space)
-                  (braces (prefixedLined ","
-                                         (map (indented indentSpaces . pretty) fields)))
-
+      PRec _ qname fields -> do
+        let horVariant = do
+              pretty qname
+              space
+              braces $ commas $ map pretty fields
+            verVariant =
+              depend (pretty qname >> space) $ do
+                case fields of
+                  [] -> write "{}"
+                  [field] -> braces $ pretty field
+                  _ -> do
+                    depend (write "{") $
+                      prefixedLined "," $ map (depend space . pretty) fields
+                    newline
+                    write "}"
+        horVariant `ifFitsOnOneLineOrElse` verVariant
       PAsPat _ n p ->
         depend (do pretty n
                    write "@")
@@ -445,28 +454,28 @@
                          (lined (map pretty stmts))
        Just st -> put st
 -- | Space out tuples.
-exp (Tuple _ boxed exps) =
-  depend (write (case boxed of
-                   Unboxed -> "(#"
-                   Boxed -> "("))
-         (do mst <- fitsOnOneLine p
-             case mst of
-               Nothing -> prefixedLined ","
-                                        (map (depend space . pretty) exps)
-               Just st -> put st
-             write (case boxed of
-                      Unboxed -> "#)"
-                      Boxed -> ")"))
-  where p = inter (write ", ") (map pretty exps)
+exp (Tuple _ boxed exps) = do
+  let horVariant = parensB boxed $ inter (write ", ") (map pretty exps)
+      verVariant = parensB boxed $ prefixedLined "," (map (depend space . pretty) exps)
+  mst <- fitsOnOneLine horVariant
+  case mst of
+    Nothing -> verVariant
+    Just st -> put st
+  where
+    parensB Unboxed = wrap "(#" "#)"
+    parensB Boxed   = parens
 -- | Space out tuples.
-exp (TupleSection _ boxed mexps) =
-  depend (write (case boxed of
-                   Unboxed -> "(#"
-                   Boxed -> "("))
-         (do inter (write ", ") (map (maybe (return ()) pretty) mexps)
-             write (case boxed of
-                      Unboxed -> "#)"
-                      Boxed -> ")"))
+exp (TupleSection _ boxed mexps) = do
+  let horVariant = parensB boxed $ inter (write ", ") (map (maybe (return ()) pretty) mexps)
+      verVariant =
+        parensB boxed $ prefixedLined "," (map (maybe (return ()) (depend space . pretty)) mexps)
+  mst <- fitsOnOneLine horVariant
+  case mst of
+    Nothing -> verVariant
+    Just st -> put st
+  where
+    parensB Unboxed = wrap "(#" "#)"
+    parensB Boxed   = parens
 -- | Infix apps, same algorithm as ChrisDone at the moment.
 exp e@(InfixApp _ a op b) =
   infixApp e a op b Nothing
@@ -534,36 +543,53 @@
              newline
              indented (-4) (depend (write "in ")
                                    (pretty e)))
-exp (ListComp _ e qstmt) =
-  brackets (do space
-               pretty e
-               unless (null qstmt)
-                      (do newline
-                          indented (-1)
-                                   (write "|")
-                          prefixedLined ","
-                                        (map (\x -> do space
-                                                       pretty x
-                                                       space)
-                                             qstmt)))
-exp (TypeApp _ _) = error "FIXME: No implementation for TypeApp"
+exp (ListComp _ e qstmt) = do
+  let horVariant = brackets $ do
+        pretty e
+        write " | "
+        commas $ map pretty qstmt
+      verVariant = do
+        write "[ "
+        pretty e
+        newline
+        depend (write "| ") $ prefixedLined ", " $ map pretty qstmt
+        newline
+        write "]"
+  horVariant `ifFitsOnOneLineOrElse` verVariant
+
+exp (ParComp _ e qstmts) = do
+  let horVariant = brackets $ do
+        pretty e
+        for_ qstmts $ \qstmt -> do
+          write " | "
+          commas $ map pretty qstmt
+      verVariant = do
+        depend (write "[ ") $ pretty e
+        newline
+        for_ qstmts $ \qstmt -> do
+          depend (write "| ") $ prefixedLined ", " $ map pretty qstmt
+          newline
+        write "]"
+  horVariant `ifFitsOnOneLineOrElse` verVariant
+
+exp (TypeApp _ t) = do
+  write "@"
+  pretty t
+
 exp (ExprHole {}) = write "_"
 exp (NegApp _ e) =
   depend (write "-")
          (pretty e)
-exp (Lambda _ ps e) =
-  depend
-    (write "\\")
-    (do spaced
-          (map
-             (\(i,x) -> do
-                case (i, x) of
-                  (0,PIrrPat {}) -> space
-                  (0,PBangPat {}) -> space
-                  _ -> return ()
-                pretty x)
-             (zip [0 :: Int ..] ps))
-        swing (write " ->") (pretty e))
+exp (Lambda _ ps e) = do
+  write "\\"
+  spaced [ do case (i, x) of
+                (0, PIrrPat {}) -> space
+                (0, PBangPat {}) -> space
+                _ -> return ()
+              pretty x
+         | (i, x) <- zip [0 :: Int ..] ps
+         ]
+  swing (write " ->") $ pretty e
 exp (Paren _ e) = parens (pretty e)
 exp (Case _ e alts) =
   do depend (write "case ")
@@ -651,6 +677,8 @@
 exp (Lit _ lit) = prettyInternal lit
 exp (Var _ q) = case q of
                   Special _ Cons{} -> parens (pretty q)
+                  Qual _ _ (Symbol _ _) -> parens (pretty q)
+                  UnQual _ (Symbol _ _) -> parens (pretty q)
                   _ -> pretty q
 exp (IPVar _ q) = pretty q
 exp (Con _ q) = case q of
@@ -674,8 +702,6 @@
 exp x@ParArrayFromTo{} = pretty' x
 exp x@ParArrayFromThenTo{} = pretty' x
 exp x@ParArrayComp{} = pretty' x
-exp ParComp{} =
-  error "FIXME: No implementation for ParComp."
 exp (OverloadedLabel _ label) = string ('#' : label)
 
 instance Pretty IPName where
@@ -689,16 +715,25 @@
   prettyInternal x =
     case x of
       QualStmt _ s -> pretty s
-      ThenTrans{} ->
-        error "FIXME: No implementation for ThenTrans."
-      ThenBy{} ->
-        error "FIXME: No implementation for ThenBy."
-      GroupBy{} ->
-        error "FIXME: No implementation for GroupBy."
-      GroupUsing{} ->
-        error "FIXME: No implementation for GroupUsing."
-      GroupByUsing{} ->
-        error "FIXME: No implementation for GroupByUsing."
+      ThenTrans _ s -> do
+        write "then "
+        pretty s
+      ThenBy _ s t -> do
+        write "then "
+        pretty s
+        write " by "
+        pretty t
+      GroupBy _ s -> do
+        write "then group by "
+        pretty s
+      GroupUsing _ s -> do
+        write "then group using "
+        pretty s
+      GroupByUsing _ s t -> do
+        write "then group by "
+        pretty s
+        write " using "
+        pretty t
 
 instance Pretty Decl where
   prettyInternal = decl'
@@ -734,8 +769,7 @@
 decl (ClassDecl _ ctx dhead fundeps decls) =
   do depend (write "class ")
             (withCtx ctx
-                     (depend (do pretty dhead
-                                 space)
+                     (depend (do pretty dhead)
                              (depend (unless (null fundeps)
                                              (do write " | "
                                                  commas (map pretty fundeps)))
@@ -750,8 +784,42 @@
                  (depend (write " = ")
                          (pretty typ')))
 
-decl TypeFamDecl{} =
-  error "FIXME: No implementation for TypeFamDecl."
+decl (TypeFamDecl _ declhead result injectivity) = do
+  write "type family "
+  pretty declhead
+  case result of
+    Just r -> do
+      space
+      let sep = case r of
+                  KindSig _ _ -> "::"
+                  TyVarSig _ _ -> "="
+      write sep
+      space
+      pretty r
+    Nothing -> return ()
+  case injectivity of
+    Just i -> do
+      space
+      pretty i
+    Nothing -> return ()
+decl (ClosedTypeFamDecl _ declhead result injectivity instances) = do
+  write "type family "
+  pretty declhead
+  for_ result $ \r -> do
+    space
+    let sep = case r of
+                KindSig _ _ -> "::"
+                TyVarSig _ _ -> "="
+    write sep
+    space
+    pretty r
+  for_ injectivity $ \i -> do
+    space
+    pretty i
+  space
+  write "where"
+  newline
+  indentedBlock (lined (map pretty instances))
 decl (DataDecl _ dataornew ctx dhead condecls mderivs) =
   do depend (do pretty dataornew
                 space)
@@ -793,19 +861,35 @@
   pretty name
 
   write " #-}"
+decl (MinimalPragma _ (Just formula)) =
+  wrap "{-# " " #-}" $ do
+    depend (write "MINIMAL ") $ pretty formula
 decl x' = pretty' x'
 
+instance Pretty TypeEqn where
+  prettyInternal (TypeEqn _ in_ out_) = do
+    pretty in_
+    write " = "
+    pretty out_
+
 instance Pretty Deriving where
   prettyInternal (Deriving _ heads) =
-    do write "deriving"
-       space
-       let heads' =
-             if length heads == 1
-                then map stripParens heads
-                else heads
-       parens (commas (map pretty heads'))
-    where stripParens (IParen _ iRule) = stripParens iRule
-          stripParens x = x
+    depend (write "deriving" >> space) $ do
+      let heads' =
+            if length heads == 1
+              then map stripParens heads
+              else heads
+      maybeDerives <- fitsOnOneLine $ parens (commas (map pretty heads'))
+      case maybeDerives of
+        Nothing -> formatMultiLine heads'
+        Just derives -> put derives
+    where
+      stripParens (IParen _ iRule) = stripParens iRule
+      stripParens x = x
+      formatMultiLine derives = do
+        depend (write "( ") $ prefixedLined ", " (map pretty derives)
+        newline
+        write ")"
 
 instance Pretty Alt where
   prettyInternal x =
@@ -824,20 +908,23 @@
   prettyInternal x =
     case x of
       ClassA _ name types -> spaced (pretty name : map pretty types)
-      i@InfixA{} -> pretty' i
-      IParam{} -> error "FIXME: No implementation for IParam."
-      EqualP _ a b ->
-        do pretty a
-           write " ~ "
-           pretty b
+      i@InfixA {} -> pretty' i
+      IParam _ name ty -> do
+        pretty name
+        write " :: "
+        pretty ty
+      EqualP _ a b -> do
+        pretty a
+        write " ~ "
+        pretty b
       ParenA _ asst -> parens (pretty asst)
-      AppA _ name tys -> spaced (pretty name : map pretty tys)
+      AppA _ name tys -> spaced (pretty name : map pretty (reverse tys))
       WildCardA _ name ->
         case name of
           Nothing -> write "_"
-          Just n ->
-            do write "_"
-               pretty n
+          Just n -> do
+            write "_"
+            pretty n
 
 instance Pretty BangType where
   prettyInternal x =
@@ -1025,9 +1112,12 @@
       DHInfix _ var name ->
         do pretty var
            space
-           write "`"
-           pretty name
-           write "`"
+           case name of
+              Ident _ _ -> do
+                write "`"
+                pretty name
+                write "`"
+              Symbol _ _ -> pretty name
       DHApp _ dhead var ->
         depend (pretty dhead)
                (do space
@@ -1064,6 +1154,7 @@
                             ,interOf newline
                                      (map (\case
                                              r@TypeSig{} -> (1,pretty r)
+                                             r@InlineSig{} -> (1, pretty r)
                                              r -> (2,pretty r))
                                           decls))])
            newline
@@ -1078,42 +1169,93 @@
       XmlPage{} -> error "FIXME: No implementation for XmlPage."
       XmlHybrid{} -> error "FIXME: No implementation for XmlHybrid."
 
--- | Format imports, preserving empty newlines between them.
+-- | Format imports, preserving empty newlines between groups.
 formatImports :: [ImportDecl NodeInfo] -> Printer ()
-formatImports imps =
-    mapM_ formatImport (zip [1 ..] (zip (Nothing : map Just imps) imps))
+formatImports =
+  sequence_ .
+  intersperse (newline >> newline) .
+  map formatImportGroup . groupAdjacentBy atNextLine
   where
-    formatImport (i, (mprev, current)) = do
-        when (difference > 1) newline
-        pretty current
-        unless (i == length imps) newline
+    atNextLine import1 import2 =
+      let end1 = srcSpanEndLine (srcInfoSpan (nodeInfoSpan (ann import1)))
+          start2 = srcSpanStartLine (srcInfoSpan (nodeInfoSpan (ann import2)))
+      in start2 - end1 <= 1
+    formatImportGroup imps = do
+      shouldSortImports <- gets $ configSortImports . psConfig
+      let imps1 =
+            if shouldSortImports
+              then sortOn moduleVisibleName imps
+              else imps
+      sequence_ . intersperse newline $ map formatImport imps1
       where
-        difference =
-            case mprev of
-                Nothing -> 0
-                Just prev ->
-                    fst (srcSpanStart (srcInfoSpan (nodeInfoSpan (ann current)))) -
-                    fst (srcSpanEnd   (srcInfoSpan (nodeInfoSpan (ann prev   ))))
+        moduleVisibleName idecl =
+          let ModuleName _ name = importModule idecl
+          in name
+    formatImport = pretty
 
+groupAdjacentBy :: (a -> a -> Bool) -> [a] -> [[a]]
+groupAdjacentBy _ [] = []
+groupAdjacentBy adj items = xs : groupAdjacentBy adj rest
+  where
+    (xs, rest) = spanAdjacentBy adj items
+
+spanAdjacentBy :: (a -> a -> Bool) -> [a] -> ([a], [a])
+spanAdjacentBy _ [] = ([], [])
+spanAdjacentBy _ [x] = ([x], [])
+spanAdjacentBy adj (x:xs@(y:_))
+  | adj x y =
+    let (xs', rest') = spanAdjacentBy adj xs
+    in (x : xs', rest')
+  | otherwise = ([x], xs)
+
 instance Pretty Bracket where
   prettyInternal x =
     case x of
       ExpBracket _ p ->
-        brackets (depend (write "|")
-                         (do pretty p
-                             write "|"))
-      PatBracket _ _ ->
-        error "FIXME: No implementation for PatBracket."
-      TypeBracket _ _ ->
-        error "FIXME: No implementation for TypeBracket."
+        brackets
+          (depend
+             (write "|")
+             (do pretty p
+                 write "|"))
+      PatBracket _ p ->
+        brackets
+          (depend
+             (write "p|")
+             (do pretty p
+                 write "|"))
+      TypeBracket _ ty ->
+        brackets
+          (depend
+             (write "t|")
+             (do pretty ty
+                 write "|"))
       d@(DeclBracket _ _) -> pretty' d
 
 instance Pretty IPBind where
   prettyInternal x =
     case x of
-      IPBind _ _ _ ->
-        error "FIXME: No implementation for IPBind."
+      IPBind _ name expr -> do
+        pretty name
+        space
+        write "="
+        space
+        pretty expr
 
+instance Pretty BooleanFormula where
+  prettyInternal (VarFormula _ i@(Ident _ _)) = pretty' i
+  prettyInternal (VarFormula _ (Symbol _ s)) = write "(" >> string s >> write ")"
+  prettyInternal (AndFormula _ fs) = do
+      maybeFormulas <- fitsOnOneLine $ inter (write ", ") $ map pretty fs
+      case maybeFormulas of
+        Nothing -> prefixedLined ", " (map pretty fs)
+        Just formulas -> put formulas
+  prettyInternal (OrFormula _ fs) = do
+      maybeFormulas <- fitsOnOneLine $ inter (write " | ") $ map pretty fs
+      case maybeFormulas of
+        Nothing -> prefixedLined "| " (map pretty fs)
+        Just formulas -> put formulas
+  prettyInternal (ParenFormula _ f) = parens $ pretty f
+
 --------------------------------------------------------------------------------
 -- * Fallback printers
 
@@ -1156,7 +1298,9 @@
   prettyInternal x = pretty' x
 
 instance Pretty Name where
-  prettyInternal = pretty' -- Var
+  prettyInternal x = case x of
+                          Ident _ _ -> pretty' x -- Identifiers.
+                          Symbol _ s -> string s -- Symbols
 
 instance Pretty QName where
   prettyInternal =
@@ -1199,7 +1343,8 @@
        maybe (return ())
              (\exports ->
                 do newline
-                   indented 2 (pretty exports))
+                   indentSpaces <- getIndentSpaces
+                   indented indentSpaces (pretty exports))
              mexports
        write " where"
 
@@ -1360,11 +1505,17 @@
 
 match :: Match NodeInfo -> Printer ()
 match (Match _ name pats rhs' mbinds) =
-  do depend (do pretty name
+  do depend (do case name of
+                  Ident _ _ ->
+                    pretty name
+                  Symbol _ _ ->
+                    do write "("
+                       pretty name
+                       write ")"
                 space)
-            (spaced (map pretty pats))
+       (spaced (map pretty pats))
      withCaseContext False (pretty rhs')
-     Data.Foldable.forM_ mbinds bindingGroup
+     for_ mbinds bindingGroup
 match (InfixMatch _ pat1 name pats rhs' mbinds) =
   do depend (do pretty pat1
                 space
@@ -1375,14 +1526,17 @@
             (do space
                 spaced (map pretty pats))
      withCaseContext False (pretty rhs')
-     Data.Foldable.forM_ mbinds bindingGroup
+     for_ mbinds bindingGroup
 
 -- | Format contexts with spaces and commas between class constraints.
 context :: Context NodeInfo -> Printer ()
 context ctx =
   case ctx of
     CxSingle _ a -> pretty a
-    CxTuple _ as -> parens (prefixedLined "," (map pretty as))
+    CxTuple _ as -> do
+      depend (write "( ") $ prefixedLined ", " (map pretty as)
+      newline
+      write ")"
     CxEmpty _ -> parens (return ())
 
 unboxParens :: Printer a -> Printer a
@@ -1403,7 +1557,8 @@
                         do write "forall "
                            spaced (map pretty ts)
                            write ". ")
-                   (withCtx ctx (pretty ty))
+                   (do indentSpaces <- getIndentSpaces
+                       withCtx ctx (indented indentSpaces (pretty ty)))
           TyFun _ a b ->
             depend (do pretty a
                        write " -> ")
@@ -1421,9 +1576,22 @@
             brackets (do write ":"
                          pretty t
                          write ":")
-          TyApp _ f a -> spaced [pretty f,pretty a]
+          TyApp _ f a -> spaced [pretty f, pretty a]
           TyVar _ n -> pretty n
-          TyCon _ p -> pretty p
+          TyCon _ p ->
+            case p of
+              Qual _ _ name ->
+                case name of
+                  Ident _ _ -> pretty p
+                  Symbol _ _ -> parens (pretty p)
+              UnQual _ name ->
+                case name of
+                  Ident _ _ -> pretty p
+                  Symbol _ _ -> parens (pretty p)
+              Special _ con ->
+                case con of
+                  FunCon _ -> parens (pretty p)
+                  _ -> pretty p
           TyParen _ e -> parens (pretty e)
           TyInfix _ a op b ->
             depend (do pretty a
@@ -1443,16 +1611,37 @@
             do pretty left
                write " ~ "
                pretty right
+          TyPromoted _ (PromotedList _ _ ts) ->
+            do write "'["
+               unless (null ts) $ write " "
+               commas (map pretty ts)
+               write "]"
+          TyPromoted _ (PromotedTuple _ ts) ->
+            do write "'("
+               unless (null ts) $ write " "
+               commas (map pretty ts)
+               write ")"
+          TyPromoted _ (PromotedCon _ _ tname) ->
+            do write "'"
+               pretty tname
           ty@TyPromoted{} -> pretty' ty
-          TySplice{} -> error "FIXME: No implementation for TySplice."
+          TySplice _ splice -> pretty splice
           TyWildCard _ name ->
             case name of
               Nothing -> write "_"
               Just n ->
                 do write "_"
                    pretty n
-          _ -> error ("FIXME: No implementation for " ++ show x)
+          TyQuasiQuote _ n s ->
+            brackets (depend (do string n
+                                 write "|")
+                             (do string s
+                                 write "|"))
 
+prettyTopName :: Name NodeInfo -> Printer ()
+prettyTopName x@Ident{} = pretty x
+prettyTopName x@Symbol{} = parens $ pretty x
+
 -- | Specially format records. Indent where clauses only 2 spaces.
 decl' :: Decl NodeInfo -> Printer ()
 -- | Pretty print type signatures like
@@ -1463,20 +1652,19 @@
 --     -> (Char -> X -> Y)
 --     -> IO ()
 --
-decl' (TypeSig _ names ty') =
-  do mst <- fitsOnOneLine (declTy ty')
-     case mst of
-       Just{} -> depend (do inter (write ", ")
-                                  (map pretty names)
-                            write " :: ")
-                          (declTy ty')
-       Nothing -> do inter (write ", ")
-                           (map pretty names)
-                     newline
-                     indentSpaces <- getIndentSpaces
-                     indented indentSpaces
-                              (depend (write ":: ")
-                                      (declTy ty'))
+decl' (TypeSig _ names ty') = do
+  mst <- fitsOnOneLine (declTy ty')
+  case mst of
+    Just {} ->
+      depend
+        (do commas (map prettyTopName names)
+            write " :: ")
+        (declTy ty')
+    Nothing -> do
+      commas (map prettyTopName names)
+      newline
+      indentSpaces <- getIndentSpaces
+      indented indentSpaces (depend (write ":: ") (declTy ty'))
 
   where declTy dty =
           case dty of
@@ -1512,7 +1700,7 @@
   withCaseContext False $
     do pretty pat
        pretty rhs'
-       Data.Foldable.forM_ mbinds bindingGroup
+       for_ mbinds bindingGroup
 
 -- | Handle records specially for a prettier display (see guide).
 decl' (DataDecl _ dataornew ctx dhead condecls@[_] mderivs)
@@ -1548,7 +1736,7 @@
   depend (do pretty name
              write " ")
          (do depend (write "{")
-                    (prefixedLined ","
+                    (prefixedLined ", "
                                    (map (depend space . pretty) fields))
              write "}")
 conDecl x = case x of
@@ -1562,7 +1750,7 @@
                 depend (do pretty name
                            space)
                        (do depend (write "{")
-                                  (prefixedLined ","
+                                  (prefixedLined ", "
                                                  (map pretty fields))
                            write "}")
 
@@ -1584,17 +1772,20 @@
 
 recUpdateExpr :: Printer () -> [FieldUpdate NodeInfo] -> Printer ()
 recUpdateExpr expWriter updates = do
+  ifFitsOnOneLineOrElse hor $ do
     expWriter
     newline
-    mapM_
-      (\(i,x) -> do
-         if i == 0
-           then write "{ "
-           else write ", "
-         pretty x
-         newline)
-      (zip [0::Int ..] updates)
-    write "}"
+    updatesHor `ifFitsOnOneLineOrElse` updatesVer
+  where
+    hor = do
+      expWriter
+      space
+      updatesHor
+    updatesHor = braces $ commas $ map pretty updates
+    updatesVer = do
+      depend (write "{ ") $ prefixedLined ", " $ map pretty updates
+      newline
+      write "}"
 
 --------------------------------------------------------------------------------
 -- Predicates
@@ -1616,6 +1807,20 @@
                 then Just st' { psHardLimit = psHardLimit st }
                 else Nothing)
 
+-- | If first printer fits, use it, else use the second one.
+ifFitsOnOneLineOrElse :: Printer a -> Printer a -> Printer a
+ifFitsOnOneLineOrElse a b = do
+  stOrig <- get
+  put stOrig{psHardLimit = True}
+  res <- fmap Just a <|> return Nothing
+  case res of
+    Just r -> do
+      modify $ \st -> st{psHardLimit = psHardLimit stOrig}
+      return r
+    Nothing -> do
+      put stOrig
+      b
+
 bindingGroup :: Binds NodeInfo -> Printer ()
 bindingGroup binds =
   do newline
@@ -1631,30 +1836,38 @@
          -> Maybe Int64
          -> Printer ()
 infixApp e a op b indent =
-  do msg <-
-          fitsOnOneLine
-            (spaced (map (\link ->
-                            case link of
-                              OpChainExp e' -> pretty e'
-                              OpChainLink qop -> pretty qop)
-                         (flattenOpChain e)))
-     case msg of
-       Nothing -> do prettyWithIndent a
-                     space
-                     pretty op
-                     newline
-                     case indent of
-                       Nothing -> prettyWithIndent b
-                       Just col ->
-                         do indentSpaces <- getIndentSpaces
-                            column (col + indentSpaces)
-                                   (prettyWithIndent b)
-       Just st -> put st
-  where prettyWithIndent e' =
-          case e' of
-            (InfixApp _ a' op' b') ->
-              infixApp e' a' op' b' indent
-            _ -> pretty e'
+  hor `ifFitsOnOneLineOrElse` ver
+  where
+    hor =
+      spaced
+        [ case link of
+          OpChainExp e' -> pretty e'
+          OpChainLink qop -> pretty qop
+        | link <- flattenOpChain e
+        ]
+    ver = do
+      prettyWithIndent a
+      beforeRhs <- case a of
+                     Do _ _ -> do
+                       indentSpaces <- getIndentSpaces
+                       column (fromMaybe 0 indent + indentSpaces + 3) (newline >> pretty op) -- 3 = "do "
+                       return space
+                     _ -> space >> pretty op >> return newline
+      case b of
+        Lambda{} -> space >> pretty b
+        LCase{} -> space >> pretty b
+        Do _ stmts -> swing (write " do") $ lined (map pretty stmts)
+        _ -> do
+          beforeRhs
+          case indent of
+            Nothing -> prettyWithIndent b
+            Just col -> do
+              indentSpaces <- getIndentSpaces
+              column (col + indentSpaces) (prettyWithIndent b)
+    prettyWithIndent e' =
+      case e' of
+        InfixApp _ a' op' b' -> infixApp e' a' op' b' indent
+        _ -> pretty e'
 
 -- | A link in a chain of operator applications.
 data OpChainLink l
diff --git a/src/HIndent/Types.hs b/src/HIndent/Types.hs
--- a/src/HIndent/Types.hs
+++ b/src/HIndent/Types.hs
@@ -1,7 +1,6 @@
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE GADTs #-}
-{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE FlexibleContexts #-}
@@ -63,6 +62,7 @@
     { configMaxColumns :: !Int64 -- ^ Maximum columns to fit code into ideally.
     , configIndentSpaces :: !Int64 -- ^ How many spaces to indent?
     , configTrailingNewline :: !Bool -- ^ End with a newline.
+    , configSortImports :: !Bool -- ^ Sort imports in groups.
     }
 
 instance FromJSON Config where
@@ -74,7 +74,8 @@
       (v Y..:? "indent-size" <|> v Y..:? "tab-size") <*>
     fmap
       (fromMaybe (configTrailingNewline defaultConfig))
-      (v Y..:? "force-trailing-newline")
+      (v Y..:? "force-trailing-newline") <*>
+    fmap (fromMaybe (configSortImports defaultConfig)) (v Y..:? "sort-imports")
   parseJSON _ = fail "Expected Object for Config value"
 
 -- | Default style configuration.
@@ -84,6 +85,7 @@
     { configMaxColumns = 80
     , configIndentSpaces = 2
     , configTrailingNewline = True
+    , configSortImports = True
     }
 
 -- | Some comment to print.
@@ -93,16 +95,17 @@
   deriving (Show, Ord, Eq)
 
 -- | Comment associated with a node.
+-- 'SrcSpan' is the original source span of the comment.
 data NodeComment
-  = CommentSameLine SomeComment
-  | CommentAfterLine SomeComment
-  | CommentBeforeLine SomeComment
+  = CommentSameLine SrcSpan SomeComment
+  | CommentAfterLine SrcSpan SomeComment
+  | CommentBeforeLine SrcSpan SomeComment
   deriving (Show, Ord, Eq)
 
 -- | Information for each node in the AST.
 data NodeInfo = NodeInfo
   { nodeInfoSpan :: !SrcSpanInfo -- ^ Location info from the parser.
-  , nodeInfoComments :: ![NodeComment] -- ^ Comment attached to this node.
+  , nodeInfoComments :: ![NodeComment] -- ^ Comments attached to this node.
   }
 instance Show NodeInfo where
   show (NodeInfo _ []) = ""
diff --git a/src/main/Main.hs b/src/main/Main.hs
--- a/src/main/Main.hs
+++ b/src/main/Main.hs
@@ -1,7 +1,5 @@
 {-# LANGUAGE Unsafe #-}
-{-# LANGUAGE PatternGuards #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE QuasiQuotes #-}
 
 -- | Main entry point to hindent.
 --
@@ -10,6 +8,7 @@
 
 import           Control.Applicative
 import           Control.Exception
+import           Control.Monad
 import qualified Data.ByteString as S
 import qualified Data.ByteString.Builder as S
 import qualified Data.ByteString.Lazy.Char8 as L8
@@ -44,11 +43,11 @@
       case mfilepath of
         Just filepath -> do
           text <- S.readFile filepath
-          tmpDir <- IO.getTemporaryDirectory
-          (fp, h) <- IO.openTempFile tmpDir "hindent.hs"
           case reformat style (Just exts) text of
             Left e -> error (filepath ++ ": " ++ e)
-            Right out -> do
+            Right out -> unless (L8.fromStrict text == S.toLazyByteString out) $ do
+              tmpDir <- IO.getTemporaryDirectory
+              (fp, h) <- IO.openTempFile tmpDir "hindent.hs"
               L8.hPutStr h (S.toLazyByteString out)
               IO.hFlush h
               IO.hClose h
@@ -56,6 +55,7 @@
                     if ioe_errno e == Just ((\(Errno a) -> a) eXDEV)
                       then IO.copyFile fp filepath >> IO.removeFile fp
                       else throw e
+              IO.copyPermissions filepath fp
               IO.renameFile fp filepath `catch` exdev
         Nothing ->
           L8.interact
@@ -116,7 +116,8 @@
            (constant "--style" "Style to print with" () *> anyString "STYLE")) <*>
       lineLen <*>
       indentSpaces <*>
-      trailingNewline
+      trailingNewline <*>
+      sortImports
     exts = fmap getExtensions (many (prefix "X" "Language extension"))
     indentSpaces =
       fmap
@@ -131,10 +132,15 @@
     trailingNewline =
       optional
         (constant "--no-force-newline" "Don't force a trailing newline" False)
-    makeStyle s mlen tabs trailing =
+    sortImports =
+      optional
+        (constant "--sort-imports" "Sort imports in groups" True <|>
+         constant "--no-sort-imports" "Don't sort imports" False)
+    makeStyle s mlen tabs trailing imports =
       s
       { configMaxColumns = fromMaybe (configMaxColumns s) mlen
       , configIndentSpaces = fromMaybe (configIndentSpaces s) tabs
       , configTrailingNewline = fromMaybe (configTrailingNewline s) trailing
+      , configSortImports = fromMaybe (configSortImports s) imports
       }
     file = fmap (fmap T.unpack) (optional (anyString "[<filename>]"))
diff --git a/src/main/Test.hs b/src/main/Test.hs
--- a/src/main/Test.hs
+++ b/src/main/Test.hs
@@ -1,12 +1,12 @@
 {-# LANGUAGE OverloadedStrings #-}
 
 -- | Test the pretty printer.
-
 module Main where
 
 import           Data.Algorithm.Diff
 import           Data.Algorithm.DiffOutput
 import qualified Data.ByteString as S
+import qualified Data.ByteString.Char8 as S8
 import           Data.ByteString.Lazy (ByteString)
 import qualified Data.ByteString.Lazy as L
 import qualified Data.ByteString.Lazy.Builder as L
@@ -15,7 +15,7 @@
 import qualified Data.ByteString.UTF8 as UTF8
 import           Data.Function
 import           Data.Monoid
-import           HIndent
+import qualified HIndent
 import           HIndent.Types
 import           Markdone
 import           Test.Hspec
@@ -27,29 +27,43 @@
     forest <- parse (tokenize bytes)
     hspec (toSpec forest)
 
--- -- | Convert the Markdone document to Spec benchmarks.
+reformat :: Config -> S.ByteString -> ByteString
+reformat cfg code =
+  either (("-- " <>) . L8.pack) L.toLazyByteString $
+  HIndent.reformat cfg (Just HIndent.defaultExtensions) code
+
+-- | Convert the Markdone document to Spec benchmarks.
 toSpec :: [Markdone] -> Spec
 toSpec = go
   where
+    cfg = HIndent.Types.defaultConfig {configTrailingNewline = False}
     go (Section name children:next) = do
-        describe (UTF8.toString name) (go children)
-        go next
+      describe (UTF8.toString name) (go children)
+      go next
     go (PlainText desc:CodeFence lang code:next) =
-      if lang == "haskell"
-        then do
-          it
-            (UTF8.toString desc)
-            (shouldBeReadable
-               (either
-                  (("-- " <>) . L8.pack)
-                  L.toLazyByteString
-                  (reformat
-                     HIndent.Types.defaultConfig {configTrailingNewline = False}
-                     (Just defaultExtensions)
-                     code))
-               (L.fromStrict code))
+      case lang of
+        "haskell" -> do
+          it (UTF8.toString desc) $
+            shouldBeReadable (reformat cfg code) (L.fromStrict code)
           go next
-        else go next
+        "haskell 4" -> do
+          let cfg' = cfg {configIndentSpaces = 4}
+          it (UTF8.toString desc) $
+            shouldBeReadable (reformat cfg' code) (L.fromStrict code)
+          go next
+        "haskell given" ->
+          case skipEmptyLines next of
+            CodeFence "haskell expect" codeExpect:next' -> do
+              it (UTF8.toString desc) $
+                shouldBeReadable (reformat cfg code) (L.fromStrict codeExpect)
+              go next'
+            _ ->
+              fail
+                "'haskell given' block must be followed by a 'haskell expect' block"
+        "haskell pending" -> do
+          it (UTF8.toString desc) pending
+          go next
+        _ -> go next
     go (PlainText {}:next) = go next
     go (CodeFence {}:next) = go next
     go [] = return ()
@@ -57,15 +71,18 @@
 -- | Version of 'shouldBe' that prints strings in a readable way,
 -- better for our use-case.
 shouldBeReadable :: ByteString -> ByteString -> Expectation
-shouldBeReadable x y = shouldBe (Readable x (Just (diff y x))) (Readable y Nothing)
+shouldBeReadable x y =
+  shouldBe (Readable x (Just (diff y x))) (Readable y Nothing)
 
 -- | Prints a string without quoting and escaping.
 data Readable = Readable
   { readableString :: ByteString
-  , readableDiff :: (Maybe String)
+  , readableDiff :: Maybe String
   }
+
 instance Eq Readable where
   (==) = on (==) readableString
+
 instance Show Readable where
   show (Readable x d') =
     "\n" ++
@@ -76,4 +93,8 @@
 
 -- | A diff display.
 diff :: ByteString -> ByteString -> String
-diff x y = ppDiff (on (getGroupedDiff) (lines . LUTF8.toString) x y)
+diff x y = ppDiff (on getGroupedDiff (lines . LUTF8.toString) x y)
+
+skipEmptyLines :: [Markdone] -> [Markdone]
+skipEmptyLines (PlainText "":rest) = rest
+skipEmptyLines other = other
