summaryrefslogtreecommitdiff
path: root/tools/codegen/src/com/android/codegen/Printer.kt
diff options
context:
space:
mode:
Diffstat (limited to 'tools/codegen/src/com/android/codegen/Printer.kt')
-rw-r--r--tools/codegen/src/com/android/codegen/Printer.kt186
1 files changed, 186 insertions, 0 deletions
diff --git a/tools/codegen/src/com/android/codegen/Printer.kt b/tools/codegen/src/com/android/codegen/Printer.kt
new file mode 100644
index 000000000000..b30e3f68b307
--- /dev/null
+++ b/tools/codegen/src/com/android/codegen/Printer.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2019 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 com.android.codegen
+
+/**
+ * Mixin for syntactic sugar around indent-aware printing into [stringBuilder]
+ */
+interface Printer<SELF: Printer<SELF>> {
+
+ val stringBuilder: StringBuilder
+
+ var currentIndent: String
+
+ fun pushIndent() {
+ currentIndent += INDENT_SINGLE
+ }
+
+ fun popIndent() {
+ currentIndent = if (currentIndent.length >= INDENT_SINGLE.length) {
+ currentIndent.substring(0, currentIndent.length - INDENT_SINGLE.length)
+ } else {
+ ""
+ }
+ }
+
+ fun backspace() = stringBuilder.setLength(stringBuilder.length - 1)
+ val lastChar get() = stringBuilder[stringBuilder.length - 1]
+
+ private fun appendRaw(s: String) {
+ stringBuilder.append(s)
+ }
+
+ fun append(s: String) {
+ if (s.isBlank() && s != "\n") {
+ appendRaw(s)
+ } else {
+ appendRaw(s.lines().map { line ->
+ if (line.startsWith(" *")) line else line.trimStart()
+ }.joinToString("\n$currentIndent"))
+ }
+ }
+
+ fun appendSameLine(s: String) {
+ while (lastChar.isWhitespace() || lastChar.isNewline()) {
+ backspace()
+ }
+ appendRaw(s)
+ }
+
+ fun rmEmptyLine() {
+ while (lastChar.isWhitespaceNonNewline()) backspace()
+ if (lastChar.isNewline()) backspace()
+ }
+
+ /**
+ * Syntactic sugar for:
+ * ```
+ * +"code()";
+ * ```
+ * to append the given string plus a newline
+ */
+ operator fun String.unaryPlus() = append("$this\n")
+
+ /**
+ * Syntactic sugar for:
+ * ```
+ * !"code()";
+ * ```
+ * to append the given string without a newline
+ */
+ operator fun String.not() = append(this)
+
+ /**
+ * Syntactic sugar for:
+ * ```
+ * ... {
+ * ...
+ * }+";"
+ * ```
+ * to append a ';' on same line after a block, and a newline afterwards
+ */
+ operator fun Unit.plus(s: String) {
+ appendSameLine(s)
+ +""
+ }
+
+ /**
+ * A multi-purpose syntactic sugar for appending the given string plus anything generated in
+ * the given [block], the latter with the appropriate deeper indent,
+ * and resetting the indent back to original at the end
+ *
+ * Usage examples:
+ *
+ * ```
+ * "if (...)" {
+ * ...
+ * }
+ * ```
+ * to append a corresponding if block appropriate indentation
+ *
+ * ```
+ * "void foo(...)" {
+ * ...
+ * }
+ * ```
+ * similar to the previous one, plus an extra empty line after the function body
+ *
+ * ```
+ * "void foo(" {
+ * <args code>
+ * }
+ * ```
+ * to use proper indentation for args code and close the bracket on same line at end
+ *
+ * ```
+ * "..." {
+ * ...
+ * }
+ * to use the correct indentation for inner code, resetting it at the end
+ */
+ operator fun String.invoke(block: SELF.() -> Unit) {
+ if (this == " {") {
+ appendSameLine(this)
+ } else {
+ append(this)
+ }
+ when {
+ endsWith("(") -> {
+ indentedBy(2, block)
+ appendSameLine(")")
+ }
+ endsWith("{") || endsWith(")") -> {
+ if (!endsWith("{")) appendSameLine(" {")
+ indentedBy(1, block)
+ +"}"
+ if ((endsWith(") {") || endsWith(")") || this == " {")
+ && !startsWith("synchronized")
+ && !startsWith("switch")
+ && !startsWith("if ")
+ && !contains(" else ")
+ && !contains("new ")
+ && !contains("return ")) {
+ +"" // extra line after function definitions
+ }
+ }
+ else -> indentedBy(2, block)
+ }
+ }
+
+ fun indentedBy(level: Int, block: SELF.() -> Unit) {
+ append("\n")
+ level times {
+ append(INDENT_SINGLE)
+ pushIndent()
+ }
+ (this as SELF).block()
+ level times { popIndent() }
+ rmEmptyLine()
+ +""
+ }
+
+ fun Iterable<FieldInfo>.forEachTrimmingTrailingComma(b: FieldInfo.() -> Unit) {
+ forEachApply {
+ b()
+ if (isLast) {
+ while (lastChar == ' ' || lastChar == '\n') backspace()
+ if (lastChar == ',') backspace()
+ }
+ }
+ }
+} \ No newline at end of file