1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE ViewPatterns #-}
5 ( -- * Constructing forests
7 , ParseTokenForestError(..), PStack(..)
9 -- * Deconstructing forests
15import Data.Text (Text)
17import Text.HTML.Parser
19-- | construct a 'Forest' from a 'Token' list.
21-- This code correctly handles void elements. Void elements are required to have a start tag and must not have an end tag. See 'nonClosing'.
23-- This code does __not__ correctly handle optional tags. It assumes all optional start and end tags are present.
25-- <https:\/\/www.w3.org\/TR\/html52\/syntax.html#optional-tags>
26tokensToForest :: [Token] -> Either ParseTokenForestError (Forest Token)
27tokensToForest = f (PStack [] [])
29 f (PStack ss []) [] = Right (reverse ss)
30 f pstack [] = Left $ ParseTokenForestErrorBracketMismatch pstack Nothing
31 f pstack (t : ts) = case t of
32 TagOpen n _ -> if n `elem` nonClosing
33 then f (pushFlatSibling t pstack) ts
34 else f (pushParent t pstack) ts
35 TagSelfClose {} -> f (pushFlatSibling t pstack) ts
36 TagClose n -> (`f` ts) =<< popParent n pstack
37 ContentChar _ -> f (pushFlatSibling t pstack) ts
38 ContentText _ -> f (pushFlatSibling t pstack) ts
39 Comment _ -> f (pushFlatSibling t pstack) ts
40 Doctype _ -> f (pushFlatSibling t pstack) ts
42-- | void elements which must not have an end tag
44-- This list does not include the obsolete @\<command\>@ and @\<keygen\>@ elements.
46-- @ nonClosing = ["br", "hr", "img", "meta", "area", "base", "col", "embed", "input", "link", "param", "source", "track", "wbr"] @
48-- <https:\/\/www.w3.org\/TR\/html52\/syntax.html#void-elements>
50nonClosing = ["br", "hr", "img", "meta", "area", "base", "col", "embed", "input", "link", "param", "source", "track", "wbr"]
52data ParseTokenForestError =
53 ParseTokenForestErrorBracketMismatch PStack (Maybe Token)
57 { _pstackToplevelSiblings :: Forest Token
58 , _pstackParents :: [(Token, Forest Token)]
62pushParent :: Token -> PStack -> PStack
63pushParent t (PStack ss ps) = PStack [] ((t, ss) : ps)
65popParent :: TagName -> PStack -> Either ParseTokenForestError PStack
66popParent n (PStack ss ((p@(TagOpen n' _), ss') : ps))
67 | n == n' = Right $ PStack (Node p (reverse ss) : ss') ps
69 = Left $ ParseTokenForestErrorBracketMismatch pstack (Just $ TagClose n)
71pushFlatSibling :: Token -> PStack -> PStack
72pushFlatSibling t (PStack ss ps) = PStack (Node t [] : ss) ps
74-- | convert a 'Forest' of 'Token' into a list of 'Token'.
76-- This code correctly handles void elements. Void elements are required to have a start tag and must not have an end tag. See 'nonClosing'.
77tokensFromForest :: Forest Token -> [Token]
78tokensFromForest = mconcat . fmap tokensFromTree
80-- | convert a 'Tree' of 'Token' into a list of 'Token'.
82-- This code correctly handles void elements. Void elements are required to have a start tag and must not have an end tag. See 'nonClosing'.
83tokensFromTree :: Tree Token -> [Token]
84tokensFromTree (Node o@(TagOpen n _) ts) | n `notElem` nonClosing
85 = [o] <> tokensFromForest ts <> [TagClose n]
86tokensFromTree (Node t [])
89 = error "renderTokenTree: leaf node with children."
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list