xref: /aosp_15_r20/external/ktfmt/ktfmt_idea_plugin/src/main/kotlin/com/facebook/ktfmt/intellij/KtfmtConfigurable.kt (revision 5be3f65c8cf0e6db0a7e312df5006e8e93cdf9ec)
1 /*
<lambda>null2  * 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.intellij
18 
19 import com.facebook.ktfmt.format.FormattingOptions
20 import com.facebook.ktfmt.intellij.KtfmtSettings.EnabledState.Disabled
21 import com.facebook.ktfmt.intellij.KtfmtSettings.EnabledState.Enabled
22 import com.facebook.ktfmt.intellij.UiFormatterStyle.Custom
23 import com.facebook.ktfmt.intellij.UiFormatterStyle.Google
24 import com.facebook.ktfmt.intellij.UiFormatterStyle.KotlinLang
25 import com.facebook.ktfmt.intellij.UiFormatterStyle.Meta
26 import com.intellij.openapi.options.BoundSearchableConfigurable
27 import com.intellij.openapi.project.Project
28 import com.intellij.openapi.ui.ComboBox
29 import com.intellij.openapi.ui.DialogPanel
30 import com.intellij.ui.dsl.builder.BottomGap
31 import com.intellij.ui.dsl.builder.Cell
32 import com.intellij.ui.dsl.builder.bindIntText
33 import com.intellij.ui.dsl.builder.bindItem
34 import com.intellij.ui.dsl.builder.bindSelected
35 import com.intellij.ui.dsl.builder.panel
36 import com.intellij.ui.layout.selected
37 import com.intellij.ui.layout.selectedValueMatches
38 import javax.swing.JCheckBox
39 import javax.swing.JTextField
40 
41 @Suppress("DialogTitleCapitalization")
42 class KtfmtConfigurable(project: Project) :
43     BoundSearchableConfigurable(
44         displayName = "ktfmt Settings",
45         _id = "com.facebook.ktfmt_idea_plugin.settings",
46         helpTopic = "ktfmt",
47     ) {
48   private val settings = KtfmtSettings.getInstance(project)
49 
50   override fun createPanel(): DialogPanel = panel {
51     lateinit var enabledCheckbox: JCheckBox
52     row {
53       enabledCheckbox =
54           checkBox("Enable ktfmt")
55               .bindSelected(
56                   getter = { settings.isEnabled },
57                   setter = { settings.setEnabled(if (it) Enabled else Disabled) },
58               )
59               .component
60     }
61 
62     lateinit var styleComboBox: ComboBox<UiFormatterStyle>
63     row {
64       styleComboBox =
65           comboBox(listOf(Meta, Google, KotlinLang, Custom))
66               .label("Code style:")
67               .bindItem(
68                   getter = { settings.uiFormatterStyle },
69                   setter = { settings.uiFormatterStyle = it ?: Meta },
70               )
71               .enabledIf(enabledCheckbox.selected)
72               .component
73     }
74 
75     group("Custom style") {
76           lateinit var maxLineLength: JTextField
77           row("Max line length:") {
78             maxLineLength =
79                 textField()
80                     .bindIntText(settings::customMaxLineLength)
81                     .validatePositiveIntegerOrEmpty()
82                     .component
83           }
84 
85           lateinit var blockIndent: JTextField
86           row("Block indent size:") {
87             blockIndent =
88                 textField()
89                     .bindIntText(settings::customBlockIndent)
90                     .validatePositiveIntegerOrEmpty()
91                     .component
92           }
93 
94           lateinit var continuationIndent: JTextField
95           row("Continuation indent size:") {
96             continuationIndent =
97                 textField()
98                     .bindIntText(settings::customContinuationIndent)
99                     .validatePositiveIntegerOrEmpty()
100                     .component
101           }
102 
103           lateinit var manageTrailingCommas: JCheckBox
104           row {
105             manageTrailingCommas =
106                 checkBox("Manage trailing commas")
107                     .bindSelected(settings::customManageTrailingCommas)
108                     .component
109           }
110 
111           lateinit var removeUnusedImports: JCheckBox
112           row {
113                 removeUnusedImports =
114                     checkBox("Remove unused imports")
115                         .bindSelected(settings::customRemoveUnusedImports)
116                         .component
117               }
118               .bottomGap(BottomGap.SMALL)
119 
120           row("Copy from:") {
121             // Note: updating must be done via the components, and not the settings,
122             // or the Kotlin DSL bindings will overwrite the values when applying
123             link(Meta.toString()) {
124                   UiFormatterStyle.getStandardFormattingOptions(Meta)
125                       .updateFields(
126                           maxLineLength,
127                           blockIndent,
128                           continuationIndent,
129                           manageTrailingCommas,
130                           removeUnusedImports,
131                       )
132                 }
133                 .component
134                 .autoHideOnDisable = false
135 
136             link(Google.toString()) {
137                   UiFormatterStyle.getStandardFormattingOptions(Google)
138                       .updateFields(
139                           maxLineLength,
140                           blockIndent,
141                           continuationIndent,
142                           manageTrailingCommas,
143                           removeUnusedImports,
144                       )
145                 }
146                 .component
147                 .autoHideOnDisable = false
148 
149             link(KotlinLang.toString()) {
150                   UiFormatterStyle.getStandardFormattingOptions(KotlinLang)
151                       .updateFields(
152                           maxLineLength,
153                           blockIndent,
154                           continuationIndent,
155                           manageTrailingCommas,
156                           removeUnusedImports,
157                       )
158                 }
159                 .component
160                 .autoHideOnDisable = false
161           }
162         }
163         .visibleIf(styleComboBox.selectedValueMatches { it == Custom })
164         .enabledIf(enabledCheckbox.selected)
165   }
166 }
167 
FormattingOptionsnull168 private fun FormattingOptions.updateFields(
169     maxLineLength: JTextField,
170     blockIndent: JTextField,
171     continuationIndent: JTextField,
172     manageTrailingCommas: JCheckBox,
173     removeUnusedImports: JCheckBox,
174 ) {
175   maxLineLength.text = maxWidth.toString()
176   blockIndent.text = this.blockIndent.toString()
177   continuationIndent.text = this.continuationIndent.toString()
178   manageTrailingCommas.isSelected = this.manageTrailingCommas
179   removeUnusedImports.isSelected = this.removeUnusedImports
180 }
181 
Cellnull182 private fun Cell<JTextField>.validatePositiveIntegerOrEmpty() = validationOnInput { jTextField ->
183   if (jTextField.text.isNotEmpty()) {
184     val parsedValue = jTextField.text.toIntOrNull()
185     when {
186       parsedValue == null -> error("Value must be an integer. Will default to 1")
187       parsedValue <= 0 -> error("Value must be greater than zero. Will default to 1")
188       else -> null
189     }
190   } else null
191 }
192