mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-10 03:59:43 +00:00
feat: code assistant (#810)
* feat: code assistant implementation * refactor: clean up
This commit is contained in:
parent
d81648549c
commit
8d6ca73064
32 changed files with 944 additions and 62 deletions
|
|
@ -1,10 +1,12 @@
|
|||
package ee.carlrobert.codegpt;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import ee.carlrobert.codegpt.predictions.CodeSuggestionDiffViewer;
|
||||
import ee.carlrobert.codegpt.settings.prompts.PersonaDetails;
|
||||
import ee.carlrobert.codegpt.ui.DocumentationDetails;
|
||||
import ee.carlrobert.llm.client.codegpt.CodeGPTUserDetails;
|
||||
import java.util.List;
|
||||
import okhttp3.Call;
|
||||
|
||||
public class CodeGPTKeys {
|
||||
|
||||
|
|
@ -26,4 +28,8 @@ public class CodeGPTKeys {
|
|||
Key.create("codegpt.isFetchingCompletion");
|
||||
public static final Key<Boolean> IS_PROMPT_TEXT_FIELD_DOCUMENT =
|
||||
Key.create("codegpt.isPromptTextFieldDocument");
|
||||
public static final Key<CodeSuggestionDiffViewer> EDITOR_PREDICTION_DIFF_VIEWER =
|
||||
Key.create("codegpt.editorPredictionDiffViewer");
|
||||
public static final Key<Call> PENDING_PREDICTION_CALL =
|
||||
Key.create("codegpt.editorPendingPredictionCall");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package ee.carlrobert.codegpt.statusbar;
|
||||
|
||||
import static ee.carlrobert.codegpt.CodeGPTKeys.IS_FETCHING_COMPLETION;
|
||||
import static ee.carlrobert.codegpt.CodeGPTKeys.PENDING_PREDICTION_CALL;
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
|
|
@ -44,7 +45,9 @@ public class CodeGPTStatusBarWidget extends EditorBasedStatusBarPopup {
|
|||
protected @NotNull WidgetState getWidgetState(@Nullable VirtualFile file) {
|
||||
var state = new WidgetState(CodeGPTBundle.get("statusBar.widget.tooltip"), "", true);
|
||||
var fetchingCompletion = IS_FETCHING_COMPLETION.get(getEditor());
|
||||
var loading = fetchingCompletion != null && fetchingCompletion;
|
||||
var pendingPredicationCall = PENDING_PREDICTION_CALL.get(getEditor());
|
||||
var loading =
|
||||
(fetchingCompletion != null && fetchingCompletion) || pendingPredicationCall != null;
|
||||
|
||||
state.setIcon(loading ? Icons.StatusBarCompletionInProgress : Icons.DefaultSmall);
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -139,8 +139,14 @@ public class UIUtil {
|
|||
}
|
||||
|
||||
public static JLabel createComment(String messageKey) {
|
||||
return createComment(messageKey, ComponentPanelBuilder.MAX_COMMENT_WIDTH);
|
||||
}
|
||||
|
||||
public static JLabel createComment(String messageKey, int maxLineLength) {
|
||||
var comment = ComponentPanelBuilder.createCommentComponent(
|
||||
CodeGPTBundle.get(messageKey), true);
|
||||
CodeGPTBundle.get(messageKey),
|
||||
true,
|
||||
maxLineLength);
|
||||
comment.setBorder(JBUI.Borders.empty(0, 4));
|
||||
return comment;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
package ee.carlrobert.codegpt
|
||||
|
||||
import com.intellij.codeInsight.lookup.Lookup
|
||||
import com.intellij.codeInsight.lookup.LookupEvent
|
||||
import com.intellij.codeInsight.lookup.LookupListener
|
||||
import com.intellij.codeInsight.lookup.LookupManagerListener
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.components.service
|
||||
import ee.carlrobert.codegpt.predictions.PredictionService
|
||||
import ee.carlrobert.codegpt.settings.GeneralSettings
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceType
|
||||
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
|
||||
|
||||
class CodeGPTLookupListener : LookupManagerListener {
|
||||
override fun activeLookupChanged(oldLookup: Lookup?, newLookup: Lookup?) {
|
||||
if (newLookup is LookupImpl) {
|
||||
newLookup.addLookupListener(object : LookupListener {
|
||||
|
||||
var beforeApply: String = ""
|
||||
var cursorOffset: Int = 0
|
||||
|
||||
override fun beforeItemSelected(event: LookupEvent): Boolean {
|
||||
beforeApply = newLookup.editor.document.text
|
||||
cursorOffset = runReadAction {
|
||||
newLookup.editor.caretModel.offset
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun itemSelected(event: LookupEvent) {
|
||||
val editor = newLookup.editor
|
||||
|
||||
if (GeneralSettings.getSelectedService() != ServiceType.CODEGPT
|
||||
|| !service<CodeGPTServiceSettings>().state.codeAssistantEnabled
|
||||
|| service<EncodingManager>().countTokens(editor.document.text) > 4098
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
service<PredictionService>().displayLookupPrediction(
|
||||
editor,
|
||||
event,
|
||||
beforeApply
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package ee.carlrobert.codegpt.actions
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import ee.carlrobert.codegpt.settings.GeneralSettings
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceType.CODEGPT
|
||||
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
|
||||
|
||||
abstract class CodeAssistantFeatureToggleAction(
|
||||
private val enableFeatureAction: Boolean
|
||||
) : DumbAwareAction() {
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val settings = service<CodeGPTServiceSettings>().state
|
||||
settings.codeAssistantEnabled = enableFeatureAction
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
val codeAssistantEnabled = service<CodeGPTServiceSettings>().state.codeAssistantEnabled
|
||||
|
||||
e.presentation.isVisible = GeneralSettings.getSelectedService() == CODEGPT
|
||||
&& codeAssistantEnabled != enableFeatureAction
|
||||
e.presentation.isEnabled = GeneralSettings.getSelectedService() == CODEGPT
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread {
|
||||
return ActionUpdateThread.BGT
|
||||
}
|
||||
}
|
||||
|
||||
class EnableCodeAssistantAction : CodeAssistantFeatureToggleAction(true)
|
||||
|
||||
class DisableCodeAssistantAction : CodeAssistantFeatureToggleAction(false)
|
||||
|
|
@ -7,11 +7,14 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
|
|||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import ee.carlrobert.codegpt.codecompletions.AcceptNextLineInlayAction
|
||||
import ee.carlrobert.codegpt.codecompletions.AcceptNextWordInlayAction
|
||||
import ee.carlrobert.codegpt.predictions.TriggerCustomPredictionAction
|
||||
|
||||
class InlayActionPromoter : ActionPromoter {
|
||||
override fun promote(actions: List<AnAction>, context: DataContext): List<AnAction> {
|
||||
val editor = CommonDataKeys.EDITOR.getData(context) ?: return emptyList()
|
||||
|
||||
actions.filterIsInstance<TriggerCustomPredictionAction>().takeIf { it.isNotEmpty() }?.let { return it }
|
||||
|
||||
if (InlineCompletionContext.getOrNull(editor) == null) {
|
||||
return emptyList()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import com.intellij.codeInsight.inline.completion.session.InlineCompletionContex
|
|||
import com.intellij.codeInsight.inline.completion.session.InlineCompletionSession
|
||||
import com.intellij.codeInsight.lookup.LookupManager
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.actionSystem.EditorAction
|
||||
|
|
@ -15,6 +17,11 @@ import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler
|
|||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.util.concurrency.ThreadingAssertions
|
||||
import ee.carlrobert.codegpt.CodeGPTKeys.REMAINING_EDITOR_COMPLETION
|
||||
import ee.carlrobert.codegpt.EncodingManager
|
||||
import ee.carlrobert.codegpt.predictions.PredictionService
|
||||
import ee.carlrobert.codegpt.settings.GeneralSettings
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceType
|
||||
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
|
||||
|
||||
class CodeCompletionInsertAction :
|
||||
EditorAction(InsertInlineCompletionHandler()), HintManagerImpl.ActionToIgnore {
|
||||
|
|
@ -34,11 +41,27 @@ class CodeCompletionInsertAction :
|
|||
val textToInsert = context.textToInsert()
|
||||
val remainingCompletion = REMAINING_EDITOR_COMPLETION.get(editor) ?: ""
|
||||
if (remainingCompletion.isNotEmpty()) {
|
||||
REMAINING_EDITOR_COMPLETION.set(editor, remainingCompletion.removePrefix(textToInsert))
|
||||
REMAINING_EDITOR_COMPLETION.set(
|
||||
editor,
|
||||
remainingCompletion.removePrefix(textToInsert)
|
||||
)
|
||||
}
|
||||
|
||||
val beforeApply = editor.document.text
|
||||
InlineCompletion.getHandlerOrNull(editor)?.insert()
|
||||
return
|
||||
|
||||
if (GeneralSettings.getSelectedService() == ServiceType.CODEGPT
|
||||
&& service<CodeGPTServiceSettings>().state.codeAssistantEnabled
|
||||
&& service<EncodingManager>().countTokens(editor.document.text) <= 4098) {
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
service<PredictionService>().displayAutocompletePrediction(
|
||||
editor,
|
||||
textToInsert,
|
||||
beforeApply
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (element in elements) {
|
||||
|
|
@ -92,7 +115,10 @@ class CodeCompletionInsertAction :
|
|||
processRemainingCompletion(remainingCompletionLine, editor, endOffset)
|
||||
}
|
||||
|
||||
private fun processPartialCompletionElement(element: CodeCompletionTextElement, editor: Editor) {
|
||||
private fun processPartialCompletionElement(
|
||||
element: CodeCompletionTextElement,
|
||||
editor: Editor
|
||||
) {
|
||||
val lineNumber = editor.document.getLineNumber(editor.caretModel.offset)
|
||||
val lineEndOffset = editor.document.getLineEndOffset(lineNumber)
|
||||
editor.caretModel.moveToOffset(lineEndOffset)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package ee.carlrobert.codegpt.codecompletions
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.messages.Topic
|
||||
|
||||
interface CodeCompletionProgressNotifier {
|
||||
|
|
@ -10,5 +11,20 @@ interface CodeCompletionProgressNotifier {
|
|||
@JvmStatic
|
||||
val CODE_COMPLETION_PROGRESS_TOPIC =
|
||||
Topic.create("codeCompletionProgressTopic", CodeCompletionProgressNotifier::class.java)
|
||||
|
||||
fun startLoading(project: Project) {
|
||||
handleLoading(project, true)
|
||||
}
|
||||
|
||||
fun stopLoading(project: Project) {
|
||||
handleLoading(project, false)
|
||||
}
|
||||
|
||||
private fun handleLoading(project: Project, loading: Boolean) {
|
||||
if (project.isDisposed) return
|
||||
project.messageBus
|
||||
.syncPublisher(CODE_COMPLETION_PROGRESS_TOPIC)
|
||||
?.loading(loading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,9 +117,10 @@ class DebouncedCodeCompletionProvider : DebouncedInlineCompletionProvider() {
|
|||
}
|
||||
|
||||
IS_FETCHING_COMPLETION.set(request.editor, true)
|
||||
request.editor.project?.messageBus
|
||||
?.syncPublisher(CodeCompletionProgressNotifier.CODE_COMPLETION_PROGRESS_TOPIC)
|
||||
?.loading(true)
|
||||
|
||||
request.editor.project?.let {
|
||||
CodeCompletionProgressNotifier.startLoading(it)
|
||||
}
|
||||
|
||||
return InlineCompletionSingleSuggestion.build(elements = channelFlow {
|
||||
val infillRequest = InfillRequestUtil.buildInfillRequest(request, completionType)
|
||||
|
|
|
|||
|
|
@ -3,21 +3,14 @@ package ee.carlrobert.codegpt.codecompletions
|
|||
import com.intellij.codeInsight.inline.completion.InlineCompletionRequest
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.diff.impl.patch.IdeaTextPatchBuilder
|
||||
import com.intellij.openapi.diff.impl.patch.UnifiedDiffWriter
|
||||
import com.intellij.openapi.vcs.VcsException
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager
|
||||
import com.intellij.refactoring.suggested.startOffset
|
||||
import ee.carlrobert.codegpt.EncodingManager
|
||||
import ee.carlrobert.codegpt.codecompletions.psi.CompletionContextService
|
||||
import ee.carlrobert.codegpt.codecompletions.psi.readText
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
|
||||
import ee.carlrobert.codegpt.util.GitUtil
|
||||
import java.io.StringWriter
|
||||
|
||||
object InfillRequestUtil {
|
||||
private val logger = thisLogger()
|
||||
|
||||
suspend fun buildInfillRequest(
|
||||
request: InlineCompletionRequest,
|
||||
|
|
@ -34,24 +27,9 @@ object InfillRequestUtil {
|
|||
|
||||
val project = request.editor.project ?: return infillRequestBuilder.build()
|
||||
if (service<ConfigurationSettings>().state.codeCompletionSettings.gitDiffEnabled) {
|
||||
GitUtil.getProjectRepository(project)?.let { repository ->
|
||||
try {
|
||||
val repoRootPath = repository.root.toNioPath()
|
||||
val changes = ChangeListManager.getInstance(project).allChanges
|
||||
.sortedBy { it.virtualFile?.timeStamp }
|
||||
val patches = IdeaTextPatchBuilder.buildPatch(
|
||||
project, changes, repoRootPath, false, true
|
||||
)
|
||||
val diffWriter = StringWriter()
|
||||
UnifiedDiffWriter.write(
|
||||
null, repoRootPath, patches, diffWriter, "\n\n", null, null
|
||||
)
|
||||
val additionalContext =
|
||||
diffWriter.toString().cleanDiff().truncateText(1024, false)
|
||||
infillRequestBuilder.additionalContext(additionalContext)
|
||||
} catch (e: VcsException) {
|
||||
logger.error("Failed to get git context", e)
|
||||
}
|
||||
val additionalContext = GitUtil.getCurrentChanges(project)
|
||||
if (!additionalContext.isNullOrEmpty()) {
|
||||
infillRequestBuilder.additionalContext(additionalContext)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,18 +40,6 @@ object InfillRequestUtil {
|
|||
return infillRequestBuilder.build()
|
||||
}
|
||||
|
||||
private fun String.cleanDiff(showContext: Boolean = false): String =
|
||||
lineSequence()
|
||||
.filterNot { line ->
|
||||
line.startsWith("index ") ||
|
||||
line.startsWith("diff --git") ||
|
||||
line.startsWith("---") ||
|
||||
line.startsWith("+++") ||
|
||||
line.startsWith("===") ||
|
||||
(!showContext && line.startsWith(" "))
|
||||
}
|
||||
.joinToString("\n")
|
||||
|
||||
private fun getInfillContext(
|
||||
request: InlineCompletionRequest,
|
||||
caretOffset: Int
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package ee.carlrobert.codegpt.predictions
|
||||
|
||||
import com.intellij.codeInsight.hint.HintManagerImpl
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.actionSystem.EditorAction
|
||||
import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler
|
||||
import ee.carlrobert.codegpt.CodeGPTKeys
|
||||
|
||||
class AcceptNextPredictionRevisionAction : EditorAction(Handler()), HintManagerImpl.ActionToIgnore {
|
||||
|
||||
companion object {
|
||||
const val ID = "codegpt.acceptNextPrediction"
|
||||
}
|
||||
|
||||
private class Handler : EditorWriteActionHandler() {
|
||||
|
||||
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
|
||||
val diffViewer = editor.getUserData(CodeGPTKeys.EDITOR_PREDICTION_DIFF_VIEWER)
|
||||
if (diffViewer != null && diffViewer.isVisible()) {
|
||||
diffViewer.applyChanges()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEnabledForCaret(editor: Editor, caret: Caret, dataContext: DataContext): Boolean {
|
||||
val diffViewer = editor.getUserData(CodeGPTKeys.EDITOR_PREDICTION_DIFF_VIEWER)
|
||||
return diffViewer != null && diffViewer.isVisible()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,340 @@
|
|||
package ee.carlrobert.codegpt.predictions
|
||||
|
||||
import com.intellij.codeInsight.inline.completion.session.InlineCompletionContext
|
||||
import com.intellij.codeInsight.inline.completion.session.InlineCompletionSession
|
||||
import com.intellij.diff.DiffContentFactory
|
||||
import com.intellij.diff.DiffContext
|
||||
import com.intellij.diff.requests.DiffRequest
|
||||
import com.intellij.diff.requests.SimpleDiffRequest
|
||||
import com.intellij.diff.tools.combined.COMBINED_DIFF_MAIN_UI
|
||||
import com.intellij.diff.tools.fragmented.UnifiedDiffChange
|
||||
import com.intellij.diff.tools.fragmented.UnifiedDiffViewer
|
||||
import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy
|
||||
import com.intellij.diff.util.DiffUtil
|
||||
import com.intellij.diff.util.Side
|
||||
import com.intellij.ide.plugins.newui.TagComponent
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.event.VisibleAreaEvent
|
||||
import com.intellij.openapi.editor.event.VisibleAreaListener
|
||||
import com.intellij.openapi.keymap.KeymapUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.popup.JBPopup
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.openapi.util.UserDataHolderBase
|
||||
import com.intellij.testFramework.LightVirtualFile
|
||||
import com.intellij.ui.components.JBScrollPane
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import ee.carlrobert.codegpt.CodeGPTKeys
|
||||
import java.awt.Dimension
|
||||
import java.awt.FlowLayout
|
||||
import java.awt.Point
|
||||
import java.awt.event.KeyAdapter
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.Box
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
|
||||
class CodeSuggestionDiffViewer(
|
||||
request: DiffRequest,
|
||||
private val mainEditor: Editor,
|
||||
private val isManuallyOpened: Boolean
|
||||
) : UnifiedDiffViewer(MyDiffContext(mainEditor.project), request), Disposable {
|
||||
|
||||
private val popup: JBPopup = createSuggestionDiffPopup(component)
|
||||
private val keyListener: KeyAdapter
|
||||
private val visibleAreaListener: VisibleAreaListener
|
||||
private val initialDocumentText: String = mainEditor.document.text
|
||||
|
||||
init {
|
||||
keyListener = getKeyListener()
|
||||
visibleAreaListener = getVisibleAreaListener()
|
||||
setupDiffEditor()
|
||||
mainEditor.contentComponent.addKeyListener(keyListener)
|
||||
mainEditor.scrollingModel.addVisibleAreaListener(visibleAreaListener)
|
||||
}
|
||||
|
||||
override fun onDispose() {
|
||||
popup.dispose()
|
||||
mainEditor.putUserData(CodeGPTKeys.EDITOR_PREDICTION_DIFF_VIEWER, null)
|
||||
mainEditor.contentComponent.removeKeyListener(keyListener)
|
||||
mainEditor.scrollingModel.removeVisibleAreaListener(visibleAreaListener)
|
||||
super.onDispose()
|
||||
}
|
||||
|
||||
override fun onAfterRediff() {
|
||||
val change = diffChanges?.firstOrNull() ?: return
|
||||
|
||||
editor.component.preferredSize =
|
||||
Dimension(
|
||||
mainEditor.component.width / 2,
|
||||
(editor.lineHeight * change.getChangedLinesCount())
|
||||
)
|
||||
adjustPopupSize(popup, editor)
|
||||
|
||||
val adjustedLocation =
|
||||
getAdjustedPopupLocation(
|
||||
popup,
|
||||
mainEditor,
|
||||
change.lineFragment.startOffset1
|
||||
)
|
||||
|
||||
if (popup.isVisible) {
|
||||
adjustPopupSize(popup, editor)
|
||||
popup.setLocation(adjustedLocation)
|
||||
} else {
|
||||
popup.showInScreenCoordinates(mainEditor.component, adjustedLocation)
|
||||
}
|
||||
|
||||
doScrollToFirstChange()
|
||||
}
|
||||
|
||||
fun applyChanges() {
|
||||
val changes = diffChanges ?: emptyList()
|
||||
val change = changes.firstOrNull() ?: return
|
||||
|
||||
if (isStateIsOutOfDate) return
|
||||
if (!isEditable(Side.LEFT, true)) return
|
||||
|
||||
val document: Document = getDocument(Side.LEFT)
|
||||
|
||||
DiffUtil.executeWriteCommand(document, project, null) {
|
||||
replaceChange(change, Side.RIGHT)
|
||||
moveCaretToChange(change, document)
|
||||
scheduleRediff()
|
||||
}
|
||||
rediff(true)
|
||||
|
||||
if (changes.size == 1) {
|
||||
popup.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
fun isVisible(): Boolean {
|
||||
return popup.isVisible
|
||||
}
|
||||
|
||||
private fun setupDiffEditor() {
|
||||
editor.apply {
|
||||
settings.apply {
|
||||
additionalLinesCount = 0
|
||||
isFoldingOutlineShown = false
|
||||
isCaretRowShown = false
|
||||
isBlinkCaret = false
|
||||
isDndEnabled = false
|
||||
isIndentGuidesShown = false
|
||||
}
|
||||
gutterComponentEx.isVisible = false
|
||||
gutterComponentEx.parent.isVisible = false
|
||||
scrollPane.horizontalScrollBar.isOpaque = false
|
||||
}
|
||||
|
||||
setupStatusLabel()
|
||||
}
|
||||
|
||||
private fun getTagPanel(): JComponent {
|
||||
val tagPanel = JPanel(FlowLayout(FlowLayout.LEADING, 0, 0))
|
||||
if (!isManuallyOpened) {
|
||||
tagPanel.add(
|
||||
TagComponent(
|
||||
"Trigger manually: ${getShortcutText(TriggerCustomPredictionAction.ID)}"
|
||||
).apply {
|
||||
font = JBUI.Fonts.smallFont()
|
||||
}
|
||||
)
|
||||
tagPanel.add(Box.createHorizontalStrut(6))
|
||||
}
|
||||
tagPanel.add(TagComponent("Accept: ${getShortcutText(AcceptNextPredictionRevisionAction.ID)}").apply {
|
||||
setListener({ _, _ ->
|
||||
applyChanges()
|
||||
popup.dispose()
|
||||
}, component)
|
||||
font = JBUI.Fonts.smallFont()
|
||||
})
|
||||
return tagPanel
|
||||
}
|
||||
|
||||
private fun setupStatusLabel() {
|
||||
(editor.scrollPane as JBScrollPane).statusComponent = BorderLayoutPanel()
|
||||
.andTransparent()
|
||||
.withBorder(JBUI.Borders.empty(4))
|
||||
.addToRight(getTagPanel())
|
||||
}
|
||||
|
||||
private fun getKeyListener(): KeyAdapter {
|
||||
return object : KeyAdapter() {
|
||||
override fun keyReleased(e: KeyEvent) {
|
||||
if (mainEditor.document.text != initialDocumentText) {
|
||||
popup.setUiVisible(false)
|
||||
onDispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getVisibleAreaListener(): VisibleAreaListener {
|
||||
return object : VisibleAreaListener {
|
||||
override fun visibleAreaChanged(event: VisibleAreaEvent) {
|
||||
val change = diffChanges?.firstOrNull() ?: return
|
||||
val adjustedLocation = getAdjustedPopupLocation(
|
||||
popup,
|
||||
mainEditor,
|
||||
change.lineFragment.startOffset1
|
||||
)
|
||||
|
||||
if (popup.isVisible && !popup.isDisposed) {
|
||||
adjustPopupSize(popup, editor)
|
||||
popup.setLocation(adjustedLocation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doScrollToFirstChange() {
|
||||
val changes = myModel.diffChanges ?: return
|
||||
|
||||
var targetChange = ScrollToPolicy.FIRST_CHANGE.select(
|
||||
ContainerUtil.filter(
|
||||
changes
|
||||
) { it: UnifiedDiffChange -> !it.isSkipped })
|
||||
if (targetChange == null) targetChange = ScrollToPolicy.FIRST_CHANGE.select(changes)
|
||||
if (targetChange == null) return
|
||||
|
||||
val pointToScroll = myEditor.offsetToXY(targetChange.lineFragment.startOffset1)
|
||||
pointToScroll.y -= myEditor.lineHeight
|
||||
DiffUtil.scrollToPoint(myEditor, pointToScroll, false)
|
||||
}
|
||||
|
||||
private fun moveCaretToChange(change: UnifiedDiffChange, document: Document) {
|
||||
val changeEndOffset = change.lineFragment.endOffset2
|
||||
val previousChar = document.getText(TextRange(changeEndOffset - 1, changeEndOffset))
|
||||
val offset = if (previousChar == "\n") changeEndOffset - 1 else changeEndOffset
|
||||
|
||||
mainEditor.caretModel.moveToOffset(offset)
|
||||
|
||||
val offsetPosition = mainEditor.offsetToXY(offset)
|
||||
val isOffsetVisible = mainEditor.scrollingModel.visibleArea.contains(offsetPosition)
|
||||
if (!isOffsetVisible) {
|
||||
DiffUtil.scrollToCaret(mainEditor, false)
|
||||
}
|
||||
}
|
||||
|
||||
private class MyDiffContext(private val project: Project?) : DiffContext() {
|
||||
private val mainUi get() = getUserData(COMBINED_DIFF_MAIN_UI)
|
||||
|
||||
private val ownContext: UserDataHolder = UserDataHolderBase()
|
||||
|
||||
override fun getProject() = project
|
||||
override fun isFocusedInWindow(): Boolean = mainUi?.isFocusedInWindow() ?: false
|
||||
override fun isWindowFocused(): Boolean = mainUi?.isWindowFocused() ?: false
|
||||
override fun requestFocusInWindow() {
|
||||
mainUi?.requestFocusInWindow()
|
||||
}
|
||||
|
||||
override fun <T> getUserData(key: Key<T>): T? {
|
||||
return ownContext.getUserData(key)
|
||||
}
|
||||
|
||||
override fun <T> putUserData(key: Key<T>, value: T?) {
|
||||
ownContext.putUserData(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@RequiresEdt
|
||||
fun displayInlineDiff(
|
||||
editor: Editor,
|
||||
nextRevision: String,
|
||||
isManuallyOpened: Boolean = false
|
||||
) {
|
||||
editor.getUserData(CodeGPTKeys.EDITOR_PREDICTION_DIFF_VIEWER)?.dispose()
|
||||
editor.putUserData(CodeGPTKeys.REMAINING_EDITOR_COMPLETION, null)
|
||||
InlineCompletionSession.getOrNull(editor)?.let {
|
||||
if (it.isActive()) {
|
||||
InlineCompletionContext.getOrNull(editor)?.clear()
|
||||
}
|
||||
}
|
||||
|
||||
val diffRequest = createSimpleDiffRequest(editor, nextRevision)
|
||||
val diffViewer = CodeSuggestionDiffViewer(diffRequest, editor, isManuallyOpened)
|
||||
editor.putUserData(CodeGPTKeys.EDITOR_PREDICTION_DIFF_VIEWER, diffViewer)
|
||||
diffViewer.rediff(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createSimpleDiffRequest(editor: Editor, nextRevision: String): SimpleDiffRequest {
|
||||
val project = editor.project
|
||||
val virtualFile = editor.virtualFile
|
||||
val tempDiffFile = LightVirtualFile(virtualFile.name, nextRevision)
|
||||
val diffContentFactory = DiffContentFactory.getInstance()
|
||||
return SimpleDiffRequest(
|
||||
null,
|
||||
diffContentFactory.create(project, virtualFile),
|
||||
diffContentFactory.create(project, tempDiffFile),
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
fun UnifiedDiffChange.getChangedLinesCount(): Int {
|
||||
val insertedLines = insertedRange.end - insertedRange.start
|
||||
val deletedLines = deletedRange.end - deletedRange.start
|
||||
return deletedLines + insertedLines + 2
|
||||
}
|
||||
|
||||
fun getAdjustedPopupLocation(popup: JBPopup, editor: Editor, changeOffset: Int): Point {
|
||||
val pointInEditor = editor.offsetToXY(changeOffset)
|
||||
val visibleArea = editor.scrollingModel.visibleArea
|
||||
val editorLocationOnScreen = editor.component.locationOnScreen
|
||||
val isOffsetVisible = visibleArea.contains(pointInEditor)
|
||||
val popupY = if (isOffsetVisible) {
|
||||
editorLocationOnScreen.y + pointInEditor.y - editor.scrollingModel.verticalScrollOffset
|
||||
} else {
|
||||
if (pointInEditor.y < visibleArea.y) {
|
||||
editorLocationOnScreen.y
|
||||
} else {
|
||||
editorLocationOnScreen.y + visibleArea.height - popup.size.height
|
||||
}
|
||||
}
|
||||
val popupX = editorLocationOnScreen.x + editor.component.width - popup.size.width
|
||||
return Point(popupX, popupY - editor.lineHeight)
|
||||
}
|
||||
|
||||
fun adjustPopupSize(popup: JBPopup, editor: Editor) {
|
||||
val newWidth = editor.component.preferredSize.width
|
||||
val newHeight = editor.component.preferredSize.height
|
||||
popup.size = Dimension(newWidth, newHeight)
|
||||
popup.content.revalidate()
|
||||
popup.content.repaint()
|
||||
}
|
||||
|
||||
fun getShortcutText(actionId: String): String {
|
||||
return KeymapUtil.getFirstKeyboardShortcutText(
|
||||
ActionManager.getInstance().getAction(actionId)
|
||||
)
|
||||
}
|
||||
|
||||
fun createSuggestionDiffPopup(content: JComponent): JBPopup {
|
||||
return JBPopupFactory.getInstance().createComponentPopupBuilder(content, null)
|
||||
.setNormalWindowLevel(true)
|
||||
.setCancelOnClickOutside(false)
|
||||
.setRequestFocus(false)
|
||||
.setFocusable(true)
|
||||
.setMovable(true)
|
||||
.setResizable(true)
|
||||
.setShowBorder(true)
|
||||
.setCancelKeyEnabled(true)
|
||||
.setCancelOnWindowDeactivation(false)
|
||||
.setCancelOnOtherWindowOpen(false)
|
||||
.createPopup()
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
package ee.carlrobert.codegpt.predictions
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupEvent
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.application.runInEdt
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import ee.carlrobert.codegpt.CodeGPTKeys.IS_FETCHING_COMPLETION
|
||||
import ee.carlrobert.codegpt.CodeGPTKeys.PENDING_PREDICTION_CALL
|
||||
import ee.carlrobert.codegpt.codecompletions.CodeCompletionProgressNotifier
|
||||
import ee.carlrobert.codegpt.completions.CompletionClientProvider
|
||||
import ee.carlrobert.codegpt.conversations.ConversationsState
|
||||
import ee.carlrobert.codegpt.settings.prompts.PromptsSettings
|
||||
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
|
||||
import ee.carlrobert.codegpt.ui.OverlayUtil
|
||||
import ee.carlrobert.codegpt.util.EditorUtil
|
||||
import ee.carlrobert.codegpt.util.GitUtil
|
||||
import ee.carlrobert.llm.client.codegpt.request.prediction.AutocompletionPredictionRequest
|
||||
import ee.carlrobert.llm.client.codegpt.request.prediction.DirectPredictionRequest
|
||||
import ee.carlrobert.llm.client.codegpt.request.prediction.PredictionRequest
|
||||
import ee.carlrobert.llm.client.codegpt.response.CodeGPTException
|
||||
import ee.carlrobert.llm.client.codegpt.response.PredictionResponse
|
||||
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionStandardMessage
|
||||
import okhttp3.Request
|
||||
|
||||
@Service
|
||||
class PredictionService {
|
||||
|
||||
fun displayDirectPrediction(editor: Editor) {
|
||||
val request = CompletionClientProvider.getCodeGPTClient()
|
||||
.buildDirectPredictionRequest(createDirectPredictionRequest(editor))
|
||||
displayInlineDiff(editor, request)
|
||||
}
|
||||
|
||||
fun displayAutocompletePrediction(editor: Editor, textToInsert: String, beforeApply: String) {
|
||||
val request = CompletionClientProvider.getCodeGPTClient()
|
||||
.buildAutocompletionPredictionRequest(
|
||||
createAutocompletePredictionRequest(editor, textToInsert, beforeApply)
|
||||
)
|
||||
displayInlineDiff(editor, request)
|
||||
}
|
||||
|
||||
fun displayLookupPrediction(editor: Editor, event: LookupEvent, beforeApply: String) {
|
||||
val request = CompletionClientProvider.getCodeGPTClient()
|
||||
.buildLookupPredictionRequest(
|
||||
createAutocompletePredictionRequest(editor, event.item?.lookupString ?: "", beforeApply)
|
||||
)
|
||||
displayInlineDiff(editor, request)
|
||||
}
|
||||
|
||||
private fun displayInlineDiff(editor: Editor, request: Request) {
|
||||
val prediction = getPrediction(editor, request)
|
||||
if (prediction != null && !prediction.nextRevision.isNullOrEmpty()) {
|
||||
runInEdt {
|
||||
CodeSuggestionDiffViewer.displayInlineDiff(editor, prediction.nextRevision)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPrediction(editor: Editor, request: Request): PredictionResponse? {
|
||||
editor.project?.let {
|
||||
CodeCompletionProgressNotifier.startLoading(it)
|
||||
}
|
||||
|
||||
val pendingCall = PENDING_PREDICTION_CALL.get(editor)
|
||||
if (pendingCall != null) {
|
||||
pendingCall.cancel()
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
val client = CompletionClientProvider.getCodeGPTClient()
|
||||
val call = client.createNewCall(request)
|
||||
PENDING_PREDICTION_CALL.set(editor, call)
|
||||
return client.getPrediction(call)
|
||||
} catch (e: CodeGPTException) {
|
||||
OverlayUtil.showNotification(e.detail, NotificationType.ERROR)
|
||||
service<CodeGPTServiceSettings>().state.codeAssistantEnabled = false
|
||||
return null
|
||||
} catch (e: Exception) {
|
||||
if (e.cause?.message != "Canceled") {
|
||||
throw e
|
||||
}
|
||||
return null
|
||||
} finally {
|
||||
IS_FETCHING_COMPLETION.set(editor, false)
|
||||
PENDING_PREDICTION_CALL.set(editor, null)
|
||||
editor.project?.let {
|
||||
CodeCompletionProgressNotifier.stopLoading(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAutocompletePredictionRequest(
|
||||
editor: Editor,
|
||||
textToInsert: String,
|
||||
beforeApply: String,
|
||||
): AutocompletionPredictionRequest {
|
||||
val predictionRequest = AutocompletionPredictionRequest()
|
||||
predictionRequest.appliedCompletion = textToInsert
|
||||
predictionRequest.previousRevision = beforeApply
|
||||
setDefaultParams(editor, predictionRequest)
|
||||
return predictionRequest
|
||||
}
|
||||
|
||||
private fun createDirectPredictionRequest(editor: Editor): DirectPredictionRequest {
|
||||
val predictionRequest = DirectPredictionRequest()
|
||||
setDefaultParams(editor, predictionRequest)
|
||||
return predictionRequest
|
||||
}
|
||||
|
||||
private fun setDefaultParams(editor: Editor, request: PredictionRequest) {
|
||||
val messages: MutableList<OpenAIChatCompletionStandardMessage> = mutableListOf()
|
||||
ConversationsState.getInstance().currentConversation.messages.forEach {
|
||||
messages.add(OpenAIChatCompletionStandardMessage("user", it.prompt))
|
||||
messages.add(OpenAIChatCompletionStandardMessage("assistant", it.response))
|
||||
}
|
||||
request.apply {
|
||||
currentRevision = runReadAction { editor.document.text }
|
||||
customPrompt =
|
||||
service<PromptsSettings>().state.coreActions.codeAssistant.instructions
|
||||
cursorOffset = runReadAction { editor.caretModel.offset }
|
||||
gitChanges = GitUtil.getCurrentChanges(editor.project!!)
|
||||
openFiles = EditorUtil.getOpenFiles(editor.project!!)
|
||||
conversationMessages = messages.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package ee.carlrobert.codegpt.predictions
|
||||
|
||||
import com.intellij.codeInsight.hint.HintManagerImpl
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.actionSystem.EditorAction
|
||||
import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler
|
||||
import ee.carlrobert.codegpt.CodeGPTKeys
|
||||
import ee.carlrobert.codegpt.EncodingManager
|
||||
import ee.carlrobert.codegpt.settings.GeneralSettings
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceType
|
||||
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
|
||||
import ee.carlrobert.codegpt.ui.OverlayUtil
|
||||
|
||||
class TriggerCustomPredictionAction : EditorAction(Handler()), HintManagerImpl.ActionToIgnore {
|
||||
|
||||
companion object {
|
||||
const val ID = "codegpt.triggerCustomPrediction"
|
||||
}
|
||||
|
||||
private class Handler : EditorWriteActionHandler() {
|
||||
|
||||
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
|
||||
if (GeneralSettings.getSelectedService() != ServiceType.CODEGPT) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!service<CodeGPTServiceSettings>().state.codeAssistantEnabled) {
|
||||
val notification = OverlayUtil.getDefaultNotification(
|
||||
"Please enable Code Assistant before using this feature.",
|
||||
NotificationType.WARNING,
|
||||
)
|
||||
notification.addAction(object : AnAction("Enable Code Assistant") {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
service<CodeGPTServiceSettings>().state.codeAssistantEnabled = true
|
||||
notification.hideBalloon()
|
||||
}
|
||||
})
|
||||
OverlayUtil.notify(notification)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (service<EncodingManager>().countTokens(editor.document.text) > 4098) {
|
||||
OverlayUtil.showNotification("The file exceeds the token limit of 4,098.")
|
||||
return
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
service<PredictionService>().displayDirectPrediction(editor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEnabledForCaret(
|
||||
editor: Editor,
|
||||
caret: Caret,
|
||||
dataContext: DataContext
|
||||
): Boolean {
|
||||
return editor.getUserData(CodeGPTKeys.EDITOR_PREDICTION_DIFF_VIEWER) == null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,15 @@ import java.time.LocalDate
|
|||
enum class Placeholder(val description: String, val code: String) {
|
||||
DATE_ISO_8601("Current date in ISO 8601 format, e.g. 2021-01-01.", "$" + "DATE_ISO_8601"),
|
||||
BRANCH_NAME("The name of the current branch.", "$" + "BRANCH_NAME"),
|
||||
GIT_DIFF(
|
||||
"The unified diff output showing uncommitted changes in the current Git working directory, including staged and unstaged modifications.",
|
||||
"$" + "GIT_DIFF"
|
||||
),
|
||||
OPEN_FILES(
|
||||
"The complete source code contents of all files currently open in the IDE editor tabs, maintaining their formatting and structure.",
|
||||
"$" + "OPEN_FILES"
|
||||
),
|
||||
ACTIVE_CONVERSATION("The complete conversation history with the AI assistant, including the most recent response and any relevant context from the current interaction.", "$" + "ACTIVE_CONVERSATION"),
|
||||
PREFIX("Code before the cursor.", "$" + "PREFIX"),
|
||||
SUFFIX("Code after the cursor.", "$" + "SUFFIX"),
|
||||
FIM_PROMPT(
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ class PromptsSettingsState : BaseState() {
|
|||
class CoreActionsState : BaseState() {
|
||||
|
||||
companion object {
|
||||
val DEFAULT_CODE_ASSISTANT_PROMPT =
|
||||
getResourceContent("/prompts/core/code-assistant.txt")
|
||||
val DEFAULT_EDIT_CODE_PROMPT = getResourceContent("/prompts/core/edit-code.txt")
|
||||
val DEFAULT_GENERATE_COMMIT_MESSAGE_PROMPT =
|
||||
getResourceContent("/prompts/core/generate-commit-message.txt")
|
||||
|
|
@ -39,7 +41,11 @@ class CoreActionsState : BaseState() {
|
|||
val DEFAULT_FIX_COMPILE_ERRORS_PROMPT =
|
||||
getResourceContent("/prompts/core/fix-compile-errors.txt")
|
||||
}
|
||||
|
||||
var codeAssistant by property(CoreActionPromptDetailsState().apply {
|
||||
name = "Code Assistant"
|
||||
code = "CODE_ASSISTANT"
|
||||
instructions = DEFAULT_CODE_ASSISTANT_PROMPT
|
||||
})
|
||||
var editCode by property(CoreActionPromptDetailsState().apply {
|
||||
name = "Edit Code"
|
||||
code = "EDIT_CODE"
|
||||
|
|
@ -166,6 +172,10 @@ abstract class PromptDetailsState : BaseState() {
|
|||
var instructions by string()
|
||||
}
|
||||
|
||||
class CodeAssistantPromptDetailsState : PromptDetailsState() {
|
||||
var code by string()
|
||||
}
|
||||
|
||||
class CoreActionPromptDetailsState : PromptDetailsState() {
|
||||
var code by string()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,10 +104,11 @@ class PromptsForm {
|
|||
|
||||
val coreActionsFormState = getFormState<CoreActionPromptDetails>(coreActionsNode)
|
||||
settings.coreActions.apply {
|
||||
editCode = coreActionsFormState[0].toState()
|
||||
fixCompileErrors = coreActionsFormState[1].toState()
|
||||
generateCommitMessage = coreActionsFormState[2].toState()
|
||||
generateNameLookups = coreActionsFormState[3].toState()
|
||||
codeAssistant = coreActionsFormState[0].toState()
|
||||
editCode = coreActionsFormState[1].toState()
|
||||
fixCompileErrors = coreActionsFormState[2].toState()
|
||||
generateCommitMessage = coreActionsFormState[3].toState()
|
||||
generateNameLookups = coreActionsFormState[4].toState()
|
||||
}
|
||||
settings.chatActions.prompts = getFormState<ChatActionPromptDetails>(chatActionsNode)
|
||||
.map { it.toState() }
|
||||
|
|
@ -159,6 +160,7 @@ class PromptsForm {
|
|||
val formState = getFormState<CoreActionPromptDetails>(coreActionsNode)
|
||||
|
||||
val stateActions = listOf(
|
||||
settingsState.codeAssistant,
|
||||
settingsState.editCode,
|
||||
settingsState.fixCompileErrors,
|
||||
settingsState.generateCommitMessage,
|
||||
|
|
@ -212,6 +214,7 @@ class PromptsForm {
|
|||
val settings = service<PromptsSettings>().state
|
||||
|
||||
listOf(
|
||||
settings.coreActions.codeAssistant,
|
||||
settings.coreActions.editCode,
|
||||
settings.coreActions.fixCompileErrors,
|
||||
settings.coreActions.generateCommitMessage,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
package ee.carlrobert.codegpt.settings.prompts.form
|
||||
|
||||
import ee.carlrobert.codegpt.settings.prompts.ChatActionPromptDetailsState
|
||||
import ee.carlrobert.codegpt.settings.prompts.CodeAssistantPromptDetailsState
|
||||
import ee.carlrobert.codegpt.settings.prompts.CoreActionPromptDetailsState
|
||||
import ee.carlrobert.codegpt.settings.prompts.PersonaPromptDetailsState
|
||||
import ee.carlrobert.codegpt.settings.prompts.form.details.ChatActionPromptDetails
|
||||
import ee.carlrobert.codegpt.settings.prompts.form.details.CoreActionPromptDetails
|
||||
import ee.carlrobert.codegpt.settings.prompts.form.details.FormPromptDetails
|
||||
import ee.carlrobert.codegpt.settings.prompts.form.details.PersonaPromptDetails
|
||||
import ee.carlrobert.codegpt.settings.prompts.form.details.*
|
||||
import javax.swing.tree.DefaultMutableTreeNode
|
||||
|
||||
object PromptsFormUtil {
|
||||
|
||||
fun CodeAssistantPromptDetails.toState(): CodeAssistantPromptDetailsState {
|
||||
val state = CodeAssistantPromptDetailsState()
|
||||
state.code = this.code
|
||||
state.name = this.name
|
||||
state.instructions = this.instructions
|
||||
return state
|
||||
}
|
||||
|
||||
fun CoreActionPromptDetails.toState(): CoreActionPromptDetailsState {
|
||||
val state = CoreActionPromptDetailsState()
|
||||
state.code = this.code
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.intellij.openapi.editor.markup.TextAttributes
|
|||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.ui.JBColor
|
||||
import java.awt.Dimension
|
||||
import javax.swing.JPanel
|
||||
|
||||
abstract class AbstractEditorPromptPanel(
|
||||
private val details: FormPromptDetails,
|
||||
|
|
@ -28,6 +29,8 @@ abstract class AbstractEditorPromptPanel(
|
|||
}
|
||||
}
|
||||
|
||||
abstract fun getPanel(): JPanel
|
||||
|
||||
private fun createEditor(): Editor {
|
||||
return service<EditorFactory>()
|
||||
.run {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class ChatActionsDetailsPanel : PromptDetailsPanel {
|
|||
service<PromptsSettings>().state.chatActions.startInNewWindow
|
||||
)
|
||||
|
||||
fun getPanel(): JPanel = panel {
|
||||
override fun getPanel(): JPanel = panel {
|
||||
row {
|
||||
cell(BorderLayoutPanel().addToTop(editor.component)).align(Align.FILL)
|
||||
.resizableColumn()
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ import com.intellij.ui.dsl.builder.Align
|
|||
import com.intellij.ui.dsl.builder.panel
|
||||
import com.intellij.ui.layout.ComponentPredicate
|
||||
import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import ee.carlrobert.codegpt.settings.Placeholder
|
||||
import ee.carlrobert.codegpt.settings.prompts.CommitMessageTemplate
|
||||
import ee.carlrobert.codegpt.settings.prompts.*
|
||||
import ee.carlrobert.codegpt.settings.prompts.CoreActionsState.Companion.DEFAULT_CODE_ASSISTANT_PROMPT
|
||||
import ee.carlrobert.codegpt.settings.prompts.CoreActionsState.Companion.DEFAULT_EDIT_CODE_PROMPT
|
||||
import ee.carlrobert.codegpt.settings.prompts.CoreActionsState.Companion.DEFAULT_FIX_COMPILE_ERRORS_PROMPT
|
||||
import ee.carlrobert.codegpt.settings.prompts.CoreActionsState.Companion.DEFAULT_GENERATE_COMMIT_MESSAGE_PROMPT
|
||||
import ee.carlrobert.codegpt.settings.prompts.CoreActionsState.Companion.DEFAULT_GENERATE_NAME_LOOKUPS_PROMPT
|
||||
import ee.carlrobert.codegpt.settings.prompts.PromptsSettings
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
|
||||
|
|
@ -25,6 +27,27 @@ class CoreActionsDetailsPanel : PromptDetailsPanel {
|
|||
|
||||
override fun create(details: CoreActionPromptDetails): JComponent {
|
||||
val editorPanel = when (details.code) {
|
||||
|
||||
"CODE_ASSISTANT" -> CoreActionEditorPanel(
|
||||
details,
|
||||
DEFAULT_CODE_ASSISTANT_PROMPT,
|
||||
buildString {
|
||||
append("<p>Template for generating code assistant messages. Use the following placeholders to insert dynamic values:</p>\n")
|
||||
append(
|
||||
"<ul>${
|
||||
listOf(
|
||||
Placeholder.GIT_DIFF,
|
||||
Placeholder.OPEN_FILES,
|
||||
Placeholder.ACTIVE_CONVERSATION,
|
||||
).joinToString("\n") {
|
||||
"<li><strong>${it.name}</strong>: ${it.description}</li>"
|
||||
}
|
||||
}</ul>\n"
|
||||
)
|
||||
},
|
||||
listOf("{GIT_DIFF}", "{OPEN_FILES}", "{ACTIVE_CONVERSATION}")
|
||||
)
|
||||
|
||||
"EDIT_CODE" -> CoreActionEditorPanel(
|
||||
details,
|
||||
DEFAULT_EDIT_CODE_PROMPT,
|
||||
|
|
@ -90,7 +113,7 @@ class CoreActionsDetailsPanel : PromptDetailsPanel {
|
|||
highlightedPlaceholders: List<String> = emptyList()
|
||||
) : AbstractEditorPromptPanel(details, highlightedPlaceholders) {
|
||||
|
||||
fun getPanel(): JPanel = panel {
|
||||
override fun getPanel(): JPanel = panel {
|
||||
row {
|
||||
cell(BorderLayoutPanel().addToTop(editor.component))
|
||||
.align(Align.FILL)
|
||||
|
|
@ -112,7 +135,7 @@ class CoreActionsDetailsPanel : PromptDetailsPanel {
|
|||
override fun invoke(): Boolean = defaultInstructions != editor.document.text
|
||||
})
|
||||
row {
|
||||
comment(description, 60)
|
||||
comment(description, 96)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package ee.carlrobert.codegpt.settings.prompts.form.details
|
|||
|
||||
import com.intellij.openapi.observable.properties.AtomicBooleanProperty
|
||||
import ee.carlrobert.codegpt.settings.prompts.ChatActionPromptDetailsState
|
||||
import ee.carlrobert.codegpt.settings.prompts.CodeAssistantPromptDetailsState
|
||||
import ee.carlrobert.codegpt.settings.prompts.CoreActionPromptDetailsState
|
||||
import ee.carlrobert.codegpt.settings.prompts.PersonaPromptDetailsState
|
||||
import javax.swing.JComponent
|
||||
|
|
@ -17,6 +18,18 @@ sealed class FormPromptDetails {
|
|||
abstract var instructions: String?
|
||||
}
|
||||
|
||||
data class CodeAssistantPromptDetails(
|
||||
override var name: String?,
|
||||
override var instructions: String?,
|
||||
val code: String?
|
||||
) : FormPromptDetails() {
|
||||
constructor(state: CodeAssistantPromptDetailsState) : this(
|
||||
name = state.name,
|
||||
instructions = state.instructions,
|
||||
code = state.code
|
||||
)
|
||||
}
|
||||
|
||||
data class CoreActionPromptDetails(
|
||||
override var name: String?,
|
||||
override var instructions: String?,
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class PersonasDetailsPanel(onSelected: (PersonaPromptDetails) -> Unit) : PromptD
|
|||
isEnabled = details.id != 1L
|
||||
}
|
||||
|
||||
fun getPanel(): JPanel = panel {
|
||||
override fun getPanel(): JPanel = panel {
|
||||
row {
|
||||
cell(BorderLayoutPanel().addToTop(editor.component))
|
||||
.align(Align.FILL)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ class CodeGPTServiceForm {
|
|||
renderer = CustomComboBoxRenderer()
|
||||
}
|
||||
|
||||
private val codeAssistantEnabledCheckBox = JBCheckBox(
|
||||
CodeGPTBundle.get("shared.enableCodeAssistant"),
|
||||
service<CodeGPTServiceSettings>().state.codeAssistantEnabled
|
||||
)
|
||||
|
||||
private val codeCompletionsEnabledCheckBox = JBCheckBox(
|
||||
CodeGPTBundle.get("codeCompletionsForm.enableFeatureText"),
|
||||
service<CodeGPTServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled
|
||||
|
|
@ -68,6 +73,11 @@ class CodeGPTServiceForm {
|
|||
UIUtil.createComment("settingsConfigurable.service.codegpt.codeCompletionModel.comment")
|
||||
)
|
||||
.addVerticalGap(4)
|
||||
.addComponent(codeAssistantEnabledCheckBox)
|
||||
.addComponent(
|
||||
UIUtil.createComment("settingsConfigurable.service.codegpt.enableCodeAssistant.comment", 90)
|
||||
)
|
||||
.addVerticalGap(4)
|
||||
.addComponent(codeCompletionsEnabledCheckBox)
|
||||
.addComponentFillVertically(JPanel(), 0)
|
||||
.panel
|
||||
|
|
@ -77,12 +87,14 @@ class CodeGPTServiceForm {
|
|||
fun isModified() = service<CodeGPTServiceSettings>().state.run {
|
||||
(chatCompletionModelComboBox.selectedItem as CodeGPTModel).code != chatCompletionSettings.model
|
||||
|| (codeCompletionModelComboBox.selectedItem as CodeGPTModel).code != codeCompletionSettings.model
|
||||
|| codeAssistantEnabledCheckBox.isSelected != codeAssistantEnabled
|
||||
|| codeCompletionsEnabledCheckBox.isSelected != codeCompletionSettings.codeCompletionsEnabled
|
||||
|| getApiKey() != getCredential(CODEGPT_API_KEY)
|
||||
}
|
||||
|
||||
fun applyChanges() {
|
||||
service<CodeGPTServiceSettings>().state.run {
|
||||
codeAssistantEnabled = codeAssistantEnabledCheckBox.isSelected
|
||||
chatCompletionSettings.model =
|
||||
(chatCompletionModelComboBox.selectedItem as CodeGPTModel).code
|
||||
codeCompletionSettings.codeCompletionsEnabled =
|
||||
|
|
@ -95,6 +107,7 @@ class CodeGPTServiceForm {
|
|||
|
||||
fun resetForm() {
|
||||
service<CodeGPTServiceSettings>().state.run {
|
||||
codeAssistantEnabledCheckBox.isSelected = codeAssistantEnabled
|
||||
chatCompletionModelComboBox.selectedItem = chatCompletionSettings.model
|
||||
codeCompletionModelComboBox.selectedItem = codeCompletionSettings.model
|
||||
codeCompletionsEnabledCheckBox.isSelected =
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class CodeGPTServiceSettings :
|
|||
class CodeGPTServiceSettingsState : BaseState() {
|
||||
var chatCompletionSettings by property(CodeGPTServiceChatCompletionSettingsState())
|
||||
var codeCompletionSettings by property(CodeGPTServiceCodeCompletionSettingsState())
|
||||
var codeAssistantEnabled by property(true)
|
||||
}
|
||||
|
||||
class CodeGPTServiceChatCompletionSettingsState : BaseState() {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import com.intellij.openapi.editor.Document
|
|||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.EditorFactory
|
||||
import com.intellij.openapi.editor.EditorKind
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.FileEditor
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl
|
||||
|
|
@ -20,9 +22,9 @@ import com.intellij.psi.PsiDocumentManager
|
|||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import com.intellij.testFramework.LightVirtualFile
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
|
||||
import ee.carlrobert.llm.client.codegpt.request.prediction.FileDetails
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import kotlin.math.min
|
||||
|
||||
object EditorUtil {
|
||||
@JvmStatic
|
||||
|
|
@ -74,6 +76,29 @@ object EditorUtil {
|
|||
return FileEditorManager.getInstance(project)?.selectedTextEditor
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getOpenFiles(project: Project): List<FileDetails> {
|
||||
val fileDocumentManager = FileDocumentManager.getInstance()
|
||||
return FileEditorManager.getInstance(project).openFiles
|
||||
.mapNotNull {
|
||||
runReadAction {
|
||||
FileDetails().apply {
|
||||
name = ""
|
||||
content = fileDocumentManager.getDocument(it)?.text ?: ""
|
||||
modificationStamp = it.modificationStamp
|
||||
}
|
||||
}
|
||||
}
|
||||
.filter { !it.content.isNullOrEmpty() }
|
||||
.sortedBy { it.modificationStamp }
|
||||
.toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getAllEditors(project: Project): List<FileEditor> {
|
||||
return FileEditorManager.getInstance(project).allEditors.toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getSelectedEditorSelectedText(project: Project): String? {
|
||||
val selectedEditor = getSelectedEditor(project)
|
||||
|
|
@ -168,4 +193,4 @@ object EditorUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,13 @@ package ee.carlrobert.codegpt.util
|
|||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.diff.impl.patch.IdeaTextPatchBuilder
|
||||
import com.intellij.openapi.diff.impl.patch.UnifiedDiffWriter
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.guessProjectDir
|
||||
import com.intellij.openapi.vcs.VcsException
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager
|
||||
import ee.carlrobert.codegpt.codecompletions.truncateText
|
||||
import git4idea.GitCommit
|
||||
import git4idea.commands.Git
|
||||
import git4idea.commands.GitCommand
|
||||
|
|
@ -12,6 +16,7 @@ import git4idea.commands.GitLineHandler
|
|||
import git4idea.history.GitHistoryUtils
|
||||
import git4idea.repo.GitRepository
|
||||
import git4idea.repo.GitRepositoryManager
|
||||
import java.io.StringWriter
|
||||
|
||||
object GitUtil {
|
||||
|
||||
|
|
@ -25,6 +30,28 @@ object GitUtil {
|
|||
?: repositoryManager.repositories.firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getCurrentChanges(project: Project): String? {
|
||||
return getProjectRepository(project)?.let { repository ->
|
||||
try {
|
||||
val repoRootPath = repository.root.toNioPath()
|
||||
val changes = ChangeListManager.getInstance(project).allChanges
|
||||
.sortedBy { it.virtualFile?.timeStamp }
|
||||
val patches = IdeaTextPatchBuilder.buildPatch(
|
||||
project, changes, repoRootPath, false, true
|
||||
)
|
||||
val diffWriter = StringWriter()
|
||||
UnifiedDiffWriter.write(
|
||||
null, repoRootPath, patches, diffWriter, "\n\n", null, null
|
||||
)
|
||||
diffWriter.toString().cleanDiff().truncateText(1024, false)
|
||||
} catch (e: VcsException) {
|
||||
logger.error("Failed to get git context", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(VcsException::class)
|
||||
fun getCommitsForHashes(
|
||||
project: Project,
|
||||
|
|
@ -100,4 +127,16 @@ object GitUtil {
|
|||
!it.startsWith("commit ")
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.cleanDiff(showContext: Boolean = false): String =
|
||||
lineSequence()
|
||||
.filterNot { line ->
|
||||
line.startsWith("index ") ||
|
||||
line.startsWith("diff --git") ||
|
||||
line.startsWith("---") ||
|
||||
line.startsWith("+++") ||
|
||||
line.startsWith("===") ||
|
||||
(!showContext && line.startsWith(" "))
|
||||
}
|
||||
.joinToString("\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
<projectListeners>
|
||||
<listener topic="com.intellij.codeInsight.lookup.LookupManagerListener"
|
||||
class="ee.carlrobert.codegpt.refactorings.RenameCompletionLookupListener"/>
|
||||
<listener topic="com.intellij.codeInsight.lookup.LookupManagerListener"
|
||||
class="ee.carlrobert.codegpt.CodeGPTLookupListener"/>
|
||||
<listener topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"
|
||||
class="ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowListener"/>
|
||||
</projectListeners>
|
||||
|
|
@ -91,6 +93,14 @@
|
|||
<keyboard-shortcut first-keystroke="TAB" keymap="$default"/>
|
||||
</action>
|
||||
|
||||
<action id="codegpt.acceptNextPrediction" class="ee.carlrobert.codegpt.predictions.AcceptNextPredictionRevisionAction">
|
||||
<keyboard-shortcut first-keystroke="TAB" keymap="$default"/>
|
||||
</action>
|
||||
|
||||
<action id="codegpt.triggerCustomPrediction" class="ee.carlrobert.codegpt.predictions.TriggerCustomPredictionAction">
|
||||
<keyboard-shortcut first-keystroke="ctrl ENTER" keymap="$default"/>
|
||||
</action>
|
||||
|
||||
<action
|
||||
id="codegpt.acceptNextInlayWord"
|
||||
text="Apply next word"
|
||||
|
|
@ -230,6 +240,22 @@
|
|||
<override-text place="MainMenu"/>
|
||||
<override-text place="popup" use-text-of-place="MainMenu"/>
|
||||
</action>
|
||||
<action
|
||||
id="statusbar.enableCodeAssistant"
|
||||
class="ee.carlrobert.codegpt.actions.EnableCodeAssistantAction">
|
||||
<keyboard-shortcut first-keystroke="ctrl shift alt a" keymap="$default"/>
|
||||
|
||||
<override-text place="MainMenu"/>
|
||||
<override-text place="popup" use-text-of-place="MainMenu"/>
|
||||
</action>
|
||||
<action
|
||||
id="statusbar.disableCodeAssistant"
|
||||
class="ee.carlrobert.codegpt.actions.DisableCodeAssistantAction">
|
||||
<keyboard-shortcut first-keystroke="ctrl shift alt a" keymap="$default"/>
|
||||
|
||||
<override-text place="MainMenu"/>
|
||||
<override-text place="popup" use-text-of-place="MainMenu"/>
|
||||
</action>
|
||||
<action
|
||||
id="statusbar.startServer"
|
||||
class="ee.carlrobert.codegpt.actions.StartServerAction">
|
||||
|
|
@ -252,6 +278,8 @@
|
|||
<separator/>
|
||||
<reference id="statusbar.stopServer" />
|
||||
<reference id="statusbar.startServer" />
|
||||
<reference id="statusbar.disableCodeAssistant" />
|
||||
<reference id="statusbar.enableCodeAssistant" />
|
||||
<reference id="statusbar.disableCompletions" />
|
||||
<reference id="statusbar.enableCompletions" />
|
||||
</group>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@ action.statusbar.enableCompletions.MainMenu.text=Enable Completions
|
|||
action.statusbar.disableCompletions.text=Disable Completions
|
||||
action.statusbar.disableCompletions.description=Disable Code Completions
|
||||
action.statusbar.disableCompletions.MainMenu.text=Disable Completions
|
||||
action.statusbar.enableCodeAssistant.text=Enable Code Assistant
|
||||
action.statusbar.enableCodeAssistant.description=Enable Code Assistant
|
||||
action.statusbar.enableCodeAssistant.MainMenu.text=Enable Code Assistant
|
||||
action.statusbar.disableCodeAssistant.text=Disable Code Assistant
|
||||
action.statusbar.disableCodeAssistant.description=Disable Code Assistant
|
||||
action.statusbar.disableCodeAssistant.MainMenu.text=Disable Code Assistant
|
||||
action.compareWithOriginal.title=Compare with Original
|
||||
action.applyDirectly.title=Auto Apply
|
||||
action.explainGitCommit.title=Explain Commit with CodeGPT
|
||||
|
|
@ -36,6 +42,7 @@ settingsConfigurable.service.label=Selected provider:
|
|||
settingsConfigurable.service.codegpt.apiKey.comment=You can find the API key in your <a href="https://codegpt.ee/account">User settings</a>.
|
||||
settingsConfigurable.service.codegpt.chatCompletionModel.comment=Choose a model optimized for conversational interactions, including assistance with general queries and explanations.
|
||||
settingsConfigurable.service.codegpt.codeCompletionModel.comment=Choose a model tailored for code completion-related tasks.
|
||||
settingsConfigurable.service.codegpt.enableCodeAssistant.comment=If checked, Code Assistant will suggest related code updates as you make changes.
|
||||
settingsConfigurable.service.custom.openai.apiKey.comment=A secret value stored in the system's Keychain or KeePass, depending on your OS. This approach is recommended over storing the secret in the header as plain text.
|
||||
settingsConfigurable.service.openai.apiKey.comment=You can find the API key in your <a href="https://platform.openai.com/account/api-keys">User settings</a>.
|
||||
settingsConfigurable.service.openai.customModel.label=Custom model:
|
||||
|
|
@ -257,6 +264,7 @@ imageAccordion.title=Attached image
|
|||
shared.image=Image
|
||||
shared.chatCompletions=Chat Completions
|
||||
shared.codeCompletions=Code Completions
|
||||
shared.enableCodeAssistant=<html>Enable Code Assistant <small><strong><sup style="color: green;">NEW</sup></strong></small></html>
|
||||
codeCompletionsForm.enableFeatureText=Enable code completions
|
||||
codeCompletionsForm.parseResponseAsChatCompletions=Parse response as Chat Completions
|
||||
codeCompletionsForm.overrideFimTemplate.label=Use built-in FIM template
|
||||
|
|
|
|||
14
src/main/resources/prompts/core/code-assistant.txt
Normal file
14
src/main/resources/prompts/core/code-assistant.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Recent changes made to the project:
|
||||
<git_diff>
|
||||
{GIT_DIFF}
|
||||
</git_diff>
|
||||
|
||||
Currently open files:
|
||||
<open_files>
|
||||
{OPEN_FILES}
|
||||
</open_files>
|
||||
|
||||
Previous chat history between the AI and user:
|
||||
<active_conversation_history>
|
||||
{ACTIVE_CONVERSATION}
|
||||
</active_conversation_history>
|
||||
|
|
@ -271,6 +271,7 @@ class CodeCompletionServiceTest : IntegrationTest() {
|
|||
|
||||
fun `test apply inline suggestions with initial following text`() {
|
||||
useCodeGPTService()
|
||||
service<CodeGPTServiceSettings>().state.codeAssistantEnabled = false
|
||||
service<ConfigurationSettings>().state.codeCompletionSettings.multiLineEnabled = false
|
||||
myFixture.configureByText(
|
||||
"CompletionTest.java",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue