From fcdd8832a324b6bd8b6ae30e7202048cf842d126 Mon Sep 17 00:00:00 2001 From: Masayuki Morita Date: Wed, 11 Mar 2020 14:31:08 +0900 Subject: [PATCH] Format in vertical when removing blocks --- editor/block_remove.go | 2 +- editor/block_remove_test.go | 1 - editor/sink.go | 76 +++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/editor/block_remove.go b/editor/block_remove.go index 94ac35a..7b2ee88 100644 --- a/editor/block_remove.go +++ b/editor/block_remove.go @@ -16,7 +16,7 @@ func RemoveBlock(r io.Reader, w io.Writer, filename string, address string) erro filters: []Filter{ &blockRemove{address: address}, }, - sink: &formater{}, + sink: &verticalFormater{}, } return e.Apply(r, w) diff --git a/editor/block_remove_test.go b/editor/block_remove_test.go index 9a35803..087f70a 100644 --- a/editor/block_remove_test.go +++ b/editor/block_remove_test.go @@ -28,7 +28,6 @@ b2 l1 { ok: true, want: ` a0 = v0 - b2 l1 { } `, diff --git a/editor/sink.go b/editor/sink.go index f07f32b..947d123 100644 --- a/editor/sink.go +++ b/editor/sink.go @@ -1,6 +1,7 @@ package editor import ( + "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/hcl/v2/hclwrite" ) @@ -20,3 +21,78 @@ func (f *formater) Sink(inFile *hclwrite.File) ([]byte, error) { out := hclwrite.Format(raw) return out, nil } + +// verticalFormater is a Sink implementation to format HCL. +// At time of writing, the default hcl formatter does not support vertical +// formatting. However, it's useful in some cases such as removing a block +// because leading and trailing newline tokens don't belong to a block, so +// deleting a block leaves extra newline tokens. +// This is not included in the original hcl implementation, so we should not be +// the default behavior of the formater not to break existing fomatted hcl configurations. +// Opt-in only where you neeed this feature. +// Note that verticalFormatter formats not only in vertical but also horizontal +// because we cannot use multiple Sink implementations at once. +type verticalFormater struct { +} + +// Sink reads HCL and writes formatted contents in vertical and horizontal. +func (f *verticalFormater) Sink(inFile *hclwrite.File) ([]byte, error) { + tokens := inFile.BuildTokens(nil) + + vertical := VerticalFormat(tokens) + + // default horizontal format + out := hclwrite.Format(vertical.Bytes()) + return out, nil +} + +// VerticalFormat formats token in vertical. +func VerticalFormat(tokens hclwrite.Tokens) hclwrite.Tokens { + trimmed := trimNewLine(tokens) + removed := removeDuplicatedNewLine(trimmed) + return removed +} + +// trimNewLine trimsleading and trailing newlines from tokens +func trimNewLine(tokens hclwrite.Tokens) hclwrite.Tokens { + begin := 0 + for ; begin < len(tokens); begin++ { + if tokens[begin].Type != hclsyntax.TokenNewline { + break + } + } + + end := len(tokens) + var eof *hclwrite.Token + for ; end > begin; end-- { + if tokens[end-1].Type == hclsyntax.TokenEOF { + // skip EOF + eof = tokens[end-1] + continue + } + if tokens[end-1].Type != hclsyntax.TokenNewline { + break + } + } + + ret := append(tokens[begin:end], eof) + return ret +} + +// removeDuplicatedNewLine removes duplicated newlines +func removeDuplicatedNewLine(tokens hclwrite.Tokens) hclwrite.Tokens { + var removed hclwrite.Tokens + before := false + + for _, token := range tokens { + if token.Type != hclsyntax.TokenNewline { + removed = append(removed, token) + before = false + } else if !before { + removed = append(removed, token) + before = true + } + } + + return removed +}