mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-12 14:10:29 +00:00
feat: add default docs and other minor improvements
This commit is contained in:
parent
a2d71efd78
commit
2a0747f923
10 changed files with 114 additions and 34 deletions
|
|
@ -1,6 +1,8 @@
|
|||
package ee.carlrobert.codegpt.settings.documentation
|
||||
|
||||
import com.intellij.openapi.components.*
|
||||
import java.time.Instant
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Service
|
||||
@State(
|
||||
|
|
@ -8,13 +10,50 @@ import com.intellij.openapi.components.*
|
|||
storages = [Storage("CodeGPT_DocumentationSettings.xml")]
|
||||
)
|
||||
class DocumentationSettings :
|
||||
SimplePersistentStateComponent<DocumentationSettingsState>(DocumentationSettingsState())
|
||||
SimplePersistentStateComponent<DocumentationSettingsState>(DocumentationSettingsState()) {
|
||||
|
||||
fun updateLastUsedDateTime(url: String) {
|
||||
state.documentations
|
||||
.find { it.url == url }
|
||||
?.run {
|
||||
lastUsedDateTime = DateTimeFormatter.ISO_INSTANT.format(Instant.now())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentationSettingsState : BaseState() {
|
||||
var documentations by list<DocumentationDetailsState>()
|
||||
|
||||
init {
|
||||
documentations.addAll(DEFAULT_DOCUMENTATIONS)
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentationDetailsState : BaseState() {
|
||||
var name by string("CodeGPT Docs")
|
||||
var url by string("https://docs.codegpt.ee")
|
||||
}
|
||||
var lastUsedDateTime by string()
|
||||
}
|
||||
|
||||
private val DEFAULT_DOCUMENTATIONS = mutableListOf(
|
||||
getDocState("Astro Runtime API", "https://docs.astro.build/en/reference/api-reference/"),
|
||||
getDocState("Flask API", "https://flask.palletsprojects.com/en/3.0.x/api/"),
|
||||
getDocState("Flutter API", "https://api.flutter.dev/"),
|
||||
getDocState("IPFS Kubo CLI", "https://docs.ipfs.tech/reference/kubo/cli/#ipfs"),
|
||||
getDocState("Kotlin Coding Conventions", "https://kotlinlang.org/docs/coding-conventions.html"),
|
||||
getDocState(
|
||||
"Next.js Authentication",
|
||||
"https://nextjs.org/docs/app/building-your-application/authentication"
|
||||
),
|
||||
getDocState("SolidJS Documentation", "https://www.solidjs.com/docs"),
|
||||
getDocState("SvelteKit Modules", "https://kit.svelte.dev/docs/modules"),
|
||||
getDocState("SwiftUI Updates", "https://developer.apple.com/documentation/updates/swiftui"),
|
||||
getDocState("Zapier CLI Documentation", "https://platform.zapier.com/reference/cli-docs"),
|
||||
)
|
||||
|
||||
private fun getDocState(name: String, url: String): DocumentationDetailsState {
|
||||
return DocumentationDetailsState().apply {
|
||||
this.name = name
|
||||
this.url = url
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,9 +43,12 @@ class DocumentationsSettingsForm {
|
|||
.align(Align.FILL)
|
||||
.resizableColumn()
|
||||
.applyToComponent {
|
||||
preferredSize = Dimension(650, 250)
|
||||
preferredSize = Dimension(600, 400)
|
||||
}
|
||||
}
|
||||
row {
|
||||
text("Documentations can be included in the chat suggestions popup by pressing the @ symbol.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +135,7 @@ class DocumentationsSettingsForm {
|
|||
private fun JBTable.setupTableColumns() {
|
||||
columnModel.apply {
|
||||
getColumn(0).preferredWidth = 200
|
||||
getColumn(1).preferredWidth = 450
|
||||
getColumn(1).preferredWidth = 400
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package ee.carlrobert.codegpt.ui.textarea
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager
|
||||
import com.intellij.openapi.editor.colors.EditorFontType
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||
|
|
@ -27,6 +28,10 @@ class CustomTextPane(
|
|||
private val onSubmit: (String) -> Unit
|
||||
) : JTextPane() {
|
||||
|
||||
companion object {
|
||||
private val logger = thisLogger()
|
||||
}
|
||||
|
||||
init {
|
||||
isOpaque = false
|
||||
background = JBColor.namedColor("Editor.SearchField.background")
|
||||
|
|
@ -42,15 +47,7 @@ class CustomTextPane(
|
|||
inputMap.put(KeyStroke.getKeyStroke("ENTER"), "text-submit")
|
||||
actionMap.put("text-submit", object : AbstractAction() {
|
||||
override fun actionPerformed(e: ActionEvent) {
|
||||
var textWithoutActions = text
|
||||
highlightedTextRanges.forEach {
|
||||
val (textRange, replacement) = it
|
||||
if (replacement) {
|
||||
textWithoutActions =
|
||||
textWithoutActions.replace(text.substring(textRange.startOffset, textRange.endOffset), "")
|
||||
}
|
||||
}
|
||||
onSubmit(textWithoutActions.trim())
|
||||
onSubmit(removeHighlightedText(text))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -123,4 +120,22 @@ class CustomTextPane(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeHighlightedText(text: String): String {
|
||||
try {
|
||||
var result = text
|
||||
highlightedTextRanges.forEach { (textRange, replacement) ->
|
||||
if (replacement) {
|
||||
result = result.replace(
|
||||
text.substring(textRange.startOffset, textRange.endOffset),
|
||||
""
|
||||
)
|
||||
}
|
||||
}
|
||||
return result.trim()
|
||||
} catch (e: Exception) {
|
||||
logger.error("Error while removing highlighted text", e)
|
||||
return text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ class SuggestionsPopupBuilder {
|
|||
.createComponentPopupBuilder(popupPanel, preferableFocusComponent)
|
||||
.setMovable(true)
|
||||
.setCancelOnClickOutside(false)
|
||||
.setCancelOnWindowDeactivation(false)
|
||||
.setCancelOnWindowDeactivation(true)
|
||||
.setRequestFocus(true)
|
||||
.setMinSize(Dimension(480, 30))
|
||||
.setCancelCallback(onCancel)
|
||||
|
|
|
|||
|
|
@ -33,19 +33,7 @@ class SuggestionsPopupManager(
|
|||
})
|
||||
}
|
||||
private val list = SuggestionList(listModel, textPane) {
|
||||
when (it) {
|
||||
is SuggestionActionItem -> {
|
||||
it.execute(project, textPane)
|
||||
hidePopup()
|
||||
}
|
||||
|
||||
is SuggestionGroupItem -> {
|
||||
selectedActionGroup = it
|
||||
updateSuggestions()
|
||||
textPane.appendHighlightedText(it.groupPrefix, withWhitespace = false)
|
||||
textPane.requestFocus()
|
||||
}
|
||||
}
|
||||
handleSuggestionItemSelection(it)
|
||||
}
|
||||
private val defaultActions: MutableList<SuggestionItem> = mutableListOf(
|
||||
FileSuggestionGroupItem(project),
|
||||
|
|
@ -110,6 +98,22 @@ class SuggestionsPopupManager(
|
|||
popup?.content?.repaint()
|
||||
}
|
||||
|
||||
private fun handleSuggestionItemSelection(item: SuggestionItem) {
|
||||
when (item) {
|
||||
is SuggestionActionItem -> {
|
||||
hidePopup()
|
||||
item.execute(project, textPane)
|
||||
}
|
||||
|
||||
is SuggestionGroupItem -> {
|
||||
selectedActionGroup = item
|
||||
updateSuggestions()
|
||||
textPane.appendHighlightedText(item.groupPrefix, withWhitespace = false)
|
||||
textPane.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun adjustPopupSize() {
|
||||
val maxVisibleRows = 15
|
||||
val newRowCount = minOf(listModel.size(), maxVisibleRows)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.intellij.openapi.vfs.VirtualFile
|
|||
import ee.carlrobert.codegpt.CodeGPTBundle
|
||||
import ee.carlrobert.codegpt.CodeGPTKeys
|
||||
import ee.carlrobert.codegpt.settings.GeneralSettings
|
||||
import ee.carlrobert.codegpt.settings.documentation.DocumentationSettings
|
||||
import ee.carlrobert.codegpt.settings.documentation.DocumentationsConfigurable
|
||||
import ee.carlrobert.codegpt.settings.persona.PersonaDetails
|
||||
import ee.carlrobert.codegpt.settings.persona.PersonaSettings
|
||||
|
|
@ -64,6 +65,7 @@ class DocumentationActionItem(
|
|||
|
||||
override fun execute(project: Project, textPane: CustomTextPane) {
|
||||
CodeGPTKeys.ADDED_DOCUMENTATION.set(project, documentationDetails)
|
||||
service<DocumentationSettings>().updateLastUsedDateTime(documentationDetails.url)
|
||||
textPane.appendHighlightedText(documentationDetails.name, ':')
|
||||
}
|
||||
}
|
||||
|
|
@ -77,6 +79,8 @@ class CreateDocumentationActionItem : SuggestionActionItem {
|
|||
override fun execute(project: Project, textPane: CustomTextPane) {
|
||||
val addDocumentationDialog = AddDocumentationDialog(project)
|
||||
if (addDocumentationDialog.showAndGet()) {
|
||||
service<DocumentationSettings>()
|
||||
.updateLastUsedDateTime(addDocumentationDialog.documentationDetails.url)
|
||||
textPane.appendHighlightedText(
|
||||
addDocumentationDialog.documentationDetails.name,
|
||||
searchChar = ':',
|
||||
|
|
@ -120,5 +124,6 @@ class WebSearchActionItem(private val onWebSearchIncluded: () -> Unit) : Suggest
|
|||
|
||||
override fun execute(project: Project, textPane: CustomTextPane) {
|
||||
onWebSearchIncluded()
|
||||
textPane.appendHighlightedText("web")
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@ import ee.carlrobert.codegpt.ui.textarea.FileSearchService
|
|||
import ee.carlrobert.codegpt.util.ResourceUtil.getDefaultPersonas
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.time.Instant
|
||||
import java.time.format.DateTimeParseException
|
||||
|
||||
class FileSuggestionGroupItem(private val project: Project) : SuggestionGroupItem {
|
||||
override val displayName: String = CodeGPTBundle.get("suggestionGroupItem.files.displayName")
|
||||
|
|
@ -102,7 +104,7 @@ class DocumentationSuggestionGroupItem : SuggestionGroupItem {
|
|||
|
||||
override suspend fun getSuggestions(searchText: String?): List<SuggestionActionItem> =
|
||||
service<DocumentationSettings>().state.documentations
|
||||
.take(10)
|
||||
.sortedByDescending { parseDateTime(it.lastUsedDateTime) }
|
||||
.filter {
|
||||
if (searchText.isNullOrEmpty()) {
|
||||
true
|
||||
|
|
@ -110,7 +112,18 @@ class DocumentationSuggestionGroupItem : SuggestionGroupItem {
|
|||
it.name?.contains(searchText, true) ?: false
|
||||
}
|
||||
}
|
||||
.take(10)
|
||||
.map {
|
||||
DocumentationActionItem(DocumentationDetails(it.name ?: "", it.url ?: ""))
|
||||
} + listOf(CreateDocumentationActionItem(), ViewAllDocumentationsActionItem())
|
||||
|
||||
private fun parseDateTime(dateTimeString: String?): Instant {
|
||||
return dateTimeString?.let {
|
||||
try {
|
||||
Instant.parse(it)
|
||||
} catch (e: DateTimeParseException) {
|
||||
Instant.EPOCH
|
||||
}
|
||||
} ?: Instant.EPOCH
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,7 @@ abstract class BaseItemRenderer(private val textPane: CustomTextPane) : ItemRend
|
|||
val searchText = textPane.text.searchText()
|
||||
label.apply {
|
||||
this.icon = icon
|
||||
disabledIcon = icon
|
||||
iconTextGap = 4
|
||||
text = searchText?.let { title.highlightSearchText(it) } ?: title
|
||||
}
|
||||
|
|
@ -98,7 +99,7 @@ class DefaultItemRenderer(textPane: CustomTextPane) : BaseItemRenderer(textPane)
|
|||
value.icon ?: EMPTY_ICON,
|
||||
getTitle(value),
|
||||
getDescription(value),
|
||||
if (value.enabled) null else "This action can only be used with CodeGPT provider."
|
||||
if (value.enabled) null else "This action can only be used with CodeGPT provider"
|
||||
).apply {
|
||||
isEnabled = value.enabled
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,14 +44,14 @@
|
|||
instance="ee.carlrobert.codegpt.settings.service.LlamaServiceConfigurable"/>
|
||||
<applicationConfigurable id="settings.codegpt.services.ollama" parentId="settings.codegpt.services" displayName="Ollama (Local)"
|
||||
instance="ee.carlrobert.codegpt.settings.service.ollama.OllamaSettingsConfigurable"/>
|
||||
<applicationConfigurable id="settings.codegpt.configuration" parentId="settings.codegpt" displayName="Configuration"
|
||||
instance="ee.carlrobert.codegpt.settings.configuration.ConfigurationConfigurable"/>
|
||||
<applicationConfigurable id="settings.codegpt.advanced" parentId="settings.codegpt" displayName="Advanced Settings"
|
||||
instance="ee.carlrobert.codegpt.settings.advanced.AdvancedSettingsConfigurable"/>
|
||||
<applicationConfigurable id="settings.codegpt.personas" parentId="settings.codegpt" displayName="Personas"
|
||||
instance="ee.carlrobert.codegpt.settings.persona.PersonasConfigurable"/>
|
||||
<applicationConfigurable id="settings.codegpt.dcoumentations" parentId="settings.codegpt" displayName="Documentations"
|
||||
instance="ee.carlrobert.codegpt.settings.documentation.DocumentationsConfigurable"/>
|
||||
<applicationConfigurable id="settings.codegpt.configuration" parentId="settings.codegpt" displayName="Configuration"
|
||||
instance="ee.carlrobert.codegpt.settings.configuration.ConfigurationConfigurable"/>
|
||||
<applicationConfigurable id="settings.codegpt.advanced" parentId="settings.codegpt" displayName="Advanced Settings"
|
||||
instance="ee.carlrobert.codegpt.settings.advanced.AdvancedSettingsConfigurable"/>
|
||||
<applicationConfigurable
|
||||
parentId="settings.codegpt"
|
||||
instance="ee.carlrobert.codegpt.telemetry.ui.preferences.TelemetryConfigurable"
|
||||
|
|
|
|||
|
|
@ -260,6 +260,6 @@ suggestionGroupItem.folders.displayName=Folders
|
|||
suggestionGroupItem.personas.displayName=Personas
|
||||
suggestionGroupItem.docs.displayName=Docs
|
||||
suggestionActionItem.webSearch.displayName=Web
|
||||
suggestionActionItem.viewDocumentations.displayName=View all
|
||||
suggestionActionItem.viewDocumentations.displayName=View all docs
|
||||
suggestionActionItem.createPersona.displayName=Create new persona
|
||||
suggestionActionItem.createDocumentation.displayName=Create new documentation
|
||||
Loading…
Add table
Add a link
Reference in a new issue