github.com/bir3/gocompiler@v0.9.2202/src/go/printer/comment.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package printer 6 7 import ( 8 "github.com/bir3/gocompiler/src/go/ast" 9 "github.com/bir3/gocompiler/src/go/doc/comment" 10 "strings" 11 ) 12 13 // formatDocComment reformats the doc comment list, 14 // returning the canonical formatting. 15 func formatDocComment(list []*ast.Comment) []*ast.Comment { 16 // Extract comment text (removing comment markers). 17 var kind, text string 18 var directives []*ast.Comment 19 if len(list) == 1 && strings.HasPrefix(list[0].Text, "/*") { 20 kind = "/*" 21 text = list[0].Text 22 if !strings.Contains(text, "\n") || allStars(text) { 23 // Single-line /* .. */ comment in doc comment position, 24 // or multiline old-style comment like 25 // /* 26 // * Comment 27 // * text here. 28 // */ 29 // Should not happen, since it will not work well as a 30 // doc comment, but if it does, just ignore: 31 // reformatting it will only make the situation worse. 32 return list 33 } 34 text = text[2 : len(text)-2] // cut /* and */ 35 } else if strings.HasPrefix(list[0].Text, "//") { 36 kind = "//" 37 var b strings.Builder 38 for _, c := range list { 39 after, found := strings.CutPrefix(c.Text, "//") 40 if !found { 41 return list 42 } 43 // Accumulate //go:build etc lines separately. 44 if isDirective(after) { 45 directives = append(directives, c) 46 continue 47 } 48 b.WriteString(strings.TrimPrefix(after, " ")) 49 b.WriteString("\n") 50 } 51 text = b.String() 52 } else { 53 // Not sure what this is, so leave alone. 54 return list 55 } 56 57 if text == "" { 58 return list 59 } 60 61 // Parse comment and reformat as text. 62 var p comment.Parser 63 d := p.Parse(text) 64 65 var pr comment.Printer 66 text = string(pr.Comment(d)) 67 68 // For /* */ comment, return one big comment with text inside. 69 slash := list[0].Slash 70 if kind == "/*" { 71 c := &ast.Comment{ 72 Slash: slash, 73 Text: "/*\n" + text + "*/", 74 } 75 return []*ast.Comment{c} 76 } 77 78 // For // comment, return sequence of // lines. 79 var out []*ast.Comment 80 for text != "" { 81 var line string 82 line, text, _ = strings.Cut(text, "\n") 83 if line == "" { 84 line = "//" 85 } else if strings.HasPrefix(line, "\t") { 86 line = "//" + line 87 } else { 88 line = "// " + line 89 } 90 out = append(out, &ast.Comment{ 91 Slash: slash, 92 Text: line, 93 }) 94 } 95 if len(directives) > 0 { 96 out = append(out, &ast.Comment{ 97 Slash: slash, 98 Text: "//", 99 }) 100 for _, c := range directives { 101 out = append(out, &ast.Comment{ 102 Slash: slash, 103 Text: c.Text, 104 }) 105 } 106 } 107 return out 108 } 109 110 // isDirective reports whether c is a comment directive. 111 // See go.dev/issue/37974. 112 // This code is also in go/ast. 113 func isDirective(c string) bool { 114 // "//line " is a line directive. 115 // "//extern " is for gccgo. 116 // "//export " is for cgo. 117 // (The // has been removed.) 118 if strings.HasPrefix(c, "line ") || strings.HasPrefix(c, "extern ") || strings.HasPrefix(c, "export ") { 119 return true 120 } 121 122 // "//[a-z0-9]+:[a-z0-9]" 123 // (The // has been removed.) 124 colon := strings.Index(c, ":") 125 if colon <= 0 || colon+1 >= len(c) { 126 return false 127 } 128 for i := 0; i <= colon+1; i++ { 129 if i == colon { 130 continue 131 } 132 b := c[i] 133 if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') { 134 return false 135 } 136 } 137 return true 138 } 139 140 // allStars reports whether text is the interior of an 141 // old-style /* */ comment with a star at the start of each line. 142 func allStars(text string) bool { 143 for i := 0; i < len(text); i++ { 144 if text[i] == '\n' { 145 j := i + 1 146 for j < len(text) && (text[j] == ' ' || text[j] == '\t') { 147 j++ 148 } 149 if j < len(text) && text[j] != '*' { 150 return false 151 } 152 } 153 } 154 return true 155 }