module Text.IndentToBrace
( i2b
) where
import Control.Monad.Trans.Writer (execWriter, tell, Writer)
import Data.List (isInfixOf)
import qualified Data.Text as T
i2b :: String -> String
i2b = ($ [])
. execWriter
. mapM_ unnest
. map addClosingCount
. nest
. map toL
. stripComments
. lines
. filter (/= '\r')
stripComments :: [String] -> [String]
stripComments =
map T.unpack . go False . map T.pack
where
go _ [] = []
go False (l:ls) =
let (before, after') = T.breakOn "/*" l
in case T.stripPrefix "/*" after' of
Nothing -> l : go False ls
Just after ->
let (x:xs) = go True $ after : ls
in before `T.append` x : xs
go True (l:ls) =
let (_, after') = T.breakOn "*/" l
in case T.stripPrefix "*/" after' of
Nothing -> T.empty : go True ls
Just after -> go False $ after : ls
data Line = Line
{ lineIndent :: Int
, lineContent :: String
}
deriving (Show, Eq)
data Nest = Nest Line Int [Nest]
| Blank String
deriving (Show, Eq)
isBlank :: Nest -> Bool
isBlank Blank{} = True
isBlank _ = False
addClosingCount :: Nest -> Nest
addClosingCount (Blank x) = Blank x
addClosingCount (Nest l c children) =
Nest l c $ increment $ map addClosingCount children
where
increment
| any (not . isBlank) children = increment'
| otherwise = id
increment' [] = error "should never happen"
increment' (Blank x:rest) = Blank x : increment' rest
increment' (n@(Nest l' c' children'):rest)
| any (not . isBlank) rest = n : increment' rest
| any (not . isBlank) children' = Nest l' c' (increment' children') : rest
| otherwise = Nest l' (c' + 1) children' : rest
toL :: String -> Either String Line
toL s
| null y = Left s
| otherwise = Right $ Line (length x) y
where
(x, y) = span (== ' ') s
nest :: [Either String Line] -> [Nest]
nest [] = []
nest (Left x:rest) = Blank x : nest rest
nest (Right l:rest) =
Nest l 0 (nest inside) : nest outside
where
(inside, outside) = span isNested rest
isNested Left{} = True
isNested (Right l2) = lineIndent l2 > lineIndent l
tell' :: String -> Writer (String -> String) ()
tell' s = tell (s ++)
unnest :: Nest -> Writer (String -> String) ()
unnest (Blank x) = do
tell' x
tell' "\n"
unnest (Nest l count inside) = do
tell' $ replicate (lineIndent l) ' '
tell' $ lineContent l
tell' $
case () of
()
| not $ all isBlank inside -> " {"
| ";" `isInfixOf` lineContent l -> ""
| otherwise -> ";"
tell' $ replicate count '}'
tell' "\n"
mapM_ unnest inside