Skip to content

Commit

Permalink
Allow granular disabling of formatter and fix unnecessary nil check.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 662580778
  • Loading branch information
txtpbfmt-copybara-robot committed Aug 13, 2024
1 parent dedd929 commit 70a5980
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 1 deletion.
3 changes: 3 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ type Node struct {
PostValuesComments []string
// Whether the braces used for the children of this node are curly braces or angle brackets.
IsAngleBracket bool
// If this is not empty, it means that formatting was disabled for this node and it contains the
// raw, unformatted node string.
Raw string
}

// NodeLess is a sorting function that compares two *Nodes, possibly using the parent Node
Expand Down
47 changes: 46 additions & 1 deletion parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ func addToConfig(metaComment string, c *Config) error {
c.WrapHTMLStrings = true
case "wrap_strings_after_newlines":
c.WrapStringsAfterNewlines = true
case "on": // This doesn't change the overall config.
case "off": // This doesn't change the overall config.
default:
return fmt.Errorf("unrecognized MetaComment: %s", metaComment)
}
Expand Down Expand Up @@ -572,6 +574,18 @@ func (p *parser) parse(isRoot bool) (result []*ast.Node, endPos ast.Position, er
p.consume('\n')
startPos := p.position()

fmtDisabled, err := p.readFormatterDisabledBlock()
if err != nil {
return nil, startPos, err
}
if len(fmtDisabled) > 0 {
res = append(res, &ast.Node{
Start: startPos,
Raw: fmtDisabled,
})
continue
}

// Read PreComments.
comments, blankLines := p.skipWhiteSpaceAndReadComments(true /* multiLine */)

Expand Down Expand Up @@ -817,6 +831,33 @@ func (p *parser) readContinuousBlocksOfComments() []string {
return preComments
}

// Returns the exact text within the block flanked by "# txtpbfmt: off" and "# txtpbfmt: on".
// The off directives must be on its own line without indenting, and the it cannot be preceded by a
// comment line. The on directive must followed by a line break. Only full nodes of a AST can be
// within this block. Partially disabled sections, like just the first line of a for loop without
// body or closing brace, are not supported. Value lists are not supported. No parsing happens
// within this block, and as parsing errors will be ignored, please exercise caution.
func (p *parser) readFormatterDisabledBlock() (string, error) {
start := p.index
if !p.consumeString("# txtpbfmt: off") {
return "", nil
}
if !p.consume('\n') {
return "", fmt.Errorf("txtpbfmt off should be followed by newline at %s", p.errorContext())
}
for ; p.index < p.length; p.index++ {
if p.consumeString("# txtpbfmt: on") {
if !p.consume('\n') {
return "", fmt.Errorf("txtpbfmt on should be followed by newline at %s", p.errorContext())
}
return string(p.in[start:p.index]), nil
}
}
// We reached the end of the file without finding the on directive.
p.index = start
return "", fmt.Errorf("unterminated txtpbfmt off at %s", p.errorContext())
}

// skipWhiteSpaceAndReadComments has multiple cases:
// - (1) reading a block of comments followed by a blank line
// - (2) reading a block of comments followed by non-blank content
Expand Down Expand Up @@ -1081,7 +1122,7 @@ func RemoveDuplicates(nodes []*ast.Node) {
}
seen := make(map[nameAndValue]bool)
for _, nd := range nodes {
if seen != nil && len(nd.Values) == 1 {
if len(nd.Values) == 1 {
key := nameAndValue{nd.Name, nd.Values[0].Value}
if _, value := seen[key]; value {
// Name-Value pair found in the same nesting level, deleting.
Expand Down Expand Up @@ -1508,6 +1549,10 @@ func (f formatter) writeNodes(nodes []*ast.Node, depth int, isSameLine, asListIt
}

for index, nd := range nodes {
if len(nd.Raw) > 0 {
f.WriteString(nd.Raw)
continue
}
for _, comment := range nd.PreComments {
if len(comment) == 0 {
if !(depth == 0 && index == 0) {
Expand Down
71 changes: 71 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ func TestError(t *testing.T) {
}, {
in: `name: "string with literal new line
"`, err: "new line",
}, {
in: `# txtpbfmt: off`,
err: "unterminated txtpbfmt off",
}}
for _, input := range inputs {
out, err := Format([]byte(input.in))
Expand Down Expand Up @@ -199,6 +202,15 @@ p {}`,
in: `"""text""" "\""`,
err: false,
testType: tripleQuotedTest,
}, {
name: "txtpbfmt: off/on",
in: `# txtpbfmt: off
foo: "bar"
{
bar: "baz"
}
# txtpbfmt: on
`, err: false,
}}
for _, input := range inputs {
bytes := []byte(input.in)
Expand Down Expand Up @@ -1449,6 +1461,65 @@ types_text_content: {
}
chart_spec: '{"columnDefinitions":[]}'
inline_script: "SELECT 'Hello' AS hello"
`}, {
name: "txtpbfmt off/on",
in: `foo: "bar"
bar {
baz: "qux"
# txtpbfmt: off
# comment
# comment
no_format {
foo: "bar"
}
enabled: {TEMPLATE_plx}
# txtpbfmt: on
}
should_format {
foo: "bar"
}
# txtpbfmt: off
no_format { foo: "bar" } # txtpbfmt: on
should_format {
foo: "bar"
}
`,
out: `foo: "bar"
bar {
baz: "qux"
# txtpbfmt: off
# comment
# comment
no_format {
foo: "bar"
}
enabled: {TEMPLATE_plx}
# txtpbfmt: on
}
should_format {
foo: "bar"
}
# txtpbfmt: off
no_format { foo: "bar" } # txtpbfmt: on
should_format {
foo: "bar"
}
`}, {
name: "txtpbfmt off/on doesn't work within list",
in: `foo: [
# txtpbfmt: off
a, b,
c
# txtpbfmt: on
]
`,
out: `foo: [
# txtpbfmt: off
a,
b,
c
# txtpbfmt: on
]
`}}
for _, input := range inputs {
out, err := Format([]byte(input.in))
Expand Down

0 comments on commit 70a5980

Please sign in to comment.