Skip to content

Commit

Permalink
feat: support reStructuredText
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaHoffmann committed Jun 15, 2022
1 parent f547e1d commit 9fdb760
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 28 deletions.
37 changes: 33 additions & 4 deletions cli/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"strings"
"unicode"

"github.com/andreyvit/diff"
c "github.com/gookit/color"
Expand Down Expand Up @@ -397,7 +398,7 @@ func findBlocksInFile(fs afero.Fs, log *logrus.Logger, filename string, verbose,
ReadOnly: true,
LineRead: blocks.ReaderIgnore,
BlockWriter: blockWriter,
BlockRead: func(br *blocks.Reader, i int, b string) error {
BlockRead: func(br *blocks.Reader, i int, b string, r bool) error {
if fmtverbs {
b = verbs.Escape(b)
}
Expand Down Expand Up @@ -428,7 +429,7 @@ func diffFile(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs, verbos
Log: log,
ReadOnly: true,
LineRead: blocks.ReaderPassthrough,
BlockRead: func(br *blocks.Reader, i int, b string) error {
BlockRead: func(br *blocks.Reader, i int, b string, r bool) error {
var fb string
var err error
if fmtverbs {
Expand All @@ -440,6 +441,10 @@ func diffFile(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs, verbos
return err
}

if r {
fb = reindent(fb, b)
}

if fb == b {
return nil
}
Expand Down Expand Up @@ -495,7 +500,7 @@ func formatFile(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs, fixF
br := blocks.Reader{
Log: log,
LineRead: blocks.ReaderPassthrough,
BlockRead: func(br *blocks.Reader, i int, b string) error {
BlockRead: func(br *blocks.Reader, i int, b string, r bool) error {
var fb string
var err error
if fmtverbs {
Expand All @@ -507,6 +512,10 @@ func formatFile(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs, fixF
return err
}

if r {
fb = reindent(fb, b)
}

hasChange := fb != b

if br.CurrentNodeCursor != nil {
Expand Down Expand Up @@ -569,7 +578,7 @@ func upgrade012File(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs,
br := blocks.Reader{
Log: log,
LineRead: blocks.ReaderPassthrough,
BlockRead: func(br *blocks.Reader, i int, b string) error {
BlockRead: func(br *blocks.Reader, i int, b string, r bool) error {
var fb string
var err error
if fmtverbs {
Expand All @@ -581,6 +590,10 @@ func upgrade012File(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs,
return err
}

if r {
fb = reindent(fb, b)
}

hasChange := fb != b

if br.CurrentNodeCursor != nil {
Expand Down Expand Up @@ -624,3 +637,19 @@ func upgrade012File(fs afero.Fs, log *logrus.Logger, filename string, fmtverbs,

return &br, err
}

func reindent(formatted string, original string) (reindented string) {
prefix := ""
for _, r := range original {
if unicode.IsSpace(r) {
if r == '\n' {
prefix = ""
continue
}
prefix += string(r)
} else {
break
}
}
return strings.TrimRight(strings.ReplaceAll(formatted, "\n", "\n"+prefix), prefix)
}
81 changes: 58 additions & 23 deletions lib/blocks/blockreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (
"go/token"
"io"
"io/ioutil"
"path/filepath"
"regexp"
"strconv"
"strings"
"unicode"

"github.com/sirupsen/logrus"
"github.com/spf13/afero"
Expand All @@ -21,13 +23,55 @@ import (

var lineWithLeadingSpacesMatcher = regexp.MustCompile("^[[:space:]]*(.*\n)$")

type blockReadFunc func(*Reader, int, string) error
type blockReadFunc func(*Reader, int, string, bool) error

type BlockWriter interface {
Write(index, startLine, endLine int, text string)
Close() error
}

type blockFormat interface {
isStartingLine(line string) bool
isFinishLine(line string) bool
keepIndentation() bool
}

type markdownBlockFormat struct{}

func (markdownBlockFormat) isStartingLine(line string) bool {
// nolint:gocritic
if strings.HasPrefix(line, "```hcl") { // documentation
return true
} else if strings.HasPrefix(line, "```terraform") { // documentation
return true
} else if strings.HasPrefix(line, "```tf") { // documentation
return true
}
return false
}

func (mbf markdownBlockFormat) isFinishLine(line string) bool {
return strings.HasPrefix(line, "```")
}

func (mbf markdownBlockFormat) keepIndentation() bool {
return false
}

type restructuredTextBlockFormat struct{}

func (restructuredTextBlockFormat) isStartingLine(line string) bool {
return strings.HasPrefix(line, ".. code:: terraform")
}

func (mbf restructuredTextBlockFormat) isFinishLine(line string) bool {
return strings.Compare(line, strings.TrimLeftFunc(line, unicode.IsSpace)) == 0
}

func (mbf restructuredTextBlockFormat) keepIndentation() bool {
return true
}

type Reader struct {
FileName string

Expand Down Expand Up @@ -71,23 +115,6 @@ func ReaderIgnore(br *Reader, number int, line string) error {
return nil
}

func IsStartLine(line string) bool {
// nolint:gocritic
if strings.HasPrefix(line, "```hcl") { // documentation
return true
} else if strings.HasPrefix(line, "```terraform") { // documentation
return true
} else if strings.HasPrefix(line, "```tf") { // documentation
return true
}

return false
}

func IsFinishLine(line string) bool {
return strings.HasPrefix(line, "```") // documentation
}

type blockVisitor struct {
br *Reader
fset *token.FileSet
Expand All @@ -113,7 +140,7 @@ func (bv blockVisitor) Visit(cursor *astutil.Cursor) bool {
bv.br.LineCount = bv.fset.Position(node.End()).Line
// This is to deal with some outputs using just LineCount and some using LineCount-BlockCurrentLine
bv.br.BlockCurrentLine = bv.fset.Position(node.End()).Line - bv.fset.Position(node.Pos()).Line
err := bv.f(bv.br, 0, value)
err := bv.f(bv.br, 0, value, false)
if err != nil {
bv.br.ErrorBlocks++
bv.br.Log.Errorf("block %d @ %s:%d failed to process with: %v", bv.br.BlockCount, bv.br.FileName, bv.fset.Position(node.Pos()).Line, err)
Expand Down Expand Up @@ -248,6 +275,14 @@ func (br *Reader) doTheThingPatternMatch(fs afero.Fs, filename string, stdin io.
}
}

var format blockFormat
switch filepath.Ext(filename) {
case ".rst":
format = restructuredTextBlockFormat{}
default:
format = markdownBlockFormat{}
}

br.LineCount = 0
br.BlockCount = 0
s := bufio.NewScanner(br.Reader)
Expand All @@ -260,7 +295,7 @@ func (br *Reader) doTheThingPatternMatch(fs afero.Fs, filename string, stdin io.
return fmt.Errorf("NB LineRead failed @ %s:%d for %s: %v", br.FileName, br.LineCount, l, err)
}

if IsStartLine(l) {
if format.isStartingLine(l) {
block := ""
br.BlockCurrentLine = 0
br.BlockCount += 1
Expand All @@ -271,7 +306,7 @@ func (br *Reader) doTheThingPatternMatch(fs afero.Fs, filename string, stdin io.
l2 := s.Text() + "\n"

// make sure we don't run into another block
if IsStartLine(l2) {
if format.isStartingLine(l2) {
// the end of current block must be malformed, so lets pass it through and log an error
br.Log.Errorf("block %d @ %s:%d failed to find end of block", br.BlockCount, br.FileName, br.LineCount-br.BlockCurrentLine)
if err := ReaderPassthrough(br, br.LineCount, block); err != nil { // is this ok or should we loop with LineRead?
Expand All @@ -287,15 +322,15 @@ func (br *Reader) doTheThingPatternMatch(fs afero.Fs, filename string, stdin io.
continue
}

if IsFinishLine(l2) {
if format.isFinishLine(l2) {
if br.FixFinishLines {
l2 = lineWithLeadingSpacesMatcher.ReplaceAllString(l2, `$1`)
}

br.LinesBlock += br.BlockCurrentLine

// todo configure this behaviour with switch's
if err := br.BlockRead(br, br.LineCount, block); err != nil {
if err := br.BlockRead(br, br.LineCount, block, format.keepIndentation()); err != nil {
// for now ignore block errors and output unformatted
br.ErrorBlocks += 1
br.Log.Errorf("block %d @ %s:%d failed to process with: %v", br.BlockCount, br.FileName, br.LineCount-br.BlockCurrentLine, err)
Expand Down
13 changes: 12 additions & 1 deletion lib/blocks/blockreader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ func TestBlockDetection(t *testing.T) {
resource "aws_s3_bucket" "leading-space-and-line" {
bucket = "tf-test-bucket-leading-space-and-line"
}
`,
},
},
},
{
sourcefile: "testdata/test3.rst",
expectedBlocks: []block{
{text: ` resource "aws_s3_bucket" "terraform" {
bucket = "tf-test-bucket-terraform"
}
`,
},
},
Expand All @@ -136,7 +147,7 @@ func TestBlockDetection(t *testing.T) {
Log: log,
ReadOnly: true,
LineRead: ReaderIgnore,
BlockRead: func(br *Reader, i int, b string) error {
BlockRead: func(br *Reader, i int, b string, r bool) error {
actualBlocks = append(actualBlocks, block{
leadingPadding: br.CurrentNodeLeadingPadding,
text: b,
Expand Down
10 changes: 10 additions & 0 deletions lib/blocks/testdata/test3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Test 3

Test fenced code block with `terraform`

.. code:: terraform
resource "aws_s3_bucket" "terraform" {
bucket = "tf-test-bucket-terraform"
}
Stuff that is not to be formatted.

0 comments on commit 9fdb760

Please sign in to comment.