1 /* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 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 package com.facebook.ktfmt.format 18 19 import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 import org.jetbrains.kotlin.psi.KtClass 21 import org.jetbrains.kotlin.psi.KtClassBody 22 import org.jetbrains.kotlin.psi.KtEnumEntry 23 import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments 24 25 /** 26 * PSI-like model of a list of enum entries. 27 * 28 * See https://youtrack.jetbrains.com/issue/KT-65157 29 */ 30 class EnumEntryList 31 private constructor( 32 val enumEntries: List<KtEnumEntry>, 33 val trailingComma: PsiElement?, 34 val terminatingSemicolon: PsiElement?, 35 ) { 36 companion object { extractParentListnull37 fun extractParentList(enumEntry: KtEnumEntry): EnumEntryList { 38 return checkNotNull(extractChildList(enumEntry.parent as KtClassBody)) 39 } 40 extractChildListnull41 fun extractChildList(classBody: KtClassBody): EnumEntryList? { 42 val clazz = classBody.parent 43 if (clazz !is KtClass || !clazz.isEnum()) return null 44 45 val enumEntries = classBody.children.filterIsInstance<KtEnumEntry>() 46 47 if (enumEntries.isEmpty()) { 48 var semicolon = classBody.firstChild 49 while (semicolon != null) { 50 if (semicolon.text == ";") break 51 semicolon = semicolon.nextSibling 52 } 53 54 return EnumEntryList( 55 enumEntries = enumEntries, 56 trailingComma = null, 57 terminatingSemicolon = semicolon, 58 ) 59 } 60 61 var semicolon: PsiElement? = null 62 var comma: PsiElement? = null 63 val lastToken = 64 checkNotNull( 65 enumEntries 66 .last() 67 .lastChild 68 .getPrevSiblingIgnoringWhitespaceAndComments(withItself = true), 69 ) 70 when (lastToken.text) { 71 "," -> { 72 comma = lastToken 73 } 74 ";" -> { 75 semicolon = lastToken 76 val prevSibling = semicolon.getPrevSiblingIgnoringWhitespaceAndComments() 77 if (prevSibling?.text == ",") { 78 comma = prevSibling 79 } 80 } 81 } 82 83 return EnumEntryList( 84 enumEntries = enumEntries, 85 trailingComma = comma, 86 terminatingSemicolon = semicolon, 87 ) 88 } 89 } 90 } 91