• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }