The Cultural Evolution of gofmt

gofmt 的文化演变

Robert Griesemer

Google, Inc.

gofmt

Original motivation

History

GRINDEF  (Bill Gosper, 1967)           first to measure line length
SOAP     (R. Scowen et al, 1969)       Simplifies Obscure Algol Programs
NEATER2  (Ken Conrow, R. Smith, 1970)  PL/1 reformatter, use as (early) error detection tool
cb       (Unix Version 7, 1979)        C program beautifier
indent   (4.2 BSD, 1983)               indent and format C source code
etc.
ClangFormat                            C/C++/Objective-C formatter
Uncrustify                             beautifier for C, C++, C#, ObjectiveC, D, Java, Pawn and VALA
etc.

Reality check

The problem with pretty printers

Formatting Go

Keep it as simple as possible

One formatting style to rule them all!

Basic structure of gofmt

Parsing source code

// 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
}

Basic formatting

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)
        }
    }

Fine tuning

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

Handling of comments

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

Representation of comments in the AST

Formatting with comments

Devil is in the details

Formatting individual comments

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

Alignment

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
)                                     )

Elastic tabstops

Regular tabs (\t) advance writing position to fixed tab stops.

Basic idea: Make tab stops elastic.

Proposed by Nick Gravgaard, 2006

Implemented by text/tabwriter package.

Elastic tabstops illustrated

Putting it all together (1)

Works well for fixed-width fonts.

Proportional fonts could be handled by an editor supporting elastic tab stops.

Putting it all together (2)

The big picture

gofmt applications

gofmt as source code transformer

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

Reactions

gofmt is everybody's favorite.

Formatting has become a non-issue.

Others are starting to take note

Automatic source code formatting is becoming a requirement
for any kind of language.

Conclusions

Evolution in programming culture

Lessons learned: Application

Want:

Lessons learned: Implementation

=> Current design makes it extremely hard to manipulate AST
and maintain comments in right places.

Want:

Going forward

演讲者

Robert Griesemer

Google, Inc.