You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义lintr章节标题检查器行号识别错误,如何修正?

问题:自定义R Linter错误标记行号

我要实现一个自定义linter,识别代码中的章节标题——这类标题由注释符、标题文本和至少一个短横线-组成,要求整行(含空白)恰好80字符。不符合的标题要生成lint提示。

我写的linter代码如下:

section_header_linter <- function() {
  lintr::Linter(function(source_expression) {
    # extract file lines
    lines <- source_expression$lines

    # early exit for empty files
    if (length(lines) == 0L) {
      return(list())
    }

    lints <- list()

    # iterate over each line
    for (i in seq_along(lines)) {
      line <- lines[[i]]

      # skip non-comment lines
      if (!grepl("^\\\\s*# ", line)) {
        next
      }

      # match section header pattern
      m <- regexec("^(\\\\s*)# ([^-]+) (-+)$", line)
      reg <- regmatches(line, m)[[1L]]

      # ignore non-section comments
      if (length(reg) == 0L) {
        next
      }

      # compute total line width
      width <- nchar(line, type = "chars")

      if (width != 80L) {
        lints[[length(lints) + 1L]] <- lintr::Lint(
          filename = source_expression$filename
          , line_number = i
          , column_number = 1L
          , type = "style"
          , message = "Section headers must use full 80 characters"
        )
      }
    }

    lints
  })
}


custom_linters <- function() {
  lintr::linters_with_defaults(
    line_length = lintr::line_length_linter(80L)
    , object_name = lintr::object_name_linter(styles = "snake_case")
    , trailing_blank_lines_linter = NULL
    , section_header = section_header_linter()
  )
}

测试文件test-file.R内容:

# Test section header ------------------------------

# this section defines my function

#' Preliminary docstring for my_func
#' @param x a number
#' @returns x squared
#' @keywords internal
my_func <- function(x) {
  x ** 2
}

# Test Section 2 ----

运行lintr::lint("test-file.R", linters = custom_linters())后,能识别出两个不符合要求的章节标题,但错误地将两者都标记为第1行,实际应为第1行和第13行。


解决方案

问题出在获取文件行的方式:你使用了source_expression$lines,这个是lintr解析后的代码行(会过滤空行或非代码行),其索引与原始文件的行号不对应。正确的做法是使用source_expression$file_lines,它保存了原始文件的所有行(包括空行、注释行),循环的索引i直接对应原始文件的行号。

同时需要修正正则表达式的转义错误:R字符串中表示正则的\s需要写两个反斜杠,原代码中的^\\\\s*# 会被解析为匹配字面量\s,而非空白字符,修正后才能正确匹配注释行。

修改后的完整section_header_linter函数:

section_header_linter <- function() {
  lintr::Linter(function(source_expression) {
    # 读取原始文件所有行(包含空行)
    lines <- source_expression$file_lines

    # 空文件直接返回
    if (length(lines) == 0L) {
      return(list())
    }

    lints <- list()

    # 遍历每一行,i对应原始文件行号
    for (i in seq_along(lines)) {
      line <- lines[[i]]

      # 跳过非注释行(匹配带可选前置空白的# )
      if (!grepl("^\\s*# ", line)) {
        next
      }

      # 匹配章节标题模式:可选空白、#、标题文本、短横线
      m <- regexec("^(\\s*)# ([^-]+) (-+)$", line)
      reg <- regmatches(line, m)[[1L]]

      # 跳过非章节标题的注释
      if (length(reg) == 0L) {
        next
      }

      # 计算行总长度
      width <- nchar(line, type = "chars")

      if (width != 80L) {
        lints[[length(lints) + 1L]] <- lintr::Lint(
          filename = source_expression$filename
          , line_number = i
          , column_number = 1L
          , type = "style"
          , message = "Section headers must use full 80 characters"
        )
      }
    }

    lints
  })
}

修改后重新运行lint命令,就能正确显示两个问题标题的原始行号(第1行和第13行)。


内容的提问来源于stack exchange,提问作者rossdrucker9

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 15:05:54