Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions internal/schemamd/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package schemamd
import (
"fmt"
"io"
"regexp"
"slices"
"sort"
"strings"
Expand All @@ -14,6 +15,11 @@ import (
"github.com/zclconf/go-cty/cty"
)

var (
// multipleNewlinesRegexRender matches multiple consecutive newlines (2 or more)
multipleNewlinesRegexRender = regexp.MustCompile(`\n\n+`)
)

// Render writes a Markdown formatted Schema definition to the specified writer.
// A Schema contains a Version and the root Block, for example:
//
Expand Down Expand Up @@ -154,6 +160,8 @@ func writeIdentityAttribute(w io.Writer, name string, attr *tfjson.IdentityAttri

desc := strings.TrimSpace(attr.Description)
if desc != "" {
// Collapse multiple newlines while keeping list formatting intact.
desc = multipleNewlinesRegexRender.ReplaceAllString(desc, " \n")
_, err = io.WriteString(w, " "+desc)
if err != nil {
return err
Expand Down
6 changes: 6 additions & 0 deletions internal/schemamd/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ func TestRender(t *testing.T) {
"testdata/deep_nested_write_only_attributes.schema.json",
"testdata/deep_nested_write_only_attributes.md",
},
{
// Reference: https://github.com/hashicorp/terraform-plugin-docs/issues/531
"multiline_descriptions",
"testdata/multiline_descriptions.schema.json",
"testdata/multiline_descriptions.md",
},
} {
t.Run(c.name, func(t *testing.T) {
t.Parallel()
Expand Down
18 changes: 18 additions & 0 deletions internal/schemamd/testdata/multiline_descriptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## Schema

### Required

- `long_description` (String) Line one.
Line two.
Line three.

### Optional

- `simple_description` (String) This is a simple single-line description.
- `two_line_description` (String) First paragraph.
Second paragraph with more details.

### Read-Only

- `id` (String) The ID of this resource.

31 changes: 31 additions & 0 deletions internal/schemamd/testdata/multiline_descriptions.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"version": 0,
"block": {
"attributes": {
"id": {
"type": "string",
"description_kind": "plain",
"computed": true
},
"two_line_description": {
"type": "string",
"description": "First paragraph.\n\nSecond paragraph with more details.",
"description_kind": "markdown",
"optional": true
},
"long_description": {
"type": "string",
"description": "Line one.\n\nLine two.\n\nLine three.",
"description_kind": "markdown",
"required": true
},
"simple_description": {
"type": "string",
"description": "This is a simple single-line description.",
"description_kind": "markdown",
"optional": true
}
}
}
}

8 changes: 8 additions & 0 deletions internal/schemamd/write_attribute_description.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ package schemamd
import (
"fmt"
"io"
"regexp"
"strings"

tfjson "github.com/hashicorp/terraform-json"
)

var (
// multipleNewlinesRegex matches multiple consecutive newlines (2 or more)
multipleNewlinesRegex = regexp.MustCompile(`\n\n+`)
)

func WriteAttributeDescription(w io.Writer, att *tfjson.SchemaAttribute, includeRW bool) error {
_, err := io.WriteString(w, "(")
if err != nil {
Expand Down Expand Up @@ -72,6 +78,8 @@ func WriteAttributeDescription(w io.Writer, att *tfjson.SchemaAttribute, include

desc := strings.TrimSpace(att.Description)
if desc != "" {
// Collapse multiple newlines while keeping list formatting intact.
desc = multipleNewlinesRegex.ReplaceAllString(desc, " \n")
_, err = io.WriteString(w, " "+desc)
if err != nil {
return err
Expand Down
26 changes: 26 additions & 0 deletions internal/schemamd/write_attribute_description_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,32 @@ func TestWriteAttributeDescription(t *testing.T) {
Description: "\n\t This is an attribute.\n\t ",
},
},

// multiple consecutive newlines (issue #531)
{
"(String, Required) First paragraph. \nSecond paragraph with more details.",
&tfjson.SchemaAttribute{
AttributeType: cty.String,
Required: true,
Description: "First paragraph.\n\nSecond paragraph with more details.",
},
},
{
"(String, Optional) Line one. \nLine two. \nLine three.",
&tfjson.SchemaAttribute{
AttributeType: cty.String,
Optional: true,
Description: "Line one.\n\nLine two.\n\nLine three.",
},
},
{
"(String, Optional) Line one. \nLine two with more text.",
&tfjson.SchemaAttribute{
AttributeType: cty.String,
Optional: true,
Description: "Line one.\n\n\nLine two with more text.",
},
},
} {
t.Run(c.expected, func(t *testing.T) {
t.Parallel()
Expand Down
8 changes: 8 additions & 0 deletions internal/schemamd/write_block_type_description.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ package schemamd
import (
"fmt"
"io"
"regexp"
"strings"

tfjson "github.com/hashicorp/terraform-json"
)

var (
// multipleNewlinesRegexBlock matches multiple consecutive newlines (2 or more)
multipleNewlinesRegexBlock = regexp.MustCompile(`\n\n+`)
)

func WriteBlockTypeDescription(w io.Writer, block *tfjson.SchemaBlockType) error {
_, err := io.WriteString(w, "(Block")
if err != nil {
Expand Down Expand Up @@ -89,6 +95,8 @@ func WriteBlockTypeDescription(w io.Writer, block *tfjson.SchemaBlockType) error

desc := strings.TrimSpace(block.Block.Description)
if desc != "" {
// Collapse multiple newlines while keeping list formatting intact.
desc = multipleNewlinesRegexBlock.ReplaceAllString(desc, " \n")
_, err = io.WriteString(w, " "+desc)
if err != nil {
return err
Expand Down
20 changes: 20 additions & 0 deletions internal/schemamd/write_block_type_description_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,26 @@ func TestWriteBlockTypeDescription(t *testing.T) {
},
},
},

// multiple consecutive newlines (issue #531)
{
"(Block, Optional) First paragraph. \nSecond paragraph with more details.",
&tfjson.SchemaBlockType{
NestingMode: tfjson.SchemaNestingModeSingle,
Block: &tfjson.SchemaBlock{
Description: "First paragraph.\n\nSecond paragraph with more details.",
},
},
},
{
"(Block List) Line one. \nLine two. \nLine three.",
&tfjson.SchemaBlockType{
NestingMode: tfjson.SchemaNestingModeList,
Block: &tfjson.SchemaBlock{
Description: "Line one.\n\nLine two.\n\nLine three.",
},
},
},
} {
t.Run(c.expected, func(t *testing.T) {
t.Parallel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ package schemamd
import (
"fmt"
"io"
"regexp"
"strings"

tfjson "github.com/hashicorp/terraform-json"
)

var (
// multipleNewlinesRegexNested matches multiple consecutive newlines (2 or more)
multipleNewlinesRegexNested = regexp.MustCompile(`\n\n+`)
)

func WriteNestedAttributeTypeDescription(w io.Writer, att *tfjson.SchemaAttribute, includeRW bool) error {
nestedAttributeType := att.AttributeNestedType
if nestedAttributeType == nil {
Expand Down Expand Up @@ -111,6 +117,8 @@ func WriteNestedAttributeTypeDescription(w io.Writer, att *tfjson.SchemaAttribut

desc := strings.TrimSpace(att.Description)
if desc != "" {
// Collapse multiple newlines while keeping list formatting intact.
desc = multipleNewlinesRegexNested.ReplaceAllString(desc, " \n")
_, err = io.WriteString(w, " "+desc)
if err != nil {
return err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,42 @@ func TestWriteNestedAttributeTypeDescription(t *testing.T) {
},
},
},

// multiple consecutive newlines (issue #531)
{
"(Attributes, Optional) First paragraph. \nSecond paragraph with more details.",
&tfjson.SchemaAttribute{
Description: "First paragraph.\n\nSecond paragraph with more details.",
AttributeNestedType: &tfjson.SchemaNestedAttributeType{
NestingMode: tfjson.SchemaNestingModeSingle,
Attributes: map[string]*tfjson.SchemaAttribute{
"foo": {
AttributeType: cty.String,
Required: true,
},
},
},
Optional: true,
},
},
{
"(Attributes List, Min: 2, Max: 3) Line one. \nLine two. \nLine three.",
&tfjson.SchemaAttribute{
Description: "Line one.\n\nLine two.\n\nLine three.",
AttributeNestedType: &tfjson.SchemaNestedAttributeType{
NestingMode: tfjson.SchemaNestingModeList,
Attributes: map[string]*tfjson.SchemaAttribute{
"foo": {
AttributeType: cty.String,
Required: true,
},
},
MinItems: 2,
MaxItems: 3,
},
Required: true,
},
},
} {
t.Run(c.expected, func(t *testing.T) {
t.Parallel()
Expand Down