css: add pseudo class relative match

This commit is contained in:
Pierre Tachoire
2024-03-19 09:25:52 +01:00
parent 75e80a47e6
commit 9c997ec86d
2 changed files with 82 additions and 0 deletions

View File

@@ -176,6 +176,31 @@ test "matchFirst" {
} } } }, } } } },
.exp = 1, .exp = 1,
}, },
.{
.q = ":not(p)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 1,
},
.{
.q = "p:has(a)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 1,
},
.{
.q = "p:has(strong)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 0,
},
.{
.q = "p:haschild(a)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 1,
},
.{
.q = "p:haschild(strong)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 0,
},
}; };
for (testcases) |tc| { for (testcases) |tc| {
@@ -315,6 +340,31 @@ test "matchAll" {
} } } }, } } } },
.exp = 1, .exp = 1,
}, },
.{
.q = ":not(p)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 2,
},
.{
.q = "p:has(a)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 1,
},
.{
.q = "p:has(strong)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 0,
},
.{
.q = "p:haschild(a)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 1,
},
.{
.q = "p:haschild(strong)",
.n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } },
.exp = 0,
},
}; };
for (testcases) |tc| { for (testcases) |tc| {

View File

@@ -143,6 +143,7 @@ pub const PseudoClass = enum {
pub const Selector = union(enum) { pub const Selector = union(enum) {
pub const Error = error{ pub const Error = error{
UnknownCombinedCombinator, UnknownCombinedCombinator,
UnsupportedRelativePseudoClass,
}; };
compound: struct { compound: struct {
@@ -289,10 +290,41 @@ pub const Selector = union(enum) {
}; };
}, },
.never_match => return false, .never_match => return false,
.pseudo_class_relative => |v| {
if (!n.isElement()) return false;
return switch (v.pseudo_class) {
.not => !try v.match.match(n),
.has => try hasDescendantMatch(v.match, n),
.haschild => try hasChildMatch(v.match, n),
else => Error.UnsupportedRelativePseudoClass,
};
},
else => false, else => false,
}; };
} }
fn hasDescendantMatch(s: *const Selector, n: anytype) anyerror!bool {
var c = try n.firstChild();
while (c != null) {
if (try s.match(c.?)) return true;
if (c.?.isElement() and try hasDescendantMatch(s, c.?)) return true;
c = try c.?.nextSibling();
}
return false;
}
fn hasChildMatch(s: *const Selector, n: anytype) anyerror!bool {
var c = try n.firstChild();
while (c != null) {
if (try s.match(c.?)) return true;
c = try c.?.nextSibling();
}
return false;
}
pub fn deinit(sel: Selector, alloc: std.mem.Allocator) void { pub fn deinit(sel: Selector, alloc: std.mem.Allocator) void {
switch (sel) { switch (sel) {
.group => |v| { .group => |v| {