mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-11 04:50:31 +00:00
Revert "Revert "feat: code completion improvements""
This reverts commit 7f586da0c1.
This commit is contained in:
parent
7f586da0c1
commit
fe4e02f7f6
8 changed files with 109 additions and 177 deletions
|
|
@ -9,6 +9,7 @@ import com.knuddels.jtokkit.api.EncodingRegistry;
|
|||
import com.knuddels.jtokkit.api.EncodingType;
|
||||
import ee.carlrobert.codegpt.conversations.Conversation;
|
||||
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionMessage;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public final class EncodingManager {
|
||||
|
|
@ -52,4 +53,19 @@ public final class EncodingManager {
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the given text to the given number of tokens.
|
||||
*
|
||||
* @param text The text to truncate.
|
||||
* @param maxTokens The maximum number of tokens to keep.
|
||||
* @param fromStart Whether to truncate from the start or the end of the text.
|
||||
* @return The truncated text.
|
||||
*/
|
||||
public String truncateText(String text, int maxTokens, boolean fromStart) {
|
||||
List<Integer> tokens = encoding.encode(text);
|
||||
int tokensToRetrieve = Math.min(maxTokens, tokens.size());
|
||||
int startIndex = fromStart ? 0 : tokens.size() - tokensToRetrieve;
|
||||
return encoding.decode(tokens.subList(startIndex, startIndex + tokensToRetrieve));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package ee.carlrobert.codegpt.actions;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import ee.carlrobert.codegpt.codecompletions.CodeGPTEditorManager;
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -10,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
/**
|
||||
* Disables code-completion.<br/> Publishes message to {@link CodeCompletionEnabledListener#TOPIC}
|
||||
*/
|
||||
public class DisableCompletionsAction extends AnAction {
|
||||
public class DisableCompletionsAction extends DumbAwareAction {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
package ee.carlrobert.codegpt.actions;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Enables code-completion.<br/> Publishes message to {@link CodeCompletionEnabledListener#TOPIC}
|
||||
*/
|
||||
public class EnableCompletionsAction extends AnAction {
|
||||
public class EnableCompletionsAction extends DumbAwareAction {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
|
|
|
|||
|
|
@ -40,8 +40,7 @@ class CodeCompletionEventListener implements CompletionEventListener {
|
|||
progressIndicator.processFinish();
|
||||
}
|
||||
|
||||
var editorManager = CodeGPTEditorManager.getInstance();
|
||||
editorManager.disposeEditorInlays(editor);
|
||||
CodeGPTEditorManager.getInstance().disposeEditorInlays(editor);
|
||||
|
||||
var inlayText = messageBuilder.toString();
|
||||
if (!inlayText.isEmpty()) {
|
||||
|
|
@ -60,7 +59,7 @@ class CodeCompletionEventListener implements CompletionEventListener {
|
|||
Notifications.Bus.notify(OverlayUtil.getDefaultNotification(
|
||||
String.format(
|
||||
CodeGPTBundle.get("notification.completionError.description"),
|
||||
ex.getMessage()),
|
||||
error.getMessage()),
|
||||
NotificationType.ERROR)
|
||||
.addAction(new OpenSettingsAction()), editor.getProject());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import static ee.carlrobert.codegpt.CodeGPTKeys.SINGLE_LINE_INLAY;
|
|||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupManager;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
|
|
@ -24,13 +25,9 @@ import com.intellij.openapi.editor.InlayModel;
|
|||
import com.intellij.openapi.keymap.KeymapManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt;
|
||||
import com.intellij.util.concurrency.annotations.RequiresReadLock;
|
||||
import com.intellij.util.concurrency.annotations.RequiresWriteLock;
|
||||
|
|
@ -38,14 +35,12 @@ import ee.carlrobert.codegpt.actions.CodeCompletionEnabledListener;
|
|||
import ee.carlrobert.codegpt.completions.CompletionRequestService;
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
|
||||
import ee.carlrobert.codegpt.util.EditorUtil;
|
||||
import ee.carlrobert.llm.completion.CompletionEventListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import javax.swing.KeyStroke;
|
||||
import okhttp3.sse.EventSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Service(PROJECT)
|
||||
|
|
@ -53,7 +48,6 @@ import org.jetbrains.annotations.NotNull;
|
|||
public final class CodeCompletionService implements Disposable {
|
||||
|
||||
public static final String APPLY_INLAY_ACTION_ID = "ApplyInlayAction";
|
||||
public static final int MAX_OFFSET = 4000;
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(CodeCompletionService.class);
|
||||
|
||||
|
|
@ -77,16 +71,13 @@ public final class CodeCompletionService implements Disposable {
|
|||
return project.getService(CodeCompletionService.class);
|
||||
}
|
||||
|
||||
public boolean isCompletionAllowed(PsiElement elementAtCaret) {
|
||||
return elementAtCaret instanceof PsiWhiteSpace;
|
||||
}
|
||||
|
||||
public void handleCompletions(Editor editor, int offset) {
|
||||
Project project = editor.getProject();
|
||||
if (project == null
|
||||
|| project.isDisposed()
|
||||
|| !ConfigurationState.getInstance().isCodeCompletionsEnabled()
|
||||
|| !EditorUtil.isSelectedEditor(editor)
|
||||
|| LookupManager.getActiveLookup(editor) != null
|
||||
|| editor.isViewer()
|
||||
|| editor.isOneLineMode()
|
||||
) {
|
||||
|
|
@ -99,54 +90,16 @@ public final class CodeCompletionService implements Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
PsiElement elementAtCaret = ReadAction.compute(() -> psiFile.findElementAt(offset));
|
||||
var completionService = CodeCompletionService.getInstance(project);
|
||||
if (!completionService.isCompletionAllowed(elementAtCaret)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var request = InfillRequestDetails.fromDocumentWithMaxOffset(document, offset);
|
||||
callDebouncer.debounce(
|
||||
Void.class,
|
||||
(progressIndicator) -> completionService.fetchCodeCompletion(
|
||||
elementAtCaret,
|
||||
offset,
|
||||
document,
|
||||
(progressIndicator) -> CompletionRequestService.getInstance().getCodeCompletionAsync(
|
||||
request,
|
||||
new CodeCompletionEventListener(editor, offset, progressIndicator)),
|
||||
500,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches code-completion (FIM) for the given position ({@code offsetInFile}) in the file. <br/>
|
||||
* By default tries to find an enclosing {@link PsiMethod} or {@link PsiClass} for the given
|
||||
* {@code offsetInFile} and only uses their content instead of the entire file's content. If no
|
||||
* such enclosing {@link PsiElement} can be found, the file's entire content is used instead.
|
||||
*
|
||||
* @param elementAtCaret PsiElement at caret
|
||||
* @param offsetInFile Global offset in the file.
|
||||
* @param document If the offset is not enclosed in a {@link PsiMethod} nor a
|
||||
* {@link PsiClass}, the entire file content is used for completion.
|
||||
* @return Completion String
|
||||
*/
|
||||
@RequiresBackgroundThread
|
||||
public EventSource fetchCodeCompletion(
|
||||
PsiElement elementAtCaret,
|
||||
int offsetInFile,
|
||||
Document document,
|
||||
CompletionEventListener eventListener) {
|
||||
InfillRequestDetails requestDetails = tryFindEnclosingPsiElementTextRange(
|
||||
List.of(PsiMethod.class, PsiClass.class), elementAtCaret)
|
||||
.map(textRange -> createInfillRequest(
|
||||
document,
|
||||
offsetInFile,
|
||||
textRange.getStartOffset(),
|
||||
textRange.getEndOffset())
|
||||
)
|
||||
.orElse(createInfillRequest(document, offsetInFile));
|
||||
return CompletionRequestService.getInstance()
|
||||
.getCodeCompletionAsync(requestDetails, eventListener);
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
public void addInlays(Editor editor, int caretOffset, String inlayText) {
|
||||
List<String> linesList = inlayText.lines().collect(toList());
|
||||
|
|
@ -201,11 +154,13 @@ public final class CodeCompletionService implements Disposable {
|
|||
Document document = editor.getDocument();
|
||||
document.insertString(offset, text);
|
||||
editor.getCaretModel().moveToOffset(offset + text.length());
|
||||
EditorUtil.reformatDocument(
|
||||
requireNonNull(editor.getProject()),
|
||||
document,
|
||||
offset,
|
||||
offset + text.length());
|
||||
if (ConfigurationState.getInstance().isAutoFormattingEnabled()) {
|
||||
EditorUtil.reformatDocument(
|
||||
requireNonNull(editor.getProject()),
|
||||
document,
|
||||
offset,
|
||||
offset + text.length());
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresReadLock
|
||||
|
|
@ -246,20 +201,4 @@ public final class CodeCompletionService implements Disposable {
|
|||
APPLY_INLAY_ACTION_ID,
|
||||
new KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), null));
|
||||
}
|
||||
|
||||
private static InfillRequestDetails createInfillRequest(Document document, int offsetInFile) {
|
||||
int begin = Integer.max(0, offsetInFile - MAX_OFFSET);
|
||||
int end = Integer.min(document.getTextLength(), offsetInFile + MAX_OFFSET);
|
||||
return createInfillRequest(document, offsetInFile, begin, end);
|
||||
}
|
||||
|
||||
private static InfillRequestDetails createInfillRequest(
|
||||
Document document,
|
||||
int caretOffset,
|
||||
int start,
|
||||
int end) {
|
||||
return new InfillRequestDetails(
|
||||
document.getText(new TextRange(start, caretOffset)),
|
||||
document.getText(new TextRange(caretOffset, end)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
package ee.carlrobert.codegpt.codecompletions;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import ee.carlrobert.codegpt.EncodingManager;
|
||||
|
||||
public class InfillRequestDetails {
|
||||
|
||||
private static final int MAX_OFFSET = 10_000;
|
||||
private static final int MAX_PROMPT_TOKENS = 512;
|
||||
|
||||
private final String prefix;
|
||||
private final String suffix;
|
||||
|
||||
|
|
@ -10,6 +17,12 @@ public class InfillRequestDetails {
|
|||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public static InfillRequestDetails fromDocumentWithMaxOffset(Document document, int caretOffset) {
|
||||
int start = Math.max(0, caretOffset - MAX_OFFSET);
|
||||
int end = Math.min(document.getTextLength(), caretOffset + MAX_OFFSET);
|
||||
return fromDocumentWithCustomRange(document, caretOffset, start, end);
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
|
@ -17,4 +30,21 @@ public class InfillRequestDetails {
|
|||
public String getSuffix() {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
private static InfillRequestDetails fromDocumentWithCustomRange(
|
||||
Document document,
|
||||
int caretOffset,
|
||||
int start,
|
||||
int end) {
|
||||
var prefix = truncateText(document, start, caretOffset, false);
|
||||
var suffix = truncateText(document, caretOffset, end, true);
|
||||
return new InfillRequestDetails(prefix, suffix);
|
||||
}
|
||||
|
||||
private static String truncateText(Document document, int start, int end, boolean fromStart) {
|
||||
return EncodingManager.getInstance().truncateText(
|
||||
document.getText(new TextRange(start, end)),
|
||||
MAX_PROMPT_TOKENS,
|
||||
fromStart);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,40 +10,29 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorCustomElementRenderer;
|
||||
import com.intellij.openapi.editor.Inlay;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.testFramework.PlatformTestUtil;
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationState;
|
||||
import ee.carlrobert.llm.client.http.exchange.StreamHttpExchange;
|
||||
import ee.carlrobert.llm.completion.CompletionEventListener;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import testsupport.IntegrationTest;
|
||||
|
||||
public class CodeCompletionServiceTest extends IntegrationTest {
|
||||
|
||||
private final VisualPosition cursorPosition = new VisualPosition(2, 8);
|
||||
private final VisualPosition cursorPosition = new VisualPosition(3, 0);
|
||||
|
||||
public void testFetchCodeCompletionLlama() {
|
||||
useLlamaService();
|
||||
var codeCompletionService = CodeCompletionService.getInstance(getProject());
|
||||
String fileContents = getResourceContent(
|
||||
"/codecompletions/code-completion-file.txt");
|
||||
PsiFile psiFile = myFixture.configureByText("CompletionTest.java", fileContents);
|
||||
ConfigurationState.getInstance().setCodeCompletionsEnabled(true);
|
||||
myFixture.configureByText(
|
||||
"CompletionTest.java",
|
||||
getResourceContent("/codecompletions/code-completion-file.txt"));
|
||||
Editor editor = myFixture.getEditor();
|
||||
Document document = editor.getDocument();
|
||||
editor.getCaretModel().moveToVisualPosition(cursorPosition);
|
||||
var prefix = "public static int gcd(int x, int y){\n";
|
||||
var suffix = "\n"
|
||||
+ " }";
|
||||
var expectedCompletion = "return xyz;";
|
||||
var expectedCompletion = "TEST_SINGLE_LINE_OUTPUT\nTEST_MULTI_LINE_OUTPUT";
|
||||
var prefix = "z".repeat(1015) + "\n[INPUT]\n"; // 512 tokens
|
||||
var suffix = "\n[\\INPUT]\n" + "z".repeat(1015); // 512 tokens
|
||||
expectLlama((StreamHttpExchange) request -> {
|
||||
assertThat(request.getUri().getPath()).isEqualTo("/completion");
|
||||
assertThat(request.getMethod()).isEqualTo("POST");
|
||||
|
|
@ -53,86 +42,43 @@ public class CodeCompletionServiceTest extends IntegrationTest {
|
|||
return List.of(jsonMapResponse(e("content", expectedCompletion), e("stop", true)));
|
||||
});
|
||||
|
||||
int caretOffset = editor.getCaretModel().getOffset();
|
||||
PsiElement elementAtCaret = ReadAction.compute(() -> psiFile.findElementAt(caretOffset));
|
||||
editor.getCaretModel().moveToVisualPosition(cursorPosition);
|
||||
|
||||
StringBuilder actualCompletion = new StringBuilder();
|
||||
codeCompletionService.fetchCodeCompletion(elementAtCaret, caretOffset, document,
|
||||
new CompletionEventListener() {
|
||||
@Override
|
||||
public void onComplete(StringBuilder messageBuilder) {
|
||||
actualCompletion.append(messageBuilder);
|
||||
await().pollInSameThread().atMost(5, SECONDS)
|
||||
.until(() -> {
|
||||
PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue();
|
||||
var singleLineInlayElement = editor.getUserData(SINGLE_LINE_INLAY);
|
||||
var multiLineInlayElement = editor.getUserData(MULTI_LINE_INLAY);
|
||||
if (singleLineInlayElement != null && multiLineInlayElement != null) {
|
||||
var singleLine =
|
||||
((InlayInlineElementRenderer) singleLineInlayElement.getRenderer()).getInlayText();
|
||||
var multiLine =
|
||||
((InlayBlockElementRenderer) multiLineInlayElement.getRenderer()).getInlayText();
|
||||
return "TEST_SINGLE_LINE_OUTPUT".equals(singleLine)
|
||||
&& "TEST_MULTI_LINE_OUTPUT".equals(multiLine);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
await().atMost(2, SECONDS)
|
||||
.until(() -> actualCompletion.length() > 0);
|
||||
assertEquals(expectedCompletion, actualCompletion.toString());
|
||||
}
|
||||
|
||||
public void testAddInlaysSingleLine() {
|
||||
var codeCompletionService = setupTestCodeCompletion();
|
||||
Editor editor = myFixture.getEditor();
|
||||
public void testApplyInlayAction() {
|
||||
ConfigurationState.getInstance().setAutoFormattingEnabled(false);
|
||||
myFixture.configureByText(
|
||||
"CompletionTest.java",
|
||||
getResourceContent("/codecompletions/code-completion-file.txt"));
|
||||
var editor = myFixture.getEditor();
|
||||
editor.getCaretModel().moveToVisualPosition(cursorPosition);
|
||||
var expectedInlay = " return xyz;";
|
||||
int caretOffset = editor.getCaretModel().getOffset();
|
||||
var expectedSingleLineInlay = "FIRST_LINE";
|
||||
var expectedMultiLineInlay = "SECOND_LINE\nTHIRD_LINE";
|
||||
var expectedInlay = expectedSingleLineInlay + "\n" + expectedMultiLineInlay;
|
||||
int cursorOffsetBeforeApply = editor.getCaretModel().getOffset();
|
||||
CodeCompletionService.getInstance(getProject())
|
||||
.addInlays(editor, cursorOffsetBeforeApply, expectedInlay);
|
||||
|
||||
codeCompletionService.addInlays(editor, caretOffset, expectedInlay);
|
||||
|
||||
checkInlay(editor.getUserData(SINGLE_LINE_INLAY), InlayInlineElementRenderer.class,
|
||||
expectedInlay, caretOffset);
|
||||
checkPerformInlayAction(editor.getDocument(), cursorPosition.line, cursorPosition.line,
|
||||
expectedInlay);
|
||||
ActionManager.getInstance().unregisterAction(APPLY_INLAY_ACTION_ID);
|
||||
}
|
||||
|
||||
public void testAddInlaysMultiLine() {
|
||||
var codeCompletionService = setupTestCodeCompletion();
|
||||
Editor editor = myFixture.getEditor();
|
||||
editor.getCaretModel().moveToVisualPosition(cursorPosition);
|
||||
var expectedInlay = " int z = 1;\n z = 2 + 3;\n return xyz;";
|
||||
int caretOffset = editor.getCaretModel().getOffset();
|
||||
|
||||
codeCompletionService.addInlays(editor, caretOffset, expectedInlay);
|
||||
|
||||
// First line of inlay
|
||||
checkInlay(editor.getUserData(SINGLE_LINE_INLAY), InlayInlineElementRenderer.class,
|
||||
expectedInlay.substring(0, expectedInlay.indexOf("\n")), caretOffset);
|
||||
// Other lines of inlay
|
||||
checkInlay(editor.getUserData(MULTI_LINE_INLAY), InlayBlockElementRenderer.class,
|
||||
expectedInlay.substring(expectedInlay.indexOf("\n") + 1), caretOffset);
|
||||
checkPerformInlayAction(editor.getDocument(), cursorPosition.line, cursorPosition.line + 2,
|
||||
expectedInlay);
|
||||
ActionManager.getInstance().unregisterAction(APPLY_INLAY_ACTION_ID);
|
||||
}
|
||||
|
||||
private CodeCompletionService setupTestCodeCompletion() {
|
||||
useLlamaService();
|
||||
var codeCompletionService = CodeCompletionService.getInstance(getProject());
|
||||
String fileContents = getResourceContent(
|
||||
"/codecompletions/code-completion-file.txt");
|
||||
myFixture.configureByText("CompletionTest.java", fileContents);
|
||||
return codeCompletionService;
|
||||
}
|
||||
|
||||
private void checkInlay(Inlay<EditorCustomElementRenderer> inlay,
|
||||
Class<? extends EditorCustomElementRenderer> clazz, String expectedText, int expectedOffset) {
|
||||
assertNotNull(inlay);
|
||||
assertTrue(clazz.isInstance(inlay.getRenderer()));
|
||||
InlayElementRenderer renderer = (InlayElementRenderer) inlay.getRenderer();
|
||||
assertEquals(expectedText, renderer.getInlayText());
|
||||
assertEquals(expectedOffset, inlay.getOffset());
|
||||
}
|
||||
|
||||
private void checkPerformInlayAction(Document document, int startLine, int endLine,
|
||||
String expectedText) {
|
||||
AnAction applyInlayAction = ActionManager.getInstance().getAction(APPLY_INLAY_ACTION_ID);
|
||||
assertNotNull(applyInlayAction);
|
||||
myFixture.performEditorAction(APPLY_INLAY_ACTION_ID);
|
||||
|
||||
TextRange inlayTextRange = new TextRange(document.getLineStartOffset(startLine),
|
||||
document.getLineEndOffset(endLine));
|
||||
assertEquals(expectedText, document.getText(inlayTextRange));
|
||||
var newTextRange = new TextRange(cursorOffsetBeforeApply, editor.getCaretModel().getOffset());
|
||||
var appliedInlay = editor.getDocument().getText(newTextRange);
|
||||
assertThat(appliedInlay).isEqualTo(expectedInlay);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
public class CompletionTest {
|
||||
public static int gcd(int x, int y){
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
[INPUT]
|
||||
|
||||
}
|
||||
}
|
||||
[\INPUT]
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Loading…
Add table
Add a link
Reference in a new issue