1
1
mirror of https://github.com/github/semantic.git synced 2024-11-28 10:15:55 +03:00

Merge remote-tracking branch 'origin/master' into fall-back-to-line-by-line-diffs

This commit is contained in:
joshvera 2015-12-10 17:06:08 -05:00
commit 984c4615c9
3 changed files with 226 additions and 81 deletions

View File

@ -1,3 +1,6 @@
table {
width: 100%;
}
table.diff td {
width: 50%;
height: 15px;

View File

@ -5,22 +5,26 @@ import Diff
import Patch
import Term
import Syntax
import Control.Monad
import Control.Comonad.Cofree
import Range
import Control.Monad
import Control.Monad.Free
import Data.ByteString.Lazy.Internal
import Text.Blaze.Html5
import Text.Blaze.Html5 hiding (map)
import qualified Text.Blaze.Html5.Attributes as A
import Text.Blaze.Html.Renderer.Utf8
import qualified Data.Maybe as Maybe
import Data.Maybe
import Data.Monoid
import qualified Data.Set as Set
import Debug.Trace
import Data.List (intersperse)
type ClassName = String
data HTML =
Text String
Break
| Text String
| Span (Maybe ClassName) String
| Ul (Maybe ClassName) [HTML]
| Dl (Maybe ClassName) [HTML]
@ -41,6 +45,7 @@ toDd (Text s) = string s
toDd e = dd $ toMarkup e
instance ToMarkup HTML where
toMarkup Break = br
toMarkup (Text s) = string s
toMarkup (Span className s) = classifyMarkup className . span $ string s
toMarkup (Ul className children) = classifyMarkup className . ul $ mconcat (toLi <$> children)
@ -48,6 +53,9 @@ instance ToMarkup HTML where
toMarkup (Div className children) = classifyMarkup className . div $ mconcat (toMarkup <$> children)
toMarkup (Dt key) = dt $ string key
trace' :: Show a => a -> a
trace' a = traceShow a a
split :: Diff a Info -> String -> String -> IO ByteString
split diff before after = return . renderHtml
. docTypeHtml
@ -56,78 +64,105 @@ split diff before after = return . renderHtml
. (table ! A.class_ (stringValue "diff"))
. mconcat $ toMarkup <$> (fst $ diffToRows diff (0, 0) before after)
data Row = Row [HTML] [HTML]
deriving (Show, Eq)
data Row = Row Line Line
deriving Eq
instance Show Row where
show (Row left right) = "\n" ++ show left ++ " | " ++ show right
instance ToMarkup Row where
toMarkup (Row left right) = (tr $ (td . mconcat $ toMarkup <$> left) <> (td . mconcat $ toMarkup <$> right))
toMarkup (Row left right) = (tr $ toMarkup left <> toMarkup right) <> string "\n"
bimap :: ([HTML] -> [HTML]) -> ([HTML] -> [HTML]) -> Row -> Row
bimap f g (Row a b) = Row (f a) (g b)
instance ToMarkup Line where
toMarkup EmptyLine = td (string "")
toMarkup (Line html) = td . mconcat $ toMarkup <$> html
data Line =
Line [HTML]
| EmptyLine
deriving Eq
unLine :: Line -> [HTML]
unLine EmptyLine = []
unLine (Line htmls) = htmls
instance Show Line where
show (Line elements) = "[" ++ (concat . intersperse ", " $ show <$> elements) ++ "]"
show EmptyLine = "EmptyLine"
instance Monoid Line where
mempty = EmptyLine
mappend EmptyLine EmptyLine = EmptyLine
mappend EmptyLine (Line ys) = Line ys
mappend (Line xs) EmptyLine = Line xs
mappend (Line xs) (Line ys) = Line (xs <> ys)
instance Monoid Row where
mempty = Row [] []
mempty = Row EmptyLine EmptyLine
mappend (Row x1 y1) (Row x2 y2) = Row (x1 <> x2) (y1 <> y2)
diffToRows :: Diff a Info -> (Int, Int) -> String -> String -> ([Row], (Range, Range))
diffToRows (Free annotated) _ before after = annotatedToRows annotated before after
diffToRows (Pure (Insert term)) (previousIndex, _) _ after = (rowWithInsertedLine <$> afterLines, (range, Range previousIndex previousIndex))
diffToRows (Pure (Insert term)) (previousIndex, _) _ after = (rowWithInsertedLine <$> afterLines, (Range previousIndex previousIndex, range))
where
(afterLines, range) = termToLines term after
rowWithInsertedLine (Line elements) = Row [] [ Div (Just "insert") elements ]
rowWithInsertedLine (Line elements) = Row EmptyLine $ Line [ Div (Just "insert") elements ]
rowWithInsertedLine EmptyLine = mempty
diffToRows (Pure (Delete term)) (_, previousIndex) before _ = (rowWithDeletedLine <$> lines, (range, Range previousIndex previousIndex))
where
(lines, range) = termToLines term before
rowWithDeletedLine (Line elements) = Row [ Div (Just "delete") elements ] []
rowWithDeletedLine (Line elements) = Row (Line [ Div (Just "delete") elements ]) EmptyLine
rowWithDeletedLine EmptyLine = mempty
diffToRows (Pure (Replace a b)) _ before after = (replacedRows, (leftRange, rightRange))
where
replacedRows = zipWithMaybe rowFromMaybeRows (replace <$> leftElements) (replace <$> rightElements)
replace = (:[]) . Div (Just "replace") . unLine
rowFromMaybeRows :: Maybe [HTML] -> Maybe [HTML] -> Row
rowFromMaybeRows a b = Row (join $ Maybe.maybeToList a) (join $ Maybe.maybeToList b)
rowFromMaybeRows a b = Row (maybe EmptyLine Line a) (maybe EmptyLine Line b)
(leftElements, leftRange) = termToLines a before
(rightElements, rightRange) = termToLines b after
newtype Line = Line { unLine :: [HTML] } deriving (Show, Eq)
instance Monoid Line where
mempty = Line []
mappend (Line xs) (Line ys) = Line (xs <> ys)
-- | Takes a term and a `source` and returns a list of HTML lines
-- | and their range within `source`.
termToLines :: Term a Info -> String -> ([Line], Range)
termToLines (Info range _ categories :< syntax) source = (rows syntax, range)
where
rows (Leaf _) = Line . (:[]) <$> elements
rows (Leaf _) = reverse $ foldl adjoin2Lines [] $ Line . (:[]) <$> elements
rows (Indexed i) = rewrapLineContentsInUl <$> childLines i
rewrapLineContentsInUl (Line elements) = Line [ Ul (classify categories) elements ]
rewrapLineContentsInUl EmptyLine = EmptyLine
lineElements r s = Line . (:[]) <$> textElements r s
childLines i = appendRemainder $ foldl sumLines ([], start range) i
appendRemainder (lines, previous) = adjoinLines lines $ lineElements (Range previous (end range)) source
appendRemainder (lines, previous) = reverse . foldl adjoin2Lines [] $ lines ++ lineElements (Range previous (end range)) source
sumLines (lines, previous) child = (allLines, end childRange)
where
separatorLines = lineElements (Range previous $ start childRange) source
allLines = lines `adjoinLines` separatorLines `adjoinLines` childLines
unadjoinedLines = lines ++ separatorLines ++ childLines
allLines = reverse $ foldl adjoin2Lines [] unadjoinedLines
(childLines, childRange) = termToLines child source
elements = Span (classify categories) <$> actualLines (substring range source)
elements = (elementAndBreak $ Span (classify categories)) =<< actualLines (substring range source)
-- | Given an Annotated and before/after strings, returns a list of `Row`s representing the newline-separated diff.
annotatedToRows :: Annotated a (Info, Info) (Diff a Info) -> String -> String -> ([Row], (Range, Range))
annotatedToRows (Annotated (Info left _ leftCategories, Info right _ rightCategories) (Leaf _)) before after = (zipWithMaybe rowFromMaybeRows leftElements rightElements, (left, right))
where
leftElements = Span (classify leftCategories) <$> actualLines (substring left before)
rightElements = Span (classify rightCategories) <$> actualLines (substring right after)
leftElements = (elementAndBreak $ Span (classify leftCategories)) =<< actualLines (substring left before)
rightElements = (elementAndBreak $ Span (classify rightCategories)) =<< actualLines (substring right after)
annotatedToRows (Annotated (Info left _ leftCategories, Info right _ rightCategories) (Indexed i)) before after = (bimap ((:[]) . Ul (classify leftCategories)) ((:[]) . Ul (classify rightCategories)) <$> rows, ranges)
annotatedToRows (Annotated (Info left _ leftCategories, Info right _ rightCategories) (Indexed i)) before after = (rewrap <$> rows, ranges)
where
wrap _ EmptyLine = EmptyLine
wrap f (Line elements) = Line [ f elements ]
rewrap (Row left right) = Row (wrap (Ul $ classify leftCategories) left) (wrap (Ul $ classify rightCategories) right)
ranges = (left, right)
rows = appendRemainder $ foldl sumRows ([], starts ranges) i
sources = (before, after)
appendRemainder (rows, previousIndices) = adjoinRows rows $ contextRows (ends ranges) previousIndices sources
appendRemainder (rows, previousIndices) = reverse . foldl adjoin2 [] $ rows ++ (contextRows (ends ranges) previousIndices sources)
sumRows (rows, previousIndices) child = (allRows, ends childRanges)
where
separatorRows = contextRows (starts childRanges) previousIndices sources
allRows = rows `adjoinRows` separatorRows `adjoinRows` childRows
allRows = rows ++ separatorRows ++ childRows
(childRows, childRanges) = diffToRows child previousIndices before after
contextRows :: (Int, Int) -> (Int, Int) -> (String, String) -> [Row]
@ -136,8 +171,15 @@ contextRows childIndices previousIndices sources = zipWithMaybe rowFromMaybeRows
leftElements = textElements (Range (fst previousIndices) (fst childIndices)) (fst sources)
rightElements = textElements (Range (snd previousIndices) (snd childIndices)) (snd sources)
elementAndBreak :: (String -> HTML) -> String -> [HTML]
elementAndBreak _ "" = []
elementAndBreak _ "\n" = [ Break ]
elementAndBreak constructor x | '\n' <- last x = [ constructor $ init x, Break ]
elementAndBreak constructor x = [ constructor x ]
textElements :: Range -> String -> [HTML]
textElements range source = Text <$> actualLines (substring range source)
textElements range source = (elementAndBreak Text) =<< actualLines s
where s = substring range source
starts :: (Range , Range) -> (Int, Int)
starts (left, right) = (start left, start right)
@ -146,22 +188,65 @@ ends :: (Range, Range) -> (Int, Int)
ends (left, right) = (end left, end right)
rowFromMaybeRows :: Maybe HTML -> Maybe HTML -> Row
rowFromMaybeRows a b = Row (Maybe.maybeToList a) (Maybe.maybeToList b)
rowFromMaybeRows a b = Row (maybe EmptyLine (Line . (:[])) a) (maybe EmptyLine (Line . (:[])) b)
-- | Adjoin a list of rows onto an existing list of rows.
adjoinRows :: [Row] -> [Row] -> [Row]
adjoinRows [] rows = rows
adjoinRows rows [] = rows
adjoinRows accum (row : rows) = reverse (adjoin2 (reverse accum) row) ++ rows
maybeLast :: [a] -> Maybe a
maybeLast list = listToMaybe $ reverse list
adjoin2 :: [Row] -> Row -> [Row]
adjoin2 [] row = [row]
adjoin2 (Row [] [] : init) row = adjoin2 init row
adjoin2 (Row [] rights : Row lefts rights' : init) (Row xs ys) =
Row [] (rights <> ys) : Row (lefts <> xs) rights' : init
adjoin2 (Row lefts [] : Row lefts' rights : init) (Row xs ys) =
Row (lefts <> xs) [] : Row lefts' (rights <> ys) : init
adjoin2 (last:init) row = (last <> row) : init
adjoin2 rows (Row EmptyLine EmptyLine) = rows
adjoin2 (Row EmptyLine EmptyLine : rows) row = adjoin2 rows row
adjoin2 rows (Row left' right') | Just _ <- openLine $ leftLines rows, Just _ <- openLine $ rightLines rows = zipWith Row lefts rights
where lefts = adjoin2Lines (leftLines rows) left'
rights = adjoin2Lines (rightLines rows) right'
adjoin2 rows (Row left' right') | Just _ <- openLine $ leftLines rows = case right' of
EmptyLine -> rest
_ -> Row EmptyLine right' : rest
where rest = zipWith Row lefts rights
lefts = adjoin2Lines (leftLines rows) left'
rights = rightLines rows
adjoin2 rows (Row left' right') | Just _ <- openLine $ rightLines rows = case left' of
EmptyLine -> rest
_ -> Row left' EmptyLine : rest
where rest = zipWith Row lefts rights
lefts = leftLines rows
rights = adjoin2Lines (rightLines rows) right'
adjoin2 rows row = row : rows
leftLines :: [Row] -> [Line]
leftLines rows = left <$> rows
where
left (Row left _) = left
rightLines :: [Row] -> [Line]
rightLines rows = right <$> rows
where
right (Row _ right) = right
openElement :: HTML -> Maybe HTML
openElement Break = Nothing
openElement (Ul _ elements) = openElement =<< maybeLast elements
openElement (Dl _ elements) = openElement =<< maybeLast elements
openElement (Div _ elements) = openElement =<< maybeLast elements
openElement h = Just h
openLine :: [Line] -> Maybe Line
openLine [] = Nothing
openLine (EmptyLine : rest) = openLine rest
openLine (line : _) = const line <$> (openElement =<< (maybeLast $ unLine line))
adjoin2Lines :: [Line] -> Line -> [Line]
adjoin2Lines [] line = [line]
adjoin2Lines (EmptyLine : xs) line | Just _ <- openLine xs = EmptyLine : adjoin2Lines xs line
adjoin2Lines (prev:rest) line | Just _ <- openLine [ prev ] = (prev <> line) : rest
adjoin2Lines lines line = line : lines
adjoinLines :: [Line] -> [Line] -> [Line]
adjoinLines [] lines = lines
@ -181,6 +266,6 @@ classify = foldr (const . Just . ("category-" ++)) Nothing
actualLines :: String -> [String]
actualLines "" = [""]
actualLines lines = case break (== '\n') lines of
(l, lines') -> l : (case lines' of
[] -> []
_:lines' -> actualLines lines')
(l, lines') -> (case lines' of
[] -> [ l ]
_:lines' -> (l ++ "\n") : actualLines lines')

View File

@ -1,55 +1,35 @@
module Main where
import Diff
import Patch
import Range
import Split
import Syntax
import Control.Comonad.Cofree
import Control.Monad.Free
import qualified Data.Set as Set
import Test.Hspec
main :: IO ()
main = hspec $ do
describe "adjoinRows" $ do
it "empty lines are the left unit" $
adjoinRows [ Row [] [] ] [ Row [ Text "a" ] [ Text "b" ] ] `shouldBe` [ Row [ Text "a" ] [ Text "b" ] ]
it "empty lines are the left unit for multiple lines" $
adjoinRows [ Row [] [] ] [ Row [ Text "a" ] [ Text "b" ], Row [ Text "a" ] [ Text "b" ] ] `shouldBe` [ Row [ Text "a" ] [ Text "b" ], Row [ Text "a" ] [ Text "b" ] ]
it "two single line elements should concatenate into a single line" $
adjoinRows [ Row [ Text "a" ] [ Text "b" ] ] [ Row [ Text "a" ] [ Text "b" ] ] `shouldBe` [ Row [ Text "a", Text "a" ] [ Text "b", Text "b" ] ]
it "single line elements on the left concatenate onto the first of multiple lines on the right" $
adjoinRows [ Row [ Text "a1" ] [ Text "b1" ] ] [ Row [ Text "a2" ] [ Text "b2" ], Row [ Text "a3" ] [ Text "b3" ] ] `shouldBe` [ Row [ Text "a1", Text "a2" ] [ Text "b1", Text "b2" ], Row [ Text "a3" ] [ Text "b3" ] ]
it "the last of multiple line elements on the left concatenate onto the first of multiple lines on the right" $
adjoinRows [ Row [ Text "a1" ] [ Text "b1" ], Row [ Text "a2" ] [ Text "b2" ] ]
[ Row [ Text "a3" ] [ Text "b3" ], Row [ Text "a4" ] [ Text "b4" ] ]
`shouldBe`
[ Row [ Text "a1" ] [ Text "b1" ],
Row [ Text "a2", Text "a3" ] [ Text "b2", Text "b3" ],
Row [ Text "a4" ] [ Text "b4" ] ]
describe "annotatedToRows" $ do
it "outputs one row for single-line unchanged leaves" $
annotatedToRows (unchanged "a" "leaf" (Leaf "")) "a" "a" `shouldBe` ([ Row [ span "a" ] [ span "a" ] ], (Range 0 1, Range 0 1))
annotatedToRows (unchanged "a" "leaf" (Leaf "")) "a" "a" `shouldBe` ([ Row (Line [ span "a" ]) (Line [ span "a" ]) ], (Range 0 1, Range 0 1))
it "outputs one row for single-line empty unchanged indexed nodes" $
annotatedToRows (unchanged "[]" "branch" (Indexed [])) "[]" "[]" `shouldBe` ([ Row [ Ul (Just "category-branch") [ Text "[]" ] ] [ Ul (Just "category-branch") [ Text "[]" ] ] ], (Range 0 2, Range 0 2))
annotatedToRows (unchanged "[]" "branch" (Indexed [])) "[]" "[]" `shouldBe` ([ Row (Line [ Ul (Just "category-branch") [ Text "[]" ] ]) (Line [ Ul (Just "category-branch") [ Text "[]" ] ]) ], (Range 0 2, Range 0 2))
it "outputs one row for single-line non-empty unchanged indexed nodes" $
annotatedToRows (unchanged "[ a, b ]" "branch" (Indexed [
Free . offsetAnnotated 2 2 $ unchanged "a" "leaf" (Leaf ""),
Free . offsetAnnotated 5 5 $ unchanged "b" "leaf" (Leaf "")
])) "[ a, b ]" "[ a, b ]" `shouldBe` ([ Row [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ", ", span "b", Text " ]" ] ] [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ", ", span "b", Text " ]" ] ] ], (Range 0 8, Range 0 8))
])) "[ a, b ]" "[ a, b ]" `shouldBe` ([ Row (Line [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ", ", span "b", Text " ]" ] ]) (Line [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ", ", span "b", Text " ]" ] ]) ], (Range 0 8, Range 0 8))
it "outputs one row for single-line non-empty formatted indexed nodes" $
annotatedToRows (formatted "[ a, b ]" "[ a, b ]" "branch" (Indexed [
Free . offsetAnnotated 2 2 $ unchanged "a" "leaf" (Leaf ""),
Free . offsetAnnotated 5 6 $ unchanged "b" "leaf" (Leaf "")
])) "[ a, b ]" "[ a, b ]" `shouldBe` ([ Row [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ", ", span "b", Text " ]" ] ] [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ", ", span "b", Text " ]" ] ] ], (Range 0 8, Range 0 9))
])) "[ a, b ]" "[ a, b ]" `shouldBe` ([ Row (Line [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ", ", span "b", Text " ]" ] ]) (Line [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ", ", span "b", Text " ]" ] ]) ], (Range 0 8, Range 0 9))
it "outputs two rows for two-line non-empty unchanged indexed nodes" $
annotatedToRows (unchanged "[ a,\nb ]" "branch" (Indexed [
@ -57,10 +37,10 @@ main = hspec $ do
Free . offsetAnnotated 5 5 $ unchanged "b" "leaf" (Leaf "")
])) "[ a,\nb ]" "[ a,\nb ]" `shouldBe`
([
Row [ Ul (Just "category-branch") [ Text "[ ", span "a", Text "," ] ]
[ Ul (Just "category-branch") [ Text "[ ", span "a", Text "," ] ],
Row [ Ul (Just "category-branch") [ Text "", span "b", Text " ]" ] ]
[ Ul (Just "category-branch") [ Text "", span "b", Text " ]" ] ]
Row (Line [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ",", Break ] ])
(Line [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ",", Break] ]),
Row (Line [ Ul (Just "category-branch") [ span "b", Text " ]" ] ])
(Line [ Ul (Just "category-branch") [ span "b", Text " ]" ] ])
], (Range 0 8, Range 0 8))
it "outputs two rows for two-line non-empty formatted indexed nodes" $
@ -69,15 +49,92 @@ main = hspec $ do
Free . offsetAnnotated 5 5 $ unchanged "b" "leaf" (Leaf "")
])) "[ a,\nb ]" "[\na,\nb ]" `shouldBe`
([
Row [ Ul (Just "category-branch") [ Text "[ ", span "a", Text "," ] ]
[ Ul (Just "category-branch") [ Text "[" ] ],
Row [ Ul (Just "category-branch") [] ]
[ Ul (Just "category-branch") [ Text "", span "a", Text "," ] ],
Row [ Ul (Just "category-branch") [ Text "", span "b", Text " ]" ] ]
[ Ul (Just "category-branch") [ Text "", span "b", Text " ]" ] ]
Row (Line [ Ul (Just "category-branch") [ Text "[ ", span "a", Text ",", Break ] ])
(Line [ Ul (Just "category-branch") [ Text "[", Break ] ]),
Row EmptyLine
(Line [ Ul (Just "category-branch") [ span "a", Text ",", Break ] ]),
Row (Line [ Ul (Just "category-branch") [ span "b", Text " ]" ] ])
(Line [ Ul (Just "category-branch") [ span "b", Text " ]" ] ])
], (Range 0 8, Range 0 8))
it "" $
let (sourceA, sourceB) = ("[\na\n,\nb]", "[a,b]") in
annotatedToRows (formatted sourceA sourceB "branch" (Indexed [
Free . offsetAnnotated 2 1 $ unchanged "a" "leaf" (Leaf ""),
Free . offsetAnnotated 6 3 $ unchanged "b" "leaf" (Leaf "")
])) sourceA sourceB `shouldBe`
([
Row (Line [ Ul (Just "category-branch") [ Text "[", Break ] ])
(Line [ Ul (Just "category-branch") [ Text "[", span "a", Text ",", span "b", Text "]" ] ]),
Row (Line [ Ul (Just "category-branch") [ span "a", Break ] ])
EmptyLine,
Row (Line [ Ul (Just "category-branch") [ Text ",", Break ] ])
EmptyLine,
Row (Line [ Ul (Just "category-branch") [ span "b", Text "]" ] ])
EmptyLine
], (Range 0 8, Range 0 5))
it "should split multi-line deletions across multiple rows" $
let (sourceA, sourceB) = ("/*\n*/\na", "a") in
annotatedToRows (formatted sourceA sourceB "branch" (Indexed [
Pure . Delete $ (Info (Range 0 5) (Range 0 2) (Set.fromList ["leaf"]) :< (Leaf "")),
Free . offsetAnnotated 6 0 $ unchanged "a" "leaf" (Leaf "")
])) sourceA sourceB `shouldBe`
([
Row (Line [ Ul (Just "category-branch") [ Div (Just "delete") [ span "/*", Break ] ] ]) EmptyLine,
Row (Line [ Ul (Just "category-branch") [ Div (Just "delete") [ span "*/" ], Break ] ]) EmptyLine,
Row (Line [ Ul (Just "category-branch") [ span "a" ] ]) (Line [ Ul (Just "category-branch") [ span "a" ] ])
], (Range 0 7, Range 0 1))
describe "adjoin2" $ do
it "appends appends HTML onto incomplete lines" $
adjoin2 [ rightRowText "[" ] (rightRowText "a") `shouldBe`
[ rightRow [ Text "[", Text "a" ] ]
it "does not append HTML onto complete lines" $
adjoin2 [ leftRow [ Break ] ] (leftRowText ",") `shouldBe`
[ leftRowText ",", leftRow [ Break ] ]
it "appends breaks onto incomplete lines" $
adjoin2 [ leftRowText "a" ] (leftRow [ Break ]) `shouldBe`
[ leftRow [ Text "a", Break ] ]
it "does not promote HTML through empty lines onto complete lines" $
adjoin2 [ rightRowText "b", leftRow [ Break ] ] (leftRowText "a") `shouldBe`
[ leftRowText "a", rightRowText "b", leftRow [ Break ] ]
it "promotes breaks through empty lines onto incomplete lines" $
adjoin2 [ rightRowText "c", rowText "a" "b" ] (leftRow [ Break ]) `shouldBe`
[ rightRowText "c", Row (Line [ Text "a", Break ]) (Line [ Text "b" ]) ]
describe "termToLines" $ do
it "splits multi-line terms into multiple lines" $
termToLines (Info (Range 0 5) (Range 0 2) (Set.singleton "leaf") :< (Leaf "")) "/*\n*/"
`shouldBe`
([
Line [ span "/*", Break ],
Line [ span "*/" ]
], Range 0 5)
describe "openLine" $ do
it "should produce the earliest non-empty line in a list, if open" $
openLine [
Line [ Div (Just "delete") [ span "*/" ] ],
Line [ Div (Just "delete") [ span " * Debugging", Break ] ],
Line [ Div (Just "delete") [ span "/*", Break ] ]
] `shouldBe` (Just $ Line [ Div (Just "delete") [ span "*/" ] ])
it "should return Nothing if the earliest non-empty line is closed" $
openLine [
Line [ Div (Just "delete") [ span " * Debugging", Break ] ]
] `shouldBe` Nothing
where
rightRowText text = rightRow [ Text text ]
rightRow xs = Row EmptyLine (Line xs)
leftRowText text = leftRow [ Text text ]
leftRow xs = Row (Line xs) EmptyLine
rowText a b = Row (Line [ Text a ]) (Line [ Text b ])
info source category = Info (totalRange source) (Range 0 0) (Set.fromList [ category ])
unchanged source category = formatted source source category
formatted source1 source2 category = Annotated (info source1 category, info source2 category)