• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.auto.value.processor;
17 
18 /**
19  * Postprocessor that runs over the output of the template engine in order to make it look nicer.
20  * Mostly, this involves removing surplus horizontal and vertical space.
21  *
22  * @author emcmanus@google.com (Éamonn McManus)
23  */
24 class Reformatter {
fixup(String s)25   static String fixup(String s) {
26     StringBuilder out = new StringBuilder();
27     JavaScanner scanner = new JavaScanner(s);
28     s = scanner.string();
29     int len = s.length();
30     for (int start = 0, previous = 0, braces = 0, parens = 0, end = 0;
31         start < len;
32         previous = start, start = end) {
33       end = scanner.tokenEnd(start);
34       // The tokenized string always ends with \n so we can usually look at s.charAt(end) without
35       // worrying about going past the end of the string.
36       switch (s.charAt(start)) {
37         case '(':
38           parens++;
39           break;
40         case ')':
41           parens--;
42           break;
43         case '{':
44           braces++;
45           break;
46         case '}':
47           braces--;
48           break;
49         case ' ':
50           // This token is a string of consecutive spaces that is not at the start of a line.
51           // Consecutive spaces at the start of a line are attached to the previous newline, and
52           // we don't expect the first line to start with spaces. So we are going to compress this
53           // into just one space, and we are going to delete it entirely if it follows '(' or
54           // precedes a newline or one of the punctuation characters here.
55           if (s.charAt(previous) != '(' && "\n.,;)".indexOf(s.charAt(end)) < 0) {
56             out.append(' ');
57           }
58           continue;
59         case '\n':
60           // This token is a newline plus any following spaces (the indentation of the next line).
61           // If it is followed by something other than a newline then we will output it. Otherwise,
62           // it is part of a sequence of newlines but it is not the last one. If this is a context
63           // where we delete blank lines, or if this is not the first new line in the sequence, or
64           // if we are at the start of the file, we will delete this one. Otherwise we will output a
65           // single newline with no following indentation. Contexts where we delete blank lines are
66           // inside parentheses or inside more than one set of braces.
67           if (end < len && s.charAt(end) != '\n') {
68             if (out.length() == 0) {
69               // Omit newlines at the very start of the file.
70               start++;
71             }
72             break; // Output the newline and its following indentation.
73           }
74           if (parens == 0 && braces < 2 && s.charAt(previous) != '\n' && out.length() > 0) {
75             out.append('\n');
76           }
77           continue;
78         default:
79           break;
80       }
81       out.append(s, start, end);
82     }
83     return out.toString();
84   }
85 }
86