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