diff --git a/README.md b/README.md index 16489d5..bc49543 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/options.go b/options.go index cb60062..b90f685 100644 --- a/options.go +++ b/options.go @@ -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. @@ -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) @@ -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) } } @@ -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} +} diff --git a/options_test.go b/options_test.go index 6d3ace0..6a721ed 100644 --- a/options_test.go +++ b/options_test.go @@ -26,6 +26,7 @@ func TestRendererOptions(t *testing.T) { WithHeadingStyle(HeadingStyleATX), WithThematicBreakStyle(ThematicBreakStyleDashed), WithThematicBreakLength(ThematicBreakLengthMinimum), + WithSubListLength(SubListLengthMinimum), }, NewConfig(), }, diff --git a/renderer.go b/renderer.go index d1fc294..9281964 100644 --- a/renderer.go +++ b/renderer.go @@ -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 @@ -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() diff --git a/renderer_test.go b/renderer_test.go index cea2dde..2ffc2fd 100644 --- a/renderer_test.go +++ b/renderer_test.go @@ -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",