    module Data.String.Interpolate.Util (unindent) where

    import           Control.Arrow ((>>>))
    import           Data.Char

    -- | Remove indentation as much as possible while preserving relative
    -- indentation levels.
    --
    -- `unindent` is useful in combination with `Data.String.Interpolate.i` to remove leading spaces that
    -- resulted from code indentation.  That way you can freely indent your string
    -- literals without the indentation ending up in the resulting strings.
    --
    -- Here is an example:
    --
    -- >>> :set -XQuasiQuotes
    -- >>> import Data.String.Interpolate
    -- >>> import Data.String.Interpolate.Util
    -- >>> :{
    --  putStr $ unindent [i|
    --      def foo
    --        23
    --      end
    --    |]
    -- :}
    -- def foo
    --   23
    -- end
    --
    -- To allow this, two additional things are being done, apart from removing
    -- indentation:
    --
    -- - One empty line at the beginning will be removed and
    -- - if the last newline character (@"\\n"@) is followed by spaces, the spaces are removed.
    unindent :: String -> String
    unindent :: String -> String
    unindent =
          String -> [String]
    lines_
      (String -> [String]) -> ([String] -> String) -> String -> String
    forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
    Category cat =>
    cat a b -> cat b c -> cat a c
    >>> [String] -> [String]
    removeLeadingEmptyLine
      ([String] -> [String])
    -> ([String] -> String) -> [String] -> String
    forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
    Category cat =>
    cat a b -> cat b c -> cat a c
    >>> [String] -> [String]
    trimLastLine
      ([String] -> [String])
    -> ([String] -> String) -> [String] -> String
    forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
    Category cat =>
    cat a b -> cat b c -> cat a c
    >>> [String] -> [String]
    removeIndentation
      ([String] -> [String])
    -> ([String] -> String) -> [String] -> String
    forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
    Category cat =>
    cat a b -> cat b c -> cat a c
    >>> [String] -> String
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat
      where
        isEmptyLine :: String -> Bool
        isEmptyLine :: String -> Bool
    isEmptyLine = (Char -> Bool) -> String -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    all Char -> Bool
    isSpace

        lines_ :: String -> [String]
        lines_ :: String -> [String]
    lines_ [] = []
        lines_ String
    s = case (Char -> Bool) -> String -> (String, String)
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    span (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Char
    '\n') String
    s of
          (String
    first, Char
    '\n' : String
    rest) -> (String
    first String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "\n") String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    : String -> [String]
    lines_ String
    rest
          (String
    first, String
    rest) -> String
    first String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    : String -> [String]
    lines_ String
    rest

        removeLeadingEmptyLine :: [String] -> [String]
        removeLeadingEmptyLine :: [String] -> [String]
    removeLeadingEmptyLine [String]
    xs = case [String]
    xs of
          String
    y:[String]
    ys | String -> Bool
    isEmptyLine String
    y -> [String]
    ys
          [String]
    _ -> [String]
    xs

        trimLastLine :: [String] -> [String]
        trimLastLine :: [String] -> [String]
    trimLastLine (String
    a : String
    b : [String]
    r) = String
    a String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    : [String] -> [String]
    trimLastLine (String
    b String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    : [String]
    r)
        trimLastLine [String
    a] = if (Char -> Bool) -> String -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    all (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    ' ') String
    a
          then []
          else [String
    a]
        trimLastLine [] = []

        removeIndentation :: [String] -> [String]
        removeIndentation :: [String] -> [String]
    removeIndentation [String]
    ys = (String -> String) -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map (Int -> String -> String
    forall {t}. (Eq t, Num t) => t -> String -> String
    dropSpaces Int
    indentation) [String]
    ys
          where
            dropSpaces :: t -> String -> String
    dropSpaces t
    0 String
    s = String
    s
            dropSpaces t
    n (Char
    ' ' : String
    r) = t -> String -> String
    dropSpaces (t
    n t -> t -> t
    forall a. Num a => a -> a -> a
    - t
    1) String
    r
            dropSpaces t
    _ String
    s = String
    s
            indentation :: Int
    indentation = [String] -> Int
    minimalIndentation [String]
    ys
            minimalIndentation :: [String] -> Int
    minimalIndentation =
                Int -> [Int] -> Int
    forall a. Ord a => a -> [a] -> a
    safeMinimum Int
    0
              ([Int] -> Int) -> ([String] -> [Int]) -> [String] -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String -> Int) -> [String] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map (String -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length (String -> Int) -> (String -> String) -> String -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    takeWhile (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    ' '))
              ([String] -> [Int]) -> ([String] -> [String]) -> [String] -> [Int]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [String] -> [String]
    removeEmptyLines
            removeEmptyLines :: [String] -> [String]
    removeEmptyLines = (String -> Bool) -> [String] -> [String]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> Bool
    isEmptyLine)

            safeMinimum :: Ord a => a -> [a] -> a
            safeMinimum :: forall a. Ord a => a -> [a] -> a
    safeMinimum a
    x [a]
    xs = case [a]
    xs of
              [] -> a
    x
              [a]
    _ -> [a] -> a
    forall a. Ord a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
    minimum [a]
    xs