• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2015 Square, Inc.
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  * https://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.squareup.kotlinpoet
17 
18 import java.io.Closeable
19 import kotlin.math.min
20 
21 /** Sentinel value that indicates that no user-provided package has been set.  */
22 private val NO_PACKAGE = String()
23 
24 internal val NULLABLE_ANY = ANY.copy(nullable = true)
25 
26 private fun extractMemberName(part: String): String {
27   require(Character.isJavaIdentifierStart(part[0])) { "not an identifier: $part" }
28   for (i in 1..part.length) {
29     if (!part.substring(0, i).isIdentifier) {
30       return part.substring(0, i - 1)
31     }
32   }
33   return part
34 }
35 
buildCodeStringnull36 internal inline fun buildCodeString(builderAction: CodeWriter.() -> Unit): String {
37   val stringBuilder = StringBuilder()
38   CodeWriter(stringBuilder, columnLimit = Integer.MAX_VALUE).use {
39     it.builderAction()
40   }
41   return stringBuilder.toString()
42 }
43 
buildCodeStringnull44 internal fun buildCodeString(
45   codeWriter: CodeWriter,
46   builderAction: CodeWriter.() -> Unit,
47 ): String {
48   val stringBuilder = StringBuilder()
49   codeWriter.emitInto(stringBuilder, builderAction)
50   return stringBuilder.toString()
51 }
52 
53 /**
54  * Converts a [FileSpec] to a string suitable to both human- and kotlinc-consumption. This honors
55  * imports, indentation, and deferred variable names.
56  */
57 internal class CodeWriter constructor(
58   out: Appendable,
59   private val indent: String = DEFAULT_INDENT,
60   imports: Map<String, Import> = emptyMap(),
61   private val importedTypes: Map<String, ClassName> = emptyMap(),
62   private val importedMembers: Map<String, MemberName> = emptyMap(),
63   columnLimit: Int = 100,
64 ) : Closeable {
65   private var out = LineWrapper(out, indent, columnLimit)
66   private var indentLevel = 0
67 
68   private var kdoc = false
69   private var comment = false
70   private var packageName = NO_PACKAGE
71   private val typeSpecStack = mutableListOf<TypeSpec>()
72   private val memberImportNames = mutableSetOf<String>()
<lambda>null73   private val importableTypes = mutableMapOf<String, List<ClassName>>().withDefault { emptyList() }
<lambda>null74   private val importableMembers = mutableMapOf<String, List<MemberName>>().withDefault { emptyList() }
75   private val referencedNames = mutableSetOf<String>()
76   private var trailingNewline = false
77 
<lambda>null78   val imports = imports.also {
79     for ((memberName, _) in imports) {
80       val lastDotIndex = memberName.lastIndexOf('.')
81       if (lastDotIndex >= 0) {
82         memberImportNames.add(memberName.substring(0, lastDotIndex))
83       }
84     }
85   }
86 
87   /**
88    * When emitting a statement, this is the line of the statement currently being written. The first
89    * line of a statement is indented normally and subsequent wrapped lines are double-indented. This
90    * is -1 when the currently-written line isn't part of a statement.
91    */
92   var statementLine = -1
93 
<lambda>null94   fun indent(levels: Int = 1) = apply {
95     indentLevel += levels
96   }
97 
<lambda>null98   fun unindent(levels: Int = 1) = apply {
99     require(indentLevel - levels >= 0) { "cannot unindent $levels from $indentLevel" }
100     indentLevel -= levels
101   }
102 
<lambda>null103   fun pushPackage(packageName: String) = apply {
104     check(this.packageName === NO_PACKAGE) { "package already set: ${this.packageName}" }
105     this.packageName = packageName
106   }
107 
<lambda>null108   fun popPackage() = apply {
109     check(packageName !== NO_PACKAGE) { "package already set: $packageName" }
110     packageName = NO_PACKAGE
111   }
112 
<lambda>null113   fun pushType(type: TypeSpec) = apply {
114     this.typeSpecStack.add(type)
115   }
116 
<lambda>null117   fun popType() = apply {
118     this.typeSpecStack.removeAt(typeSpecStack.size - 1)
119   }
120 
emitCommentnull121   fun emitComment(codeBlock: CodeBlock) {
122     trailingNewline = true // Force the '//' prefix for the comment.
123     comment = true
124     try {
125       emitCode(codeBlock)
126       emit("\n")
127     } finally {
128       comment = false
129     }
130   }
131 
emitKdocnull132   fun emitKdoc(kdocCodeBlock: CodeBlock) {
133     if (kdocCodeBlock.isEmpty()) return
134 
135     emit("/**\n")
136     kdoc = true
137     try {
138       emitCode(kdocCodeBlock, ensureTrailingNewline = true)
139     } finally {
140       kdoc = false
141     }
142     emit(" */\n")
143   }
144 
emitAnnotationsnull145   fun emitAnnotations(annotations: List<AnnotationSpec>, inline: Boolean) {
146     for (annotationSpec in annotations) {
147       annotationSpec.emit(this, inline)
148       emit(if (inline) " " else "\n")
149     }
150   }
151 
152   /**
153    * Emits `modifiers` in the standard order. Modifiers in `implicitModifiers` will not
154    * be emitted except for [KModifier.PUBLIC]
155    */
emitModifiersnull156   fun emitModifiers(
157     modifiers: Set<KModifier>,
158     implicitModifiers: Set<KModifier> = emptySet(),
159   ) {
160     if (shouldEmitPublicModifier(modifiers, implicitModifiers)) {
161       emit(KModifier.PUBLIC.keyword)
162       emit(" ")
163     }
164     val uniqueNonPublicExplicitOnlyModifiers =
165       modifiers
166         .filterNot { it == KModifier.PUBLIC }
167         .filterNot { implicitModifiers.contains(it) }
168         .toEnumSet()
169     for (modifier in uniqueNonPublicExplicitOnlyModifiers) {
170       emit(modifier.keyword)
171       emit(" ")
172     }
173   }
174 
175   /**
176    * Emits the `context` block for [contextReceivers].
177    */
emitContextReceiversnull178   fun emitContextReceivers(contextReceivers: List<TypeName>, suffix: String = "") {
179     if (contextReceivers.isNotEmpty()) {
180       val receivers = contextReceivers
181         .map { CodeBlock.of("%T", it) }
182         .joinToCode(prefix = "context(", suffix = ")")
183       emitCode(receivers)
184       emit(suffix)
185     }
186   }
187 
188   /**
189    * Emit type variables with their bounds. If a type variable has more than a single bound - call
190    * [emitWhereBlock] with same input to produce an additional `where` block.
191    *
192    * This should only be used when declaring type variables; everywhere else bounds are omitted.
193    */
emitTypeVariablesnull194   fun emitTypeVariables(typeVariables: List<TypeVariableName>) {
195     if (typeVariables.isEmpty()) return
196 
197     emit("<")
198     typeVariables.forEachIndexed { index, typeVariable ->
199       if (index > 0) emit(", ")
200       if (typeVariable.variance != null) {
201         emit("${typeVariable.variance.keyword} ")
202       }
203       if (typeVariable.isReified) {
204         emit("reified ")
205       }
206       emitCode("%L", typeVariable.name)
207       if (typeVariable.bounds.size == 1 && typeVariable.bounds[0] != NULLABLE_ANY) {
208         emitCode(" : %T", typeVariable.bounds[0])
209       }
210     }
211     emit(">")
212   }
213 
214   /**
215    * Emit a `where` block containing type bounds for each type variable that has at least two
216    * bounds.
217    */
emitWhereBlocknull218   fun emitWhereBlock(typeVariables: List<TypeVariableName>) {
219     if (typeVariables.isEmpty()) return
220 
221     var firstBound = true
222     for (typeVariable in typeVariables) {
223       if (typeVariable.bounds.size > 1) {
224         for (bound in typeVariable.bounds) {
225           if (!firstBound) emitCode(", ") else emitCode(" where ")
226           emitCode("%L : %T", typeVariable.name, bound)
227           firstBound = false
228         }
229       }
230     }
231   }
232 
emitCodenull233   fun emitCode(s: String) = emitCode(CodeBlock.of(s))
234 
235   fun emitCode(format: String, vararg args: Any?) = emitCode(CodeBlock.of(format, *args))
236 
237   fun emitCode(
238     codeBlock: CodeBlock,
239     isConstantContext: Boolean = false,
240     ensureTrailingNewline: Boolean = false,
241   ) = apply {
242     var a = 0
243     var deferredTypeName: ClassName? = null // used by "import static" logic
244     val partIterator = codeBlock.formatParts.listIterator()
245     while (partIterator.hasNext()) {
246       when (val part = partIterator.next()) {
247         "%L" -> emitLiteral(codeBlock.args[a++], isConstantContext)
248 
249         "%N" -> emit(codeBlock.args[a++] as String)
250 
251         "%S" -> {
252           val string = codeBlock.args[a++] as String?
253           // Emit null as a literal null: no quotes.
254           val literal = if (string != null) {
255             stringLiteralWithQuotes(
256               string,
257               isInsideRawString = false,
258               isConstantContext = isConstantContext,
259             )
260           } else {
261             "null"
262           }
263           emit(literal, nonWrapping = true)
264         }
265 
266         "%P" -> {
267           val string = codeBlock.args[a++]?.let { arg ->
268             if (arg is CodeBlock) {
269               arg.toString(this@CodeWriter)
270             } else {
271               arg as String?
272             }
273           }
274           // Emit null as a literal null: no quotes.
275           val literal = if (string != null) {
276             stringLiteralWithQuotes(
277               string,
278               isInsideRawString = true,
279               isConstantContext = isConstantContext,
280             )
281           } else {
282             "null"
283           }
284           emit(literal, nonWrapping = true)
285         }
286 
287         "%T" -> {
288           var typeName = codeBlock.args[a++] as TypeName
289           if (typeName.isAnnotated) {
290             typeName.emitAnnotations(this)
291             typeName = typeName.copy(annotations = emptyList())
292           }
293           // defer "typeName.emit(this)" if next format part will be handled by the default case
294           var defer = false
295           if (typeName is ClassName && partIterator.hasNext()) {
296             if (!codeBlock.formatParts[partIterator.nextIndex()].startsWith("%")) {
297               val candidate = typeName
298               if (candidate.canonicalName in memberImportNames) {
299                 check(deferredTypeName == null) { "pending type for static import?!" }
300                 deferredTypeName = candidate
301                 defer = true
302               }
303             }
304           }
305           if (!defer) typeName.emit(this)
306           typeName.emitNullable(this)
307         }
308 
309         "%M" -> {
310           val memberName = codeBlock.args[a++] as MemberName
311           memberName.emit(this)
312         }
313 
314         "%%" -> emit("%")
315 
316         "⇥" -> indent()
317 
318         "⇤" -> unindent()
319 
320         "«" -> {
321           check(statementLine == -1) {
322             """
323             |Can't open a new statement until the current statement is closed (opening « followed
324             |by another « without a closing »).
325             |Current code block:
326             |- Format parts: ${codeBlock.formatParts.map(::escapeCharacterLiterals)}
327             |- Arguments: ${codeBlock.args}
328             |
329             """.trimMargin()
330           }
331           statementLine = 0
332         }
333 
334         "»" -> {
335           check(statementLine != -1) {
336             """
337             |Can't close a statement that hasn't been opened (closing » is not preceded by an
338             |opening «).
339             |Current code block:
340             |- Format parts: ${codeBlock.formatParts.map(::escapeCharacterLiterals)}
341             |- Arguments: ${codeBlock.args}
342             |
343             """.trimMargin()
344           }
345           if (statementLine > 0) {
346             unindent(2) // End a multi-line statement. Decrease the indentation level.
347           }
348           statementLine = -1
349         }
350 
351         else -> {
352           // Handle deferred type.
353           var doBreak = false
354           if (deferredTypeName != null) {
355             if (part.startsWith(".")) {
356               if (emitStaticImportMember(deferredTypeName.canonicalName, part)) {
357                 // Okay, static import hit and all was emitted, so clean-up and jump to next part.
358                 deferredTypeName = null
359                 doBreak = true
360               }
361             }
362             if (!doBreak) {
363               deferredTypeName!!.emit(this)
364               deferredTypeName = null
365             }
366           }
367           if (!doBreak) {
368             emit(part)
369           }
370         }
371       }
372     }
373     if (ensureTrailingNewline && out.hasPendingSegments) {
374       emit("\n")
375     }
376   }
377 
emitStaticImportMembernull378   private fun emitStaticImportMember(canonical: String, part: String): Boolean {
379     val partWithoutLeadingDot = part.substring(1)
380     if (partWithoutLeadingDot.isEmpty()) return false
381     val first = partWithoutLeadingDot[0]
382     if (!Character.isJavaIdentifierStart(first)) return false
383     val explicit = imports[canonical + "." + extractMemberName(partWithoutLeadingDot)]
384     if (explicit != null) {
385       if (explicit.alias != null) {
386         val memberName = extractMemberName(partWithoutLeadingDot)
387         emit(partWithoutLeadingDot.replaceFirst(memberName, explicit.alias))
388       } else {
389         emit(partWithoutLeadingDot)
390       }
391       return true
392     }
393     return false
394   }
395 
emitLiteralnull396   private fun emitLiteral(o: Any?, isConstantContext: Boolean) {
397     when (o) {
398       is TypeSpec -> o.emit(this, null)
399       is AnnotationSpec -> o.emit(this, inline = true, asParameter = isConstantContext)
400       is PropertySpec -> o.emit(this, emptySet())
401       is FunSpec -> o.emit(
402         codeWriter = this,
403         enclosingName = null,
404         implicitModifiers = setOf(KModifier.PUBLIC),
405         includeKdocTags = true,
406       )
407       is TypeAliasSpec -> o.emit(this)
408       is CodeBlock -> emitCode(o, isConstantContext = isConstantContext)
409       else -> emit(o.toString())
410     }
411   }
412 
413   /**
414    * Returns the best name to identify `className` with in the current context. This uses the
415    * available imports and the current scope to find the shortest name available. It does not honor
416    * names visible due to inheritance.
417    */
lookupNamenull418   fun lookupName(className: ClassName): String {
419     // Find the shortest suffix of className that resolves to className. This uses both local type
420     // names (so `Entry` in `Map` refers to `Map.Entry`). Also uses imports.
421     var nameResolved = false
422     var c: ClassName? = className
423     while (c != null) {
424       val alias = imports[c.canonicalName]?.alias
425       val simpleName = alias ?: c.simpleName
426       val resolved = resolve(simpleName)
427       nameResolved = resolved != null
428 
429       // We don't care about nullability and type annotations here, as it's irrelevant for imports.
430       if (resolved == c.copy(nullable = false, annotations = emptyList())) {
431         if (alias != null) return alias
432         val suffixOffset = c.simpleNames.size - 1
433         referencedNames.add(className.topLevelClassName().simpleName)
434         return className.simpleNames.subList(
435           suffixOffset,
436           className.simpleNames.size,
437         ).joinToString(".")
438       }
439       c = c.enclosingClassName()
440     }
441 
442     // If the name resolved but wasn't a match, we're stuck with the fully qualified name.
443     if (nameResolved) {
444       return className.canonicalName
445     }
446 
447     // If the class is in the same package, we're done.
448     if (packageName == className.packageName) {
449       referencedNames.add(className.topLevelClassName().simpleName)
450       return className.simpleNames.joinToString(".")
451     }
452 
453     // We'll have to use the fully-qualified name. Mark the type as importable for a future pass.
454     if (!kdoc) {
455       importableType(className)
456     }
457 
458     return className.canonicalName
459   }
460 
lookupNamenull461   fun lookupName(memberName: MemberName): String {
462     val simpleName = imports[memberName.canonicalName]?.alias ?: memberName.simpleName
463     // Match an imported member.
464     val importedMember = importedMembers[simpleName]
465     if (importedMember == memberName) {
466       return simpleName
467     } else if (importedMember != null && memberName.enclosingClassName != null) {
468       val enclosingClassName = lookupName(memberName.enclosingClassName)
469       return "$enclosingClassName.$simpleName"
470     }
471 
472     // If the member is in the same package, we're done.
473     if (packageName == memberName.packageName && memberName.enclosingClassName == null) {
474       referencedNames.add(memberName.simpleName)
475       return memberName.simpleName
476     }
477 
478     // We'll have to use the fully-qualified name.
479     // Mark the member as importable for a future pass unless the name clashes with
480     // a method in the current context
481     if (!kdoc && (
482         memberName.isExtension ||
483           !isMethodNameUsedInCurrentContext(memberName.simpleName)
484         )
485     ) {
486       importableMember(memberName)
487     }
488 
489     return memberName.canonicalName
490   }
491 
492   // TODO(luqasn): also honor superclass members when resolving names.
isMethodNameUsedInCurrentContextnull493   private fun isMethodNameUsedInCurrentContext(simpleName: String): Boolean {
494     for (it in typeSpecStack.reversed()) {
495       if (it.funSpecs.any { it.name == simpleName }) {
496         return true
497       }
498       if (!it.modifiers.contains(KModifier.INNER)) {
499         break
500       }
501     }
502     return false
503   }
504 
importableTypenull505   private fun importableType(className: ClassName) {
506     val topLevelClassName = className.topLevelClassName()
507     val simpleName = imports[className.canonicalName]?.alias ?: topLevelClassName.simpleName
508     // Check for name clashes with members.
509     if (simpleName !in importableMembers) {
510       importableTypes[simpleName] = importableTypes.getValue(simpleName) + topLevelClassName
511     }
512   }
513 
importableMembernull514   private fun importableMember(memberName: MemberName) {
515     if (memberName.packageName.isNotEmpty()) {
516       val simpleName = imports[memberName.canonicalName]?.alias ?: memberName.simpleName
517       // Check for name clashes with types.
518       if (simpleName !in importableTypes) {
519         importableMembers[simpleName] = importableMembers.getValue(simpleName) + memberName
520       }
521     }
522   }
523 
524   /**
525    * Returns the class or enum value referenced by `simpleName`, using the current nesting context and
526    * imports.
527    */
528   // TODO(jwilson): also honor superclass members when resolving names.
resolvenull529   private fun resolve(simpleName: String): ClassName? {
530     // Match a child of the current (potentially nested) class.
531     for (i in typeSpecStack.indices.reversed()) {
532       val typeSpec = typeSpecStack[i]
533       if (simpleName in typeSpec.nestedTypesSimpleNames) {
534         return stackClassName(i, simpleName)
535       }
536     }
537 
538     if (typeSpecStack.size > 0) {
539       val typeSpec = typeSpecStack[0]
540       if (typeSpec.name == simpleName) {
541         // Match the top-level class.
542         return ClassName(packageName, simpleName)
543       }
544       if (typeSpec.isEnum && typeSpec.enumConstants.keys.contains(simpleName)) {
545         // Match a top level enum value.
546         // Enum values are not proper classes but can still be modeled using ClassName.
547         return ClassName(packageName, typeSpec.name!!).nestedClass(simpleName)
548       }
549     }
550 
551     // Match an imported type.
552     val importedType = importedTypes[simpleName]
553     if (importedType != null) return importedType
554 
555     // No match.
556     return null
557   }
558 
559   /** Returns the class named `simpleName` when nested in the class at `stackDepth`.  */
stackClassNamenull560   private fun stackClassName(stackDepth: Int, simpleName: String): ClassName {
561     var className = ClassName(packageName, typeSpecStack[0].name!!)
562     for (i in 1..stackDepth) {
563       className = className.nestedClass(typeSpecStack[i].name!!)
564     }
565     return className.nestedClass(simpleName)
566   }
567 
568   /**
569    * Emits `s` with indentation as required. It's important that all code that writes to
570    * [CodeWriter.out] does it through here, since we emit indentation lazily in order to avoid
571    * unnecessary trailing whitespace.
572    */
<lambda>null573   fun emit(s: String, nonWrapping: Boolean = false) = apply {
574     var first = true
575     for (line in s.split('\n')) {
576       // Emit a newline character. Make sure blank lines in KDoc & comments look good.
577       if (!first) {
578         if ((kdoc || comment) && trailingNewline) {
579           emitIndentation()
580           out.appendNonWrapping(if (kdoc) " *" else "//")
581         }
582         out.newline()
583         trailingNewline = true
584         if (statementLine != -1) {
585           if (statementLine == 0) {
586             indent(2) // Begin multiple-line statement. Increase the indentation level.
587           }
588           statementLine++
589         }
590       }
591 
592       first = false
593       if (line.isEmpty()) continue // Don't indent empty lines.
594 
595       // Emit indentation and comment prefix if necessary.
596       if (trailingNewline) {
597         emitIndentation()
598         if (kdoc) {
599           out.appendNonWrapping(" * ")
600         } else if (comment) {
601           out.appendNonWrapping("// ")
602         }
603       }
604 
605       if (nonWrapping) {
606         out.appendNonWrapping(line)
607       } else {
608         out.append(
609           line,
610           indentLevel = if (kdoc) indentLevel else indentLevel + 2,
611           linePrefix = if (kdoc) " * " else "",
612         )
613       }
614       trailingNewline = false
615     }
616   }
617 
emitIndentationnull618   private fun emitIndentation() {
619     for (j in 0 until indentLevel) {
620       out.appendNonWrapping(indent)
621     }
622   }
623 
624   /**
625    * Returns whether a [KModifier.PUBLIC] should be emitted.
626    *
627    * If [modifiers] contains [KModifier.PUBLIC], this method always returns `true`.
628    *
629    * Otherwise, this will return `true` when [KModifier.PUBLIC] is one of the [implicitModifiers]
630    * and there are no other opposing modifiers (like [KModifier.PROTECTED] etc.) supplied by the
631    * consumer in [modifiers].
632    */
shouldEmitPublicModifiernull633   private fun shouldEmitPublicModifier(
634     modifiers: Set<KModifier>,
635     implicitModifiers: Set<KModifier>,
636   ): Boolean {
637     if (modifiers.contains(KModifier.PUBLIC)) {
638       return true
639     }
640 
641     if (!implicitModifiers.contains(KModifier.PUBLIC)) {
642       return false
643     }
644 
645     val hasOtherConsumerSpecifiedVisibility =
646       modifiers.containsAnyOf(KModifier.PRIVATE, KModifier.INTERNAL, KModifier.PROTECTED)
647 
648     return !hasOtherConsumerSpecifiedVisibility
649   }
650 
651   /**
652    * Returns the types that should have been imported for this code. If there were any simple name
653    * collisions, import aliases will be generated.
654    */
suggestedTypeImportsnull655   private fun suggestedTypeImports(): Map<String, Set<ClassName>> {
656     return importableTypes.filterKeys { it !in referencedNames }.mapValues { it.value.toSet() }
657   }
658 
659   /**
660    * Returns the members that should have been imported for this code. If there were any simple name
661    * collisions, import aliases will be generated.
662    */
suggestedMemberImportsnull663   private fun suggestedMemberImports(): Map<String, Set<MemberName>> {
664     return importableMembers.filterKeys { it !in referencedNames }.mapValues { it.value.toSet() }
665   }
666 
667   /**
668    * Perform emitting actions on the current [CodeWriter] using a custom [Appendable]. The
669    * [CodeWriter] will continue using the old [Appendable] after this method returns.
670    */
emitIntonull671   inline fun emitInto(out: Appendable, action: CodeWriter.() -> Unit) {
672     val codeWrapper = this
673     LineWrapper(out, indent = DEFAULT_INDENT, columnLimit = Int.MAX_VALUE).use { newOut ->
674       val oldOut = codeWrapper.out
675       codeWrapper.out = newOut
676       action()
677       codeWrapper.out = oldOut
678     }
679   }
680 
closenull681   override fun close() {
682     out.close()
683   }
684 
685   companion object {
686     /**
687      * Makes a pass to collect imports by executing [emitStep], and returns an instance of
688      * [CodeWriter] pre-initialized with collected imports.
689      */
withCollectedImportsnull690     fun withCollectedImports(
691       out: Appendable,
692       indent: String,
693       memberImports: Map<String, Import>,
694       emitStep: (importsCollector: CodeWriter) -> Unit,
695     ): CodeWriter {
696       // First pass: emit the entire class, just to collect the types we'll need to import.
697       val importsCollector = CodeWriter(
698         NullAppendable,
699         indent,
700         memberImports,
701         columnLimit = Integer.MAX_VALUE,
702       )
703       emitStep(importsCollector)
704       val generatedImports = mutableMapOf<String, Import>()
705       val suggestedTypeImports = importsCollector.suggestedTypeImports()
706         .generateImports(
707           generatedImports,
708           canonicalName = ClassName::canonicalName,
709           packageName = ClassName::packageName,
710           capitalizeAliases = true,
711         )
712       val suggestedMemberImports = importsCollector.suggestedMemberImports()
713         .generateImports(
714           generatedImports,
715           canonicalName = MemberName::canonicalName,
716           packageName = MemberName::packageName,
717           capitalizeAliases = false,
718         )
719       importsCollector.close()
720 
721       return CodeWriter(
722         out,
723         indent,
724         memberImports + generatedImports.filterKeys { it !in memberImports },
725         suggestedTypeImports,
726         suggestedMemberImports,
727       )
728     }
729 
generateImportsnull730     private fun <T> Map<String, Set<T>>.generateImports(
731       generatedImports: MutableMap<String, Import>,
732       canonicalName: T.() -> String,
733       packageName: T.() -> String,
734       capitalizeAliases: Boolean,
735     ): Map<String, T> {
736       return flatMap { (simpleName, qualifiedNames) ->
737         if (qualifiedNames.size == 1) {
738           listOf(simpleName to qualifiedNames.first()).also {
739             val canonicalName = qualifiedNames.first().canonicalName()
740             generatedImports[canonicalName] = Import(canonicalName)
741           }
742         } else {
743           generateImportAliases(simpleName, qualifiedNames, packageName, capitalizeAliases)
744             .onEach { (alias, qualifiedName) ->
745               val canonicalName = qualifiedName.canonicalName()
746               generatedImports[canonicalName] = Import(canonicalName, alias)
747             }
748         }
749       }.toMap()
750     }
751 
generateImportAliasesnull752     private fun <T> generateImportAliases(
753       simpleName: String,
754       qualifiedNames: Set<T>,
755       packageName: T.() -> String,
756       capitalizeAliases: Boolean,
757     ): List<Pair<String, T>> {
758       val packageNameSegments = qualifiedNames.associateWith { qualifiedName ->
759         qualifiedName.packageName().split('.').map { it.replaceFirstChar(Char::uppercaseChar) }
760       }
761       val aliasNames = mutableMapOf<String, T>()
762       var segmentsToUse = 0
763       // Iterate until we have unique aliases for all names.
764       while (aliasNames.size != qualifiedNames.size) {
765         segmentsToUse += 1
766         aliasNames.clear()
767         for ((qualifiedName, segments) in packageNameSegments) {
768           val aliasPrefix = segments.takeLast(min(segmentsToUse, segments.size))
769             .joinToString(separator = "")
770             .replaceFirstChar { if (!capitalizeAliases) it.lowercaseChar() else it }
771           val aliasName = aliasPrefix + simpleName.replaceFirstChar(Char::uppercaseChar)
772           aliasNames[aliasName] = qualifiedName
773         }
774       }
775       return aliasNames.toList()
776     }
777   }
778 }
779