gofmt 的文化演变

The Cultural Evolution of gofmt

Robert Griesemer

Google, Inc.

gofmt

初衷

历史

GRINDEF  (Bill Gosper, 1967)           第一个计算行长度
SOAP     (R. Scowen et al, 1969)       简化了晦涩的算法程序
NEATER2  (Ken Conrow, R. Smith, 1970)  PL/1格式器,作为(早期的)纠错工具
cb       (Unix Version 7, 1979)        C程序美化器
indent   (4.2 BSD, 1983)               缩进和格化化C代码
等等
ClangFormat                            C/C++/Objective-C 格式器
Uncrustify                             C, C++, C#, ObjectiveC, D, Java, Pawn and VALA的美化器
等等

事实上

好的格式美化器的问题

格式化Go

尽量保证其简单

一个格化标准搞定所有!

gofmt的基本结构

处理源代码

// Syntax of an if statement.
IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .

// An IfStmt node represents an if statement.
IfStmt struct {
    If   token.Pos // position of "if" keyword
    Init Stmt      // initialization statement; or nil
    Cond Expr      // condition
    Body *BlockStmt
    Else Stmt // else branch; or nil
}

基本的格式化

case *ast.IfStmt:
    p.print(token.IF)
    p.controlClause(false, s.Init, s.Cond, nil)
    p.block(s.Body, 1)
    if s.Else != nil {
        p.print(blank, token.ELSE, blank)
        switch s.Else.(type) {
        case *ast.BlockStmt, *ast.IfStmt:
            p.stmt(s.Else, nextIsRBrace)
        default:
            p.print(token.LBRACE, indent, formfeed)
            p.stmt(s.Else, true)
            p.print(unindent, formfeed, token.RBRACE)
        }
    }

细致的调节

x = a + b
x = a + b*c
if a+b <= d {
if a+b*c <= d {

注释的处理

// A CommentGroup represents a sequence of comments
// with no other tokens and no empty lines between.
//
type CommentGroup struct {
    List []*Comment // len(List) > 0
}

注释在 AST 上的表达

格式化注释

魔鬼就在细节中

格式化单独的注释

func f() {              func() {
 /*                             /*
  * foo                          * foo
  * bar         ==>              * bar
  * bal                          * bal
 */                              */
        if ...                   if  ...
}                       }

对齐

var (                                 var (
        x, y int = 2, 3 // foo                x, y int     = 2, 3 // foo
        z float32 // bar         ==>          z    float32        // bar
        s string // bal                       s    string         // bal
)                                     )

灵活的制表符宽度

通常的制表符把当前的写位置移动到下一个固定的位置.

基本的办法:让制表符宽度更加灵活.

被 Nick Gravgaard 提出于2006

实现在 text/tabwriter 包中.

灵活制表符宽度的展示

综合在一起 (1)

对于固定宽度的字体,处理的很好.

比例大小的字体也可以被编辑器支持,如果这个编辑器可以支持灵活的制表符宽度.

综合在一起 (2)

从宏观上看

gofmt 的应用

gofmt 作为源代码变换工具

gofmt -w -r 'a[i:len(x)] -> a[i:]' *.go

大家的反应

现在,格式已经不是一个问题。

其它语言也在向我们学习

现在,任何语言都被要求带有自动的源代码格式器。

总结

编程文化的演变

至今的收获:应用程序

我们想要:

至今的收获:实现过程

=> 现在的设计使得操作 AST 和保持注释在正确的地方十分困难。

我们想要:

将来的计划

演讲者

Robert Griesemer

Google, Inc.