nvim-treesitter:在 javascript 文件中查询 jsdoc 描述

发布于 2025-01-15 07:02:26 字数 4141 浏览 2 评论 0原文

我正在为我的 neovim 插件 regexplainer 尝试测试机制。这个想法是将包含预期输出的 JSDoc 文档块放在每个查询的顶部。

/** 
 * @group CaptureGroups
 * **0-9** (_>= 0x_)
 * capture group 1:
 *   **0-9**
 */
/\d*(\d)/;

然后,我的测试文件将迭代该文件,从 javascript 树中获取每个正则表达式,以及注释的预期输出。然后我可以在正则表达式上运行我的插件并根据注释检查输出。

我编写了这个函数来查询文档中的注释,并使用下面的正则表达式。

function M.get_regexes_with_descriptions()
  local log = require'regexplainer.utils'.debug

  local query_js = vim.treesitter.query.parse_query('javascript', [[
    (comment) @comment
    (expression_statement
      (regex
        (regex_pattern) @regex))
  ]])

  local query_jsdoc = vim.treesitter.query.parse_query('jsdoc', [[
    (tag_name) @tag_name
    (description) @description
  ]])

  local parser = vim.treesitter.get_parser(0, 'javascript')

  local tree, other = unpack(parser:parse())

  for id, node, metadata in query_js:iter_captures(tree:root(), 0) do
    local name = query_js.captures[id] -- name of the capture in the query
    if node:type() == 'comment' then
      for cid, cnode, cmetadata in node:iter_children() do
        local cname = query_jsdoc.captures[cid] -- name of the capture in the query
        log(get_info_on_capture(cid, cnode, cname, cmetadata))
      end
    end
  end
end

期望这会记录'jsdoc'语法中的标签名称和描述节点,但我实际上什么也没得到

那么我如何“查询”到嵌入的JSDoc部分树的?我尝试了此查询,但在解析查询时出现 query: invalid node type atposition 10 错误:

(comment (description) @desc) @comment
(expression_statement
  (regex
    (regex_pattern) @regex))

相关文件的 TSPlayground 输出如下所示:

comment [0, 0] - [5, 3]
  tag [1, 3] - [4, 12]
    tag_name [1, 3] - [1, 9]
    description [1, 10] - [4, 12]
expression_statement [6, 0] - [6, 10]
  regex [6, 0] - [6, 9]
    pattern: regex_pattern [6, 1] - [6, 8]
      term [6, 1] - [6, 8]
        character_class_escape [6, 1] - [6, 3]
        zero_or_more [6, 3] - [6, 4]
        anonymous_capturing_group [6, 4] - [6, 8]
          pattern [6, 5] - [6, 7]
            term [6, 5] - [6, 7]
              character_class_escape [6, 5] - [6, 7]

编辑:一种解决方法

我开发了此解决方法,但是这有点令人不满意,我更愿意在单个查询中获取评论的内容以及正则表达式

function M.get_regexes_with_descriptions()
  local log = require'regexplainer.utils'.debug
  local parsers = require "nvim-treesitter.parsers"

  local query_js = vim.treesitter.query.parse_query('javascript', [[
    (expression_statement
      (regex
        (regex_pattern) @regex)) @expr
    ]])

  local query_jsdoc = vim.treesitter.query.parse_query('jsdoc', [[
    (tag
      (tag_name) @tag_name
      ((description) @description
      ;; (#eq? @tag_name "example")
      ))
  ]])

  local parser = parsers.get_parser(0)

  local tree = unpack(parser:parse())

  local caps = {}

  for id, node in query_js:iter_captures(tree:root(), 0) do
    local cap = {}
    local name = query_js.captures[id] -- name of the capture in the query
    log(id, name, ts_utils.get_node_text(node))
    if name == 'expr' then
      cap.regex = node:named_child('pattern')
      cap.comment = node:prev_sibling()
      table.insert(caps, cap)
    end

  end

  local results = {}

  for _, cap in ipairs(caps) do
    local result = {}
    result.text = ts_utils.get_node_text(cap.regex)

    local comment_str = table.concat(ts_utils.get_node_text(cap.comment), '\n')
    local jsdoc_parser = vim.treesitter.get_string_parser(comment_str, 'jsdoc')
    local jsdoc_tree = jsdoc_parser:parse()[1]

    for id, ch, metadata in query_jsdoc:iter_captures(jsdoc_tree:root()) do
      if query_jsdoc.captures[id] == 'description' then
        result.description = table.concat(
          vim.tbl_map(function(line)
            return line:gsub('^%s+%*', '')
          end, ts_utils.get_node_text(ch)), '\n')
      end
    end
    table.insert(results, result)
  end

  return results
end

I'm experimenting with a testing regime for my neovim plugin regexplainer. The idea is to put a JSDoc docblock containing the expected output on top of each query.

/** 
 * @group CaptureGroups
 * **0-9** (_>= 0x_)
 * capture group 1:
 *   **0-9**
 */
/\d*(\d)/;

My test file would then iterate through the file, getting each regex from the javascript tree, along with it's expected output from the comment. I could then run my plugin on the regexp and check the output against the comment.

I wrote this function to query the document for comments with regexp following.

function M.get_regexes_with_descriptions()
  local log = require'regexplainer.utils'.debug

  local query_js = vim.treesitter.query.parse_query('javascript', [[
    (comment) @comment
    (expression_statement
      (regex
        (regex_pattern) @regex))
  ]])

  local query_jsdoc = vim.treesitter.query.parse_query('jsdoc', [[
    (tag_name) @tag_name
    (description) @description
  ]])

  local parser = vim.treesitter.get_parser(0, 'javascript')

  local tree, other = unpack(parser:parse())

  for id, node, metadata in query_js:iter_captures(tree:root(), 0) do
    local name = query_js.captures[id] -- name of the capture in the query
    if node:type() == 'comment' then
      for cid, cnode, cmetadata in node:iter_children() do
        local cname = query_jsdoc.captures[cid] -- name of the capture in the query
        log(get_info_on_capture(cid, cnode, cname, cmetadata))
      end
    end
  end
end

I expect this to log the tag name and description nodes from the 'jsdoc' grammar, but i actually get nothing

So how do I "query down" into the embedded JSDoc part of the tree? I tried this query, but got a query: invalid node type at position 10 error when parsing the query:

(comment (description) @desc) @comment
(expression_statement
  (regex
    (regex_pattern) @regex))

The TSPlayground output for the file in question looks like this:

comment [0, 0] - [5, 3]
  tag [1, 3] - [4, 12]
    tag_name [1, 3] - [1, 9]
    description [1, 10] - [4, 12]
expression_statement [6, 0] - [6, 10]
  regex [6, 0] - [6, 9]
    pattern: regex_pattern [6, 1] - [6, 8]
      term [6, 1] - [6, 8]
        character_class_escape [6, 1] - [6, 3]
        zero_or_more [6, 3] - [6, 4]
        anonymous_capturing_group [6, 4] - [6, 8]
          pattern [6, 5] - [6, 7]
            term [6, 5] - [6, 7]
              character_class_escape [6, 5] - [6, 7]

EDIT: A Workaround

I developed this workaround but it's a bit unsatisfying, I'd prefer to get the comment's contents along with the regexp in a single query

function M.get_regexes_with_descriptions()
  local log = require'regexplainer.utils'.debug
  local parsers = require "nvim-treesitter.parsers"

  local query_js = vim.treesitter.query.parse_query('javascript', [[
    (expression_statement
      (regex
        (regex_pattern) @regex)) @expr
    ]])

  local query_jsdoc = vim.treesitter.query.parse_query('jsdoc', [[
    (tag
      (tag_name) @tag_name
      ((description) @description
      ;; (#eq? @tag_name "example")
      ))
  ]])

  local parser = parsers.get_parser(0)

  local tree = unpack(parser:parse())

  local caps = {}

  for id, node in query_js:iter_captures(tree:root(), 0) do
    local cap = {}
    local name = query_js.captures[id] -- name of the capture in the query
    log(id, name, ts_utils.get_node_text(node))
    if name == 'expr' then
      cap.regex = node:named_child('pattern')
      cap.comment = node:prev_sibling()
      table.insert(caps, cap)
    end

  end

  local results = {}

  for _, cap in ipairs(caps) do
    local result = {}
    result.text = ts_utils.get_node_text(cap.regex)

    local comment_str = table.concat(ts_utils.get_node_text(cap.comment), '\n')
    local jsdoc_parser = vim.treesitter.get_string_parser(comment_str, 'jsdoc')
    local jsdoc_tree = jsdoc_parser:parse()[1]

    for id, ch, metadata in query_jsdoc:iter_captures(jsdoc_tree:root()) do
      if query_jsdoc.captures[id] == 'description' then
        result.description = table.concat(
          vim.tbl_map(function(line)
            return line:gsub('^%s+%*', '')
          end, ts_utils.get_node_text(ch)), '\n')
      end
    end
    table.insert(results, result)
  end

  return results
end

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文