@gkd-kit/selector
TypeScript icon, indicating that this package has built-in type declarations

0.0.25 • Public • Published

@gkd-kit/selector

一个类似 css 选择器的高级选择器

示例: 选择器路径视图

image

语法

与 css 类似, 一个选择器由 属性选择器 和 关系选择器 交叉组成, 并且开头末尾必须是 属性选择器

示例 div > img 的结构是 属性选择器 关系选择器 属性选择器, 它表示选择一个 img 节点并且它父节点是 div, 这与相同 css 语法语义一致

另外 属性选择器 和 关系选择器 之前必须强制用空格隔开, 也就是 div>img 是非法的, 必须写成 div > img

下面分别介绍 属性选择器 和 关系选择器

属性选择器

它和 css 语法的 属性选择器很相似, 但更强大, 如下是一个示例

@TextView[a=1][b^='2'][c*='a'||d.length>7&&e=false]

@ 表示选择此节点, 一条规则最后属性选择器 @ 生效, 如果没有 @, 取最后一个属性选择器

TextView 代表节点的 name 属性, 而且与 css 相似, * 表示匹配任意属性

由于该选择器主要用于 Android 平台, 节点的 name 都是 java 类如 android.text.TextView 这种形式

为了方便书写规则, TextView 等价 [name='TextView'||name$='.TextView']

[] 内部是一个 逻辑表达式/布尔表达式

逻辑表达式 有操作符 ||&&. 此外 && 优先级更高, 即 [a>1||b>1&&c>1||d>1] 等价于 [a>1||(b>1&&c>1)||d>1]

布尔表达式 由 属性名 操作符 构成

属性名: 正则匹配 ^[_a-zA-Z][a-zA-Z0-9_]*(\.[_a-zA-Z][a-zA-Z0-9_]*)*$ 的字符串, 它类似变量名 a/a.length

操作符: =, !=, >, <, >=, <=, ^=, *=, $=, !^=, !*=, !$=

^= -> startsWith

*= -> contains

$= -> endsWith

!^= -> notStartsWith

!*= -> notContains

!$= -> notEndsWith

~= -> matches (需要 v1.7.0)

!~= -> notMatches (需要 v1.7.0)

可以不用关心的提示: 如果你学过 CSS, 你可能已经注意到 ~= 在 CSS 里的语义(包含单词)与在 GKD 里的语义不一致

附加说明: matches/notMatches 要求 值 必须是合法的 Java/Kotlin 正则表达式, 否则提示语法错误

一些优化: 如果正则表达式满足下面的条件, 选择器将使用内置的简单的函数匹配, 而不是真正地去运行一个正则表达式

  • [text~="(?is)abc.*"] -> startsWith('abc', ignoreCase = true)
  • [text~="(?is).*abc.*"] -> contains('abc', ignoreCase = true)
  • [text~="(?is).*abc"] -> endsWith('abc', ignoreCase = true)
  • [text!~="(?is)abc.*"] -> notStartsWith('abc', ignoreCase = true)
  • [text!~="(?is).*abc.*"] -> notContains('abc', ignoreCase = true)
  • [text!~="(?is).*abc"] -> notEndsWith('abc', ignoreCase = true)

上面的 abc 指代不包含 \^$.?*|+()[]{} 这类特殊字符的任意字符串, 如 ikun 符合, ikun? 不符合

简单来说就是如果你只想忽略大小写去简单匹配或不匹配一些字符, 那么直接使用上面的格式

由于 选择器 需要同时满足 浏览器/Js(审查工具), Android/Java(GKD) 运行, 而这两个平台的正则表达式的底层实现和语法表示略有不同

因此为了在 Js 端实现和 Java 一致的正则表达式规范, 网页审查工具借助 Kotlin Wasm 将正则表达式的 matches 函数接口编译为 wasm 提供给 Js 调用

Kotlin Wasm 需要你的浏览器支持 WasmGC, 也就是版本需要满足下列条件

image

如果你的浏览器版本不满足, 正则表达式将自动回退到 Js 端实现, 以下是在 Js 端使用正则表达式需要注意的地方

比如上面的例子中开头的 (?is) 是 Java 正则表达式的 inline flags 语法, 但实际上 Js 并不支持这样写, 只是选择器内部做了一些兼容让它支持

并且选择器的 Js 端只兼容在开头的 flags, 在内部的 flags 不支持, 此外 Java 和 Js 支持的 flags 也有不同, 某些特殊的表达式也表现也不一致

总之不要使用太过复杂(多复杂我也不知道)的正则表达式, 某些正则表达式有可能在审查工具上匹配, 但是在 GKD 上不匹配

如果你能确保正则表达式在 Js 和 Java/Kotlin 的匹配行为一致, 那就没问题

总之两种情况

  • 高版本浏览器可以使用 完整一致 的正则表达式
  • 低版本浏览器使用 小范围不完整不一致 的正则表达式

如果你的选择器没有使用正则表达式则不用关心此问题


值: 4 种类型, null, boolean, string, int

  • null
  • boolean 使用 true/false
  • int 匹配 [0-9], 仅支持 10 进制自然数
  • string 使用 ' ` " 之一成对包裹, 内部字符转义使用 \
    所有的转义字符示例 \\, \', \", \`, \n, \r, \t, \b, \xFF, \uFFFF
    不支持多行字符, 处于 [0, 0x1F] 的控制字符必须使用转义字符表示

操作符只能使用在对应的类型, 比如 a>'' 类型不匹配, 将提示 非法类型/非法选择器

下面表格中 - 表示类型不匹配

null boolean int string
=
!=
> - - -
< - - -
>= - - -
<= - - -
^= - - -
*= - - -
$= - - -
~= - - -
!^= - - -
!*= - - -
!$= - - -
!~= - - -

关系选择器

用于连接两个属性选择器, 简单示例: div > a, 它表示两个节点之间的关系

关系选择器 由 关系操作符 和 关系表达式 构成

关系操作符 表示查找节点的方向, 有 5 种关系操作符, +, -, >, <, <<

关系表达式 有两种

  • 元组表达式 (a1,a2,a3,a_n), 其中 a1, a2, a3, a_n 是常量有序递增正整数, 示例 (1), (2,3,5)
  • 多项式表达式 (an+b), 其中 a 和 b 是常量整数, 它是元组表达式的另一种表示, 这个元组的数字满足集合 {an+b|an+b>=1,n>=1} 如果集合为空集则表达式非法
    当 a<=0 时, 它具有等价的元组表达式
    示例 (-n+4) 等价于 (1,2,3)
    示例 (-3n+10) 等价于 (1,4,7)
    当 a>0 时, 它表示无限的元组表达式
    示例 (n), 它表示 (1,2,3,...) 一个无限的元组
    示例 (2n-1), 它表示 (1,3,5,...) 一个无限的元组

将 关系操作符 和 关系表达式 连接起来就得到了 关系选择器

A +(a1,a2,a3,a_n) B : A 是 B 的前置兄弟节点, 并且 A.index 满足 B.index-(a_m), 其中 a_m 是元组的任意一个数字

A -(a1,a2,a3,a_n) B : A 是 B 的后置兄弟节点, 并且 A.index 满足 B.index+(a_m)

A >(a1,a2,a3,a_n) B : A 是 B 的祖先节点, 并且 A.depth 满足 B.depth-(a_m), 根节点的 depth=0

A <(a1,a2,a3,a_n) B : A 是 B 的直接子节点, 并且 A.index 满足 a_m-1

A <<(a1,a2,a3,a_n) B : A 是 B 的子孙节点, 并且 A.order 满足 a_m-1, A.order 是深度优先先序遍历的索引

一些表达式的简写

当 a=0 或 b=0 时, 括号可以省略, 比如 A +(3n+0) B -> A +(3n) B -> A +3n B, A +(0n+3) B -> A +(+3) B -> A +3 B

当 a=0 且 b=1 时, an+b 可以省略, 比如 A <(0n+1) B -> A < B, 此外 A + B,A > B 都与等价的 css 语法语义相同

当 a=1 且 b=0 且操作符是 >, 可以进一步简写, 比如 A >(1n+0) B -> A >n B -> A B, 这与等价的 css 语法语义相同

示例

@LinearLayout > TextView[id=`com.byted.pangle:id/tt_item_tv`][text=`不感兴趣`]

首先找到 id=`com.byted.pangle:id/tt_item_tv` 和 text=`不感兴趣` 的 TextView, 并且父节点是 LinearLayout 的节点

此时我们得到两个节点 [LinearLayout, TextView] 根据 @ 知道目标节点是 LinearLayout

实际上它与

TextView[id=`com.byted.pangle:id/tt_item_tv`][text=`不感兴趣`] <n LinearLayout

的目标匹配节点是等价的, 但是在查询算法时间复杂度上, 后者更慢

如下是网页无障碍快照审查工具, 使用它的搜索框的选择器查询可以实时测试编写的选择器

Readme

Keywords

none

Package Sidebar

Install

npm i @gkd-kit/selector

Weekly Downloads

482

Version

0.0.25

License

GPL-v3

Unpacked Size

509 kB

Total Files

9

Last publish

Collaborators

  • lisonge