    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE RecordWildCards #-}
    {-# LANGUAGE ViewPatterns #-}

    -- | Common definitions for pre- and post- processing.
    module Ormolu.Processing.Common
      ( removeIndentation,
        reindent,
        linesInRegion,
        intSetToRegions,
      )
    where

    import Data.Char (isSpace)
    import Data.IntSet (IntSet)
    import Data.IntSet qualified as IntSet
    import Data.Text (Text)
    import Data.Text qualified as T
    import Ormolu.Config

    -- | Remove indentation from a given 'Text'. Return the input with indentation
    -- removed and the detected indentation level.
    removeIndentation :: Text -> (Text, Int)
    removeIndentation :: Text -> (Text, Int)
    removeIndentation (Text -> [Text]
    T.lines -> [Text]
    xs) = ([Text] -> Text
    T.unlines (Int -> Text -> Text
    T.drop Int
    n (Text -> Text) -> [Text] -> [Text]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [Text]
    xs), Int
    n)
      where
        n :: Int
    n = [Int] -> Int
    forall a. Ord a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
    minimum (Text -> Int
    getIndent (Text -> Int) -> [Text] -> [Int]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [Text]
    xs)
        getIndent :: Text -> Int
    getIndent Text
    y =
          if (Char -> Bool) -> Text -> Bool
    T.all Char -> Bool
    isSpace Text
    y
            then Int
    0
            else Text -> Int
    T.length ((Char -> Bool) -> Text -> Text
    T.takeWhile Char -> Bool
    isSpace Text
    y)

    -- | Add indentation to a 'Text'.
    reindent :: Int -> Text -> Text
    reindent :: Int -> Text -> Text
    reindent Int
    i = [Text] -> Text
    T.unlines ([Text] -> Text) -> (Text -> [Text]) -> Text -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text -> Text) -> [Text] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (Int -> Text -> Text
    T.replicate Int
    i Text
    " " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>) ([Text] -> [Text]) -> (Text -> [Text]) -> Text -> [Text]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> [Text]
    T.lines

    -- | All lines in the region specified by 'RegionDeltas'.
    linesInRegion :: RegionDeltas -> Text -> Text
    linesInRegion :: RegionDeltas -> Text -> Text
    linesInRegion RegionDeltas {Int
    regionPrefixLength :: Int
    regionSuffixLength :: Int
    regionSuffixLength :: RegionDeltas -> Int
    regionPrefixLength :: RegionDeltas -> Int
    ..} (Text -> [Text]
    T.lines -> [Text]
    ls) = [Text] -> Text
    T.unlines [Text]
    middle
      where
        ([Text]
    _, [Text]
    nonPrefix) = Int -> [Text] -> ([Text], [Text])
    forall a. Int -> [a] -> ([a], [a])
    splitAt Int
    regionPrefixLength [Text]
    ls
        middle :: [Text]
    middle = Int -> [Text] -> [Text]
    forall a. Int -> [a] -> [a]
    take ([Text] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [Text]
    nonPrefix Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    regionSuffixLength) [Text]
    nonPrefix

    -- | Convert a set of line indices into disjoint 'RegionDelta's
    intSetToRegions ::
      -- | Total number of lines
      Int ->
      IntSet ->
      [RegionDeltas]
    intSetToRegions :: Int -> IntSet -> [RegionDeltas]
    intSetToRegions Int
    total (IntSet -> [Int]
    IntSet.toAscList -> [Int]
    indices) =
      Int -> RegionIndices -> RegionDeltas
    regionIndicesToDeltas Int
    total (RegionIndices -> RegionDeltas)
    -> [RegionIndices] -> [RegionDeltas]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe (Int, Int) -> [Int] -> [RegionIndices]
    go Maybe (Int, Int)
    forall a. Maybe a
    Nothing [Int]
    indices
      where
        go :: Maybe (Int, Int) -> [Int] -> [RegionIndices]
    go Maybe (Int, Int)
    Nothing [] = []
        go (Just (Int
    a, Int
    b)) [] = [Maybe Int -> Maybe Int -> RegionIndices
    RegionIndices (Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    a) (Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    b)]
        go Maybe (Int, Int)
    Nothing (Int
    i : [Int]
    is) = Maybe (Int, Int) -> [Int] -> [RegionIndices]
    go ((Int, Int) -> Maybe (Int, Int)
    forall a. a -> Maybe a
    Just (Int
    i, Int
    i)) [Int]
    is
        go (Just (Int
    a, Int
    b)) (Int
    i : [Int]
    is)
          | Int
    b Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    1 Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    i = Maybe (Int, Int) -> [Int] -> [RegionIndices]
    go ((Int, Int) -> Maybe (Int, Int)
    forall a. a -> Maybe a
    Just (Int
    a, Int
    i)) [Int]
    is
          | Bool
    otherwise = Maybe Int -> Maybe Int -> RegionIndices
    RegionIndices (Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    a) (Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    b) RegionIndices -> [RegionIndices] -> [RegionIndices]
    forall a. a -> [a] -> [a]
    : Maybe (Int, Int) -> [Int] -> [RegionIndices]
    go ((Int, Int) -> Maybe (Int, Int)
    forall a. a -> Maybe a
    Just (Int
    i, Int
    i)) [Int]
    is