]> Cypherpunks repositories - gostls13.git/commitdiff
go/doc/comment: parse and print code
authorRuss Cox <rsc@golang.org>
Sun, 3 Apr 2022 20:40:03 +0000 (16:40 -0400)
committerRuss Cox <rsc@golang.org>
Mon, 11 Apr 2022 16:31:47 +0000 (16:31 +0000)
[This CL is part of a sequence implementing the proposal #51082.
The design doc is at https://go.dev/s/godocfmt-design.]

Implement indented code blocks.

For #51082.

Change-Id: I0eacbf56e101424a875386cb6f26174b239561f5
Reviewed-on: https://go-review.googlesource.com/c/go/+/397285
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/go/doc/comment/html.go
src/go/doc/comment/markdown.go
src/go/doc/comment/parse.go
src/go/doc/comment/print.go
src/go/doc/comment/testdata/code.txt [new file with mode: 0644]
src/go/doc/comment/testdata/code2.txt [new file with mode: 0644]
src/go/doc/comment/testdata/code3.txt [new file with mode: 0644]
src/go/doc/comment/testdata/text9.txt [new file with mode: 0644]
src/go/doc/comment/text.go

index f6ea588b3d5574ccc87d4154eca526a41737650c..14a20b91e5e917087e6c8909ec58d321c1fd497f 100644 (file)
@@ -51,6 +51,11 @@ func (p *htmlPrinter) block(out *bytes.Buffer, x Block) {
                out.WriteString("</h")
                out.WriteString(h)
                out.WriteString(">\n")
+
+       case *Code:
+               out.WriteString("<pre>")
+               p.escape(out, x.Text)
+               out.WriteString("</pre>\n")
        }
 }
 
index 44ea727daeeaae5542d30d31fea9e3a30527d939..9e86cd8aeff4e1a5437a41b9941801adc7a2187d 100644 (file)
@@ -54,6 +54,18 @@ func (p *mdPrinter) block(out *bytes.Buffer, x Block) {
                        out.WriteString("}")
                }
                out.WriteString("\n")
+
+       case *Code:
+               md := x.Text
+               for md != "" {
+                       var line string
+                       line, md, _ = strings.Cut(md, "\n")
+                       if line != "" {
+                               out.WriteString("\t")
+                               out.WriteString(line)
+                       }
+                       out.WriteString("\n")
+               }
        }
 }
 
index 25b5f10f2fcce63fa48747bd0d145223d4323788..7f97e41a62a42fd3af8ef1c19424b6fed4f66828 100644 (file)
@@ -309,6 +309,9 @@ func (p *Parser) Parse(text string) *Doc {
                case line == "":
                        // emit nothing
 
+               case isIndented(line):
+                       b, lines = d.code(lines)
+
                case (len(lines) == 1 || lines[1] == "") && !didHeading && isOldHeading(line, all, len(all)-n):
                        b = d.oldHeading(line)
                        didHeading = true
@@ -473,17 +476,51 @@ func (d *parseDoc) heading(line string) Block {
        return &Heading{Text: []Text{Plain(strings.TrimSpace(line[1:]))}}
 }
 
+// code returns a code block built from the indented text
+// at the start of lines, along with the remainder of the lines.
+// If there is no indented text at the start, or if the indented
+// text consists only of empty lines, code returns a nil Block.
+func (d *parseDoc) code(lines []string) (b Block, rest []string) {
+       lines, rest = indented(lines)
+       body := unindent(lines)
+       if len(body) == 0 {
+               return nil, rest
+       }
+       body = append(body, "") // to get final \n from Join
+       return &Code{Text: strings.Join(body, "\n")}, rest
+}
+
+// isIndented reports whether the line is indented,
+// meaning it starts with a space or tab.
+func isIndented(line string) bool {
+       return line != "" && (line[0] == ' ' || line[0] == '\t')
+}
+
+// indented splits lines into an initial indented section
+// and the remaining lines, returning the two halves.
+func indented(lines []string) (indented, rest []string) {
+       // Blank lines mid-run are OK, but not at the end.
+       i := 0
+       for i < len(lines) && (isIndented(lines[i]) || lines[i] == "") {
+               i++
+       }
+       for i > 0 && lines[i-1] == "" {
+               i--
+       }
+       return lines[:i], lines[i:]
+}
+
 // paragraph returns a paragraph block built from the
 // unindented text at the start of lines, along with the remainder of the lines.
 // If there is no unindented text at the start of lines,
 // then paragraph returns a nil Block.
 func (d *parseDoc) paragraph(lines []string) (b Block, rest []string) {
-       // TODO: Paragraph should be interrupted by any indented line,
+       // Paragraph is interrupted by any indented line,
        // which is either a list or a code block,
        // and of course by a blank line.
-       // It should not be interrupted by a # line - headings must stand alone.
+       // It is not interrupted by a # line - headings must stand alone.
        i := 0
-       for i < len(lines) && lines[i] != "" {
+       for i < len(lines) && lines[i] != "" && !isIndented(lines[i]) {
                i++
        }
        lines, rest = lines[:i], lines[i:]
index db520e81925e9edbee59582627631a8ed62aebc8..d426b8176187b4790c119b17867188d6ab1d0763 100644 (file)
@@ -213,6 +213,18 @@ func (p *commentPrinter) block(out *bytes.Buffer, x Block) {
                out.WriteString("# ")
                p.text(out, "", x.Text)
                out.WriteString("\n")
+
+       case *Code:
+               md := x.Text
+               for md != "" {
+                       var line string
+                       line, md, _ = strings.Cut(md, "\n")
+                       if line != "" {
+                               out.WriteString("\t")
+                               out.WriteString(line)
+                       }
+                       out.WriteString("\n")
+               }
        }
 }
 
diff --git a/src/go/doc/comment/testdata/code.txt b/src/go/doc/comment/testdata/code.txt
new file mode 100644 (file)
index 0000000..06b1519
--- /dev/null
@@ -0,0 +1,94 @@
+-- input --
+Text.
+       A tab-indented
+       (no, not eight-space indented)
+       code block and haiku.
+More text.
+ One space
+  is
+   enough
+    to
+     start
+      a
+       block.
+More text.
+
+      Blocks
+    can
+
+  have
+    blank
+      lines.
+-- gofmt --
+Text.
+
+       A tab-indented
+       (no, not eight-space indented)
+       code block and haiku.
+
+More text.
+
+       One space
+        is
+         enough
+          to
+           start
+            a
+             block.
+
+More text.
+
+           Blocks
+         can
+
+       have
+         blank
+           lines.
+-- markdown --
+Text.
+
+       A tab-indented
+       (no, not eight-space indented)
+       code block and haiku.
+
+More text.
+
+       One space
+        is
+         enough
+          to
+           start
+            a
+             block.
+
+More text.
+
+           Blocks
+         can
+
+       have
+         blank
+           lines.
+-- html --
+<p>Text.
+<pre>A tab-indented
+(no, not eight-space indented)
+code block and haiku.
+</pre>
+<p>More text.
+<pre>One space
+ is
+  enough
+   to
+    start
+     a
+      block.
+</pre>
+<p>More text.
+<pre>    Blocks
+  can
+
+have
+  blank
+    lines.
+</pre>
diff --git a/src/go/doc/comment/testdata/code2.txt b/src/go/doc/comment/testdata/code2.txt
new file mode 100644 (file)
index 0000000..0810bed
--- /dev/null
@@ -0,0 +1,31 @@
+-- input --
+Text.
+
+       A tab-indented
+       (no, not eight-space indented)
+       code block and haiku.
+
+More text.
+-- gofmt --
+Text.
+
+       A tab-indented
+       (no, not eight-space indented)
+       code block and haiku.
+
+More text.
+-- markdown --
+Text.
+
+       A tab-indented
+       (no, not eight-space indented)
+       code block and haiku.
+
+More text.
+-- html --
+<p>Text.
+<pre>A tab-indented
+(no, not eight-space indented)
+code block and haiku.
+</pre>
+<p>More text.
diff --git a/src/go/doc/comment/testdata/code3.txt b/src/go/doc/comment/testdata/code3.txt
new file mode 100644 (file)
index 0000000..4a96a0e
--- /dev/null
@@ -0,0 +1,33 @@
+-- input --
+Text.
+
+       $
+       A tab-indented
+       (surrounded by more blank lines)
+       code block and haiku.
+       $
+
+More text.
+-- gofmt --
+Text.
+
+       A tab-indented
+       (surrounded by more blank lines)
+       code block and haiku.
+
+More text.
+-- markdown --
+Text.
+
+       A tab-indented
+       (surrounded by more blank lines)
+       code block and haiku.
+
+More text.
+-- html --
+<p>Text.
+<pre>A tab-indented
+(surrounded by more blank lines)
+code block and haiku.
+</pre>
+<p>More text.
diff --git a/src/go/doc/comment/testdata/text9.txt b/src/go/doc/comment/testdata/text9.txt
new file mode 100644 (file)
index 0000000..07a64aa
--- /dev/null
@@ -0,0 +1,12 @@
+{"TextPrefix":"|", "TextCodePrefix": "@"}
+-- input --
+Hello, world
+ Code block here.
+-- gofmt --
+Hello, world
+
+       Code block here.
+-- text --
+|Hello, world
+|
+@Code block here.
index 1eddad30fd51e86a0222052169f89c42cb30f262..e35e5ccfd1a6845c84bc683b9c406ee041536d4f 100644 (file)
@@ -15,18 +15,23 @@ import (
 // A textPrinter holds the state needed for printing a Doc as plain text.
 type textPrinter struct {
        *Printer
-       long   strings.Builder
-       prefix string
-       width  int
+       long       strings.Builder
+       prefix     string
+       codePrefix string
+       width      int
 }
 
 // Text returns a textual formatting of the Doc.
 // See the [Printer] documentation for ways to customize the text output.
 func (p *Printer) Text(d *Doc) []byte {
        tp := &textPrinter{
-               Printer: p,
-               prefix:  p.TextPrefix,
-               width:   p.TextWidth,
+               Printer:    p,
+               prefix:     p.TextPrefix,
+               codePrefix: p.TextCodePrefix,
+               width:      p.TextWidth,
+       }
+       if tp.codePrefix == "" {
+               tp.codePrefix = p.TextPrefix + "\t"
        }
        if tp.width == 0 {
                tp.width = 80 - utf8.RuneCountInString(tp.prefix)
@@ -35,6 +40,7 @@ func (p *Printer) Text(d *Doc) []byte {
        var out bytes.Buffer
        for i, x := range d.Content {
                if i > 0 && blankBefore(x) {
+                       out.WriteString(tp.prefix)
                        writeNL(&out)
                }
                tp.block(&out, x)
@@ -86,6 +92,18 @@ func (p *textPrinter) block(out *bytes.Buffer, x Block) {
                out.WriteString(p.prefix)
                out.WriteString("# ")
                p.text(out, x.Text)
+
+       case *Code:
+               text := x.Text
+               for text != "" {
+                       var line string
+                       line, text, _ = strings.Cut(text, "\n")
+                       if line != "" {
+                               out.WriteString(p.codePrefix)
+                               out.WriteString(line)
+                       }
+                       writeNL(out)
+               }
        }
 }