| // Copyright (C) 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package template |
| |
| import ( |
| "bytes" |
| "fmt" |
| |
| "android.googlesource.com/platform/tools/gpu/client/shell" |
| "golang.org/x/tools/imports" |
| ) |
| |
| const ( |
| disable = '⋖' |
| enable = '⋗' |
| indent = '»' |
| unindent = '«' |
| suppress = '§' |
| newline = '¶' |
| whitespace = '•' |
| ) |
| |
| // Reflow does the primitive reflow, but no language specific handling. |
| func (f *Functions) Reflow(indentSize int, value string) (string, error) { |
| f.ctx.Print("Reflowing string") |
| result, err := reflow(value, indentSize) |
| if err != nil { |
| return "", fmt.Errorf("%s : %s", f.active.Name(), err) |
| } |
| return string(result), nil |
| } |
| |
| const goIndent = 2 // Required by go style guide |
| |
| // GoFmt reflows the string as if it were go code using the standard go fmt library. |
| func (f *Functions) GoFmt(value string) (string, error) { |
| f.ctx.Print("Reflowing go code") |
| result, err := reflow(value, goIndent) |
| if err != nil { |
| return "", fmt.Errorf("%s : %s", f.active.Name(), err) |
| } |
| opt := &imports.Options{ |
| TabWidth: goIndent, |
| TabIndent: true, |
| Comments: true, |
| Fragment: true, |
| } |
| formatted, err := imports.Process(f.active.Name(), result, opt) |
| if err != nil { |
| f.ctx.Info().Fail(err, "GoFmt") |
| return string(result), nil |
| } |
| return string(formatted), nil |
| } |
| |
| // Format reflows the string using an external command. |
| func (f *Functions) Format(command stringList, value string) (string, error) { |
| if len(command) == 0 { |
| return "", fmt.Errorf("%s : Invalid Format command", f.active.Name()) |
| } |
| binary := command[0] |
| f.ctx.Info().S("binary", binary).Log("Reflowing code") |
| // indent level is arbitrary, because we expect the external formatter to redo it anyway |
| result, err := reflow(value, 4) |
| if err != nil { |
| return "", fmt.Errorf("%s : %s", f.active.Name(), err) |
| } |
| stdin := bytes.NewBuffer(result) |
| stdout := &bytes.Buffer{} |
| stderr := &bytes.Buffer{} |
| err = shell.Command(binary, command[1:]...).Capture(stdout, stderr).Read(stdin).Run(f.ctx) |
| if err != nil { |
| f.ctx.Info().S("stderr", stderr.String()).Fail(err, "Reformat") |
| return string(result), nil |
| } |
| return stdout.String(), nil |
| } |
| |
| func panicWrite(buf *bytes.Buffer, r rune) { |
| _, err := buf.WriteRune(r) |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| func reflow(in string, indentSize int) ([]byte, error) { |
| depth := 0 |
| wasNewline := false |
| suppressing := true |
| join := false |
| enabled := true |
| buf := &bytes.Buffer{} |
| flushPending := func() { |
| if wasNewline && !suppressing { |
| // write the indent |
| panicWrite(buf, '\n') |
| for i := 0; i < depth*indentSize; i++ { |
| panicWrite(buf, ' ') |
| } |
| } |
| suppressing = false |
| wasNewline = false |
| join = false |
| } |
| for _, ch := range in { |
| if !enabled { |
| if ch == enable { |
| enabled = true |
| } else { |
| panicWrite(buf, ch) |
| } |
| } else { |
| switch ch { |
| case disable: |
| flushPending() |
| enabled = false |
| ch = 0 |
| case whitespace: |
| ch = ' ' |
| case suppress: |
| suppressing = true |
| ch = 0 |
| case newline: |
| panicWrite(buf, '\n') |
| fallthrough |
| case '\n', '\r': |
| if !join { |
| wasNewline = true |
| } |
| ch = 0 |
| case '\t', ' ': |
| if wasNewline { |
| ch = 0 |
| } |
| case indent: |
| ch = 0 |
| depth += 1 |
| case '{', '[': |
| flushPending() |
| depth += 1 |
| case unindent: |
| ch = 0 |
| fallthrough |
| case '}', ']': |
| depth -= 1 |
| } |
| |
| if ch != 0 { |
| flushPending() |
| panicWrite(buf, ch) |
| } |
| } |
| } |
| return buf.Bytes(), nil |
| } |