gofmt 保留换行符

gofmt preserving of newlines

使用 gofmt 格式化 go 源代码时,它会保留换行符,以便您可以将项目组合在一起。我对这实际上是如何实现的很感兴趣。我尝试查看 github 存储库 golang/go 中的源代码,但无法立即找到它。如果你看 https://github.com/golang/go/blob/master/src/go/printer/printer.go#L979:

// intersperse extra newlines if present in the source

打印机如何知道源中存在那些额外的换行符? 有人可以指出我正确的方向吗?

internal.go package `第 40-41 行中有这样的内容:

// Insert using a ;, not a newline, so that the line numbers

// psrc 匹配 src.

然后是这个:

psrc := append([]byte("package p;"), src...)
file, err = parser.ParseFile(fset, filename, psrc, parserMode)

这是您要找的吗?如果我答对了你的问题。

与大多数词法分析器不同,go 词法分析器包括经常被编译器的词法分析器删除或省略的标记。词法分析器发出的标记流包括注释标记、隐含的分号、换行符、换页符 (FF) 和其他空格。这允许使用相同的标记流来重新生成源代码,并创建编译器所需的结构,例如 AST。

gofmt 在 AST 上工作。当您查看 https://golang.org/pkg/go/ast 时,您会看到每个节点都有函数 Pos() 和 End() ,它们分别 return 开始和结束的 token.Pos 。这些本质上是源文件中的偏移量,因此对 numbers/breaks.

行一无所知

但是当与 token.Fileset 组合时,这样的 token.Pos 可以转换为包含行号的 token.Position。 gofmt 在函数 printer.go:lineFor().

中执行此操作

换行符的实际插入是在 nodes.go:linebreak() 中完成的。 linebreak() 的第一个参数是通过在相应 token.Pos 上调用上述 lineFor() 获得的行号。该函数计算此行号与打印的最后一个标记的行号之间的差异(在 struct printer 的 pos 字段中跟踪)。这告诉它现在要打印的标记是否与前一个标记在输入文件中的同一行。如果不是,则意味着程序员在原始源代码中包含了一个或多个换行符,而 linebreak() 将最多输出 1 个空行。虽然它可以保留所有输入换行符,但 gofmt 的策略是将一系列空行压缩到只有 1 个空行。

如果您问这个问题的原因是您想要自定义 gofmt,请查看 https://github.com/mbenkmann/goformat