自定义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
相关产品推荐
相关产品推荐

