{-# LANGUAGE OverloadedStrings #-}
module Text.Pandoc.Readers.Docx.Combine ( smushInlines
, smushBlocks
)
where
import Data.List
import Data.Bifunctor
import Data.Sequence ( ViewL (..), ViewR (..), viewl, viewr, spanr, spanl
, (><), (|>) )
import Text.Pandoc.Builder
data Modifier a = Modifier (a -> a)
| AttrModifier (Attr -> a -> a) Attr
spaceOutInlinesL :: Inlines -> (Inlines, Inlines)
spaceOutInlinesL ms = (l, stackInlines fs (m' <> r))
where (l, (fs, m'), r) = spaceOutInlines ms
spaceOutInlinesR :: Inlines -> (Inlines, Inlines)
spaceOutInlinesR ms = (stackInlines fs (l <> m'), r)
where (l, (fs, m'), r) = spaceOutInlines ms
spaceOutInlines :: Inlines -> (Inlines, ([Modifier Inlines], Inlines), Inlines)
spaceOutInlines ils =
let (fs, ils') = unstackInlines ils
(left, (right, contents')) = second (spanr isSpace) $ spanl isSpace $ unMany ils'
in (Many left, (fs, Many contents'), Many right)
isSpace :: Inline -> Bool
isSpace Space = True
isSpace SoftBreak = True
isSpace _ = False
stackInlines :: [Modifier Inlines] -> Inlines -> Inlines
stackInlines [] ms = ms
stackInlines (Modifier f : fs) ms =
if null ms
then stackInlines fs ms
else f $ stackInlines fs ms
stackInlines (AttrModifier f attr : fs) ms = f attr $ stackInlines fs ms
unstackInlines :: Inlines -> ([Modifier Inlines], Inlines)
unstackInlines ms = case ilModifierAndInnards ms of
Nothing -> ([], ms)
Just (f, inner) -> first (f :) $ unstackInlines inner
ilModifierAndInnards :: Inlines -> Maybe (Modifier Inlines, Inlines)
ilModifierAndInnards ils = case viewl $ unMany ils of
x :< xs | null xs -> second fromList <$> case x of
Emph lst -> Just (Modifier emph, lst)
Strong lst -> Just (Modifier strong, lst)
SmallCaps lst -> Just (Modifier smallcaps, lst)
Strikeout lst -> Just (Modifier strikeout, lst)
Underline lst -> Just (Modifier underline, lst)
Superscript lst -> Just (Modifier superscript, lst)
Subscript lst -> Just (Modifier subscript, lst)
Link attr lst tgt -> Just (Modifier $ linkWith attr (fst tgt) (snd tgt), lst)
Span attr lst -> Just (AttrModifier spanWith attr, lst)
_ -> Nothing
_ -> Nothing
inlinesL :: Inlines -> (Inlines, Inlines)
inlinesL ils = case viewl $ unMany ils of
(s :< sq) -> (singleton s, Many sq)
_ -> (mempty, ils)
inlinesR :: Inlines -> (Inlines, Inlines)
inlinesR ils = case viewr $ unMany ils of
(sq :> s) -> (Many sq, singleton s)
_ -> (ils, mempty)
combineInlines :: Inlines -> Inlines -> Inlines
combineInlines x y =
let (xs', x') = inlinesR x
(y', ys') = inlinesL y
in
xs' <> combineSingletonInlines x' y' <> ys'
combineSingletonInlines :: Inlines -> Inlines -> Inlines
combineSingletonInlines x y =
let (xfs, xs) = unstackInlines x
(yfs, ys) = unstackInlines y
shared = xfs `intersect` yfs
x_remaining = xfs \\ shared
y_remaining = yfs \\ shared
x_rem_attr = filter isAttrModifier x_remaining
y_rem_attr = filter isAttrModifier y_remaining
in
case null shared of
True | null xs && null ys ->
stackInlines (x_rem_attr <> y_rem_attr) mempty
| null xs ->
let (sp, y') = spaceOutInlinesL y in
stackInlines x_rem_attr mempty <> sp <> y'
| null ys ->
let (x', sp) = spaceOutInlinesR x in
x' <> sp <> stackInlines y_rem_attr mempty
| otherwise ->
let (x', xsp) = spaceOutInlinesR x
(ysp, y') = spaceOutInlinesL y
in
x' <> xsp <> ysp <> y'
False -> stackInlines shared $
combineInlines
(stackInlines x_remaining xs)
(stackInlines y_remaining ys)
combineBlocks :: Blocks -> Blocks -> Blocks
combineBlocks bs cs
| bs' :> BlockQuote bs'' <- viewr (unMany bs)
, BlockQuote cs'' :< cs' <- viewl (unMany cs) =
Many $ (bs' |> BlockQuote (bs'' <> cs'')) >< cs'
| bs' :> CodeBlock attr codeStr <- viewr (unMany bs)
, CodeBlock attr' codeStr' :< cs' <- viewl (unMany cs)
, attr == attr' =
Many $ (bs' |> CodeBlock attr (codeStr <> "\n" <> codeStr')) >< cs'
combineBlocks bs cs = bs <> cs
instance (Monoid a, Eq a) => Eq (Modifier a) where
(Modifier f) == (Modifier g) = f mempty == g mempty
(AttrModifier f attr) == (AttrModifier g attr') = f attr mempty == g attr' mempty
_ == _ = False
isAttrModifier :: Modifier a -> Bool
isAttrModifier (AttrModifier _ _) = True
isAttrModifier _ = False
smushInlines :: [Inlines] -> Inlines
smushInlines xs = combineInlines xs' mempty
where xs' = foldl combineInlines mempty xs
smushBlocks :: [Blocks] -> Blocks
smushBlocks xs = foldl combineBlocks mempty xs