8

我正在为 Sublime/TextMate 语言文件中的某些语法高亮处理正则表达式,它要求我在非自结束 html 标记上“开始”,并在相应的结束标记上结束:

  • 开始:(<)([a-zA-Z0-9:.]+)[^/>]*(>)

  • 结尾: (</)(\2)([^>]*>)

到目前为止,一切顺利,我能够捕获标签名称,并且它匹配能够为标签之间的区域应用适当的模式。

jsx-tag-area:
    begin: (<)([a-zA-Z0-9:.]+)[^/>]*>
    beginCaptures:
      '1': {name: punctuation.definition.tag.begin.jsx}
      '2': {name: entity.name.tag.jsx}
    end: (</)(\2)([^>]*>)
    endCaptures:
      '1': {name: punctuation.definition.tag.begin.jsx}
      '2': {name: entity.name.tag.jsx}
      '3': {name: punctuation.definition.tag.end.jsx}
    name: jsx.tag-area.jsx
    patterns:
    - {include: '#jsx'}
    - {include: '#jsx-evaluated-code'}

现在,我还希望能够在开始标记中捕获零个或多个 html 属性,以便能够突出显示它们。

所以如果标签是<div attr="Something" data-attr="test" data-foo>

它将能够匹配attr, data-attr, and data-foo, 以及 the <anddiv

类似的东西(这很粗糙):

(<)([a-zA-Z0-9:.]+)(?:\s(?:([0-9a-zA-Z_-]*=?))\s?)*)[^/>]*(>)

它不需要完美,它只是为了一些语法突出显示,但我很难弄清楚如何在标签中实现多个捕获组,我是否应该使用环视等,或者这是否是甚至可以用一个表达式。

编辑:这里有关于具体案例/问题的更多细节 - https://github.com/reactjs/sublime-react/issues/18

4

4 回答 4

1

我可能会找到一个可能的解决方案。

这并不完美,因为正如@skamazin 在评论中所说,如果您尝试捕获任意数量的属性,您将不得不重复与属性匹配的模式多次,以限制您允许的属性数量。

正则表达式非常可怕,但它可能对您的目标有用。也许可以稍微简化一下,或者你可能需要调整一些东西

只有一个属性是这样的:

(<)([a-zA-Z0-9:.]+)(?:(?: ((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)(?: |>))

演示

对于更多属性,您需要根据需要多次添加:

(?:(?:((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)(?: |>))?

因此,例如,如果您想允许最多 3 个属性,您的正则表达式将如下所示:

(<)([a-zA-Z0-9:.]+)(?:(?: ((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)(?: |>))(?:(?:((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)?(?: |>))?(?:(?:((?<= )[^ ]+?(?==| |>)))(?:=[^ >]+)?(?: |>))?

演示

告诉我它是否适合您以及您是否需要更多详细信息。

于 2014-09-10T09:28:50.953 回答
0

单独的正则表达式似乎还不够好,但是由于您在这里使用的是 sublime 的脚本,因此有一种方法可以简化代码和流程。请记住,我是 vim 用户,不熟悉 sublime 的内部结构——另外,我通常使用 javascript 正则表达式,而不是 PCRE(这似乎是 sublime 使用的格式,或者最接近的格式)。

思路如下:

  • 使用正则表达式获取标签、属性(在字符串中)和标签的内容
  • 如有必要,使用捕获组进行进一步处理和匹配

在这种情况下,我做了这个正则表达式:

<([a-z]+)\ ?([a-z]+=\".*?\"\ ?)?>([.\n\sa-z]*)(<\/\1>)?

它首先找到一个开始标签,为标签名称创建一个控制组,如果它找到一个空格,它会继续,匹配大部分属性(在\"...\"我可以用来\"[^\"]*?\"匹配非引号字符的模式内,但我故意匹配贪婪地匹配任何字符直到结束引号 -​​ 这是为了匹配大部分属性,我们可以稍后处理),匹配标签之间的任何文本,然后最终匹配结束标签。

它创建 4 个捕获组:

  1. 标签名
  2. 属性字符串
  3. 标签内容
  4. 结束标签

正如你在这个演示中看到的那样,如果没有结束标签,我们就不会得到它的捕获组,属性也是如此,但我们总是会得到一个标签内容的捕获组。这通常是一个问题(因为我们不能假设捕获的特征将在同一个组中)但它不在这里,因为在我们没有属性和内容的冲突情况下,因此第二个捕获组是空的,我们可以假设它意味着没有属性,并且缺少第三组不言自明。如果没有要解析的内容,则不会错误地解析任何内容。

现在要解析属性,我们可以简单地使用:

([a-z]+=\"[^\"]*?\")

演示在这里。这给了我们确切的属性。如果 sublime 的脚本能让你走到这一步,它肯定会让你在必要时进行进一步的处理。你当然可以总是使用这样的东西:

(([a-z]+)=\"([^\"]*?)\")

这将为整个属性及其名称和值分别提供捕获组。

使用这种方法,您应该能够很好地解析标签,以便在 2-3 遍中突出显示,并将突出显示的内容发送到您想要的任何荧光笔(或者只是以您想要的任何花哨的方式将其突出显示为纯文本)。

于 2014-09-11T13:43:31.487 回答
0

我不熟悉 sublimetext 或 react-jsx,但这对我来说听起来像是“Regex 是您的工具,而不是您的解决方案”的情况。

使用正则表达式作为工具的解决方案类似于这个 JsFiddle (请注意,正则表达式由于像&gt;for>等 html 实体而被稍微混淆)

进行实际替换的代码:

blabla.replace(/(&lt;!--(?:[^-]|-(?!-&gt;))*--&gt;)|(&lt;(?:(?!&gt;).)+&gt;)|(\{[^\}]+\})/g, function(m, c, t, a) {
    if (c!=undefined)
        return '<span class="comment">' + c + '</span>';
    if (t!=undefined)
        return '<span class="tag">' + t.replace(/ [a-z_-]+=?/ig, '<span class="attr">$&</span>') + '</span>';
    if (a!=undefined)
        return a.replace(/'[^']+'/g, '<span class="quoted">$&</span>');
});

因此,在这里,我首先按照适用于带有 accolade-blocks 的 HTML 用例的通用模式来捕获单独类型的组。这些捕获被馈送到一个函数,该函数确定我们正在处理的捕获类型,并进一步用它自己的.replace()语句替换此捕获中的子组。

真的没有其他可靠的方法可以做到这一点。我无法告诉您这如何转化为您的环境,但也许这会有所帮助。

于 2014-09-10T10:34:05.927 回答
0

您自己的正则表达式对回答您的问题很有帮助。

这似乎对我很有效:

/(:?<|<\/)([a-zA-Z0-9:.]+)(?:\s(?:([0-9a-zA-Z_-]*=?))\s?)*[^/>]*(:?>|\/>)/g

/开头和结尾的只是正则表达式通常需要的包装器。此外,g末尾的 代表全局,因此它也适用于重复。

我用来找出我的正则表达式做错了什么的好工具是:http ://regexr.com/

希望这可以帮助!

于 2016-08-08T18:42:33.130 回答