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