Skip to content

Commit 2b802a0

Browse files
authored
feat: context editor improvements (file/folder picker). Target IDE shift (#1224)
- Target IDE build shifted to 262.* max supported (current EAP). - Added context settings for file/folder picker to be more usable: File filter settings, max files, max folders, display right options for better views, sorting.
1 parent 00b0f62 commit 2b802a0

14 files changed

Lines changed: 669 additions & 41 deletions

File tree

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pluginVersion = 3.8.1
88

99
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
1010
pluginSinceBuild = 261
11-
pluginUntilBuild = 261.*
11+
pluginUntilBuild = 262.*
1212

1313
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
1414
platformType = IC

src/main/java/ee/carlrobert/codegpt/settings/configuration/ConfigurationComponent.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class ConfigurationComponent {
2929
private final JBTextField temperatureField;
3030
private final CodeCompletionConfigurationForm codeCompletionForm;
3131
private final ChatCompletionConfigurationForm chatCompletionForm;
32+
private final ContextSuggestionConfigurationForm contextSuggestionForm;
3233
private final ScreenshotConfigurationForm screenshotForm;
3334

3435
public ConfigurationComponent(
@@ -75,6 +76,7 @@ public void changedUpdate(DocumentEvent e) {
7576

7677
codeCompletionForm = new CodeCompletionConfigurationForm();
7778
chatCompletionForm = new ChatCompletionConfigurationForm();
79+
contextSuggestionForm = new ContextSuggestionConfigurationForm();
7880
screenshotForm = new ScreenshotConfigurationForm();
7981
screenshotForm.loadState(configuration.getScreenshotWatchPaths());
8082

@@ -96,6 +98,9 @@ public void changedUpdate(DocumentEvent e) {
9698
.addComponent(new TitledSeparator(
9799
CodeGPTBundle.get("configurationConfigurable.section.chatCompletion.title")))
98100
.addComponent(chatCompletionForm.createPanel())
101+
.addComponent(new TitledSeparator(
102+
CodeGPTBundle.get("configurationConfigurable.section.contextSuggestions.title")))
103+
.addComponent(contextSuggestionForm.createPanel())
99104
.addComponentFillVertically(new JPanel(), 0)
100105
.getPanel();
101106
}
@@ -114,6 +119,7 @@ public ConfigurationSettingsState getCurrentFormState() {
114119
state.setAutoFormattingEnabled(autoFormattingCheckBox.isSelected());
115120
state.setCodeCompletionSettings(codeCompletionForm.getFormState());
116121
state.setChatCompletionSettings(chatCompletionForm.getFormState());
122+
state.setContextSuggestionSettings(contextSuggestionForm.getFormState());
117123

118124
var screenshotPaths = screenshotForm.getState();
119125
state.getScreenshotWatchPaths().clear();
@@ -132,6 +138,7 @@ public void resetForm() {
132138
autoFormattingCheckBox.setSelected(configuration.getAutoFormattingEnabled());
133139
codeCompletionForm.resetForm(configuration.getCodeCompletionSettings());
134140
chatCompletionForm.resetForm(configuration.getChatCompletionSettings());
141+
contextSuggestionForm.resetForm(configuration.getContextSuggestionSettings());
135142
screenshotForm.loadState(configuration.getScreenshotWatchPaths());
136143
}
137144

src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ConfigurationSettings.kt

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ee.carlrobert.codegpt.settings.configuration
22

33
import com.intellij.openapi.components.*
4+
import ee.carlrobert.codegpt.CodeGPTBundle
45
import ee.carlrobert.codegpt.actions.editor.EditorActionsUtil
56
import ee.carlrobert.codegpt.settings.prompts.CoreActionsState
67
import kotlin.math.max
@@ -36,6 +37,7 @@ class ConfigurationSettingsState : BaseState() {
3637
var tableData by map<String, String>()
3738
var chatCompletionSettings by property(ChatCompletionSettingsState())
3839
var codeCompletionSettings by property(CodeCompletionSettingsState())
40+
var contextSuggestionSettings by property(ContextSuggestionSettingsState())
3941

4042
init {
4143
tableData.putAll(EditorActionsUtil.DEFAULT_ACTIONS)
@@ -57,6 +59,59 @@ class ChatCompletionSettingsState : BaseState() {
5759
var sendWithShiftEnter by property(false)
5860
}
5961

62+
object ContextSuggestionSettings {
63+
const val DEFAULT_MAX_FILE_SUGGESTIONS = 50
64+
const val DEFAULT_MAX_DIRECTORY_SUGGESTIONS = 25
65+
const val MAX_FILE_SUGGESTIONS = 500
66+
const val MAX_DIRECTORY_SUGGESTIONS = 70
67+
68+
private const val MIN_SUGGESTIONS = 1
69+
private const val LOOKUP_RESULT_HEADROOM = 32
70+
71+
fun normalizeMaxFileSuggestions(value: Int): Int =
72+
value.coerceIn(MIN_SUGGESTIONS, MAX_FILE_SUGGESTIONS)
73+
74+
fun normalizeMaxDirectorySuggestions(value: Int): Int =
75+
value.coerceIn(MIN_SUGGESTIONS, MAX_DIRECTORY_SUGGESTIONS)
76+
77+
fun maxLookupResults(state: ConfigurationSettingsState = ConfigurationSettings.getState()): Int {
78+
val fileLimit = normalizeMaxFileSuggestions(state.contextSuggestionSettings.maxFileSuggestions)
79+
val directoryLimit =
80+
normalizeMaxDirectorySuggestions(state.contextSuggestionSettings.maxDirectorySuggestions)
81+
return fileLimit + directoryLimit + LOOKUP_RESULT_HEADROOM
82+
}
83+
}
84+
85+
class ContextSuggestionSettingsState : BaseState() {
86+
var maxFileSuggestions by property(ContextSuggestionSettings.DEFAULT_MAX_FILE_SUGGESTIONS)
87+
var maxDirectorySuggestions by property(ContextSuggestionSettings.DEFAULT_MAX_DIRECTORY_SUGGESTIONS)
88+
var blankFileSuggestionMode by enum(ContextSuggestionBlankFileSuggestionMode.OPEN_AND_RECENT)
89+
var fileSortMode by enum(ContextSuggestionFileSortMode.PRESERVE_CURRENT_ORDER)
90+
var pathDetailsMode by enum(ContextSuggestionPathDetailsMode.FULL_PATH)
91+
}
92+
93+
enum class ContextSuggestionBlankFileSuggestionMode(private val messageKey: String) {
94+
OPEN_AND_RECENT("configurationConfigurable.section.contextSuggestions.blankFileSuggestionMode.option.openAndRecent"),
95+
OPEN_RECENT_AND_PROJECT("configurationConfigurable.section.contextSuggestions.blankFileSuggestionMode.option.openRecentAndProject");
96+
97+
fun displayName(): String = CodeGPTBundle.get(messageKey)
98+
}
99+
100+
enum class ContextSuggestionFileSortMode(private val messageKey: String) {
101+
FILE_NAME_ASCENDING("configurationConfigurable.section.contextSuggestions.fileSortMode.option.fileNameAscending"),
102+
FOLDER_THEN_FILE_ASCENDING("configurationConfigurable.section.contextSuggestions.fileSortMode.option.folderThenFileAscending"),
103+
PRESERVE_CURRENT_ORDER("configurationConfigurable.section.contextSuggestions.fileSortMode.option.preserveCurrentOrder");
104+
105+
fun displayName(): String = CodeGPTBundle.get(messageKey)
106+
}
107+
108+
enum class ContextSuggestionPathDetailsMode(private val messageKey: String) {
109+
FULL_PATH("configurationConfigurable.section.contextSuggestions.pathDetailsMode.option.fullPath"),
110+
DIRECTORY_ONLY("configurationConfigurable.section.contextSuggestions.pathDetailsMode.option.directoryOnly");
111+
112+
fun displayName(): String = CodeGPTBundle.get(messageKey)
113+
}
114+
60115
class CodeCompletionSettingsState : BaseState() {
61116
var treeSitterProcessingEnabled by property(true)
62117
var gitDiffEnabled by property(true)
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package ee.carlrobert.codegpt.settings.configuration
2+
3+
import com.intellij.openapi.components.service
4+
import com.intellij.openapi.ui.ComboBox
5+
import com.intellij.openapi.ui.DialogPanel
6+
import com.intellij.ui.EnumComboBoxModel
7+
import com.intellij.ui.components.fields.IntegerField
8+
import com.intellij.ui.dsl.builder.panel
9+
import com.intellij.util.ui.JBUI
10+
import ee.carlrobert.codegpt.CodeGPTBundle
11+
import java.awt.Component
12+
import javax.swing.DefaultListCellRenderer
13+
import javax.swing.JList
14+
15+
class ContextSuggestionConfigurationForm {
16+
17+
private val maxFileSuggestionsField = IntegerField(
18+
"max_file_suggestions",
19+
1,
20+
ContextSuggestionSettings.MAX_FILE_SUGGESTIONS
21+
).apply {
22+
columns = 12
23+
value = service<ConfigurationSettings>().state.contextSuggestionSettings.maxFileSuggestions
24+
}
25+
26+
private val maxDirectorySuggestionsField = IntegerField(
27+
"max_directory_suggestions",
28+
1,
29+
ContextSuggestionSettings.MAX_DIRECTORY_SUGGESTIONS
30+
).apply {
31+
columns = 12
32+
value = service<ConfigurationSettings>().state.contextSuggestionSettings.maxDirectorySuggestions
33+
}
34+
private val blankFileSuggestionModeComboBox =
35+
ComboBox(EnumComboBoxModel(ContextSuggestionBlankFileSuggestionMode::class.java)).apply {
36+
selectedItem = service<ConfigurationSettings>().state.contextSuggestionSettings.blankFileSuggestionMode
37+
renderer = object : DefaultListCellRenderer() {
38+
override fun getListCellRendererComponent(
39+
list: JList<*>?,
40+
value: Any?,
41+
index: Int,
42+
isSelected: Boolean,
43+
cellHasFocus: Boolean
44+
): Component {
45+
val displayValue =
46+
(value as? ContextSuggestionBlankFileSuggestionMode)?.displayName() ?: value
47+
return super.getListCellRendererComponent(
48+
list,
49+
displayValue,
50+
index,
51+
isSelected,
52+
cellHasFocus
53+
)
54+
}
55+
}
56+
}
57+
private val fileSortModeComboBox =
58+
ComboBox(EnumComboBoxModel(ContextSuggestionFileSortMode::class.java)).apply {
59+
selectedItem = service<ConfigurationSettings>().state.contextSuggestionSettings.fileSortMode
60+
renderer = object : DefaultListCellRenderer() {
61+
override fun getListCellRendererComponent(
62+
list: JList<*>?,
63+
value: Any?,
64+
index: Int,
65+
isSelected: Boolean,
66+
cellHasFocus: Boolean
67+
): Component {
68+
val displayValue = (value as? ContextSuggestionFileSortMode)?.displayName() ?: value
69+
return super.getListCellRendererComponent(
70+
list,
71+
displayValue,
72+
index,
73+
isSelected,
74+
cellHasFocus
75+
)
76+
}
77+
}
78+
}
79+
private val pathDetailsModeComboBox =
80+
ComboBox(EnumComboBoxModel(ContextSuggestionPathDetailsMode::class.java)).apply {
81+
selectedItem = service<ConfigurationSettings>().state.contextSuggestionSettings.pathDetailsMode
82+
renderer = object : DefaultListCellRenderer() {
83+
override fun getListCellRendererComponent(
84+
list: JList<*>?,
85+
value: Any?,
86+
index: Int,
87+
isSelected: Boolean,
88+
cellHasFocus: Boolean
89+
): Component {
90+
val displayValue = (value as? ContextSuggestionPathDetailsMode)?.displayName() ?: value
91+
return super.getListCellRendererComponent(
92+
list,
93+
displayValue,
94+
index,
95+
isSelected,
96+
cellHasFocus
97+
)
98+
}
99+
}
100+
}
101+
102+
fun createPanel(): DialogPanel {
103+
return panel {
104+
row {
105+
label(
106+
CodeGPTBundle.get(
107+
"configurationConfigurable.section.contextSuggestions.maxFileSuggestions.title"
108+
)
109+
)
110+
cell(maxFileSuggestionsField)
111+
.comment(
112+
CodeGPTBundle.get(
113+
"configurationConfigurable.section.contextSuggestions.maxFileSuggestions.description"
114+
)
115+
)
116+
}
117+
row {
118+
label(
119+
CodeGPTBundle.get(
120+
"configurationConfigurable.section.contextSuggestions.maxDirectorySuggestions.title"
121+
)
122+
)
123+
cell(maxDirectorySuggestionsField)
124+
.comment(
125+
CodeGPTBundle.get(
126+
"configurationConfigurable.section.contextSuggestions.maxDirectorySuggestions.description"
127+
)
128+
)
129+
}
130+
row {
131+
label(
132+
CodeGPTBundle.get(
133+
"configurationConfigurable.section.contextSuggestions.blankFileSuggestionMode.title"
134+
)
135+
)
136+
cell(blankFileSuggestionModeComboBox)
137+
.comment(
138+
CodeGPTBundle.get(
139+
"configurationConfigurable.section.contextSuggestions.blankFileSuggestionMode.description"
140+
)
141+
)
142+
}
143+
row {
144+
label(
145+
CodeGPTBundle.get(
146+
"configurationConfigurable.section.contextSuggestions.fileSortMode.title"
147+
)
148+
)
149+
cell(fileSortModeComboBox)
150+
.comment(
151+
CodeGPTBundle.get(
152+
"configurationConfigurable.section.contextSuggestions.fileSortMode.description"
153+
)
154+
)
155+
}
156+
row {
157+
label(
158+
CodeGPTBundle.get(
159+
"configurationConfigurable.section.contextSuggestions.pathDetailsMode.title"
160+
)
161+
)
162+
cell(pathDetailsModeComboBox)
163+
.comment(
164+
CodeGPTBundle.get(
165+
"configurationConfigurable.section.contextSuggestions.pathDetailsMode.description"
166+
)
167+
)
168+
}
169+
}.withBorder(JBUI.Borders.emptyLeft(16))
170+
}
171+
172+
fun resetForm(prevState: ContextSuggestionSettingsState) {
173+
maxFileSuggestionsField.value = prevState.maxFileSuggestions
174+
maxDirectorySuggestionsField.value = prevState.maxDirectorySuggestions
175+
blankFileSuggestionModeComboBox.selectedItem = prevState.blankFileSuggestionMode
176+
fileSortModeComboBox.selectedItem = prevState.fileSortMode
177+
pathDetailsModeComboBox.selectedItem = prevState.pathDetailsMode
178+
}
179+
180+
fun getFormState(): ContextSuggestionSettingsState {
181+
return ContextSuggestionSettingsState().apply {
182+
maxFileSuggestions = maxFileSuggestionsField.value
183+
maxDirectorySuggestions = maxDirectorySuggestionsField.value
184+
blankFileSuggestionMode =
185+
blankFileSuggestionModeComboBox.selectedItem as? ContextSuggestionBlankFileSuggestionMode
186+
?: ContextSuggestionBlankFileSuggestionMode.OPEN_AND_RECENT
187+
fileSortMode = fileSortModeComboBox.selectedItem as? ContextSuggestionFileSortMode
188+
?: ContextSuggestionFileSortMode.PRESERVE_CURRENT_ORDER
189+
pathDetailsMode = pathDetailsModeComboBox.selectedItem as? ContextSuggestionPathDetailsMode
190+
?: ContextSuggestionPathDetailsMode.FULL_PATH
191+
}
192+
}
193+
}

src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/PromptTextField.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import com.intellij.util.IncorrectOperationException
3838
import com.intellij.util.ui.JBUI
3939
import ee.carlrobert.codegpt.CodeGPTBundle
4040
import ee.carlrobert.codegpt.CodeGPTKeys.IS_PROMPT_TEXT_FIELD_DOCUMENT
41+
import ee.carlrobert.codegpt.settings.configuration.ContextSuggestionSettings
4142
import ee.carlrobert.codegpt.settings.service.FeatureType
4243
import ee.carlrobert.codegpt.ui.dnd.FileDragAndDrop
4344
import ee.carlrobert.codegpt.ui.textarea.header.tag.TagManager
@@ -1005,7 +1006,7 @@ class PromptTextField(
10051006
) {
10061007
val reusableItems = lookupItems
10071008
.filterNot { it is LoadingLookupItem || it is StatusLookupItem }
1008-
.take(PromptTextFieldConstants.MAX_SEARCH_RESULTS)
1009+
.take(ContextSuggestionSettings.maxLookupResults())
10091010
if (reusableItems.isEmpty()) {
10101011
if (!lookupSearchLoading) {
10111012
clearVisibleLookupItems()

src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/SearchManager.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ee.carlrobert.codegpt.ui.textarea
33
import com.intellij.openapi.diagnostic.thisLogger
44
import com.intellij.openapi.project.Project
55
import com.intellij.psi.codeStyle.MinusculeMatcher
6+
import ee.carlrobert.codegpt.settings.configuration.ContextSuggestionSettings
67
import ee.carlrobert.codegpt.settings.service.FeatureType
78
import ee.carlrobert.codegpt.ui.textarea.header.tag.TagManager
89
import ee.carlrobert.codegpt.ui.textarea.lookup.LookupActionItem
@@ -202,7 +203,7 @@ class SearchManager(
202203
}
203204
.sortedByDescending { it.second }
204205
.map { it.first }
205-
.take(PromptTextFieldConstants.MAX_SEARCH_RESULTS)
206+
.take(ContextSuggestionSettings.maxLookupResults())
206207
}
207208

208209
private fun createMatcher(searchText: String): MinusculeMatcher {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package ee.carlrobert.codegpt.ui.textarea.lookup.action
2+
3+
import com.intellij.openapi.project.Project
4+
import com.intellij.openapi.project.guessProjectDir
5+
import com.intellij.openapi.vfs.VfsUtil
6+
import com.intellij.openapi.vfs.VirtualFile
7+
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
8+
import ee.carlrobert.codegpt.settings.configuration.ContextSuggestionPathDetailsMode
9+
10+
internal fun contextSuggestionTypeText(project: Project, file: VirtualFile): String? {
11+
val projectDir = project.guessProjectDir()
12+
return when (ConfigurationSettings.getState().contextSuggestionSettings.pathDetailsMode) {
13+
ContextSuggestionPathDetailsMode.FULL_PATH -> projectRelativePath(projectDir, file) ?: file.path
14+
ContextSuggestionPathDetailsMode.DIRECTORY_ONLY -> parentDirectoryPath(projectDir, file)
15+
}
16+
}
17+
18+
private fun parentDirectoryPath(projectDir: VirtualFile?, file: VirtualFile): String? {
19+
val parent = file.parent ?: return null
20+
return if (projectDir != null) {
21+
val relativeParentPath = VfsUtil.getRelativePath(parent, projectDir) ?: return parent.path
22+
if (relativeParentPath.isEmpty()) "." else relativeParentPath
23+
} else {
24+
parent.path
25+
}
26+
}
27+
28+
private fun projectRelativePath(projectDir: VirtualFile?, file: VirtualFile): String? {
29+
if (projectDir == null) {
30+
return null
31+
}
32+
return VfsUtil.getRelativePath(file, projectDir)
33+
}

0 commit comments

Comments
 (0)