|
7 | 7 | #include <stdexcept> |
8 | 8 | #include <string> |
9 | 9 | #include <tuple> |
| 10 | +#include <unordered_set> |
10 | 11 | #include <variant> |
11 | 12 |
|
12 | 13 | #include <lexbor/html/html.h> |
@@ -43,8 +44,10 @@ auto resource = fine::Atom("resource"); |
43 | 44 |
|
44 | 45 | struct DocumentRef { |
45 | 46 | lxb_html_document_t *document; |
| 47 | + bool is_fragment; |
46 | 48 |
|
47 | | - DocumentRef(lxb_html_document_t *document) : document(document) {} |
| 49 | + DocumentRef(lxb_html_document_t *document, bool is_fragment) |
| 50 | + : document(document), is_fragment(is_fragment) {} |
48 | 51 |
|
49 | 52 | ~DocumentRef() { lxb_html_document_destroy(this->document); } |
50 | 53 | }; |
@@ -97,7 +100,7 @@ ExLazyHTML from_document(ErlNifEnv *env, ErlNifBinary html) { |
97 | 100 | throw std::runtime_error("failed to parse html document"); |
98 | 101 | } |
99 | 102 |
|
100 | | - auto document_ref = std::make_shared<DocumentRef>(document); |
| 103 | + auto document_ref = std::make_shared<DocumentRef>(document, false); |
101 | 104 | document_guard.deactivate(); |
102 | 105 |
|
103 | 106 | auto nodes = std::vector<lxb_dom_node_t *>(); |
@@ -129,7 +132,7 @@ ExLazyHTML from_fragment(ErlNifEnv *env, ErlNifBinary html) { |
129 | 132 | throw std::runtime_error("failed to parse html fragment"); |
130 | 133 | } |
131 | 134 |
|
132 | | - auto document_ref = std::make_shared<DocumentRef>(document); |
| 135 | + auto document_ref = std::make_shared<DocumentRef>(document, true); |
133 | 136 | document_guard.deactivate(); |
134 | 137 |
|
135 | 138 | auto nodes = std::vector<lxb_dom_node_t *>(); |
@@ -522,7 +525,10 @@ ExLazyHTML from_tree(ErlNifEnv *env, std::vector<fine::Term> tree) { |
522 | 525 | nodes.push_back(node); |
523 | 526 | } |
524 | 527 |
|
525 | | - auto document_ref = std::make_shared<DocumentRef>(document); |
| 528 | + bool is_fragment = |
| 529 | + nodes.empty() || !lxb_html_tree_node_is(nodes.front(), LXB_TAG_HTML); |
| 530 | + |
| 531 | + auto document_ref = std::make_shared<DocumentRef>(document, is_fragment); |
526 | 532 | document_guard.deactivate(); |
527 | 533 |
|
528 | 534 | return ExLazyHTML(fine::make_resource<LazyHTML>(document_ref, nodes, false)); |
@@ -714,6 +720,57 @@ ExLazyHTML child_nodes(ErlNifEnv *env, ExLazyHTML ex_lazy_html) { |
714 | 720 |
|
715 | 721 | FINE_NIF(child_nodes, 0); |
716 | 722 |
|
| 723 | +ExLazyHTML parent_node(ErlNifEnv *env, ExLazyHTML ex_lazy_html) { |
| 724 | + bool is_document = !ex_lazy_html.resource->document_ref->is_fragment; |
| 725 | + auto nodes = std::vector<lxb_dom_node_t *>(); |
| 726 | + auto inserted_nodes = std::unordered_set<lxb_dom_node_t *>(); |
| 727 | + |
| 728 | + for (auto node : ex_lazy_html.resource->nodes) { |
| 729 | + auto parent = lxb_dom_node_parent(node); |
| 730 | + if (parent != NULL && parent->type == LXB_DOM_NODE_TYPE_ELEMENT && |
| 731 | + (is_document || !lxb_html_tree_node_is(parent, LXB_TAG_HTML))) { |
| 732 | + auto inserted_node = inserted_nodes.find(parent); |
| 733 | + if (inserted_node == inserted_nodes.end()) { |
| 734 | + inserted_nodes.insert(parent); |
| 735 | + nodes.push_back(parent); |
| 736 | + } |
| 737 | + } |
| 738 | + } |
| 739 | + return ExLazyHTML(fine::make_resource<LazyHTML>( |
| 740 | + ex_lazy_html.resource->document_ref, nodes, true)); |
| 741 | +} |
| 742 | +FINE_NIF(parent_node, ERL_NIF_DIRTY_JOB_CPU_BOUND); |
| 743 | + |
| 744 | +std::vector<int64_t> nth_child(ErlNifEnv *env, ExLazyHTML ex_lazy_html) { |
| 745 | + auto values = std::vector<int64_t>(); |
| 746 | + for (auto node : ex_lazy_html.resource->nodes) { |
| 747 | + if (node->type != LXB_DOM_NODE_TYPE_ELEMENT) { |
| 748 | + continue; |
| 749 | + } |
| 750 | + |
| 751 | + auto parent = lxb_dom_node_parent(node); |
| 752 | + if (parent == NULL) { |
| 753 | + // We're at the root, nth_child is 1 |
| 754 | + values.push_back(1); |
| 755 | + } else { |
| 756 | + int64_t i = 1; |
| 757 | + for (auto child = lxb_dom_node_first_child(parent); child != NULL; |
| 758 | + child = lxb_dom_node_next(child)) { |
| 759 | + if (child == node) { |
| 760 | + break; |
| 761 | + } |
| 762 | + if (child->type == LXB_DOM_NODE_TYPE_ELEMENT) { |
| 763 | + i++; |
| 764 | + } |
| 765 | + } |
| 766 | + values.push_back(i); |
| 767 | + } |
| 768 | + } |
| 769 | + |
| 770 | + return values; |
| 771 | +} |
| 772 | +FINE_NIF(nth_child, ERL_NIF_DIRTY_JOB_CPU_BOUND); |
| 773 | + |
717 | 774 | std::string text(ErlNifEnv *env, ExLazyHTML ex_lazy_html) { |
718 | 775 | auto document = ex_lazy_html.resource->document_ref->document; |
719 | 776 |
|
|
0 commit comments