2017-07-18 01:35:59 +01:00
|
|
|
// Copyright (c) 2017, Google Inc.
|
|
|
|
//
|
|
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
|
|
// copyright notice and this permission notice appear in all copies.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
|
|
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
|
|
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
|
|
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// convert_comments.go converts C-style block comments to C++-style line
|
|
|
|
// comments. A block comment is converted if all of the following are true:
|
|
|
|
//
|
|
|
|
// * The comment begins after the first blank line, to leave the license
|
|
|
|
// blocks alone.
|
|
|
|
//
|
|
|
|
// * There are no characters between the '*/' and the end of the line.
|
|
|
|
//
|
|
|
|
// * Either one of the following are true:
|
|
|
|
//
|
|
|
|
// - The comment fits on one line.
|
|
|
|
//
|
|
|
|
// - Each line the comment spans begins with N spaces, followed by '/*' for
|
|
|
|
// the initial line or ' *' for subsequent lines, where N is the same for
|
|
|
|
// each line.
|
|
|
|
//
|
|
|
|
// This tool is a heuristic. While it gets almost all cases correct, the final
|
|
|
|
// output should still be looked over and fixed up as needed.
|
|
|
|
|
|
|
|
// allSpaces returns true if |s| consists entirely of spaces.
|
|
|
|
func allSpaces(s string) bool {
|
|
|
|
return strings.IndexFunc(s, func(r rune) bool { return r != ' ' }) == -1
|
|
|
|
}
|
|
|
|
|
|
|
|
// isContinuation returns true if |s| is a continuation line for a multi-line
|
|
|
|
// comment indented to the specified column.
|
|
|
|
func isContinuation(s string, column int) bool {
|
|
|
|
if len(s) < column+2 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !allSpaces(s[:column]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return s[column:column+2] == " *"
|
|
|
|
}
|
|
|
|
|
|
|
|
// indexFrom behaves like strings.Index but only reports matches starting at
|
|
|
|
// |idx|.
|
|
|
|
func indexFrom(s, sep string, idx int) int {
|
|
|
|
ret := strings.Index(s[idx:], sep)
|
|
|
|
if ret < 0 {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
return idx + ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// writeLine writes |line| to |out|, followed by a newline.
|
|
|
|
func writeLine(out *bytes.Buffer, line string) {
|
|
|
|
out.WriteString(line)
|
|
|
|
out.WriteByte('\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertComments(in []byte) []byte {
|
|
|
|
lines := strings.Split(string(in), "\n")
|
|
|
|
var out bytes.Buffer
|
|
|
|
|
|
|
|
// Account for the trailing newline.
|
|
|
|
if len(lines) > 0 && len(lines[len(lines)-1]) == 0 {
|
|
|
|
lines = lines[:len(lines)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the license block separator.
|
|
|
|
for len(lines) > 0 {
|
|
|
|
line := lines[0]
|
|
|
|
lines = lines[1:]
|
|
|
|
writeLine(&out, line)
|
|
|
|
if len(line) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// inComment is true if we are in the middle of a comment.
|
|
|
|
var inComment bool
|
|
|
|
// comment is the currently buffered multi-line comment to convert. If
|
|
|
|
// |inComment| is true and it is nil, the current multi-line comment is
|
|
|
|
// not convertable and we copy lines to |out| as-is.
|
|
|
|
var comment []string
|
|
|
|
// column is the column offset of |comment|.
|
|
|
|
var column int
|
|
|
|
for len(lines) > 0 {
|
|
|
|
line := lines[0]
|
|
|
|
lines = lines[1:]
|
|
|
|
|
|
|
|
var idx int
|
|
|
|
if inComment {
|
|
|
|
// Stop buffering if this comment isn't eligible.
|
|
|
|
if comment != nil && !isContinuation(line, column) {
|
|
|
|
for _, l := range comment {
|
|
|
|
writeLine(&out, l)
|
|
|
|
}
|
|
|
|
comment = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look for the end of the current comment.
|
|
|
|
idx = strings.Index(line, "*/")
|
|
|
|
if idx < 0 {
|
|
|
|
if comment != nil {
|
|
|
|
comment = append(comment, line)
|
|
|
|
} else {
|
|
|
|
writeLine(&out, line)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
inComment = false
|
|
|
|
if comment != nil {
|
|
|
|
if idx == len(line)-2 {
|
|
|
|
// This is a convertable multi-line comment.
|
|
|
|
if idx >= column+2 {
|
|
|
|
// |idx| may be equal to
|
|
|
|
// |column| + 1, if the line is
|
|
|
|
// a '*/' on its own. In that
|
|
|
|
// case, we discard the line.
|
|
|
|
comment = append(comment, line[:idx])
|
|
|
|
}
|
|
|
|
for _, l := range comment {
|
|
|
|
out.WriteString(l[:column])
|
|
|
|
out.WriteString("//")
|
|
|
|
writeLine(&out, strings.TrimRight(l[column+2:], " "))
|
|
|
|
}
|
|
|
|
comment = nil
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flush the buffered comment unmodified.
|
|
|
|
for _, l := range comment {
|
|
|
|
writeLine(&out, l)
|
|
|
|
}
|
|
|
|
comment = nil
|
|
|
|
}
|
|
|
|
idx += 2
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse starting from |idx|, looking for either a convertable
|
|
|
|
// line comment or a multi-line comment.
|
|
|
|
for {
|
|
|
|
idx = indexFrom(line, "/*", idx)
|
|
|
|
if idx < 0 {
|
|
|
|
writeLine(&out, line)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
endIdx := indexFrom(line, "*/", idx)
|
|
|
|
if endIdx < 0 {
|
2017-07-29 06:57:34 +01:00
|
|
|
// The comment is, so far, eligible for conversion.
|
2017-07-18 01:35:59 +01:00
|
|
|
inComment = true
|
2017-07-29 06:57:34 +01:00
|
|
|
column = idx
|
|
|
|
comment = []string{line}
|
2017-07-18 01:35:59 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if endIdx != len(line)-2 {
|
|
|
|
// Continue parsing for more comments in this line.
|
|
|
|
idx = endIdx + 2
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
out.WriteString(line[:idx])
|
|
|
|
|
|
|
|
// Google C++ style prefers two spaces before a
|
|
|
|
// comment if it is on the same line as code,
|
|
|
|
// but clang-format has been placing one space
|
|
|
|
// for block comments. Fix this.
|
2017-07-29 06:46:10 +01:00
|
|
|
if !allSpaces(line[:idx]) && line[idx-1] != '(' {
|
2017-07-18 01:35:59 +01:00
|
|
|
if line[idx-1] != ' ' {
|
|
|
|
out.WriteString(" ")
|
|
|
|
} else if line[idx-2] != ' ' {
|
|
|
|
out.WriteString(" ")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out.WriteString("//")
|
|
|
|
writeLine(&out, strings.TrimRight(line[idx+2:endIdx], " "))
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return out.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
for _, arg := range os.Args[1:] {
|
|
|
|
in, err := ioutil.ReadFile(arg)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if err := ioutil.WriteFile(arg, convertComments(in), 0666); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|