Skip to content

Commit

Permalink
Add SubListLength option
Browse files Browse the repository at this point in the history
This allow you to set the indentation length for sublists. The default
is 1. Setting it to 2+ can be used when you want to be compatible with
non-CommonMark renderers that assume that a sub-list needs at minimum 4
spaces for indentation, like https://python-markdown.github.io/.
  • Loading branch information
thiagokokada authored and teekennedy committed Dec 14, 2024
1 parent 0cdef01 commit b1ec3b8
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 11 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ the renderer.
| WithHeadingStyle | markdown.HeadingStyle | Render markdown headings as ATX (`#`-based), Setext (underlined with `===` or `---`), or variants thereof. |
| WithThematicBreakStyle | markdown.ThematicBreakStyle | Render thematic breaks with `-`, `*`, or `_`. |
| WithThematicBreakLength | markdown.ThematicBreakLength | Number of characters to use in a thematic break (minimum 3). |
| WithSubListLength | markdown.SubListLength | Number of characters to use in a sub list indentation (minimum 1). |

## As a markdown transformer

Expand Down
50 changes: 46 additions & 4 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import (

// Config struct holds configurations for the markdown based renderer.
type Config struct {
IndentStyle IndentStyle
HeadingStyle HeadingStyle
ThematicBreakStyle ThematicBreakStyle
ThematicBreakLength ThematicBreakLength
IndentStyle
HeadingStyle
ThematicBreakStyle
ThematicBreakLength
SubListLength
}

// NewConfig returns a new Config with defaults and the given options.
Expand All @@ -19,6 +20,7 @@ func NewConfig(options ...Option) *Config {
HeadingStyle: HeadingStyle(HeadingStyleATX),
ThematicBreakStyle: ThematicBreakStyle(ThematicBreakStyleDashed),
ThematicBreakLength: ThematicBreakLength(ThematicBreakLengthMinimum),
SubListLength: SubListLength(SubListLengthMinimum),
}
for _, opt := range options {
opt.SetMarkdownOption(c)
Expand All @@ -37,6 +39,8 @@ func (c *Config) SetOption(name renderer.OptionName, value interface{}) {
c.ThematicBreakStyle = value.(ThematicBreakStyle)
case optThematicBreakLength:
c.ThematicBreakLength = value.(ThematicBreakLength)
case optSubListLength:
c.SubListLength = value.(SubListLength)
}
}

Expand Down Expand Up @@ -227,3 +231,41 @@ func WithThematicBreakLength(style ThematicBreakLength) interface {
} {
return &withThematicBreakLength{style}
}

// ============================================================================
// SubListLength Option
// ============================================================================

// optSubListLength is an option name used in WithSubListLength
const optSubListLength renderer.OptionName = "SubListLength"

// SubListLength configures the character length of sublist indentation
type SubListLength int

const (
// SubListLengthMinimum is the minimum length of a sublist length. This is the default.
// Any lengths less than this minimum are converted to the minimum.
// Ex: ---
SubListLengthMinimum = 1
)

type withSubListLength struct {
value SubListLength
}

func (o *withSubListLength) SetConfig(c *renderer.Config) {
c.Options[optSubListLength] = o.value
}

// SetMarkdownOption implements renderer.Option
func (o *withSubListLength) SetMarkdownOption(c *Config) {
c.SubListLength = o.value
}

// WithSubListLength is a functional option that sets the length of sub lists indentation.
func WithSubListLength(style SubListLength) interface {
renderer.Option
Option
} {
return &withSubListLength{style}
}
1 change: 1 addition & 0 deletions options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func TestRendererOptions(t *testing.T) {
WithHeadingStyle(HeadingStyleATX),
WithThematicBreakStyle(ThematicBreakStyleDashed),
WithThematicBreakLength(ThematicBreakLengthMinimum),
WithSubListLength(SubListLengthMinimum),
},
NewConfig(),
},
Expand Down
11 changes: 4 additions & 7 deletions renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,7 @@ func (r *Renderer) renderThematicBreak(node ast.Node, entering bool) ast.WalkSta
if entering {
breakChars := []byte{'-', '*', '_'}
breakChar := breakChars[r.config.ThematicBreakStyle : r.config.ThematicBreakStyle+1]
var breakLen int
if r.config.ThematicBreakLength < ThematicBreakLengthMinimum {
breakLen = int(ThematicBreakLengthMinimum)
} else {
breakLen = int(r.config.ThematicBreakLength)
}
breakLen := int(max(r.config.ThematicBreakLength, ThematicBreakLengthMinimum))
r.rc.writer.WriteBytes(bytes.Repeat(breakChar, breakLen))
}
return ast.WalkContinue
Expand Down Expand Up @@ -294,7 +289,9 @@ func (r *Renderer) renderListItem(node ast.Node, entering bool) ast.WalkStatus {
// Prefix the current line with the item prefix
r.rc.writer.PushPrefix(itemPrefix, 0, 0)
// Prefix subsequent lines with padding the same length as the item prefix
r.rc.writer.PushPrefix(bytes.Repeat([]byte(" "), len(itemPrefix)), 1)
indentLen := int(max(r.config.SubListLength, SubListLengthMinimum))
indent := bytes.Repeat([]byte{' '}, indentLen)
r.rc.writer.PushPrefix(bytes.Repeat(indent, len(itemPrefix)), 1)
} else {
r.rc.writer.PopPrefix()
r.rc.writer.PopPrefix()
Expand Down
6 changes: 6 additions & 0 deletions renderer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,12 @@ func TestRenderedOutput(t *testing.T) {
"1. A1\n2. B1\n - C2\n 1. D3\n 2. E3\n - F2\n - G2\n3. H1\n",
"1. A1\n2. B1\n - C2\n 1. D3\n 2. E3\n - F2\n - G2\n3. H1\n",
},
{
"Sub list length",
[]Option{WithSubListLength(2)},
"1. A1\n2. B1\n - C2\n 1. D3\n 2. E3\n - F2\n - G2\n3. H1\n",
"1. A1\n2. B1\n - C2\n 1. D3\n 2. E3\n - F2\n - G2\n3. H1\n",
},
// Block separators
{
"ATX heading block separator",
Expand Down

0 comments on commit b1ec3b8

Please sign in to comment.