1 /* <lambda>null2 * Copyright (C) 2019 The Android Open Source Project 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 17 18 package com.android.codegen 19 20 /** 21 * Mixin for syntactic sugar around indent-aware printing into [stringBuilder] 22 */ 23 interface Printer<SELF: Printer<SELF>> { 24 25 val stringBuilder: StringBuilder 26 27 var currentIndent: String 28 29 fun pushIndent() { 30 currentIndent += INDENT_SINGLE 31 } 32 33 fun popIndent() { 34 currentIndent = if (currentIndent.length >= INDENT_SINGLE.length) { 35 currentIndent.substring(0, currentIndent.length - INDENT_SINGLE.length) 36 } else { 37 "" 38 } 39 } 40 41 fun backspace() = stringBuilder.setLength(stringBuilder.length - 1) 42 val lastChar get() = stringBuilder[stringBuilder.length - 1] 43 44 private fun appendRaw(s: String) { 45 stringBuilder.append(s) 46 } 47 48 fun append(s: String) { 49 if (s.isBlank() && s != "\n") { 50 appendRaw(s) 51 } else { 52 appendRaw(s.lines().map { line -> 53 if (line.startsWith(" *")) line else line.trimStart() 54 }.joinToString("\n$currentIndent")) 55 } 56 } 57 58 fun appendSameLine(s: String) { 59 while (lastChar.isWhitespace() || lastChar.isNewline()) { 60 backspace() 61 } 62 appendRaw(s) 63 } 64 65 fun rmEmptyLine() { 66 while (lastChar.isWhitespaceNonNewline()) backspace() 67 if (lastChar.isNewline()) backspace() 68 } 69 70 /** 71 * Syntactic sugar for: 72 * ``` 73 * +"code()"; 74 * ``` 75 * to append the given string plus a newline 76 */ 77 operator fun String.unaryPlus() = append("$this\n") 78 79 /** 80 * Syntactic sugar for: 81 * ``` 82 * !"code()"; 83 * ``` 84 * to append the given string without a newline 85 */ 86 operator fun String.not() = append(this) 87 88 /** 89 * Syntactic sugar for: 90 * ``` 91 * ... { 92 * ... 93 * }+";" 94 * ``` 95 * to append a ';' on same line after a block, and a newline afterwards 96 */ 97 operator fun Unit.plus(s: String) { 98 appendSameLine(s) 99 +"" 100 } 101 102 /** 103 * A multi-purpose syntactic sugar for appending the given string plus anything generated in 104 * the given [block], the latter with the appropriate deeper indent, 105 * and resetting the indent back to original at the end 106 * 107 * Usage examples: 108 * 109 * ``` 110 * "if (...)" { 111 * ... 112 * } 113 * ``` 114 * to append a corresponding if block appropriate indentation 115 * 116 * ``` 117 * "void foo(...)" { 118 * ... 119 * } 120 * ``` 121 * similar to the previous one, plus an extra empty line after the function body 122 * 123 * ``` 124 * "void foo(" { 125 * <args code> 126 * } 127 * ``` 128 * to use proper indentation for args code and close the bracket on same line at end 129 * 130 * ``` 131 * "..." { 132 * ... 133 * } 134 * to use the correct indentation for inner code, resetting it at the end 135 */ 136 operator fun String.invoke(block: SELF.() -> Unit) { 137 if (this == " {") { 138 appendSameLine(this) 139 } else { 140 append(this) 141 } 142 when { 143 endsWith("(") -> { 144 indentedBy(2, block) 145 appendSameLine(")") 146 } 147 endsWith("{") || endsWith(")") -> { 148 if (!endsWith("{")) appendSameLine(" {") 149 indentedBy(1, block) 150 +"}" 151 if ((endsWith(") {") || endsWith(")") || this == " {") 152 && !startsWith("synchronized") 153 && !startsWith("switch") 154 && !startsWith("if ") 155 && !contains(" else ") 156 && !contains("new ") 157 && !contains("return ")) { 158 +"" // extra line after function definitions 159 } 160 } 161 else -> indentedBy(2, block) 162 } 163 } 164 165 fun indentedBy(level: Int, block: SELF.() -> Unit) { 166 append("\n") 167 level times { 168 append(INDENT_SINGLE) 169 pushIndent() 170 } 171 (this as SELF).block() 172 level times { popIndent() } 173 rmEmptyLine() 174 +"" 175 } 176 177 fun Iterable<FieldInfo>.forEachTrimmingTrailingComma(b: FieldInfo.() -> Unit) { 178 forEachApply { 179 b() 180 if (isLast) { 181 while (lastChar == ' ' || lastChar == '\n') backspace() 182 if (lastChar == ',') backspace() 183 } 184 } 185 } 186 }