go-yaml icon indicating copy to clipboard operation
go-yaml copied to clipboard

[Question]: Apply global indentation rules

Open efd6 opened this issue 2 years ago • 7 comments

Say I have an input like

root1:
 - element1
 - element2
root2:
     - element3
root3:
- element4

and I want to canonicalise the indentation to some defined standard, say

root1:
  - element1
  - element2
root2:
  - element3
root3:
  - element4

How would I go about doing that? I've tried walking over the AST and setting each node's token's column to its indentlevel*2+1 (for the case here) and that almost works, but does not correctly handle the root* indentation positions (resulting in both ugliness and broken yaml).

efd6 avatar Mar 07 '23 23:03 efd6

This can be done by decoding the target yaml once and then specifying the yaml.Indent and yaml.IndentSequence option when encoding.

goccy avatar Mar 08 '23 08:03 goccy

So not from the AST? In the use I have, I have already done mutations on the AST to rewrite parts, so it seems like I could continue to use it, but I guess just piping it through again works if it does.

efd6 avatar Mar 08 '23 10:03 efd6

Actually, unless we go through the AST this won't work for me since I need to retain comments.

efd6 avatar Mar 08 '23 11:03 efd6

@efd6 It is possible to decode/encode while retaining comments, using yaml.CommentToMap / yaml.WithComment option. If you don't know how to use it, please refer to the test code.

goccy avatar Mar 09 '23 05:03 goccy

Thanks. It appears to not be able to differentiate 0-level sequence from above, so any sequence-only docs end up with a gutter of indentation. This is solved by post-processing, but there is other lossiness due to moving out of the AST: movement of comments and change of string types.

func redent(doc string, indent int) (string, error) {
	cm := make(yaml.CommentMap)
	var v interface{}
	err := yaml.NewDecoder(strings.NewReader(doc), yaml.CommentToMap(cm)).Decode(&v)
	if err != nil {
		return "", err
	}
	var buf strings.Builder
	err = yaml.NewEncoder(&buf,
		yaml.WithComment(cm),
		yaml.Indent(indent),
		yaml.IndentSequence(true),
	).Encode(v)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}

(For others, works with 1.10.0, not with 1.9.8)

efd6 avatar Mar 09 '23 06:03 efd6

I wasn't sure of the situation, could you share the yaml you are targeting in more detail ? If you provide it to me in the form of reproduced code, I can verify it as well.

goccy avatar Mar 09 '23 07:03 goccy

Sure, the tool is github.com/efd6/fumpt, I've put the state with this reformatting in https://github.com/efd6/fumpt/tree/redent The test data shows the kind of things we see (txtar format for ease of reading). current master: https://github.com/efd6/fumpt/blob/master/pkg_want.txtar with this addition: https://github.com/efd6/fumpt/blob/redent/pkg_want.txtar

efd6 avatar Mar 09 '23 07:03 efd6