1
1
mirror of https://github.com/aelve/guide.git synced 2024-11-27 10:10:50 +03:00
guide/lib/Merge.hs

81 lines
2.3 KiB
Haskell
Raw Normal View History

2016-06-19 00:52:19 +03:00
{-# LANGUAGE
OverloadedStrings,
NoImplicitPrelude
#-}
module Merge
(
merge,
)
where
import BasePrelude
-- Lenses
import Lens.Micro.Platform hiding ((&))
-- Text
import qualified Data.Text.All as T
import Data.Text.All (Text)
import Data.List.Split
-- Vector
import qualified Data.Vector as V
-- Diffing
import qualified Data.Patch as PV
-- | An implementation of a 3-way diff and merge.
merge
:: Text -- ^ Original text
-> Text -- ^ Variant A (preferred)
-> Text -- ^ Variant B
-> Text -- ^ Merged text
merge orig a b = T.concat . V.toList $ PV.apply (pa <> pb') orig'
where
(orig', a', b') = (orig, a, b) & each %~
V.fromList . consolidate . map T.toStrict . break' . T.toString
pa = PV.diff orig' a'
pb = PV.diff orig' b'
(_, pb') = PV.transformWith PV.ours pa pb
-- | Break a string into words, spaces, and special characters.
break' :: String -> [String]
break' = split . dropInitBlank . dropFinalBlank . dropInnerBlanks . whenElt $
\c -> not (isAlphaNum c) && c /= '\''
-- | Consolidate some of the things into tokens (like links, consecutive
-- spaces, and Markdown elements).
consolidate :: [Text] -> [Text]
-- spaces
consolidate s@(" ":_) =
let (l, r) = span (== " ") s
in T.concat l : consolidate r
-- breaks between paragraphs
consolidate s@("\n":_) =
let (l, r) = span (== "\n") s
in T.concat l : consolidate r
-- code block markers
consolidate s@("~":_) =
let (l, r) = span (== "~") s
in if length l >= 3 then T.concat l : consolidate r else l ++ consolidate r
consolidate s@("`":_) =
let (l, r) = span (== "`") s
in if length l >= 3 then T.concat l : consolidate r else l ++ consolidate r
-- hrules
consolidate s@("-":_) =
let (l, r) = span (== "-") s
in if length l >= 3 then T.concat l : consolidate r else l ++ consolidate r
-- ellipses
consolidate (".":".":".":xs) = "..." : consolidate xs
-- links
consolidate s@("http":":":"/":"/":_) =
let (l, r) = span (\x -> x /= ")" && not (isSpace (T.head x))) s
in T.concat l : consolidate r
consolidate s@("https":":":"/":"/":_) =
let (l, r) = span (\x -> x /= ")" && not (isSpace (T.head x))) s
in T.concat l : consolidate r
consolidate ("(":"@":"hk":")":xs) = "(" : "@hk" : ")" : consolidate xs
-- the rest
consolidate (x:xs) = x : consolidate xs
consolidate [] = []