From cbb0e4a4c4c344b5f672b70548c46b5b2374a0d9 Mon Sep 17 00:00:00 2001 From: Mattt Date: Thu, 14 Jan 2021 13:12:28 -0800 Subject: [PATCH 1/4] Add replace(child:with:) methods --- .../Supporting Types/Children.swift | 52 +++++++++++++++++++ .../ContainerManipulationTests.swift | 24 ++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/Sources/CommonMark/Supporting Types/Children.swift b/Sources/CommonMark/Supporting Types/Children.swift index 5392255..2aa93a7 100644 --- a/Sources/CommonMark/Supporting Types/Children.swift +++ b/Sources/CommonMark/Supporting Types/Children.swift @@ -133,6 +133,19 @@ extension ContainerOfBlocks { return add(child) { cmark_node_insert_after(sibling.cmark_node, child.cmark_node) } } + /** + Replaces a block with the specified node. + + - Parameters: + - child: The block to replace. + - replacement: The block to replace the existing block. + - Returns: `true` if successful, otherwise `false`. + */ + @discardableResult + public func replace(child: Block & Node, with replacement: Block & Node) -> Bool { + return add(replacement) { cmark_node_replace(child.cmark_node, replacement.cmark_node) } + } + /** Removes a block from the node's children. @@ -255,6 +268,19 @@ extension ContainerOfInlineElements { return add(child) { cmark_node_insert_after(sibling.cmark_node, child.cmark_node) } } + /** + Replaces an inline element with the specified node. + + - Parameters: + - child: The inline element to replace. + - replacement: The inline element to replace the existing inline element. + - Returns: `true` if successful, otherwise `false`. + */ + @discardableResult + public func replace(child: Inline & Node, with replacement: Inline & Node) -> Bool { + return add(replacement) { cmark_node_replace(child.cmark_node, replacement.cmark_node) } + } + /** Removes an inline element from the node's children. @@ -364,6 +390,19 @@ extension List { return add(child) { cmark_node_insert_after(sibling.cmark_node, child.cmark_node) } } + /** + Replaces an item with the specified node. + + - Parameters: + - child: The item to replace. + - replacement: The item to replace the existing item. + - Returns: `true` if successful, otherwise `false`. + */ + @discardableResult + public func replace(child: Item, with replacement: Item) -> Bool { + return add(replacement) { cmark_node_replace(child.cmark_node, replacement.cmark_node) } + } + /** Removes an item from the list. @@ -473,6 +512,19 @@ extension List.Item { return add(child) { cmark_node_insert_after(sibling.cmark_node, child.cmark_node) } } + /** + Replaces a node with a specified node. + + - Parameters: + - child: The node to replace. + - replacement: The node to replace the existing node. + - Returns: `true` if successful, otherwise `false`. + */ + @discardableResult + public func replace(child: Node, with replacement: Node) -> Bool { + return add(replacement) { cmark_node_replace(child.cmark_node, replacement.cmark_node) } + } + /** Removes a node from the list item's children. diff --git a/Tests/CommonMarkTests/ContainerManipulationTests.swift b/Tests/CommonMarkTests/ContainerManipulationTests.swift index 22b19ca..555e3e9 100644 --- a/Tests/CommonMarkTests/ContainerManipulationTests.swift +++ b/Tests/CommonMarkTests/ContainerManipulationTests.swift @@ -20,13 +20,33 @@ final class ContainerManipulationTests: XCTestCase { political or other opinion, national or social origin, property, birth or other status. """#) let wasNewParagraphInserted = document.insert(child: newParagraph, after: paragraph) - XCTAssertTrue(wasNewParagraphInserted) XCTAssertEqual(document.children.count, 4) XCTAssertEqual(newParagraph, document.children[3]) + let replacementParagraph = Paragraph(text: + #""" + Everyone is entitled to all the rights and freedoms set forth in this Declaration, + without distinction of any kind, such as + race, + colour, + sex, + language, + religion, + political or other opinion, + national or social origin, + property, + birth or + other status. + """# + ) + let wasNewParagraphReplaced = document.replace(child: newParagraph, with: replacementParagraph) + XCTAssertTrue(wasNewParagraphReplaced) + XCTAssertEqual(document.children.count, 4) + XCTAssertEqual(replacementParagraph, document.children[3]) + let newSubheading = Heading(level: 2, text: "Article 2.") - let wasSubheadingInserted = document.insert(child: newSubheading, before: newParagraph) + let wasSubheadingInserted = document.insert(child: newSubheading, before: replacementParagraph) XCTAssertTrue(wasSubheadingInserted) XCTAssertEqual(document.children.count, 5) XCTAssertEqual(newSubheading, document.children[3]) From d8e2b250e9f427d54d51399d9d05b0eba9acab46 Mon Sep 17 00:00:00 2001 From: Mattt Date: Thu, 14 Jan 2021 13:22:04 -0800 Subject: [PATCH 2/4] Refactor implementation of child manipulation methods --- .../Supporting Types/Children.swift | 132 +++++++++++++----- 1 file changed, 96 insertions(+), 36 deletions(-) diff --git a/Sources/CommonMark/Supporting Types/Children.swift b/Sources/CommonMark/Supporting Types/Children.swift index 2aa93a7..538c8d9 100644 --- a/Sources/CommonMark/Supporting Types/Children.swift +++ b/Sources/CommonMark/Supporting Types/Children.swift @@ -36,22 +36,6 @@ struct CMarkNodeChildIterator: IteratorProtocol { // MARK: - -fileprivate func add(_ child: Child, with operation: () -> Int32) -> Bool { - let status = operation() - switch status { - case 1: - child.managed = false - return true - case 0: - return false - default: - assertionFailure("unexpected status code: \(status)") - return false - } -} - -// MARK: - - public protocol ContainerOfBlocks: Node { var children: [Block & Node] { get } } @@ -92,7 +76,11 @@ extension ContainerOfBlocks { */ @discardableResult public func prepend(child: Block & Node) -> Bool { - return add(child) { cmark_node_prepend_child(cmark_node, child.cmark_node) } + guard cmark_node_prepend_child(cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -104,7 +92,11 @@ extension ContainerOfBlocks { */ @discardableResult public func append(child: Block & Node) -> Bool { - return add(child) { cmark_node_append_child(cmark_node, child.cmark_node) } + guard cmark_node_append_child(cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -117,7 +109,11 @@ extension ContainerOfBlocks { */ @discardableResult public func insert(child: Block & Node, before sibling: Block & Node) -> Bool { - return add(child) { cmark_node_insert_before(sibling.cmark_node, child.cmark_node) } + guard cmark_node_insert_before(sibling.cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -130,7 +126,11 @@ extension ContainerOfBlocks { */ @discardableResult public func insert(child: Block & Node, after sibling: Block & Node) -> Bool { - return add(child) { cmark_node_insert_after(sibling.cmark_node, child.cmark_node) } + guard cmark_node_insert_after(sibling.cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -143,7 +143,12 @@ extension ContainerOfBlocks { */ @discardableResult public func replace(child: Block & Node, with replacement: Block & Node) -> Bool { - return add(replacement) { cmark_node_replace(child.cmark_node, replacement.cmark_node) } + guard cmark_node_replace(child.cmark_node, replacement.cmark_node) == 1 else { return false } + + replacement.managed = false + child.managed = true + + return true } /** @@ -156,6 +161,7 @@ extension ContainerOfBlocks { @discardableResult public func remove(child: Block & Node) -> Bool { guard child.parent == self else { return false } + child.unlink() return true @@ -227,7 +233,11 @@ extension ContainerOfInlineElements { */ @discardableResult public func prepend(child: Inline & Node) -> Bool { - return add(child) { cmark_node_prepend_child(cmark_node, child.cmark_node) } + guard cmark_node_prepend_child(cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -239,7 +249,11 @@ extension ContainerOfInlineElements { */ @discardableResult public func append(child: Inline & Node) -> Bool { - return add(child) { cmark_node_append_child(cmark_node, child.cmark_node) } + guard cmark_node_append_child(cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -252,7 +266,11 @@ extension ContainerOfInlineElements { */ @discardableResult public func insert(child: Inline & Node, before sibling: Inline & Node) -> Bool { - return add(child) { cmark_node_insert_before(sibling.cmark_node, child.cmark_node) } + guard cmark_node_insert_before(sibling.cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -265,7 +283,11 @@ extension ContainerOfInlineElements { */ @discardableResult public func insert(child: Inline & Node, after sibling: Inline & Node) -> Bool { - return add(child) { cmark_node_insert_after(sibling.cmark_node, child.cmark_node) } + guard cmark_node_insert_after(sibling.cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -278,7 +300,12 @@ extension ContainerOfInlineElements { */ @discardableResult public func replace(child: Inline & Node, with replacement: Inline & Node) -> Bool { - return add(replacement) { cmark_node_replace(child.cmark_node, replacement.cmark_node) } + guard cmark_node_replace(child.cmark_node, replacement.cmark_node) == 1 else { return false } + + replacement.managed = false + child.managed = true + + return true } /** @@ -291,6 +318,7 @@ extension ContainerOfInlineElements { @discardableResult public func remove(child: Inline & Node) -> Bool { guard child.parent == self else { return false } + child.unlink() return true @@ -349,7 +377,11 @@ extension List { */ @discardableResult public func prepend(child: Item) -> Bool { - return add(child) { cmark_node_prepend_child(cmark_node, child.cmark_node) } + guard cmark_node_prepend_child(cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -361,7 +393,11 @@ extension List { */ @discardableResult public func append(child: Item) -> Bool { - return add(child) { cmark_node_append_child(cmark_node, child.cmark_node) } + guard cmark_node_append_child(cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -374,7 +410,11 @@ extension List { */ @discardableResult public func insert(child: Item, before sibling: Item) -> Bool { - return add(child) { cmark_node_insert_before(sibling.cmark_node, child.cmark_node) } + guard cmark_node_insert_before(sibling.cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -387,7 +427,11 @@ extension List { */ @discardableResult public func insert(child: Item, after sibling: Item) -> Bool { - return add(child) { cmark_node_insert_after(sibling.cmark_node, child.cmark_node) } + guard cmark_node_insert_after(sibling.cmark_node, child.cmark_node) == 1 else { return false } + + child.managed = false + + return true } /** @@ -400,7 +444,12 @@ extension List { */ @discardableResult public func replace(child: Item, with replacement: Item) -> Bool { - return add(replacement) { cmark_node_replace(child.cmark_node, replacement.cmark_node) } + guard cmark_node_replace(child.cmark_node, replacement.cmark_node) == 1 else { return false } + + replacement.managed = false + child.managed = true + + return true } /** @@ -471,7 +520,9 @@ extension List.Item { */ @discardableResult public func prepend(child: Node) -> Bool { - return add(child) { cmark_node_prepend_child(cmark_node, child.cmark_node) } + guard cmark_node_prepend_child(cmark_node, child.cmark_node) == 1 else { return false } + child.managed = false + return true } /** @@ -483,7 +534,10 @@ extension List.Item { */ @discardableResult public func append(child: Node) -> Bool { - return add(child) { cmark_node_append_child(cmark_node, child.cmark_node) } + guard cmark_node_append_child(cmark_node, child.cmark_node) == 1 else { return false } + child.managed = false + return true + } /** @@ -496,7 +550,9 @@ extension List.Item { */ @discardableResult public func insert(child: Node, before sibling: Node) -> Bool { - return add(child) { cmark_node_insert_before(sibling.cmark_node, child.cmark_node) } + guard cmark_node_insert_before(sibling.cmark_node, child.cmark_node) == 1 else { return false } + child.managed = false + return true } /** @@ -509,7 +565,9 @@ extension List.Item { */ @discardableResult public func insert(child: Node, after sibling: Node) -> Bool { - return add(child) { cmark_node_insert_after(sibling.cmark_node, child.cmark_node) } + guard cmark_node_insert_after(sibling.cmark_node, child.cmark_node) == 1 else { return false } + child.managed = false + return true } /** @@ -522,7 +580,9 @@ extension List.Item { */ @discardableResult public func replace(child: Node, with replacement: Node) -> Bool { - return add(replacement) { cmark_node_replace(child.cmark_node, replacement.cmark_node) } + guard cmark_node_replace(child.cmark_node, replacement.cmark_node) == 1 else { return false } + replacement.managed = false + return true } /** From 451345459071943fc625279f55c59cd1a5231b09 Mon Sep 17 00:00:00 2001 From: Mattt Date: Sat, 16 Jan 2021 04:56:58 -0800 Subject: [PATCH 3/4] Whitespace --- Sources/CommonMark/Supporting Types/Children.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/CommonMark/Supporting Types/Children.swift b/Sources/CommonMark/Supporting Types/Children.swift index 538c8d9..a099bea 100644 --- a/Sources/CommonMark/Supporting Types/Children.swift +++ b/Sources/CommonMark/Supporting Types/Children.swift @@ -535,9 +535,10 @@ extension List.Item { @discardableResult public func append(child: Node) -> Bool { guard cmark_node_append_child(cmark_node, child.cmark_node) == 1 else { return false } + child.managed = false + return true - } /** From fef20df421f274dd135963bef939790d384ae2d4 Mon Sep 17 00:00:00 2001 From: Mattt Date: Sat, 16 Jan 2021 04:58:26 -0800 Subject: [PATCH 4/4] Add changelog entry for #26 --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index ae85a23..eaf92e7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added `replace(child:with:)` methods for container nodes. + #26 by @mattt. + ## [0.5.0] - 2021-01-14 ### Fixed