fmt.mx raw

   1  package main
   2  
   3  import (
   4  	"os"
   5  )
   6  
   7  const maxLine = int32(80)
   8  
   9  type Printer struct {
  10  	toks      []FTok
  11  	out       []byte
  12  	pos       int32
  13  	indent    int32
  14  	col       int32
  15  	atBOL     bool
  16  	errs      []string
  17  	caseDepth int32
  18  }
  19  
  20  func newPrinter(toks []FTok) (p *Printer) {
  21  	p = &Printer{
  22  		toks:   toks,
  23  		out:    []byte{:0:4096},
  24  		errs:   []string{:0:4},
  25  		pos:    0,
  26  		indent: 0,
  27  		col:    0,
  28  		atBOL:  true,
  29  	}
  30  	return
  31  }
  32  
  33  func (p *Printer) errorf(line uint32, msg string) {
  34  	s := int32(line).String() | ":" | msg
  35  	p.errs = append(p.errs, s)
  36  }
  37  
  38  func (p *Printer) emit(s string) {
  39  	p.out = append(p.out, s...)
  40  	for i := int32(0); i < int32(len(s)); i++ {
  41  		if s[i] == '\n' {
  42  			p.col = 0
  43  			p.atBOL = true
  44  		} else if s[i] == '\t' {
  45  			p.col += 4 - (p.col % 4)
  46  		} else {
  47  			p.col++
  48  		}
  49  	}
  50  }
  51  
  52  func (p *Printer) newline() { p.emit("\n"); p.atBOL = true }
  53  
  54  func (p *Printer) writeIndent() {
  55  	for i := int32(0); i < p.indent; i++ { p.emit("\t") }
  56  	p.atBOL = false
  57  }
  58  
  59  func (p *Printer) space() { p.emit(" ") }
  60  
  61  func (p *Printer) cur() (t *FTok) { t = &p.toks[p.pos]; return }
  62  
  63  func (p *Printer) peek(off int32) (t *FTok) {
  64  	idx := p.pos + off
  65  	if idx < 0 { idx = 0 }
  66  	if idx >= int32(len(p.toks)) { idx = int32(len(p.toks)) - 1 }
  67  	t = &p.toks[idx]
  68  	return
  69  }
  70  
  71  func (p *Printer) advance() {
  72  	if p.pos < int32(len(p.toks))-1 { p.pos++ }
  73  }
  74  
  75  // --- matching ---
  76  
  77  func matchBrace(toks []FTok, from int32) (end int32) {
  78  	depth := int32(1)
  79  	i := from + 1
  80  	for i < int32(len(toks)) {
  81  		tt := toks[i].tok
  82  		if tt == TokLbrace { depth++ } else if tt == TokRbrace {
  83  			depth--
  84  			if depth == 0 { end = i; return }
  85  		} else if tt == TokEOF { end = i; return }
  86  		i++
  87  	}
  88  	end = int32(len(toks)) - 1
  89  	return
  90  }
  91  
  92  func matchBrack(toks []FTok, from int32) (end int32) {
  93  	depth := int32(1)
  94  	i := from + 1
  95  	for i < int32(len(toks)) {
  96  		tt := toks[i].tok
  97  		if tt == TokLbrack { depth++ } else if tt == TokRbrack {
  98  			depth--
  99  			if depth == 0 { end = i; return }
 100  		} else if tt == TokEOF { end = i; return }
 101  		i++
 102  	}
 103  	end = int32(len(toks)) - 1
 104  	return
 105  }
 106  
 107  func matchParen(toks []FTok, from int32) (end int32) {
 108  	depth := int32(1)
 109  	i := from + 1
 110  	for i < int32(len(toks)) {
 111  		tt := toks[i].tok
 112  		if tt == TokLparen { depth++ } else if tt == TokRparen {
 113  			depth--
 114  			if depth == 0 { end = i; return }
 115  		} else if tt == TokEOF { end = i; return }
 116  		i++
 117  	}
 118  	end = int32(len(toks)) - 1
 119  	return
 120  }
 121  
 122  // --- width measurement ---
 123  
 124  func singleLineWidth(toks []FTok, from int32, to int32) (w int32) {
 125  	w = 0
 126  	for i := from; i <= to; i++ {
 127  		t := &toks[i]
 128  		if t.tok == TokComment { w = maxLine + 1; return }
 129  		if t.tok == TokSemi { w += 2; continue }
 130  		txt := tokText(t)
 131  		w += int32(len(txt))
 132  		if i < to {
 133  			next := &toks[i+1]
 134  			if needSpace(t, next) { w++ }
 135  		}
 136  	}
 137  	return
 138  }
 139  
 140  func parenFitsLine(toks []FTok, openIdx int32, startCol int32) (yes bool) {
 141  	end := matchParen(toks, openIdx)
 142  	w := int32(1)
 143  	for i := openIdx + 1; i <= end; i++ {
 144  		t := &toks[i]
 145  		if t.tok == TokSemi { w += 2; continue }
 146  		if t.tok == TokComment { return }
 147  		w += int32(len(tokText(t)))
 148  		if i < end {
 149  			next := &toks[i+1]
 150  			if needSpace(t, next) { w++ }
 151  		}
 152  	}
 153  	yes = startCol + w <= maxLine
 154  	return
 155  }
 156  
 157  func hasInnerBrace(toks []FTok, from int32, to int32) (yes bool) {
 158  	for i := from; i <= to; i++ {
 159  		if toks[i].tok == TokLbrace { yes = true; return }
 160  	}
 161  	return
 162  }
 163  
 164  // --- binary/unary disambiguation ---
 165  
 166  func isBinaryMulStar(toks []FTok, idx int32) (yes bool) {
 167  	if idx <= 0 { return }
 168  	prev := toks[idx-1].tok
 169  	if prev != TokName && prev != TokLit && prev != TokRparen &&
 170  		prev != TokRbrack && prev != TokRbrace { return }
 171  	if idx < 2 { yes = true; return }
 172  	pp := toks[idx-2].tok
 173  	if pp == TokVar || pp == TokConst || pp == TokType || pp == TokComma ||
 174  		pp == TokFunc || pp == TokChan || pp == TokMap || pp == TokReturn ||
 175  		pp == TokAssign || pp == TokDefine || pp == TokArrow ||
 176  		pp == TokColon || pp == TokLparen || pp == TokLbrack ||
 177  		pp == TokCase || pp == TokSemi || pp == TokLbrace {
 178  		return
 179  	}
 180  	yes = true
 181  	return
 182  }
 183  
 184  func isBinaryArith(toks []FTok, idx int32) (yes bool) {
 185  	if idx <= 0 { return }
 186  	prev := toks[idx-1].tok
 187  	if prev == TokName || prev == TokLit || prev == TokRparen ||
 188  		prev == TokRbrack || prev == TokRbrace {
 189  		yes = true
 190  	}
 191  	return
 192  }
 193  
 194  // --- brace spacing ---
 195  
 196  func braceNeedsSpace(toks []FTok, braceIdx int32) (yes bool) {
 197  	if braceIdx < 1 { return }
 198  	prev := toks[braceIdx-1].tok
 199  	if prev != TokName && prev != TokRparen { return }
 200  	if prev == TokRparen { yes = true; return }
 201  	for i := braceIdx - 1; i >= 0; i-- {
 202  		tt := toks[i].tok
 203  		if tt == TokFunc || tt == TokIf || tt == TokFor || tt == TokElse ||
 204  			tt == TokSelect || tt == TokSwitch {
 205  			yes = true; return
 206  		}
 207  		if tt == TokSemi || tt == TokLbrace || tt == TokRbrace || tt == TokEOF { return }
 208  	}
 209  	return
 210  }
 211  
 212  func isSliceColonCtx(toks []FTok, idx int32) (yes bool) {
 213  	bdepth := int32(0)
 214  	pdepth := int32(0)
 215  	for i := idx - 1; i >= 0; i-- {
 216  		tt := toks[i].tok
 217  		if tt == TokRbrack { bdepth++ } else if tt == TokLbrack {
 218  			if bdepth == 0 { yes = true; return }
 219  			bdepth--
 220  		} else if tt == TokRbrace { pdepth++ } else if tt == TokLbrace {
 221  			if pdepth == 0 { yes = true; return }
 222  			pdepth--
 223  		} else if tt == TokSemi { return }
 224  	}
 225  	return
 226  }
 227  
 228  // --- spacing ---
 229  
 230  func needSpaceCtx(toks []FTok, curIdx int32, prev *FTok, cur *FTok) (yes bool) {
 231  	if cur.tok == TokLbrack && prev.tok == TokName && curIdx+1 < int32(len(toks)) {
 232  		next := toks[curIdx+1].tok
 233  		if next == TokRbrack {
 234  			rbIdx := curIdx + 2
 235  			if rbIdx < int32(len(toks)) {
 236  				after := toks[rbIdx].tok
 237  				if after == TokName || after == TokStar || after == TokLbrack ||
 238  					after == TokFunc || after == TokMap || after == TokChan ||
 239  					after == TokStruct || after == TokInterface {
 240  					yes = true; return
 241  				}
 242  			}
 243  		}
 244  		if next == TokLit {
 245  			end := matchBrack(toks, curIdx)
 246  			if end+1 < int32(len(toks)) {
 247  				after := toks[end+1].tok
 248  				if after == TokName || after == TokStar || after == TokLbrack ||
 249  					after == TokFunc || after == TokMap || after == TokChan ||
 250  					after == TokStruct || after == TokInterface {
 251  					yes = true; return
 252  				}
 253  			}
 254  		}
 255  	}
 256  	if cur.tok == TokLbrace && braceNeedsSpace(toks, curIdx) {
 257  		yes = true; return
 258  	}
 259  	if prev.tok == TokStar && isBinaryMulStar(toks, curIdx-1) {
 260  		yes = true; return
 261  	}
 262  	if cur.tok == TokStar && isBinaryMulStar(toks, curIdx) {
 263  		yes = true; return
 264  	}
 265  	if prev.tok == TokOp && (prev.lit == "-" || prev.lit == "+" || prev.lit == "^") {
 266  		if !isBinaryArith(toks, curIdx-1) { return }
 267  	}
 268  	if prev.tok == TokOp && (prev.lit == "&" || prev.lit == "!") {
 269  		if !isBinaryArith(toks, curIdx-1) { return }
 270  	}
 271  	if cur.tok == TokColon && isSliceColonCtx(toks, curIdx) { return }
 272  	if prev.tok == TokColon && isSliceColonCtx(toks, curIdx-1) { return }
 273  	yes = needSpaceBefore(prev, cur)
 274  	return
 275  }
 276  
 277  func needSpaceBefore(prev *FTok, cur *FTok) (yes bool) {
 278  	if cur.tok == TokLbrace {
 279  		pt := prev.tok
 280  		if pt == TokStruct || pt == TokInterface || pt == TokRbrack ||
 281  			pt == TokName || pt == TokStar { return }
 282  		yes = true; return
 283  	}
 284  	yes = needSpace(prev, cur)
 285  	return
 286  }
 287  
 288  func needSpace(prev *FTok, next *FTok) (yes bool) {
 289  	pt := prev.tok
 290  	nt := next.tok
 291  
 292  	if pt == TokComment || nt == TokComment { yes = true; return }
 293  	if nt == TokIncOp { return }
 294  
 295  	if nt == TokLparen {
 296  		if pt == TokName || pt == TokRparen || pt == TokRbrack { return }
 297  		yes = true; return
 298  	}
 299  	if nt == TokLbrack {
 300  		if pt == TokName || pt == TokRparen || pt == TokRbrack || pt == TokLit { return }
 301  	}
 302  	if pt == TokRbrack {
 303  		if nt == TokName || nt == TokStar || nt == TokLbrack || nt == TokMap ||
 304  			nt == TokStruct || nt == TokInterface || nt == TokFunc || nt == TokChan { return }
 305  	}
 306  	if pt == TokLparen || pt == TokLbrack || nt == TokRparen || nt == TokRbrack { return }
 307  	if nt == TokComma || nt == TokSemi || nt == TokColon { return }
 308  	if pt == TokComma || pt == TokSemi { yes = true; return }
 309  	if pt == TokColon { yes = true; return }
 310  	if nt == TokDot || pt == TokDot { return }
 311  
 312  	if pt == TokStar {
 313  		if nt == TokName || nt == TokLparen || nt == TokStar || nt == TokLbrack { return }
 314  		yes = true; return
 315  	}
 316  	if nt == TokStar {
 317  		if pt == TokLparen || pt == TokComma || pt == TokLbrack ||
 318  			pt == TokFunc || pt == TokChan || pt == TokMap ||
 319  			pt == TokArrow || pt == TokAssign || pt == TokDefine ||
 320  			pt == TokReturn || pt == TokCase || pt == TokOp ||
 321  			pt == TokColon || pt == TokDotDotDot { return }
 322  		yes = true; return
 323  	}
 324  
 325  	if pt == TokOp && prev.lit == "!" { return }
 326  
 327  	if nt == TokLbrace {
 328  		pt2 := prev.tok
 329  		if pt2 == TokStruct || pt2 == TokInterface || pt2 == TokRbrack || pt2 == TokName { return }
 330  		yes = true; return
 331  	}
 332  	if pt == TokLbrace || nt == TokRbrace { yes = true; return }
 333  	if pt == TokOp || pt == TokAssign || pt == TokDefine || pt == TokAssignOp ||
 334  		pt == TokArrow { yes = true; return }
 335  	if nt == TokOp || nt == TokAssign || nt == TokDefine || nt == TokAssignOp ||
 336  		nt == TokArrow { yes = true; return }
 337  	if pt == TokIncOp { yes = true; return }
 338  	if pt == TokDotDotDot || nt == TokDotDotDot { return }
 339  
 340  	yes = true
 341  	return
 342  }
 343  
 344  // --- predicates ---
 345  
 346  func isEmptyBrace(toks []FTok, bracePos int32) (yes bool) {
 347  	end := matchBrace(toks, bracePos)
 348  	for i := bracePos + 1; i < end; i++ {
 349  		if toks[i].tok != TokSemi { return }
 350  	}
 351  	yes = true; return
 352  }
 353  
 354  func fitsOneLine(toks []FTok, bracePos int32, preambleCol int32) (yes bool) {
 355  	end := matchBrace(toks, bracePos)
 356  	innerFrom := bracePos + 1
 357  	innerTo := end - 1
 358  	if innerTo < innerFrom { yes = true; return }
 359  	for i := innerFrom; i <= innerTo; i++ {
 360  		if toks[i].tok == TokComment { return }
 361  	}
 362  	if hasInnerBrace(toks, innerFrom, innerTo) { return }
 363  	w := singleLineWidth(toks, innerFrom, innerTo)
 364  	total := preambleCol + 2 + w + 2
 365  	yes = total <= maxLine
 366  	return
 367  }
 368  
 369  func isGroupKeyword(tok Token) (yes bool) {
 370  	if tok == TokImport || tok == TokConst || tok == TokVar || tok == TokType { yes = true }
 371  	return
 372  }
 373  
 374  func isImportContext(p *Printer) (yes bool) {
 375  	for i := p.pos - 1; i >= 0; i-- {
 376  		t := &p.toks[i]
 377  		if t.tok == TokImport { yes = true; return }
 378  		if t.tok == TokSemi || t.tok == TokRbrace || t.tok == TokLbrace { return }
 379  	}
 380  	return
 381  }
 382  
 383  func isLabel(toks []FTok, colonIdx int32) (yes bool) {
 384  	if colonIdx < 1 { return }
 385  	if toks[colonIdx-1].tok != TokName { return }
 386  	if colonIdx < 2 { yes = true; return }
 387  	pp := toks[colonIdx-2].tok
 388  	if pp == TokSemi || pp == TokLbrace || pp == TokColon { yes = true }
 389  	return
 390  }
 391  
 392  func isSliceLitBrace(toks []FTok, bracePos int32) (yes bool) {
 393  	end := matchBrace(toks, bracePos)
 394  	for i := bracePos + 1; i < end; i++ {
 395  		if toks[i].tok == TokSemi { continue }
 396  		if toks[i].tok == TokColon { yes = true }
 397  		return
 398  	}
 399  	return
 400  }
 401  
 402  // --- bare return type diagnostic ---
 403  
 404  func checkBareReturns(toks []FTok, p *Printer) {
 405  	i := int32(0)
 406  	for i < int32(len(toks)) {
 407  		if toks[i].tok == TokFunc {
 408  			checkFuncSig(toks, i, p)
 409  		}
 410  		i++
 411  	}
 412  }
 413  
 414  func checkFuncSig(toks []FTok, funcIdx int32, p *Printer) {
 415  	i := funcIdx + 1
 416  	n := int32(len(toks))
 417  	if i >= n { return }
 418  	if toks[i].tok == TokLparen {
 419  		i = matchParen(toks, i) + 1
 420  	}
 421  	if i >= n { return }
 422  	if toks[i].tok != TokName { return }
 423  	i++
 424  	if i >= n { return }
 425  	if toks[i].tok != TokLparen { return }
 426  	i = matchParen(toks, i) + 1
 427  	if i >= n { return }
 428  	tt := toks[i].tok
 429  	if tt == TokLbrace || tt == TokSemi { return }
 430  	if tt == TokLparen { return }
 431  	p.errorf(toks[funcIdx].line, " bare return type (wrap in parens with name)")
 432  }
 433  
 434  // --- line-break after comma at 80+ ---
 435  
 436  func (p *Printer) emitSpaceOrBreak(prev *FTok) {
 437  	if prev.tok == TokComma && p.col >= maxLine {
 438  		p.newline()
 439  		p.writeIndent()
 440  	} else {
 441  		p.space()
 442  	}
 443  }
 444  
 445  // --- paren group wrapping ---
 446  
 447  func (p *Printer) formatParenGroup() {
 448  	end := matchParen(p.toks, p.pos)
 449  	if parenFitsLine(p.toks, p.pos, p.col) {
 450  		p.emit("(")
 451  		p.advance()
 452  		for p.pos < end {
 453  			t := p.cur()
 454  			if t.tok == TokSemi { p.advance(); continue }
 455  			prev := p.peek(-1)
 456  			if needSpaceCtx(p.toks, p.pos, prev, t) { p.space() }
 457  			p.emit(tokText(t))
 458  			p.advance()
 459  		}
 460  		p.emit(")")
 461  		p.advance()
 462  		return
 463  	}
 464  	p.emit("(")
 465  	p.advance()
 466  	p.indent++
 467  
 468  	needNL2 := true
 469  	for p.pos < end {
 470  		t := p.cur()
 471  		if t.tok == TokSemi { p.advance(); continue }
 472  		if t.tok == TokComment {
 473  			p.formatComment(t)
 474  			p.advance()
 475  			continue
 476  		}
 477  		if t.tok == TokComma {
 478  			p.emit(",")
 479  			p.advance()
 480  			needNL2 = true
 481  			continue
 482  		}
 483  		if needNL2 {
 484  			p.newline()
 485  			p.writeIndent()
 486  			needNL2 = false
 487  		} else {
 488  			prev := p.peek(-1)
 489  			if needSpaceCtx(p.toks, p.pos, prev, t) { p.space() }
 490  		}
 491  		if t.tok == TokLparen {
 492  			p.formatParenGroup()
 493  		} else {
 494  			p.emit(tokText(t))
 495  			p.advance()
 496  		}
 497  	}
 498  
 499  	p.indent--
 500  	p.newline()
 501  	p.writeIndent()
 502  	p.emit(")")
 503  	p.advance()
 504  }
 505  
 506  // --- main format ---
 507  
 508  func (p *Printer) format() {
 509  	for p.pos < int32(len(p.toks)) {
 510  		t := p.cur()
 511  		if t.tok == TokEOF { break }
 512  		if t.tok == TokComment { p.formatComment(t); p.advance(); continue }
 513  		if t.tok == TokSemi { p.advance(); continue }
 514  
 515  		if t.tok == TokElse && p.pos > 0 {
 516  			prev := p.peek(-1)
 517  			if prev.tok == TokRbrace {
 518  				p.space()
 519  				p.emit(tokText(t))
 520  				p.advance()
 521  				continue
 522  			}
 523  		}
 524  
 525  		if t.nlBefore && p.pos > 0 {
 526  			p.ensureNewline()
 527  			if t.blankBef { p.newline() }
 528  			p.writeIndent()
 529  		} else if p.atBOL {
 530  			p.writeIndent()
 531  		} else if p.pos > 0 {
 532  			prev := p.peek(-1)
 533  			if needSpaceCtx(p.toks, p.pos, prev, t) { p.space() }
 534  		}
 535  
 536  		if t.tok == TokLbrace {
 537  			p.formatBrace()
 538  		} else if t.tok == TokLparen && p.pos > 0 && isGroupKeyword(p.peek(-1).tok) {
 539  			p.formatGroupBlock()
 540  		} else if t.tok == TokLparen && !parenFitsLine(p.toks, p.pos, p.col) {
 541  			p.formatParenGroup()
 542  		} else {
 543  			p.emit(tokText(t))
 544  			p.advance()
 545  		}
 546  	}
 547  
 548  	if len(p.out) > 0 && p.out[len(p.out)-1] != '\n' { p.newline() }
 549  }
 550  
 551  func (p *Printer) ensureNewline() {
 552  	if len(p.out) > 0 && p.out[len(p.out)-1] != '\n' { p.newline() }
 553  }
 554  
 555  func (p *Printer) formatComment(t *FTok) {
 556  	if t.nlBefore && p.pos > 0 {
 557  		p.ensureNewline()
 558  		if t.blankBef { p.newline() }
 559  		p.writeIndent()
 560  	} else if p.atBOL {
 561  		p.writeIndent()
 562  	} else {
 563  		p.space()
 564  	}
 565  	p.emit(t.lit)
 566  }
 567  
 568  // --- brace formatting ---
 569  
 570  func (p *Printer) formatBrace() {
 571  	if fitsOneLine(p.toks, p.pos, p.col) && !isImportContext(p) {
 572  		p.formatBraceSingleLine()
 573  	} else {
 574  		p.formatBraceMultiLine()
 575  	}
 576  }
 577  
 578  func (p *Printer) formatBraceSingleLine() {
 579  	end := matchBrace(p.toks, p.pos)
 580  	p.emit("{")
 581  	p.advance()
 582  
 583  	if isEmptyBrace(p.toks, p.pos-1) {
 584  		for p.pos < end { p.advance() }
 585  		p.emit("}")
 586  		p.advance()
 587  		return
 588  	}
 589  
 590  	sliceLit := isSliceLitBrace(p.toks, p.pos-1)
 591  	if !sliceLit { p.space() }
 592  	first := true
 593  	for p.pos < end {
 594  		t := p.cur()
 595  		if t.tok == TokSemi {
 596  			next := p.peek(1)
 597  			if next.tok != TokRbrace && p.pos+1 < end { p.emit("; ") }
 598  			p.advance()
 599  			first = true
 600  			continue
 601  		}
 602  		if !first {
 603  			prev := p.peek(-1)
 604  			if prev.tok != TokSemi && needSpaceCtx(p.toks, p.pos, prev, t) { p.space() }
 605  		}
 606  		first = false
 607  		p.emit(tokText(t))
 608  		p.advance()
 609  	}
 610  	if !sliceLit { p.space() }
 611  	p.emit("}")
 612  	p.advance()
 613  }
 614  
 615  func (p *Printer) formatBraceMultiLine() {
 616  	end := matchBrace(p.toks, p.pos)
 617  	p.emit("{")
 618  	p.advance()
 619  	p.indent++
 620  
 621  	caseIndented := false
 622  	caseAwait := false
 623  	needNL := true
 624  	for p.pos < end {
 625  		t := p.cur()
 626  
 627  		if t.tok == TokSemi {
 628  			if t.lit == ";" {
 629  				p.emit(";")
 630  				needNL = false
 631  			} else {
 632  				needNL = true
 633  			}
 634  			p.advance()
 635  			continue
 636  		}
 637  
 638  		if t.tok == TokComment {
 639  			p.formatComment(t)
 640  			needNL = true
 641  			p.advance()
 642  			continue
 643  		}
 644  
 645  		if t.tok == TokElse {
 646  			p.space()
 647  			p.emit(tokText(t))
 648  			p.advance()
 649  			needNL = false
 650  			continue
 651  		}
 652  
 653  		if (t.tok == TokCase || t.tok == TokDefault) && needNL {
 654  			if caseIndented { p.indent--; caseIndented = false }
 655  			caseAwait = true
 656  			p.ensureNewline()
 657  			if t.blankBef { p.newline() }
 658  			p.writeIndent()
 659  			needNL = false
 660  			p.emit(tokText(t))
 661  			p.advance()
 662  			continue
 663  		}
 664  
 665  		if needNL {
 666  			p.ensureNewline()
 667  			if t.blankBef { p.newline() }
 668  			p.writeIndent()
 669  			needNL = false
 670  		} else {
 671  			prev := p.peek(-1)
 672  			if prev.tok == TokComma && p.col >= maxLine {
 673  				p.newline()
 674  				p.writeIndent()
 675  			} else if needSpaceCtx(p.toks, p.pos, prev, t) {
 676  				p.space()
 677  			}
 678  		}
 679  
 680  		if t.tok == TokColon && caseAwait {
 681  			p.emit(":")
 682  			p.advance()
 683  			caseAwait = false
 684  			caseIndented = true
 685  			p.indent++
 686  			needNL = true
 687  		} else if t.tok == TokColon && isLabel(p.toks, p.pos) {
 688  			p.emit(":")
 689  			p.advance()
 690  			needNL = true
 691  		} else if t.tok == TokLbrace {
 692  			p.formatBrace()
 693  			needNL = true
 694  		} else if t.tok == TokLparen && p.pos > 0 && isGroupKeyword(p.peek(-1).tok) {
 695  			p.formatGroupBlock()
 696  		} else if t.tok == TokLparen && !parenFitsLine(p.toks, p.pos, p.col) {
 697  			p.formatParenGroup()
 698  		} else {
 699  			p.emit(tokText(t))
 700  			p.advance()
 701  		}
 702  	}
 703  
 704  	if caseIndented { p.indent-- }
 705  	p.indent--
 706  	p.ensureNewline()
 707  	p.writeIndent()
 708  	p.emit("}")
 709  	p.advance()
 710  }
 711  
 712  // --- group block (import/const/var/type) ---
 713  
 714  func (p *Printer) formatGroupBlock() {
 715  	end := matchParen(p.toks, p.pos)
 716  	p.emit("(")
 717  	p.advance()
 718  	p.indent++
 719  
 720  	needNL := true
 721  	for p.pos < end {
 722  		t := p.cur()
 723  		if t.tok == TokSemi { needNL = true; p.advance(); continue }
 724  		if t.tok == TokComment {
 725  			p.formatComment(t)
 726  			needNL = true
 727  			p.advance()
 728  			continue
 729  		}
 730  		if needNL {
 731  			p.ensureNewline()
 732  			if t.blankBef { p.newline() }
 733  			p.writeIndent()
 734  			needNL = false
 735  		} else {
 736  			prev := p.peek(-1)
 737  			if needSpaceCtx(p.toks, p.pos, prev, t) { p.space() }
 738  		}
 739  		p.emit(tokText(t))
 740  		p.advance()
 741  	}
 742  
 743  	p.indent--
 744  	p.ensureNewline()
 745  	p.writeIndent()
 746  	p.emit(")")
 747  	p.advance()
 748  }
 749  
 750  // --- entry ---
 751  
 752  func formatSource(src string) (out string, errs []string) {
 753  	toks := tokenize(src)
 754  	p := newPrinter(toks)
 755  	checkBareReturns(toks, p)
 756  	p.format()
 757  	out = alignOutput(string(p.out))
 758  	errs = p.errs
 759  	return
 760  }
 761  
 762  func reportErrors(path string, errs []string) {
 763  	for i := int32(0); i < int32(len(errs)); i++ {
 764  		os.Stderr().Write([]byte(path | ":" | errs[i] | "\n"))
 765  	}
 766  }
 767