Skip to content

Commit

Permalink
go/doc/comment: parse and print lists
Browse files Browse the repository at this point in the history
[This CL is part of a sequence implementing the proposal golang#51082.
The design doc is at https://go.dev/s/godocfmt-design.]

Implement lists, like:

	Three numbers:

	  - One
	  - Two
	  - Three

For golang#51082.

Change-Id: Id87d9c19bca677be968f3803809a9ea6c705f3ad
Reviewed-on: https://go-review.googlesource.com/c/go/+/397286
Run-TryBot: Russ Cox <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
Reviewed-by: Jonathan Amsterdam <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
  • Loading branch information
rsc committed Apr 11, 2022
1 parent 3f5d099 commit e1b0862
Show file tree
Hide file tree
Showing 15 changed files with 869 additions and 5 deletions.
48 changes: 47 additions & 1 deletion src/go/doc/comment/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
// An htmlPrinter holds the state needed for printing a Doc as HTML.
type htmlPrinter struct {
*Printer
tight bool
}

// HTML returns an HTML formatting of the Doc.
Expand All @@ -33,7 +34,9 @@ func (p *htmlPrinter) block(out *bytes.Buffer, x Block) {
fmt.Fprintf(out, "?%T", x)

case *Paragraph:
out.WriteString("<p>")
if !p.tight {
out.WriteString("<p>")
}
p.text(out, x.Text)
out.WriteString("\n")

Expand All @@ -56,7 +59,50 @@ func (p *htmlPrinter) block(out *bytes.Buffer, x Block) {
out.WriteString("<pre>")
p.escape(out, x.Text)
out.WriteString("</pre>\n")

case *List:
kind := "ol>\n"
if x.Items[0].Number == "" {
kind = "ul>\n"
}
out.WriteString("<")
out.WriteString(kind)
next := "1"
for _, item := range x.Items {
out.WriteString("<li")
if n := item.Number; n != "" {
if n != next {
out.WriteString(` value="`)
out.WriteString(n)
out.WriteString(`"`)
next = n
}
next = inc(next)
}
out.WriteString(">")
p.tight = !x.BlankBetween()
for _, blk := range item.Content {
p.block(out, blk)
}
p.tight = false
}
out.WriteString("</")
out.WriteString(kind)
}
}

// inc increments the decimal string s.
// For example, inc("1199") == "1200".
func inc(s string) string {
b := []byte(s)
for i := len(b) - 1; i >= 0; i-- {
if b[i] < '9' {
b[i]++
return string(b)
}
b[i] = '0'
}
return "1" + string(b)
}

// text prints the text sequence x to out.
Expand Down
23 changes: 23 additions & 0 deletions src/go/doc/comment/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,29 @@ func (p *mdPrinter) block(out *bytes.Buffer, x Block) {
}
out.WriteString("\n")
}

case *List:
loose := x.BlankBetween()
for i, item := range x.Items {
if i > 0 && loose {
out.WriteString("\n")
}
if n := item.Number; n != "" {
out.WriteString(" ")
out.WriteString(n)
out.WriteString(". ")
} else {
out.WriteString(" - ") // SP SP - SP
}
for i, blk := range item.Content {
const fourSpace = " "
if i > 0 {
out.WriteString("\n" + fourSpace)
}
p.text(out, blk.(*Paragraph).Text)
out.WriteString("\n")
}
}
}
}

Expand Down
91 changes: 91 additions & 0 deletions src/go/doc/comment/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ func (p *Parser) Parse(text string) *Doc {
case line == "":
// emit nothing

case isList(line):
prevWasBlank := len(lines) < len(all) && all[len(all)-len(lines)-1] == ""
b, lines = d.list(lines, prevWasBlank)

case isIndented(line):
b, lines = d.code(lines)

Expand Down Expand Up @@ -575,6 +579,93 @@ func parseLink(line string) (*LinkDef, bool) {
return &LinkDef{Text: text, URL: url}, true
}

// list returns a list built from the indented text at the start of lines,
// using forceBlankBefore as the value of the List's ForceBlankBefore field.
// The caller is responsible for ensuring that the first line of lines
// satisfies isList.
// list returns the *List as a Block along with the remaining lines.
func (d *parseDoc) list(lines []string, forceBlankBefore bool) (b Block, rest []string) {
lines, rest = indented(lines)

num, _, _ := listMarker(lines[0])
var (
list *List = &List{ForceBlankBefore: forceBlankBefore}
item *ListItem
text []string
)
flush := func() {
if item != nil {
if para, _ := d.paragraph(text); para != nil {
item.Content = append(item.Content, para)
}
}
text = nil
}

for _, line := range lines {
if n, after, ok := listMarker(line); ok && (n != "") == (num != "") {
// start new list item
flush()

item = &ListItem{Number: n}
list.Items = append(list.Items, item)
line = after
}
line = strings.TrimSpace(line)
if line == "" {
list.ForceBlankBetween = true
flush()
continue
}
text = append(text, strings.TrimSpace(line))
}
flush()
return list, rest
}

// listMarker parses the line as an indented line beginning with a list marker.
// If it can do that, it returns the numeric marker ("" for a bullet list),
// the rest of the line, and ok == true.
// Otherwise, it returns "", "", false.
func listMarker(line string) (num, rest string, ok bool) {
if !isIndented(line) {
return "", "", false
}
line = strings.TrimSpace(line)
if line == "" {
return "", "", false
}

// Can we find a marker?
if r, n := utf8.DecodeRuneInString(line); r == '•' || r == '*' || r == '+' || r == '-' {
num, rest = "", line[n:]
} else if '0' <= line[0] && line[0] <= '9' {
n := 1
for n < len(line) && '0' <= line[n] && line[n] <= '9' {
n++
}
if n >= len(line) || (line[n] != '.' && line[n] != ')') {
return "", "", false
}
num, rest = line[:n], line[n+1:]
} else {
return "", "", false
}

if !isIndented(rest) || strings.TrimSpace(rest) == "" {
return "", "", false
}

return num, rest, true
}

// isList reports whether the line is the first line of a list,
// meaning is indented and starts with a list marker.
func isList(line string) bool {
_, _, ok := listMarker(line)
return ok
}

// parseLinkedText parses text that is allowed to contain explicit links,
// such as [math.Sin] or [Go home page], into a slice of Text items.
//
Expand Down
23 changes: 23 additions & 0 deletions src/go/doc/comment/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,29 @@ func (p *commentPrinter) block(out *bytes.Buffer, x Block) {
}
out.WriteString("\n")
}

case *List:
loose := x.BlankBetween()
for i, item := range x.Items {
if i > 0 && loose {
out.WriteString("\n")
}
out.WriteString(" ")
if item.Number == "" {
out.WriteString(" - ")
} else {
out.WriteString(item.Number)
out.WriteString(". ")
}
for i, blk := range item.Content {
const fourSpace = " "
if i > 0 {
out.WriteString("\n" + fourSpace)
}
p.text(out, fourSpace, blk.(*Paragraph).Text)
out.WriteString("\n")
}
}
}
}

Expand Down
48 changes: 48 additions & 0 deletions src/go/doc/comment/testdata/list.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- input --
Text.
- Not a list.
- Here is the list.
• Using multiple bullets.
* Indentation does not matter.
+ Lots of bullets.
More text.

-- gofmt --
Text.
- Not a list.
- Here is the list.
- Using multiple bullets.
- Indentation does not matter.
- Lots of bullets.

More text.

-- text --
Text. - Not a list.
- Here is the list.
- Using multiple bullets.
- Indentation does not matter.
- Lots of bullets.

More text.

-- markdown --
Text. - Not a list.

- Here is the list.
- Using multiple bullets.
- Indentation does not matter.
- Lots of bullets.

More text.

-- html --
<p>Text.
- Not a list.
<ul>
<li>Here is the list.
<li>Using multiple bullets.
<li>Indentation does not matter.
<li>Lots of bullets.
</ul>
<p>More text.
57 changes: 57 additions & 0 deletions src/go/doc/comment/testdata/list2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
-- input --
Text.
1. Uno
2) Dos
3. Tres
5. Cinco
7. Siete
11. Once
12. Doce
13. Trece.

-- gofmt --
Text.
1. Uno
2. Dos
3. Tres
5. Cinco
7. Siete
11. Once
12. Doce
13. Trece.

-- text --
Text.
1. Uno
2. Dos
3. Tres
5. Cinco
7. Siete
11. Once
12. Doce
13. Trece.

-- markdown --
Text.

1. Uno
2. Dos
3. Tres
5. Cinco
7. Siete
11. Once
12. Doce
13. Trece.

-- html --
<p>Text.
<ol>
<li>Uno
<li>Dos
<li>Tres
<li value="5">Cinco
<li value="7">Siete
<li value="11">Once
<li>Doce
<li>Trece.
</ol>
32 changes: 32 additions & 0 deletions src/go/doc/comment/testdata/list3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- input --
Text.

1. Uno
1. Dos
1. Tres
1. Quatro

-- gofmt --
Text.

1. Uno
1. Dos
1. Tres
1. Quatro

-- markdown --
Text.

1. Uno
1. Dos
1. Tres
1. Quatro

-- html --
<p>Text.
<ol>
<li>Uno
<li value="1">Dos
<li value="1">Tres
<li value="1">Quatro
</ol>
Loading

0 comments on commit e1b0862

Please sign in to comment.