module Text.Reprinter
( reprint
, splitBySpan
, Position(..)
, Source
, Reprinting
, initPosition
, catchAll
, genReprinting
, Refactorable(..)
, RefactorType(..)
) where
import Data.Generics.Zipper
import Debug.Trace
import qualified Data.ByteString.Char8 as B
import Data.Data
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State.Lazy
type Source = B.ByteString
data Position = Position { posColumn :: Int, posLine :: Int }
deriving (Data, Show)
initPosition = Position { posColumn = 1, posLine = 1 }
type Reprinting m =
forall b . Typeable b
=> b -> m (Maybe (RefactorType, Source, (Position, Position)))
reprint :: (Monad m, Data p)
=> Reprinting m -> p -> Source -> m Source
reprint reprinting tree input
| B.null input = return B.empty
| otherwise = do
let state0 = (initPosition, input)
(out, (_, remaining)) <- runStateT (enter reprinting (toZipper tree)) state0
return $ out `B.append` remaining
enter, enterDown, enterRight
:: Monad m
=> Reprinting m -> Zipper a -> StateT (Position, Source) m Source
enter reprinting z = do
refactoringInfo <- lift $ query reprinting z
output <-
case refactoringInfo of
Nothing -> enterDown reprinting z
Just (typ, output, (lb, ub)) -> do
(cursor, inp) <- get
case typ of
Replace -> do
let (p0, inp') = splitBySpan (cursor, lb) inp
let (_, inp'') = splitBySpan (lb, ub) inp'
put (ub, inp'')
return $ B.concat [p0, output]
After -> do
let (p0, inp') = splitBySpan (cursor, ub) inp
put (ub, inp')
return $ B.concat [p0, output]
Before -> do
let (p0, inp') = splitBySpan (cursor, lb) inp
let (p1, inp'') = splitBySpan (lb, ub) inp'
put (ub, inp'')
return $ B.concat [p0, output, p1]
output' <- enterRight reprinting z
return $ B.concat [output, output']
enterDown reprinting z =
case down' z of
Just dz -> enter reprinting dz
Nothing -> return B.empty
enterRight reprinting z =
case right z of
Just rz -> enter reprinting rz
Nothing -> return B.empty
splitBySpan :: (Position, Position) -> Source -> (Source, Source)
splitBySpan (l, u) = subtext (ll, lc) (ll, lc) (ul, uc)
where (Position lc ll) = l
(Position uc ul) = u
subtext :: (Int, Int) -> (Int, Int) -> (Int, Int) -> B.ByteString -> (B.ByteString, B.ByteString)
subtext cursor (lowerLn, lowerCol) (upperLn, upperCol) =
subtext' B.empty cursor
where
subtext' acc (cursorLn, cursorCol) input
| cursorLn <= lowerLn && (cursorCol >= lowerCol ==> cursorLn < lowerLn) =
case B.uncons input of
Nothing -> (B.reverse acc, input)
Just ('\n', input') -> subtext' acc (cursorLn+1, 1) input'
Just (_, input') -> subtext' acc (cursorLn, cursorCol+1) input'
| cursorLn <= upperLn && (cursorCol >= upperCol ==> cursorLn < upperLn) =
case B.uncons input of
Nothing -> (B.reverse acc, input)
Just ('\n', input') -> subtext' (B.cons '\n' acc) (cursorLn+1, 1) input'
Just (x, input') -> subtext' (B.cons x acc) (cursorLn, cursorCol+1) input'
| otherwise =
(B.reverse acc, input)
(==>) :: Bool -> Bool -> Bool; infix 2 ==>
a ==> b = a <= b
data RefactorType = Before | After | Replace
class Refactorable t where
isRefactored :: t -> Maybe RefactorType
getSpan :: t -> (Position, Position)
genReprinting :: (Monad m, Refactorable t, Typeable t)
=> (t -> m Source)
-> t -> m (Maybe (RefactorType, Source, (Position, Position)))
genReprinting f z = do
case isRefactored z of
Nothing -> return Nothing
Just refactorType -> do
output <- f z
return $ Just (refactorType, output, getSpan z)
catchAll :: Monad m => a -> m (Maybe b)
catchAll _ = return Nothing