feat: inline edit
Some checks failed
Build / Build (push) Has been cancelled
Build / Verify Plugin (push) Has been cancelled

This commit is contained in:
Carl-Robert Linnupuu 2025-09-16 01:38:17 +01:00
parent 80ba956c5e
commit fdfde4243d
55 changed files with 4274 additions and 696 deletions

View file

@ -1,11 +1,14 @@
package ee.carlrobert.codegpt;
import com.intellij.openapi.util.Key;
import ee.carlrobert.codegpt.inlineedit.InlineEditSession;
import ee.carlrobert.codegpt.inlineedit.InlineEditInlayRenderer;
import ee.carlrobert.codegpt.predictions.CodeSuggestionDiffViewer;
import ee.carlrobert.codegpt.toolwindow.chat.editor.ToolWindowEditorFileDetails;
import ee.carlrobert.llm.client.codegpt.CodeGPTUserDetails;
import ee.carlrobert.service.NextEditResponse;
import ee.carlrobert.service.PartialCodeCompletionResponse;
import javax.swing.JComponent;
public class CodeGPTKeys {
@ -21,6 +24,12 @@ public class CodeGPTKeys {
Key.create("codegpt.isPromptTextFieldDocument");
public static final Key<CodeSuggestionDiffViewer> EDITOR_PREDICTION_DIFF_VIEWER =
Key.create("codegpt.editorPredictionDiffViewer");
public static final Key<InlineEditSession> EDITOR_INLINE_EDIT_SESSION =
Key.create("codegpt.editorInlineEditSession");
public static final Key<InlineEditInlayRenderer> EDITOR_INLINE_EDIT_RENDERER =
Key.create("codegpt.editorInlineEditRenderer");
public static final Key<JComponent> EDITOR_INLINE_EDIT_COMPARE_LINK =
Key.create("codegpt.editorInlineEditCompareLink");
public static final Key<PartialCodeCompletionResponse> REMAINING_CODE_COMPLETION =
Key.create("codegpt.remainingCodeCompletion");
public static final Key<NextEditResponse> REMAINING_PREDICTION_RESPONSE =

View file

@ -37,17 +37,15 @@ public abstract class BaseEditorAction extends AnAction {
var project = event.getProject();
var editor = event.getData(PlatformDataKeys.EDITOR);
if (editor != null && project != null) {
actionPerformed(project, editor, editor.getSelectionModel().getSelectedText());
var selectedText = editor.getSelectionModel().getSelectedText();
actionPerformed(project, editor, selectedText != null ? selectedText : "");
}
}
public void update(AnActionEvent event) {
Project project = event.getProject();
Editor editor = event.getData(PlatformDataKeys.EDITOR);
boolean menuAllowed = false;
if (editor != null && project != null) {
menuAllowed = editor.getSelectionModel().getSelectedText() != null;
}
boolean menuAllowed = editor != null && project != null;
event.getPresentation().setEnabled(menuAllowed);
}

View file

@ -93,15 +93,15 @@ public final class CompletionRequestService {
return getChatCompletionAsync(request, eventListener, serviceType, FeatureType.COMMIT_MESSAGE);
}
public EventSource getEditCodeCompletionAsync(
EditCodeCompletionParameters params,
public EventSource getInlineEditCompletionAsync(
InlineEditCompletionParameters params,
CompletionEventListener<String> eventListener) {
var serviceType =
ModelSelectionService.getInstance().getServiceForFeature(FeatureType.EDIT_CODE);
ModelSelectionService.getInstance().getServiceForFeature(FeatureType.INLINE_EDIT);
var request = CompletionRequestFactory
.getFactory(serviceType)
.createEditCodeRequest(params);
return getChatCompletionAsync(request, eventListener, serviceType, FeatureType.EDIT_CODE);
.createInlineEditRequest(params);
return getChatCompletionAsync(request, eventListener, serviceType, FeatureType.INLINE_EDIT);
}
public EventSource getChatCompletionAsync(

View file

@ -5,7 +5,7 @@ public enum FeatureType {
CODE_COMPLETION,
AUTO_APPLY,
COMMIT_MESSAGE,
EDIT_CODE,
INLINE_EDIT,
NEXT_EDIT,
LOOKUP
}

View file

@ -105,9 +105,12 @@ public class ChatToolWindowTabPanel implements Disposable {
project,
totalTokensPanel,
this,
FeatureType.CHAT,
tagManager,
this::handleSubmit,
this::handleCancel);
this::handleCancel,
true,
true);
userInputPanel.requestFocus();
rootPanel = createRootPanel();

View file

@ -5,6 +5,7 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.AsyncFileListener
@ -181,8 +182,11 @@ class PsiStructureRepository(
coroutineContext.ensureActive()
try {
PsiManager.getInstance(project).findFile(virtualFile)
} catch (exc: Exception) {
logger.warn("Failed to find file ${virtualFile.name}", exc)
} catch (ex: Exception) {
if (ex is ProcessCanceledException) {
throw ex
}
logger.warn("Failed to find file ${virtualFile.name}", ex)
null
}
}
@ -289,4 +293,4 @@ class PsiStructureRepository(
}
}
.toSet()
}
}

View file

@ -66,12 +66,13 @@ public class ModelComboBoxAction extends ComboBoxAction {
private final Project project;
private final List<ServiceType> availableProviders;
private final boolean showConfigureModels;
private final FeatureType featureType;
public ModelComboBoxAction(
Project project,
Consumer<ServiceType> onModelChange,
ServiceType selectedService) {
this(project, onModelChange, selectedService, Arrays.asList(ServiceType.values()), true);
this(project, onModelChange, selectedService, Arrays.asList(ServiceType.values()), true, FeatureType.CHAT);
}
public ModelComboBoxAction(
@ -80,10 +81,21 @@ public class ModelComboBoxAction extends ComboBoxAction {
ServiceType selectedProvider,
List<ServiceType> availableProviders,
boolean showConfigureModels) {
this(project, onModelChange, selectedProvider, availableProviders, showConfigureModels, FeatureType.CHAT);
}
public ModelComboBoxAction(
Project project,
Consumer<ServiceType> onModelChange,
ServiceType selectedProvider,
List<ServiceType> availableProviders,
boolean showConfigureModels,
FeatureType featureType) {
this.project = project;
this.onModelChange = onModelChange;
this.availableProviders = availableProviders;
this.showConfigureModels = showConfigureModels;
this.featureType = featureType;
setSmallVariant(true);
updateTemplatePresentation(selectedProvider);
@ -92,8 +104,12 @@ public class ModelComboBoxAction extends ComboBoxAction {
ModelChangeNotifier.getTopic(),
new ModelChangeNotifierAdapter() {
@Override
public void chatModelChanged(@NotNull String newModel, @NotNull ServiceType serviceType) {
updateTemplatePresentation(serviceType);
public void modelChanged(@NotNull FeatureType changedFeature,
@NotNull String newModel,
@NotNull ServiceType serviceType) {
if (changedFeature == featureType) {
updateTemplatePresentation(serviceType);
}
}
});
}
@ -275,12 +291,13 @@ public class ModelComboBoxAction extends ComboBoxAction {
var application = ApplicationManager.getApplication();
var templatePresentation = getTemplatePresentation();
var chatModel = application.getService(ModelSettings.class).getState()
.getModelSelection(FeatureType.CHAT);
.getModelSelection(featureType);
var modelCode = chatModel != null ? chatModel.getModel() : null;
switch (selectedService) {
case PROXYAI:
var proxyAIModel = ModelRegistry.getInstance().getProxyAIChatModels().stream()
var proxyAIModel = ModelRegistry.getInstance().getAllModelsForFeature(featureType).stream()
.filter(it -> it.getProvider() == PROXYAI)
.filter(it -> modelCode != null && it.getModel().equals(modelCode))
.findFirst();
templatePresentation.setIcon(
@ -391,7 +408,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
var application = ApplicationManager.getApplication();
application
.getService(ModelSettings.class)
.setModel(FeatureType.CHAT, model.getModel(), PROXYAI);
.setModel(featureType, model.getModel(), PROXYAI);
handleModelChange(PROXYAI);
});
@ -420,7 +437,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
.setModel(model);
application
.getService(ModelSettings.class)
.setModel(FeatureType.CHAT, model, OLLAMA);
.setModel(featureType, model, OLLAMA);
});
}
@ -434,7 +451,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
Icons.OpenAI,
comboBoxPresentation,
() -> ApplicationManager.getApplication().getService(ModelSettings.class)
.setModel(FeatureType.CHAT, model.getCode(), OPENAI));
.setModel(featureType, model.getCode(), OPENAI));
}
private AnAction createCustomOpenAIModelAction(
@ -446,7 +463,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
Icons.OpenAI,
comboBoxPresentation,
() -> ApplicationManager.getApplication().getService(ModelSettings.class)
.setModel(FeatureType.CHAT, model.getName(), CUSTOM_OPENAI));
.setModel(featureType, model.getName(), CUSTOM_OPENAI));
}
private AnAction createGoogleModelAction(GoogleModel model, Presentation comboBoxPresentation) {
@ -457,7 +474,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
Icons.Google,
comboBoxPresentation,
() -> ApplicationManager.getApplication().getService(ModelSettings.class)
.setModel(FeatureType.CHAT, model.getCode(), GOOGLE));
.setModel(featureType, model.getCode(), GOOGLE));
}
private AnAction createAnthropicModelAction(
@ -470,7 +487,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
Icons.Anthropic,
comboBoxPresentation,
() -> ApplicationManager.getApplication().getService(ModelSettings.class)
.setModel(FeatureType.CHAT, modelCode, ANTHROPIC));
.setModel(featureType, modelCode, ANTHROPIC));
}
private AnAction createLlamaModelAction(Presentation comboBoxPresentation) {
@ -480,7 +497,7 @@ public class ModelComboBoxAction extends ComboBoxAction {
Icons.Llama,
comboBoxPresentation,
() -> ApplicationManager.getApplication().getService(ModelSettings.class)
.setModel(FeatureType.CHAT,
.setModel(featureType,
LlamaSettings.getCurrentState().getHuggingFaceModel().getCode(), LLAMA_CPP));
}
@ -492,12 +509,12 @@ public class ModelComboBoxAction extends ComboBoxAction {
Icons.Mistral,
comboBoxPresentation,
() -> ApplicationManager.getApplication().getService(ModelSettings.class)
.setModel(FeatureType.CHAT, modelCode, MISTRAL));
.setModel(featureType, modelCode, MISTRAL));
}
private String getMistralPresentationText() {
var chatModel = ApplicationManager.getApplication().getService(ModelSettings.class).getState()
.getModelSelection(FeatureType.CHAT);
.getModelSelection(featureType);
var modelCode = chatModel != null ? chatModel.getModel() : null;
return ModelRegistry.getInstance().getModelDisplayName(MISTRAL, modelCode);
}