summaryrefslogtreecommitdiffstats
path: root/src/go/printer/comment.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/go/printer/comment.go')
-rw-r--r--src/go/printer/comment.go155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/go/printer/comment.go b/src/go/printer/comment.go
new file mode 100644
index 0000000..9012714
--- /dev/null
+++ b/src/go/printer/comment.go
@@ -0,0 +1,155 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package printer
+
+import (
+ "go/ast"
+ "go/doc/comment"
+ "strings"
+)
+
+// formatDocComment reformats the doc comment list,
+// returning the canonical formatting.
+func formatDocComment(list []*ast.Comment) []*ast.Comment {
+ // Extract comment text (removing comment markers).
+ var kind, text string
+ var directives []*ast.Comment
+ if len(list) == 1 && strings.HasPrefix(list[0].Text, "/*") {
+ kind = "/*"
+ text = list[0].Text
+ if !strings.Contains(text, "\n") || allStars(text) {
+ // Single-line /* .. */ comment in doc comment position,
+ // or multiline old-style comment like
+ // /*
+ // * Comment
+ // * text here.
+ // */
+ // Should not happen, since it will not work well as a
+ // doc comment, but if it does, just ignore:
+ // reformatting it will only make the situation worse.
+ return list
+ }
+ text = text[2 : len(text)-2] // cut /* and */
+ } else if strings.HasPrefix(list[0].Text, "//") {
+ kind = "//"
+ var b strings.Builder
+ for _, c := range list {
+ after, found := strings.CutPrefix(c.Text, "//")
+ if !found {
+ return list
+ }
+ // Accumulate //go:build etc lines separately.
+ if isDirective(after) {
+ directives = append(directives, c)
+ continue
+ }
+ b.WriteString(strings.TrimPrefix(after, " "))
+ b.WriteString("\n")
+ }
+ text = b.String()
+ } else {
+ // Not sure what this is, so leave alone.
+ return list
+ }
+
+ if text == "" {
+ return list
+ }
+
+ // Parse comment and reformat as text.
+ var p comment.Parser
+ d := p.Parse(text)
+
+ var pr comment.Printer
+ text = string(pr.Comment(d))
+
+ // For /* */ comment, return one big comment with text inside.
+ slash := list[0].Slash
+ if kind == "/*" {
+ c := &ast.Comment{
+ Slash: slash,
+ Text: "/*\n" + text + "*/",
+ }
+ return []*ast.Comment{c}
+ }
+
+ // For // comment, return sequence of // lines.
+ var out []*ast.Comment
+ for text != "" {
+ var line string
+ line, text, _ = strings.Cut(text, "\n")
+ if line == "" {
+ line = "//"
+ } else if strings.HasPrefix(line, "\t") {
+ line = "//" + line
+ } else {
+ line = "// " + line
+ }
+ out = append(out, &ast.Comment{
+ Slash: slash,
+ Text: line,
+ })
+ }
+ if len(directives) > 0 {
+ out = append(out, &ast.Comment{
+ Slash: slash,
+ Text: "//",
+ })
+ for _, c := range directives {
+ out = append(out, &ast.Comment{
+ Slash: slash,
+ Text: c.Text,
+ })
+ }
+ }
+ return out
+}
+
+// isDirective reports whether c is a comment directive.
+// See go.dev/issue/37974.
+// This code is also in go/ast.
+func isDirective(c string) bool {
+ // "//line " is a line directive.
+ // "//extern " is for gccgo.
+ // "//export " is for cgo.
+ // (The // has been removed.)
+ if strings.HasPrefix(c, "line ") || strings.HasPrefix(c, "extern ") || strings.HasPrefix(c, "export ") {
+ return true
+ }
+
+ // "//[a-z0-9]+:[a-z0-9]"
+ // (The // has been removed.)
+ colon := strings.Index(c, ":")
+ if colon <= 0 || colon+1 >= len(c) {
+ return false
+ }
+ for i := 0; i <= colon+1; i++ {
+ if i == colon {
+ continue
+ }
+ b := c[i]
+ if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') {
+ return false
+ }
+ }
+ return true
+}
+
+// allStars reports whether text is the interior of an
+// old-style /* */ comment with a star at the start of each line.
+func allStars(text string) bool {
+ for i := 0; i < len(text); i++ {
+ if text[i] == '\n' {
+ j := i + 1
+ for j < len(text) && (text[j] == ' ' || text[j] == '\t') {
+ j++
+ }
+ if j < len(text) && text[j] != '*' {
+ return false
+ }
+ }
+ }
+ return true
+}