go-jsonnet/internal/formatter/fix_indentation.go
Angus Lees 856bd58872 Add 'importbin' statement
Add `importbin` statement.  Similar to `importstr` but the result is
an array of numbers (all integers 0-255).
2022-03-03 22:49:02 +00:00

813 lines
23 KiB
Go

/*
Copyright 2019 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package formatter
import (
"strings"
"github.com/google/go-jsonnet/ast"
"github.com/google/go-jsonnet/internal/pass"
)
// FixIndentation is a formatter pass that changes the indentation of new line
// fodder so that it follows the nested structure of the code.
type FixIndentation struct {
pass.Base
column int
Options Options
}
// indent is the representation of the indentation level. The field lineUp is
// what is generally used to indent after a new line. The field base is used to
// help derive a new Indent struct when the indentation level increases. lineUp
// is generally > base.
//
// In the following case (where spaces are replaced with underscores):
// ____foobar(1,
// ___________2)
//
// At the AST representing the 2, the indent has base == 4 and lineUp == 11.
type indent struct {
base int
lineUp int
}
// setIndents sets the indentation values within the fodder elements.
// The last one gets a special indentation value, all the others are set to the same thing.
func (c *FixIndentation) setIndents(
fodder ast.Fodder, allButLastIndent int, lastIndent int) {
// First count how many there are.
count := 0
for _, f := range fodder {
if f.Kind != ast.FodderInterstitial {
count++
}
}
// Now set the indents.
i := 0
for index := range fodder {
f := &fodder[index]
if f.Kind != ast.FodderInterstitial {
if i+1 < count {
f.Indent = allButLastIndent
} else {
if i != count-1 {
panic("Shouldn't get here")
}
f.Indent = lastIndent
}
i++
}
}
}
// fill sets the indentation on the fodder elements and adjusts the c.column
// counter as if it was printed.
// To understand fodder, crowded, separateToken, see the documentation of
// unparse.fill.
// allButLastIndent is the new indentation value for all but the final fodder
// element.
// lastIndent is the new indentation value for the final fodder element.
func (c *FixIndentation) fillLast(
fodder ast.Fodder, crowded bool, separateToken bool,
allButLastIndent int, lastIndent int) {
c.setIndents(fodder, allButLastIndent, lastIndent)
// A model of unparser.fill that just keeps track of the
// c.column counter.
for _, fod := range fodder {
switch fod.Kind {
case ast.FodderParagraph:
c.column = fod.Indent
crowded = false
case ast.FodderLineEnd:
c.column = fod.Indent
crowded = false
case ast.FodderInterstitial:
if crowded {
c.column++
}
c.column += len(fod.Comment[0])
crowded = true
}
}
if separateToken && crowded {
c.column++
}
}
// fill is like fillLast but where the final and prior fodder get the same
// currIndent.
func (c *FixIndentation) fill(
fodder ast.Fodder, crowded bool, separateToken bool, indent int) {
c.fillLast(fodder, crowded, separateToken, indent, indent)
}
// newIndent calculates the indentation of sub-expressions.
// If the first sub-expression is on the same line as the current node, then subsequent
// ones will be lined up, otherwise subsequent ones will be on the next line indented
// by 'indent'.
func (c *FixIndentation) newIndent(firstFodder ast.Fodder, old indent, lineUp int) indent {
if len(firstFodder) == 0 || firstFodder[0].Kind == ast.FodderInterstitial {
return indent{old.base, lineUp}
}
// Reset
return indent{old.base + c.Options.Indent, old.base + c.Options.Indent}
}
// Calculate the indentation of sub-expressions.
// If the first sub-expression is on the same line as the current node, then
// subsequent ones will be lined up and further indentations in their
// subexpressions will be based from this c.column.
func (c *FixIndentation) newIndentStrong(firstFodder ast.Fodder, old indent, lineUp int) indent {
if len(firstFodder) == 0 || firstFodder[0].Kind == ast.FodderInterstitial {
return indent{lineUp, lineUp}
}
// Reset
return indent{old.base + c.Options.Indent, old.base + c.Options.Indent}
}
// Calculate the indentation of sub-expressions.
// If the first sub-expression is on the same line as the current node, then
// subsequent ones will be lined up, otherwise subseqeuent ones will be on the
// next line with no additional currIndent.
func (c *FixIndentation) align(firstFodder ast.Fodder, old indent, lineUp int) indent {
if len(firstFodder) == 0 || firstFodder[0].Kind == ast.FodderInterstitial {
return indent{old.base, lineUp}
}
// Reset
return old
}
// alignStrong calculates the indentation of sub-expressions.
// If the first sub-expression is on the same line as the current node, then
// subsequent ones will be lined up and further indentations in their
// subexpresssions will be based from this c.column. Otherwise, subseqeuent ones
// will be on the next line with no additional currIndent.
func (c *FixIndentation) alignStrong(firstFodder ast.Fodder, old indent, lineUp int) indent {
if len(firstFodder) == 0 || firstFodder[0].Kind == ast.FodderInterstitial {
return indent{lineUp, lineUp}
}
// Reset
return old
}
/** Does the given fodder contain at least one new line? */
func (c *FixIndentation) hasNewLines(fodder ast.Fodder) bool {
for _, f := range fodder {
if f.Kind != ast.FodderInterstitial {
return true
}
}
return false
}
// specs indents comprehension forspecs.
func (c *FixIndentation) specs(spec *ast.ForSpec, currIndent indent) {
if spec.Outer != nil {
c.specs(spec.Outer, currIndent)
}
c.fill(spec.ForFodder, true, true, currIndent.lineUp)
c.column += 3 // for
c.fill(spec.VarFodder, true, true, currIndent.lineUp)
c.column += len(spec.VarName)
c.fill(spec.InFodder, true, true, currIndent.lineUp)
c.column += 2 // in
newIndent := c.newIndent(*openFodder(spec.Expr), currIndent, c.column)
c.Visit(spec.Expr, newIndent, true)
for _, cond := range spec.Conditions {
c.fill(cond.IfFodder, true, true, currIndent.lineUp)
c.column += 2 // if
newIndent := c.newIndent(*openFodder(spec.Expr), currIndent, c.column)
c.Visit(spec.Expr, newIndent, true)
}
}
func (c *FixIndentation) params(fodderL ast.Fodder, params []ast.Parameter,
trailingComma bool, fodderR ast.Fodder, currIndent indent) {
c.fill(fodderL, false, false, currIndent.lineUp)
c.column++ // (
var firstInside ast.Fodder
gotFodder := false
for _, param := range params {
firstInside = param.NameFodder
gotFodder = true
break
}
if !gotFodder {
firstInside = fodderR
}
newIndent := c.newIndent(firstInside, currIndent, c.column)
first := true
for _, param := range params {
if !first {
c.column++ // ','
}
c.fill(param.NameFodder, !first, true, newIndent.lineUp)
c.column += len(param.Name)
if param.DefaultArg != nil {
c.fill(param.EqFodder, false, false, newIndent.lineUp)
// default arg, no spacing: x=e
c.column++
c.Visit(param.DefaultArg, newIndent, false)
}
c.fill(param.CommaFodder, false, false, newIndent.lineUp)
first = false
}
if trailingComma {
c.column++
}
c.fillLast(fodderR, false, false, newIndent.lineUp, currIndent.lineUp)
c.column++ // )
}
func (c *FixIndentation) fieldParams(field ast.ObjectField, currIndent indent) {
m := field.Method
if m != nil {
c.params(m.ParenLeftFodder, m.Parameters, m.TrailingComma,
m.ParenRightFodder, currIndent)
}
}
// fields indents fields within an object.
// indent is the indent of the first field
// crowded is whether the first field is crowded (see unparser.fill)
func (c *FixIndentation) fields(fields ast.ObjectFields, currIndent indent, crowded bool) {
newIndent := currIndent.lineUp
for i, field := range fields {
if i > 0 {
c.column++ // ','
}
// An aux function so we don't repeat ourselves for the 3 kinds of
// basic field.
unparseFieldRemainder := func(field ast.ObjectField) {
c.fieldParams(field, currIndent)
c.fill(field.OpFodder, false, false, newIndent)
if field.SuperSugar {
c.column++
}
switch field.Hide {
case ast.ObjectFieldInherit:
c.column++
case ast.ObjectFieldHidden:
c.column += 2
case ast.ObjectFieldVisible:
c.column += 3
}
c.Visit(field.Expr2,
c.newIndent(*openFodder(field.Expr2), currIndent, c.column),
true)
}
switch field.Kind {
case ast.ObjectLocal:
c.fill(field.Fodder1, i > 0 || crowded, true, currIndent.lineUp)
c.column += 5 // local
c.fill(field.Fodder2, true, true, currIndent.lineUp)
c.column += len(*field.Id)
c.fieldParams(field, currIndent)
c.fill(field.OpFodder, true, true, currIndent.lineUp)
c.column++ // =
newIndent2 := c.newIndent(*openFodder(field.Expr2), currIndent, c.column)
c.Visit(field.Expr2, newIndent2, true)
case ast.ObjectFieldID:
c.fill(field.Fodder1, i > 0 || crowded, true, newIndent)
c.column += len(*field.Id)
unparseFieldRemainder(field)
case ast.ObjectFieldStr:
c.Visit(field.Expr1, currIndent, i > 0 || crowded)
unparseFieldRemainder(field)
case ast.ObjectFieldExpr:
c.fill(field.Fodder1, i > 0 || crowded, true, newIndent)
c.column++ // [
c.Visit(field.Expr1, currIndent, false)
c.fill(field.Fodder2, false, false, newIndent)
c.column++ // ]
unparseFieldRemainder(field)
case ast.ObjectAssert:
c.fill(field.Fodder1, i > 0 || crowded, true, newIndent)
c.column += 6 // assert
// + 1 for the space after the assert
newIndent2 := c.newIndent(*openFodder(field.Expr2), currIndent, c.column+1)
c.Visit(field.Expr2, currIndent, true)
if field.Expr3 != nil {
c.fill(field.OpFodder, true, true, newIndent2.lineUp)
c.column++ // ":"
c.Visit(field.Expr3, newIndent2, true)
}
}
c.fill(field.CommaFodder, false, false, newIndent)
}
}
// Visit has logic common to all nodes.
func (c *FixIndentation) Visit(expr ast.Node, currIndent indent, crowded bool) {
separateToken := leftRecursive(expr) == nil
c.fill(*expr.OpenFodder(), crowded, separateToken, currIndent.lineUp)
switch node := expr.(type) {
case *ast.Apply:
initFodder := *openFodder(node.Target)
newColumn := c.column
if crowded {
newColumn++
}
newIndent := c.align(initFodder, currIndent, newColumn)
c.Visit(node.Target, newIndent, crowded)
c.fill(node.FodderLeft, false, false, newIndent.lineUp)
c.column++ // (
firstFodder := node.FodderRight
for _, arg := range node.Arguments.Named {
firstFodder = arg.NameFodder
break
}
for _, arg := range node.Arguments.Positional {
firstFodder = *openFodder(arg.Expr)
break
}
strongIndent := false
// Need to use strong indent if any of the
// arguments (except the first) are preceded by newlines.
first := true
for _, arg := range node.Arguments.Positional {
if first {
// Skip first element.
first = false
continue
}
if c.hasNewLines(*openFodder(arg.Expr)) {
strongIndent = true
}
}
for _, arg := range node.Arguments.Named {
if first {
// Skip first element.
first = false
continue
}
if c.hasNewLines(arg.NameFodder) {
strongIndent = true
}
}
var argIndent indent
if strongIndent {
argIndent = c.newIndentStrong(firstFodder, currIndent, c.column)
} else {
argIndent = c.newIndent(firstFodder, currIndent, c.column)
}
first = true
for _, arg := range node.Arguments.Positional {
if !first {
c.column++ // ","
}
space := !first
c.Visit(arg.Expr, argIndent, space)
c.fill(arg.CommaFodder, false, false, argIndent.lineUp)
first = false
}
for _, arg := range node.Arguments.Named {
if !first {
c.column++ // ","
}
space := !first
c.fill(arg.NameFodder, space, false, argIndent.lineUp)
c.column += len(arg.Name)
c.column++ // "="
c.Visit(arg.Arg, argIndent, false)
c.fill(arg.CommaFodder, false, false, argIndent.lineUp)
first = false
}
if node.TrailingComma {
c.column++ // ","
}
c.fillLast(node.FodderRight, false, false, argIndent.lineUp, currIndent.base)
c.column++ // )
if node.TailStrict {
c.fill(node.TailStrictFodder, true, true, currIndent.base)
c.column += 10 // tailstrict
}
case *ast.ApplyBrace:
initFodder := *openFodder(node.Left)
newColumn := c.column
if crowded {
newColumn++
}
newIndent := c.align(initFodder, currIndent, newColumn)
c.Visit(node.Left, newIndent, crowded)
c.Visit(node.Right, newIndent, true)
case *ast.Array:
c.column++ // '['
// First fodder element exists and is a newline
var firstFodder ast.Fodder
if len(node.Elements) > 0 {
firstFodder = *openFodder(node.Elements[0].Expr)
} else {
firstFodder = node.CloseFodder
}
newColumn := c.column
if c.Options.PadArrays {
newColumn++
}
strongIndent := false
// Need to use strong indent if there are not newlines before any of the sub-expressions
for i, el := range node.Elements {
if i == 0 {
continue
}
if c.hasNewLines(*openFodder(el.Expr)) {
strongIndent = true
}
}
var newIndent indent
if strongIndent {
newIndent = c.newIndentStrong(firstFodder, currIndent, newColumn)
} else {
newIndent = c.newIndent(firstFodder, currIndent, newColumn)
}
for i, el := range node.Elements {
if i > 0 {
c.column++
}
c.Visit(el.Expr, newIndent, i > 0 || c.Options.PadArrays)
c.fill(el.CommaFodder, false, false, newIndent.lineUp)
}
if node.TrailingComma {
c.column++
}
// Handle penultimate newlines from expr.CloseFodder if there are any.
c.fillLast(node.CloseFodder,
len(node.Elements) > 0,
c.Options.PadArrays,
newIndent.lineUp,
currIndent.base)
c.column++ // ']'
case *ast.ArrayComp:
c.column++ // [
newColumn := c.column
if c.Options.PadArrays {
newColumn++
}
newIndent :=
c.newIndent(*openFodder(node.Body), currIndent, newColumn)
c.Visit(node.Body, newIndent, c.Options.PadArrays)
c.fill(node.TrailingCommaFodder, false, false, newIndent.lineUp)
if node.TrailingComma {
c.column++ // ','
}
c.specs(&node.Spec, newIndent)
c.fillLast(node.CloseFodder, true, c.Options.PadArrays,
newIndent.lineUp, currIndent.base)
c.column++ // ]
case *ast.Assert:
c.column += 6 // assert
// + 1 for the space after the assert
newIndent := c.newIndent(*openFodder(node.Cond), currIndent, c.column+1)
c.Visit(node.Cond, newIndent, true)
if node.Message != nil {
c.fill(node.ColonFodder, true, true, newIndent.lineUp)
c.column++ // ":"
c.Visit(node.Message, newIndent, true)
}
c.fill(node.SemicolonFodder, false, false, newIndent.lineUp)
c.column++ // ";"
c.Visit(node.Rest, currIndent, true)
case *ast.Binary:
firstFodder := *openFodder(node.Left)
// Need to use strong indent in the case of
/*
A
+ B
or
A +
B
*/
innerColumn := c.column
if crowded {
innerColumn++
}
var newIndent indent
if c.hasNewLines(node.OpFodder) || c.hasNewLines(*openFodder(node.Right)) {
newIndent = c.alignStrong(firstFodder, currIndent, innerColumn)
} else {
newIndent = c.align(firstFodder, currIndent, innerColumn)
}
c.Visit(node.Left, newIndent, crowded)
c.fill(node.OpFodder, true, true, newIndent.lineUp)
c.column += len(node.Op.String())
// Don't calculate a new indent for here, because we like being able to do:
// true &&
// true &&
// true
c.Visit(node.Right, newIndent, true)
case *ast.Conditional:
c.column += 2 // if
condIndent := c.newIndent(*openFodder(node.Cond), currIndent, c.column+1)
c.Visit(node.Cond, condIndent, true)
c.fill(node.ThenFodder, true, true, currIndent.base)
c.column += 4 // then
trueIndent := c.newIndent(*openFodder(node.BranchTrue), currIndent, c.column+1)
c.Visit(node.BranchTrue, trueIndent, true)
if node.BranchFalse != nil {
c.fill(node.ElseFodder, true, true, currIndent.base)
c.column += 4 // else
falseIndent := c.newIndent(*openFodder(node.BranchFalse), currIndent, c.column+1)
c.Visit(node.BranchFalse, falseIndent, true)
}
case *ast.Dollar:
c.column++ // $
case *ast.Error:
c.column += 5 // error
newIndent := c.newIndent(*openFodder(node.Expr), currIndent, c.column+1)
c.Visit(node.Expr, newIndent, true)
case *ast.Function:
c.column += 8 // function
c.params(node.ParenLeftFodder, node.Parameters,
node.TrailingComma, node.ParenRightFodder, currIndent)
newIndent := c.newIndent(*openFodder(node.Body), currIndent, c.column+1)
c.Visit(node.Body, newIndent, true)
case *ast.Import:
c.column += 6 // import
newIndent := c.newIndent(*openFodder(node.File), currIndent, c.column+1)
c.Visit(node.File, newIndent, true)
case *ast.ImportStr:
c.column += 9 // importstr
newIndent := c.newIndent(*openFodder(node.File), currIndent, c.column+1)
c.Visit(node.File, newIndent, true)
case *ast.ImportBin:
c.column += 9 // importbin
newIndent := c.newIndent(*openFodder(node.File), currIndent, c.column+1)
c.Visit(node.File, newIndent, true)
case *ast.InSuper:
c.Visit(node.Index, currIndent, crowded)
c.fill(node.InFodder, true, true, currIndent.lineUp)
c.column += 2 // in
c.fill(node.SuperFodder, true, true, currIndent.lineUp)
c.column += 5 // super
case *ast.Index:
c.Visit(node.Target, currIndent, crowded)
c.fill(node.LeftBracketFodder, false, false, currIndent.lineUp) // Can also be DotFodder
if node.Id != nil {
c.column++ // "."
newIndent := c.newIndent(node.RightBracketFodder, currIndent, c.column)
c.fill(node.RightBracketFodder, false, false, newIndent.lineUp) // Can also be IdFodder
c.column += len(*node.Id)
} else {
c.column++ // "["
newIndent := c.newIndent(*openFodder(node.Index), currIndent, c.column)
c.Visit(node.Index, newIndent, false)
c.fillLast(node.RightBracketFodder, false, false, newIndent.lineUp, currIndent.base)
c.column++ // "]"
}
case *ast.Slice:
c.Visit(node.Target, currIndent, crowded)
c.fill(node.LeftBracketFodder, false, false, currIndent.lineUp)
c.column++ // "["
var newIndent indent
if node.BeginIndex != nil {
newIndent = c.newIndent(*openFodder(node.BeginIndex), currIndent, c.column)
c.Visit(node.BeginIndex, newIndent, false)
}
if node.EndIndex != nil {
newIndent = c.newIndent(node.EndColonFodder, currIndent, c.column)
c.fill(node.EndColonFodder, false, false, newIndent.lineUp)
c.column++ // ":"
c.Visit(node.EndIndex, newIndent, false)
}
if node.Step != nil {
if node.EndIndex == nil {
newIndent = c.newIndent(node.EndColonFodder, currIndent, c.column)
c.fill(node.EndColonFodder, false, false, newIndent.lineUp)
c.column++ // ":"
}
c.fill(node.StepColonFodder, false, false, newIndent.lineUp)
c.column++ // ":"
c.Visit(node.Step, newIndent, false)
}
if node.BeginIndex == nil && node.EndIndex == nil && node.Step == nil {
newIndent = c.newIndent(node.EndColonFodder, currIndent, c.column)
c.fill(node.EndColonFodder, false, false, newIndent.lineUp)
c.column++ // ":"
}
c.column++ // "]"
case *ast.Local:
c.column += 5 // local
if len(node.Binds) == 0 {
panic("Not enough binds in local")
}
first := true
newIndent := c.newIndent(node.Binds[0].VarFodder, currIndent, c.column+1)
for _, bind := range node.Binds {
if !first {
c.column++ // ','
}
first = false
c.fill(bind.VarFodder, true, true, newIndent.lineUp)
c.column += len(bind.Variable)
if bind.Fun != nil {
c.params(bind.Fun.ParenLeftFodder,
bind.Fun.Parameters,
bind.Fun.TrailingComma,
bind.Fun.ParenRightFodder,
newIndent)
}
c.fill(bind.EqFodder, true, true, newIndent.lineUp)
c.column++ // '='
newIndent2 := c.newIndent(*openFodder(bind.Body), newIndent, c.column+1)
c.Visit(bind.Body, newIndent2, true)
c.fillLast(bind.CloseFodder, false, false, newIndent2.lineUp,
currIndent.base)
}
c.column++ // ';'
c.Visit(node.Body, currIndent, true)
case *ast.LiteralBoolean:
if node.Value {
c.column += 4
} else {
c.column += 5
}
case *ast.LiteralNumber:
c.column += len(node.OriginalString)
case *ast.LiteralString:
switch node.Kind {
case ast.StringDouble:
c.column += 2 + len(node.Value) // Include quotes
case ast.StringSingle:
c.column += 2 + len(node.Value) // Include quotes
case ast.StringBlock:
node.BlockIndent = strings.Repeat(" ", currIndent.base+c.Options.Indent)
node.BlockTermIndent = strings.Repeat(" ", currIndent.base)
c.column = currIndent.base // blockTermIndent
c.column += 3 // "|||"
case ast.VerbatimStringSingle:
c.column += 3 // Include @, start and end quotes
for _, r := range node.Value {
if r == '\'' {
c.column += 2
} else {
c.column++
}
}
case ast.VerbatimStringDouble:
c.column += 3 // Include @, start and end quotes
for _, r := range node.Value {
if r == '"' {
c.column += 2
} else {
c.column++
}
}
}
case *ast.LiteralNull:
c.column += 4 // null
case *ast.Object:
c.column++ // '{'
var firstFodder ast.Fodder
if len(node.Fields) == 0 {
firstFodder = node.CloseFodder
} else {
if node.Fields[0].Kind == ast.ObjectFieldStr {
firstFodder = *openFodder(node.Fields[0].Expr1)
} else {
firstFodder = node.Fields[0].Fodder1
}
}
newColumn := c.column
if c.Options.PadObjects {
newColumn++
}
newIndent := c.newIndent(firstFodder, currIndent, newColumn)
c.fields(node.Fields, newIndent, c.Options.PadObjects)
if node.TrailingComma {
c.column++
}
c.fillLast(node.CloseFodder,
len(node.Fields) > 0,
c.Options.PadObjects,
newIndent.lineUp,
currIndent.base)
c.column++ // '}'
case *ast.ObjectComp:
c.column++ // '{'
var firstFodder ast.Fodder
if len(node.Fields) == 0 {
firstFodder = node.CloseFodder
} else {
if node.Fields[0].Kind == ast.ObjectFieldStr {
firstFodder = *openFodder(node.Fields[0].Expr1)
} else {
firstFodder = node.Fields[0].Fodder1
}
}
newColumn := c.column
if c.Options.PadObjects {
newColumn++
}
newIndent := c.newIndent(firstFodder, currIndent, newColumn)
c.fields(node.Fields, newIndent, c.Options.PadObjects)
if node.TrailingComma {
c.column++ // ','
}
c.specs(&node.Spec, newIndent)
c.fillLast(node.CloseFodder,
true,
c.Options.PadObjects,
newIndent.lineUp,
currIndent.base)
c.column++ // '}'
case *ast.Parens:
c.column++ // (
newIndent := c.newIndentStrong(*openFodder(node.Inner), currIndent, c.column)
c.Visit(node.Inner, newIndent, false)
c.fillLast(node.CloseFodder, false, false, newIndent.lineUp, currIndent.base)
c.column++ // )
case *ast.Self:
c.column += 4 // self
case *ast.SuperIndex:
c.column += 5 // super
c.fill(node.DotFodder, false, false, currIndent.lineUp)
if node.Id != nil {
c.column++ // ".";
newIndent := c.newIndent(node.IDFodder, currIndent, c.column)
c.fill(node.IDFodder, false, false, newIndent.lineUp)
c.column += len(*node.Id)
} else {
c.column++ // "[";
newIndent := c.newIndent(*openFodder(node.Index), currIndent, c.column)
c.Visit(node.Index, newIndent, false)
c.fillLast(node.IDFodder, false, false, newIndent.lineUp, currIndent.base)
c.column++ // "]";
}
case *ast.Unary:
c.column += len(node.Op.String())
newIndent := c.newIndent(*openFodder(node.Expr), currIndent, c.column)
_, leftIsDollar := leftRecursiveDeep(node.Expr).(*ast.Dollar)
c.Visit(node.Expr, newIndent, leftIsDollar)
case *ast.Var:
c.column += len(node.Id)
}
}
// VisitFile corrects the whole file including the final fodder.
func (c *FixIndentation) VisitFile(body ast.Node, finalFodder ast.Fodder) {
c.Visit(body, indent{0, 0}, false)
c.setIndents(finalFodder, 0, 0)
}