zenacy-html 2.0.7 → 2.1.0
raw patch · 7 files changed
+201/−15 lines, 7 files
Files
- CHANGES.md +5/−0
- src/Zenacy/HTML/Internal/DOM.hs +20/−2
- src/Zenacy/HTML/Internal/Parser.hs +19/−10
- test/Examples.hs +107/−0
- test/Issues.hs +41/−0
- test/TestSuite.hs +4/−0
- zenacy-html.cabal +5/−3
CHANGES.md view
@@ -1,5 +1,10 @@ # Change Log +## 2.1.0++* Fix removal of earliest active format element+* Fix DOM node attribute matching+ ## 2.0.7 * Update benchmarks for ST MonadFail removal
src/Zenacy/HTML/Internal/DOM.hs view
@@ -45,7 +45,9 @@ , domElementHasAttr , domElementFindAttr , domElementAttrValue+ , domAttrEq , domAttrMerge+ , domAttrSet , domMatch , domLastChild , domMapID@@ -111,6 +113,13 @@ , insert , keys )+import Data.Set+ ( Set+ )+import qualified Data.Set as Set+ ( empty+ , insert+ ) import Data.Sequence ( Seq(..) , ViewL(..)@@ -492,11 +501,20 @@ domMatch d i j = case (domGetNode d i, domGetNode d j) of (Just (DOMElement _ _ n1 s1 a1 _), Just (DOMElement _ _ n2 s2 a2 _)) ->- n1 == n2 && s1 == s2 && a1 == a1+ n1 == n2 && s1 == s2 && domAttrEq a1 a2 (Just (DOMTemplate _ _ s1 a1 _ ), Just (DOMTemplate _ _ s2 a2 _)) ->- s1 == s2 && a1 == a1+ s1 == s2 && domAttrEq a1 a2 _otherwise -> False++-- | Determine if two sequences of attributes are equivalent.+domAttrEq :: Seq DOMAttr -> Seq DOMAttr -> Bool+domAttrEq a b = domAttrSet a == domAttrSet b++-- | Converta an attribute sequence to an attribute set in order+-- to be insensitive to order.+domAttrSet :: Seq DOMAttr -> Set DOMAttr+domAttrSet = foldr Set.insert Set.empty -- | Returns the last child of a node if it exists. domLastChild :: DOM -> DOMID -> Maybe DOMID
src/Zenacy/HTML/Internal/Parser.hs view
@@ -883,17 +883,26 @@ activeFormatAddMarker Parser {..} = uref parserActiveFormatList (ParserFormatMarker:) +-- | Limits matching active format elements up to the first+-- marker to two so that a third can be added (Noah's Ark clause).+activeFormatLimit :: (DOMID -> Bool) -> [ParserFormatItem] -> [ParserFormatItem]+activeFormatLimit match xs =+ f 0 xs+ where+ f :: Int -> [ParserFormatItem] -> [ParserFormatItem]+ f n [] = []+ f n (e@(ParserFormatMarker):es) = e : es+ f n (e@(ParserFormatElement domId _):es)+ | match domId = if n < 2 then e : f (n + 1) es else f n es+ | otherwise = e : f n es+ -- | Adds an element to the list of active format elements. activeFormatAddElement :: Parser s -> Token -> DOMID -> ST s () activeFormatAddElement p@Parser {..} t x = do d <- getDOM p a <- activeFormatList p- let match (ParserFormatElement y _) = domMatch d x y- b = takeWhile (not . formatItemIsMarker) a- n = (foldr (\i z -> z + if match i then 1 else 0) 0 b) :: Int- a' = if n < 3 then a else removeFirst match a- e' = ParserFormatElement x t : a'- wref parserActiveFormatList e'+ wref parserActiveFormatList $+ ParserFormatElement x t : activeFormatLimit (domMatch d x) a -- | Adds the current node to the list of active format elements. activeFormatAddCurrentNode :: Parser s -> Token -> ST s ()@@ -952,15 +961,15 @@ case a of [] -> pure () (x:xs)- | isOpen e x -> pure ()+ | openOrMarker e x -> pure () | otherwise -> do- let b = reverse . takeWhile (not . isOpen e) $ a+ let b = reverse . takeWhile (not . openOrMarker e) $ a a' = drop (length b) a reopen p b a' -- | Determines is a format item is open.-isOpen :: [DOMID] -> ParserFormatItem -> Bool-isOpen x = \case+openOrMarker :: [DOMID] -> ParserFormatItem -> Bool+openOrMarker x = \case ParserFormatMarker -> True ParserFormatElement i _ -> i `elem` x
+ test/Examples.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE QuasiQuotes #-}++module Examples+ ( testExamples+ ) where++import Test.Framework+ ( Test+ , testGroup+ )+import Test.Framework.Providers.HUnit+ ( testCase+ )+import Test.HUnit+ ( assertEqual+ )+import Data.Text+ ( Text+ )+import qualified Data.Text as T+ ( pack+ )+import Zenacy.HTML++testExamples :: Test+testExamples = testGroup "Examples"+ [ testMisnested+ , testUnexpected+ , testUnclosed+ ]++testMisnested :: Test+testMisnested = testCase "Misnested tags" $ do+ flip (assertEqual "13.2.10.1 Misnested tags")+ (htmlRender $ htmlParseEasy+ "<html><head></head><body><p>1<b>2<i>3</b>4</i>5</p></body></html>")+ "<html><head></head><body><p>1<b>2<i>3</i></b><i>4</i>5</p></body></html>"+ flip (assertEqual "13.2.10.2 Misnested tags")+ (htmlRender $ htmlParseEasy+ "<html><head></head><body><b>1<p>2</b>3</p></body></html>")+ "<html><head></head><body><b>1</b><p><b>2</b>3</p></body></html>"++testUnexpected :: Test+testUnexpected = testCase "Unexpected markup in tables" $ do+ flip (assertEqual "13.2.10.3 Unexpected markup in tables")+ (htmlRender $ htmlParseEasy+ "<html><head></head><body><table><b><tr><td>aaa</td></tr>bbb</table>ccc</body></html>")+ "<html><head></head><body>\+ \<b></b><b>bbb</b><table><tbody><tr><td>aaa</td></tr></tbody></table><b>ccc</b>\+ \</body></html>"++testUnclosed :: Test+testUnclosed = testCase "Unclosed formatting elements" $ do+ flip (assertEqual "13.2.10.6 Unclosed formatting elements")+ (htmlRender $ htmlParseEasy+ "<!DOCTYPE html>\+ \<p><b class=x><b class=x><b><b class=x><b class=x><b>X\+ \<p>X\+ \<p><b><b class=x><b>X\+ \<p></b></b></b></b></b></b>X")++ "<!DOCTYPE html><html><head></head><body>\+ \<p>\+ \<b class=\"x\">\+ \<b class=\"x\">\+ \<b>\+ \<b class=\"x\">\+ \<b class=\"x\">\+ \<b>X</b>\+ \</b>\+ \</b>\+ \</b>\+ \</b>\+ \</b>\+ \</p>\+ \<p>\+ \<b class=\"x\">\+ \<b>\+ \<b class=\"x\">\+ \<b class=\"x\">\+ \<b>X</b>\+ \</b>\+ \</b>\+ \</b>\+ \</b>\+ \</p>\+ \<p>\+ \<b class=\"x\">\+ \<b>\+ \<b class=\"x\">\+ \<b class=\"x\">\+ \<b>\+ \<b>\+ \<b class=\"x\">\+ \<b>X</b>\+ \</b>\+ \</b>\+ \</b>\+ \</b>\+ \</b>\+ \</b>\+ \</b>\+ \</p>\+ \<p>X</p>\+ \</body></html>"
+ test/Issues.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE QuasiQuotes #-}++module Issues+ ( testIssues+ ) where++import Test.Framework+ ( Test+ , testGroup+ )+import Test.Framework.Providers.HUnit+ ( testCase+ )+import Test.HUnit+ ( assertEqual+ )+import Data.Text+ ( Text+ )+import qualified Data.Text as T+ ( pack+ )+import Zenacy.HTML++testIssues :: Test+testIssues = testGroup "Issues"+ [ testIssue01+ ]++testIssue01 :: Test+testIssue01 = testCase "issue 01" $ do+ flip (assertEqual "Issue 1 case 1")+ (htmlRender $ htmlParseEasy+ "<html><body> <p><i><i>a<i>b<i></i></i></i></i></p> </body></html>")+ "<html><head></head><body> <p><i><i>a<i>b<i></i></i></i></i></p> </body></html>"+ flip (assertEqual "Issue 1 case 2")+ (htmlRender $ htmlParseEasy+ "<html><body>x<p><i><i>a<i>b<i></i></i></i></i></p>y</body></html>")+ "<html><head></head><body>x<p><i><i>a<i>b<i></i></i></i></i></p>y</body></html>"
test/TestSuite.hs view
@@ -1,5 +1,7 @@ module Main where +import Examples+import Issues import Samples import Zenacy.HTML.Internal.Buffer.Tests import Zenacy.HTML.Internal.Entity.Tests@@ -29,5 +31,7 @@ , testTrie , testZip , testQuery+ , testExamples , testSamples+ , testIssues ]
zenacy-html.cabal view
@@ -1,5 +1,5 @@ cabal-version: >= 1.10-version: 2.0.7+version: 2.1.0 name: zenacy-html synopsis:@@ -149,7 +149,10 @@ default-language: Haskell2010 other-modules:- Zenacy.HTML.Internal.Buffer.Tests+ Examples+ , Issues+ , Samples+ , Zenacy.HTML.Internal.Buffer.Tests , Zenacy.HTML.Internal.Entity.Tests , Zenacy.HTML.Internal.HTML.Tests , Zenacy.HTML.Internal.Image.Tests@@ -160,7 +163,6 @@ , Zenacy.HTML.Internal.Token.Tests , Zenacy.HTML.Internal.Trie.Tests , Zenacy.HTML.Internal.Zip.Tests- , Samples benchmark zenacy-html-bench type: