mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-12 05:51:28 +00:00
refactor: popup suggestions strategy
This commit is contained in:
parent
44db3495b4
commit
2986c42ad9
3 changed files with 125 additions and 164 deletions
|
|
@ -0,0 +1,102 @@
|
|||
package ee.carlrobert.codegpt.ui.textarea
|
||||
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ContentIterator
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import ee.carlrobert.codegpt.util.ResourceUtil
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
|
||||
interface SuggestionStrategy {
|
||||
suspend fun getSuggestions(project: Project, searchText: String? = null): List<SuggestionItem>
|
||||
}
|
||||
|
||||
class FileSuggestionStrategy : SuggestionStrategy {
|
||||
override suspend fun getSuggestions(
|
||||
project: Project,
|
||||
searchText: String?
|
||||
): List<SuggestionItem> {
|
||||
if (searchText == null) {
|
||||
val projectFileIndex = project.service<ProjectFileIndex>()
|
||||
return readAction {
|
||||
project.service<FileEditorManager>().openFiles
|
||||
.filter { projectFileIndex.isInContent(it) }
|
||||
.take(10)
|
||||
.map { SuggestionItem.FileItem(File(it.path)) }
|
||||
}
|
||||
}
|
||||
return project.service<FileSearchService>().searchFiles(searchText)
|
||||
.take(10)
|
||||
.map { SuggestionItem.FileItem(File(it)) }
|
||||
}
|
||||
}
|
||||
|
||||
class FolderSuggestionStrategy : SuggestionStrategy {
|
||||
private val projectFoldersCache = mutableMapOf<Project, List<String>>()
|
||||
|
||||
override suspend fun getSuggestions(
|
||||
project: Project,
|
||||
searchText: String?
|
||||
): List<SuggestionItem> {
|
||||
if (searchText == null) {
|
||||
return getProjectFolders(project)
|
||||
.take(10)
|
||||
.map { SuggestionItem.FolderItem(Path.of(it).toFile()) }
|
||||
}
|
||||
return getProjectFolders(project)
|
||||
.filter { it.contains(searchText, ignoreCase = true) }
|
||||
.take(10)
|
||||
.map { SuggestionItem.FolderItem(Path.of(it).toFile()) }
|
||||
}
|
||||
|
||||
private suspend fun getProjectFolders(project: Project): List<String> {
|
||||
return projectFoldersCache.getOrPut(project) {
|
||||
findProjectFolders(project)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun findProjectFolders(project: Project): List<String> =
|
||||
withContext(Dispatchers.IO) {
|
||||
val uniqueFolders = mutableSetOf<String>()
|
||||
val iterator = ContentIterator { file: VirtualFile ->
|
||||
if (file.isDirectory && !file.name.startsWith(".")) {
|
||||
val folderPath = file.path
|
||||
if (uniqueFolders.none { it.startsWith(folderPath) }) {
|
||||
uniqueFolders.removeAll { it.startsWith(folderPath) }
|
||||
uniqueFolders.add(folderPath)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
project.service<ProjectFileIndex>().iterateContent(iterator)
|
||||
uniqueFolders.toList()
|
||||
}
|
||||
}
|
||||
|
||||
class PersonaSuggestionStrategy : SuggestionStrategy {
|
||||
override suspend fun getSuggestions(
|
||||
project: Project,
|
||||
searchText: String?
|
||||
): List<SuggestionItem> {
|
||||
if (searchText == null) {
|
||||
return ResourceUtil.getFilteredPersonaSuggestions(null)
|
||||
}
|
||||
return ResourceUtil.getFilteredPersonaSuggestions {
|
||||
it.name.contains(searchText, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultSuggestionStrategy : SuggestionStrategy {
|
||||
override suspend fun getSuggestions(
|
||||
project: Project,
|
||||
searchText: String?
|
||||
): List<SuggestionItem> = emptyList()
|
||||
}
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
package ee.carlrobert.codegpt.ui.textarea
|
||||
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ContentIterator
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import ee.carlrobert.codegpt.util.ResourceUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import javax.swing.DefaultListModel
|
||||
|
||||
interface SuggestionUpdateStrategy {
|
||||
fun populateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>,
|
||||
)
|
||||
|
||||
fun updateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>,
|
||||
searchText: String,
|
||||
)
|
||||
}
|
||||
|
||||
class FileSuggestionActionStrategy : SuggestionUpdateStrategy {
|
||||
override fun populateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>,
|
||||
) {
|
||||
val projectFileIndex = project.service<ProjectFileIndex>()
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val openFilePaths = project.service<FileEditorManager>().openFiles
|
||||
.filter { readAction { projectFileIndex.isInContent(it) } }
|
||||
.take(10)
|
||||
.map { file -> file.path }
|
||||
listModel.clear()
|
||||
listModel.addAll(openFilePaths.map { SuggestionItem.FileItem(File(it)) })
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>,
|
||||
searchText: String,
|
||||
) {
|
||||
val filePaths = project.service<FileSearchService>().searchFiles(searchText).take(10)
|
||||
listModel.clear()
|
||||
listModel.addAll(filePaths.map { SuggestionItem.FileItem(File(it)) })
|
||||
}
|
||||
}
|
||||
|
||||
class FolderSuggestionActionStrategy : SuggestionUpdateStrategy {
|
||||
private val projectFoldersCache = mutableMapOf<Project, List<String>>()
|
||||
|
||||
override fun populateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>
|
||||
) {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val folderPaths = getProjectFolders(project)
|
||||
.take(10)
|
||||
.map { SuggestionItem.FolderItem(Path.of(it).toFile()) }
|
||||
listModel.clear()
|
||||
listModel.addAll(folderPaths)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>,
|
||||
searchText: String
|
||||
) {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val filteredFolders = getProjectFolders(project)
|
||||
.filter { it.contains(searchText, ignoreCase = true) }
|
||||
.take(10)
|
||||
.map { SuggestionItem.FolderItem(Path.of(it).toFile()) }
|
||||
listModel.clear()
|
||||
listModel.addAll(filteredFolders)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getProjectFolders(project: Project): List<String> {
|
||||
return projectFoldersCache.getOrPut(project) {
|
||||
findProjectFolders(project)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun findProjectFolders(project: Project): List<String> =
|
||||
withContext(Dispatchers.IO) {
|
||||
val uniqueFolders = mutableSetOf<String>()
|
||||
val iterator = ContentIterator { file: VirtualFile ->
|
||||
if (file.isDirectory && !file.name.startsWith(".")) {
|
||||
val folderPath = file.path
|
||||
if (uniqueFolders.none { it.startsWith(folderPath) }) {
|
||||
uniqueFolders.removeAll { it.startsWith(folderPath) }
|
||||
uniqueFolders.add(folderPath)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
project.service<ProjectFileIndex>().iterateContent(iterator)
|
||||
uniqueFolders.toList()
|
||||
}
|
||||
}
|
||||
|
||||
class PersonaSuggestionActionStrategy : SuggestionUpdateStrategy {
|
||||
|
||||
override fun populateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>,
|
||||
) {
|
||||
listModel.clear()
|
||||
listModel.addAll(ResourceUtil.getFilteredPersonaSuggestions(null))
|
||||
}
|
||||
|
||||
override fun updateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>,
|
||||
searchText: String,
|
||||
) {
|
||||
listModel.clear()
|
||||
listModel.addAll(ResourceUtil.getFilteredPersonaSuggestions {
|
||||
it.name.contains(
|
||||
searchText,
|
||||
true
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultSuggestionActionStrategy : SuggestionUpdateStrategy {
|
||||
override fun populateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>,
|
||||
) {
|
||||
}
|
||||
|
||||
override fun updateSuggestions(
|
||||
project: Project,
|
||||
listModel: DefaultListModel<SuggestionItem>,
|
||||
searchText: String,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package ee.carlrobert.codegpt.ui.textarea
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.application.runInEdt
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.options.ShowSettingsUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
|
|
@ -19,6 +20,9 @@ import ee.carlrobert.codegpt.CodeGPTBundle
|
|||
import ee.carlrobert.codegpt.settings.persona.PersonaDetails
|
||||
import ee.carlrobert.codegpt.settings.persona.PersonaSettings
|
||||
import ee.carlrobert.codegpt.settings.persona.PersonasConfigurable
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.awt.Dimension
|
||||
import java.awt.Point
|
||||
import java.io.File
|
||||
|
|
@ -64,7 +68,7 @@ class SuggestionsPopupManager(
|
|||
private val textPane: CustomTextPane,
|
||||
) {
|
||||
|
||||
private var currentActionStrategy: SuggestionUpdateStrategy = DefaultSuggestionActionStrategy()
|
||||
private var currentActionStrategy: SuggestionStrategy = DefaultSuggestionStrategy()
|
||||
private val appliedActions: MutableList<SuggestionItem.ActionItem> = mutableListOf()
|
||||
private var popup: JBPopup? = null
|
||||
private var originalLocation: Point? = null
|
||||
|
|
@ -115,10 +119,18 @@ class SuggestionsPopupManager(
|
|||
list.selectNext()
|
||||
}
|
||||
|
||||
fun updateSuggestions(searchText: String) {
|
||||
currentActionStrategy.updateSuggestions(project, listModel, searchText)
|
||||
list.revalidate()
|
||||
list.repaint()
|
||||
fun updateSuggestions(searchText: String? = null) {
|
||||
val suggestions = runBlocking {
|
||||
withContext(Dispatchers.Default) {
|
||||
currentActionStrategy.getSuggestions(project, searchText)
|
||||
}
|
||||
}
|
||||
runInEdt {
|
||||
listModel.clear()
|
||||
listModel.addAll(suggestions)
|
||||
list.revalidate()
|
||||
list.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
fun reset(clearPrevious: Boolean = true) {
|
||||
|
|
@ -143,22 +155,22 @@ class SuggestionsPopupManager(
|
|||
appliedActions.add(item)
|
||||
currentActionStrategy = when (item.action) {
|
||||
DefaultAction.FILES -> {
|
||||
FileSuggestionActionStrategy()
|
||||
FileSuggestionStrategy()
|
||||
}
|
||||
|
||||
DefaultAction.FOLDERS -> {
|
||||
FolderSuggestionActionStrategy()
|
||||
FolderSuggestionStrategy()
|
||||
}
|
||||
|
||||
DefaultAction.PERSONAS -> {
|
||||
PersonaSuggestionActionStrategy()
|
||||
PersonaSuggestionStrategy()
|
||||
}
|
||||
|
||||
else -> {
|
||||
DefaultSuggestionActionStrategy()
|
||||
DefaultSuggestionStrategy()
|
||||
}
|
||||
}
|
||||
currentActionStrategy.populateSuggestions(project, listModel)
|
||||
updateSuggestions()
|
||||
textPane.appendHighlightedText(item.action.code, withWhitespace = false)
|
||||
textPane.requestFocus()
|
||||
}
|
||||
|
|
@ -238,7 +250,7 @@ class SuggestionsPopupManager(
|
|||
.setMinSize(Dimension(480, 30))
|
||||
.setCancelCallback {
|
||||
originalLocation = null
|
||||
currentActionStrategy = DefaultSuggestionActionStrategy()
|
||||
currentActionStrategy = DefaultSuggestionStrategy()
|
||||
true
|
||||
}
|
||||
.setResizable(true)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue