Skip to content

Commit

Permalink
Extend nodeOuterHtml to all node types
Browse files Browse the repository at this point in the history
  • Loading branch information
neongreen committed Nov 4, 2021
1 parent 951141e commit 5994969
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 2 deletions.
11 changes: 10 additions & 1 deletion IHP/ServerSideComponent/HtmlDiff.hs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ diffNodes' path Children { children = oldChildren } Children { children = newChi
patchElements (new:nextNewNode:newRest) (old:oldRest) !index | (not $ new `isNodeEqIgnoringPosition` old) && (old `isNodeEqIgnoringPosition` nextNewNode) = [ CreateNode { html = nodeOuterHtml new ?newHtml, path = (index:path) } ] <> (patchElements (newRest) (oldRest) (index + 2)) -- [A, C <old>] -> [A, B <new>, C <nextNewNode>]
patchElements (new:newRest) (old:nextOld:oldRest) !index | (not $ new `isNodeEqIgnoringPosition` old) && (new `isNodeEqIgnoringPosition` nextOld) = [ DeleteNode { node = old, path = (index:path) } ] <> (patchElements (newRest) (oldRest) (index + 1)) -- [A, B <old>, C <nextOldNode> ] -> [A, C <new>]
patchElements (new:newRest) (old:oldRest) !index = (diffNodes' (index:path) old new) <> (patchElements newRest oldRest (index + 1))
patchElements (new:newRest) oldRest !index = [ CreateNode { html = nodeOuterHtml new ?newHtml, path = (index:path) } ] <> (patchElements newRest oldRest (index + 1))
patchElements (new:newRest) [] !index = [ CreateNode { html = nodeOuterHtml new ?newHtml, path = (index:path) } ] <> (patchElements newRest [] (index + 1))
patchElements [] (old:oldRest) !index = [ DeleteNode { node = old, path = (index:path) } ] <> (patchElements [] oldRest (index + 1))
patchElements [] [] _ = []
in
Expand Down Expand Up @@ -108,10 +108,19 @@ diffAttributes old new = addOrUpdateAttributes <> deleteAttributes
matchAttribute :: [Attribute] -> Attribute -> Maybe Attribute
matchAttribute attributes Attribute { attributeName } = find (\Attribute { attributeName = attributeName' } -> attributeName == attributeName' ) attributes

-- | Grabs the entire HTML string corresponding to the node boundaries.
--
-- Node boundaries are only stored for 'Node'. Other nodes ('TextNode', etc) don't store start/end offset, so we render
-- them by ourselves.
nodeOuterHtml :: Node -> Text -> Text
nodeOuterHtml Node { startOffset, endOffset } html = html
|> Text.drop startOffset
|> Text.take (endOffset - startOffset)
-- Assuming chars are already escaped, because that's what HSX produces
nodeOuterHtml TextNode { textContent } _ = textContent
nodeOuterHtml PreEscapedTextNode { textContent } _ = textContent
nodeOuterHtml Children { children } html = mconcat $ map (`nodeOuterHtml` html) children
nodeOuterHtml CommentNode { comment } _ = "<!--" <> comment <> "-->"

isNodeEqIgnoringPosition :: Node -> Node -> Bool
isNodeEqIgnoringPosition a@(Node {}) b@(Node {}) = (a { startOffset = 0, endOffset = 0 }) == (b { startOffset = 0, endOffset = 0 })
Expand Down
2 changes: 1 addition & 1 deletion IHP/ServerSideComponent/HtmlParser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ data Attribute = Attribute
} deriving (Eq, Show)

data Node = Node { tagName :: !Text, attributes :: ![Attribute], children :: ![Node], startOffset :: Int, endOffset :: Int }
| TextNode { textContent :: !Text }
| TextNode { textContent :: !Text } -- ^ Note: doesn't unescape chars like &lt;
| PreEscapedTextNode { textContent :: !Text } -- ^ Used in @script@ or @style@ bodies
| Children { children :: ![Node] }
| CommentNode { comment :: !Text }
Expand Down
10 changes: 10 additions & 0 deletions Test/ServerSideComponent/HtmlDiffSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ tests = do
let expected = [UpdatePreEscapedTextNode { textContent = "alert(2)", path = [] }]
result `shouldBe` expected

it "should insert text and comment nodes (#1192)" do
let a = Node {tagName = "span", attributes = [], children = [], startOffset = 0, endOffset = 0}
let b = Node {tagName = "span", attributes = [], children = [TextNode {textContent = "string"}, CommentNode {comment = "test"}], startOffset = 0, endOffset = 0}
let result = diffNodes a b
let expected =
[ CreateNode {html = "string", path = [0]}
, CreateNode {html = "<!--test-->", path = [1]}
]
result `shouldBe` expected

it "should replace nodes if the tag name is different" do
let a = Node { tagName = "a", attributes = [Attribute { attributeName = "href", attributeValue = "#" }], children = [ TextNode { textContent = "hello" } ], startOffset = 0, endOffset = 0 }
let b = Node { tagName = "div", attributes = [Attribute { attributeName = "class", attributeValue = "d-block" }], children = [], startOffset = 0, endOffset = 0 }
Expand Down

0 comments on commit 5994969

Please sign in to comment.