From 2671cda98f36467cca2202ee1e1b7764ed7efcd4 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 25 Mar 2024 11:43:32 +0100 Subject: [PATCH] css: implement :lang match --- src/css/match_test.zig | 20 ++++++++++++++++++++ src/css/selector.zig | 19 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/css/match_test.zig b/src/css/match_test.zig index 8e659043..47fbdb78 100644 --- a/src/css/match_test.zig +++ b/src/css/match_test.zig @@ -215,6 +215,16 @@ test "matchFirst" { .n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } }, .exp = 0, }, + .{ + .q = "p:lang(en)", + .n = .{ .child = &.{ .name = "p", .att = "en-US", .child = &.{ .name = "a" } } }, + .exp = 1, + }, + .{ + .q = "a:lang(en)", + .n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a", .par = &.{ .att = "en-US" } } } }, + .exp = 1, + }, }; for (testcases) |tc| { @@ -379,6 +389,16 @@ test "matchAll" { .n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a" }, .sibling = &.{ .name = "strong" } } }, .exp = 0, }, + .{ + .q = "p:lang(en)", + .n = .{ .child = &.{ .name = "p", .att = "en-US", .child = &.{ .name = "a" } } }, + .exp = 1, + }, + .{ + .q = "a:lang(en)", + .n = .{ .child = &.{ .name = "p", .child = &.{ .name = "a", .par = &.{ .att = "en-US" } } } }, + .exp = 1, + }, }; for (testcases) |tc| { diff --git a/src/css/selector.zig b/src/css/selector.zig index a0c5d4d0..72485cac 100644 --- a/src/css/selector.zig +++ b/src/css/selector.zig @@ -337,11 +337,28 @@ pub const Selector = union(enum) { } }, .pseudo_class_only_child => |v| onlyChildMatch(v, n), - .pseudo_class_lang => return false, + .pseudo_class_lang => |v| langMatch(v, n), .pseudo_element => return false, }; } + fn langMatch(lang: []const u8, n: anytype) anyerror!bool { + if (try n.attr("lang")) |own| { + if (std.mem.eql(u8, own, lang)) return true; + + // check if the lang attr starts with lang+'-' + if (std.mem.startsWith(u8, own, lang)) { + if (own.len > lang.len and own[lang.len] == '-') return true; + } + } + + // if the tag doesn't match, try the parent. + const p = try n.parent(); + if (p == null) return false; + + return langMatch(lang, p.?); + } + // onlyChildMatch implements :only-child // If `ofType` is true, it implements :only-of-type instead. fn onlyChildMatch(of_type: bool, n: anytype) anyerror!bool {