{-# 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 qualified Data.IntSet as IntSet
import Data.Text (Text)
import qualified Data.Text 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 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Text]
xs), Int
n)
  where
    n :: Int
n = forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum (Text -> Int
getIndent 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 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> Text -> Text
T.replicate Int
i Text
" " forall a. Semigroup a => a -> a -> a
<>) 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
regionSuffixLength :: RegionDeltas -> Int
regionPrefixLength :: RegionDeltas -> Int
regionSuffixLength :: Int
regionPrefixLength :: Int
..} (Text -> [Text]
T.lines -> [Text]
ls) = [Text] -> Text
T.unlines [Text]
middle
  where
    ([Text]
_, [Text]
nonPrefix) = forall a. Int -> [a] -> ([a], [a])
splitAt Int
regionPrefixLength [Text]
ls
    middle :: [Text]
middle = forall a. Int -> [a] -> [a]
take (forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
nonPrefix 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 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe (Int, Int) -> [Int] -> [RegionIndices]
go 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 (forall a. a -> Maybe a
Just Int
a) (forall a. a -> Maybe a
Just Int
b)]
    go Maybe (Int, Int)
Nothing (Int
i : [Int]
is) = Maybe (Int, Int) -> [Int] -> [RegionIndices]
go (forall a. a -> Maybe a
Just (Int
i, Int
i)) [Int]
is
    go (Just (Int
a, Int
b)) (Int
i : [Int]
is)
      | Int
b forall a. Num a => a -> a -> a
+ Int
1 forall a. Eq a => a -> a -> Bool
== Int
i = Maybe (Int, Int) -> [Int] -> [RegionIndices]
go (forall a. a -> Maybe a
Just (Int
a, Int
i)) [Int]
is
      | Bool
otherwise = Maybe Int -> Maybe Int -> RegionIndices
RegionIndices (forall a. a -> Maybe a
Just Int
a) (forall a. a -> Maybe a
Just Int
b) forall a. a -> [a] -> [a]
: Maybe (Int, Int) -> [Int] -> [RegionIndices]
go (forall a. a -> Maybe a
Just (Int
i, Int
i)) [Int]
is