Skip to content

Commit

Permalink
feat(eclipse): support cycle multiple choices. (#3268)
Browse files Browse the repository at this point in the history
  • Loading branch information
icycodes authored Oct 13, 2024
1 parent c1b464e commit f0850a0
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 18 deletions.
4 changes: 2 additions & 2 deletions clients/eclipse/feature/feature.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<feature
id="com.tabbyml.features.tabby4eclipse"
label="Tabby"
version="0.0.2.24"
version="0.0.2.25"
provider-name="com.tabbyml">

<description url="http://www.example.com/description">
Expand All @@ -19,6 +19,6 @@

<plugin
id="com.tabbyml.tabby4eclipse"
version="0.0.2.24"/>
version="0.0.2.25"/>

</feature>
2 changes: 1 addition & 1 deletion clients/eclipse/plugin/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tabby Plugin for Eclipse
Bundle-SymbolicName: com.tabbyml.tabby4eclipse;singleton:=true
Bundle-Version: 0.0.2.24
Bundle-Version: 0.0.2.25
Bundle-Activator: com.tabbyml.tabby4eclipse.Activator
Bundle-Vendor: com.tabbyml
Require-Bundle: org.eclipse.ui,
Expand Down
67 changes: 56 additions & 11 deletions clients/eclipse/plugin/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,27 +102,42 @@
</category>
<command
categoryId="com.tabbyml.tabby4eclipse.commands"
name="Open Settings"
name="Open Tabby Settings"
icon="image/settings.png"
id="com.tabbyml.tabby4eclipse.commands.openPreferences">
</command>
<category
id="com.tabbyml.tabby4eclipse.commands.inlineCompletion"
name="Code Completion">
name="Tabby Code Completion">
</category>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.inlineCompletion"
name="Accept"
name="Trigger Inline Completion"
id="com.tabbyml.tabby4eclipse.commands.inlineCompletion.trigger">
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.inlineCompletion"
name="Previous Inline Completion"
id="com.tabbyml.tabby4eclipse.commands.inlineCompletion.previous">
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.inlineCompletion"
name="Next Inline Completion"
id="com.tabbyml.tabby4eclipse.commands.inlineCompletion.next">
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.inlineCompletion"
name="Accept Inline Completion"
id="com.tabbyml.tabby4eclipse.commands.inlineCompletion.accept">
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.inlineCompletion"
name="Dismiss"
name="Dismiss Inline Completion"
id="com.tabbyml.tabby4eclipse.commands.inlineCompletion.dismiss">
</command>
<category
id="com.tabbyml.tabby4eclipse.commands.chat"
name="Chat">
name="Tabby Chat">
</category>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.chat"
Expand All @@ -137,32 +152,32 @@
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.chat"
name="Add Selection to Chat"
name="Add Selection to Tabby Chat"
id="com.tabbyml.tabby4eclipse.commands.chat.addSelectionToChat">
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.chat"
name="Add File to Chat"
name="Add File to Tabby Chat"
id="com.tabbyml.tabby4eclipse.commands.chat.addFileToChat">
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.chat"
name="Explain"
name="Explain using Tabby"
id="com.tabbyml.tabby4eclipse.commands.chat.explain">
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.chat"
name="Fix"
name="Fix using Tabby"
id="com.tabbyml.tabby4eclipse.commands.chat.fix">
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.chat"
name="Generate Docs"
name="Generate Docs using Tabby"
id="com.tabbyml.tabby4eclipse.commands.chat.generateDocs">
</command>
<command
categoryId="com.tabbyml.tabby4eclipse.commands.chat"
name="Generate Tests"
name="Generate Tests using Tabby"
id="com.tabbyml.tabby4eclipse.commands.chat.generateTests">
</command>
</extension>
Expand All @@ -172,6 +187,18 @@
class="com.tabbyml.tabby4eclipse.commands.OpenPreferences"
commandId="com.tabbyml.tabby4eclipse.commands.openPreferences">
</handler>
<handler
class="com.tabbyml.tabby4eclipse.commands.inlineCompletion.Trigger"
commandId="com.tabbyml.tabby4eclipse.commands.inlineCompletion.trigger">
</handler>
<handler
class="com.tabbyml.tabby4eclipse.commands.inlineCompletion.Previous"
commandId="com.tabbyml.tabby4eclipse.commands.inlineCompletion.previous">
</handler>
<handler
class="com.tabbyml.tabby4eclipse.commands.inlineCompletion.Next"
commandId="com.tabbyml.tabby4eclipse.commands.inlineCompletion.next">
</handler>
<handler
class="com.tabbyml.tabby4eclipse.commands.inlineCompletion.Accept"
commandId="com.tabbyml.tabby4eclipse.commands.inlineCompletion.accept">
Expand Down Expand Up @@ -221,6 +248,24 @@
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
sequence="M1+M3+L">
</key>
<key
commandId="com.tabbyml.tabby4eclipse.commands.inlineCompletion.trigger"
contextId="org.eclipse.ui.textEditorScope"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
sequence="M3+\">
</key>
<key
commandId="com.tabbyml.tabby4eclipse.commands.inlineCompletion.previous"
contextId="org.eclipse.ui.textEditorScope"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
sequence="M3+[">
</key>
<key
commandId="com.tabbyml.tabby4eclipse.commands.inlineCompletion.next"
contextId="org.eclipse.ui.textEditorScope"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
sequence="M3+]">
</key>
<key
commandId="com.tabbyml.tabby4eclipse.commands.inlineCompletion.accept"
contextId="org.eclipse.ui.textEditorScope"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.tabbyml.tabby4eclipse.commands.inlineCompletion;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;

import com.tabbyml.tabby4eclipse.Logger;
import com.tabbyml.tabby4eclipse.inlineCompletion.InlineCompletionService;

public class Next extends AbstractHandler {
private Logger logger = new Logger("Commands.InlineCompletion.Next");

@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
logger.debug("Cycle inline completion choices: Next.");
InlineCompletionService.getInstance().next();
return null;
}

@Override
public boolean isEnabled() {
return InlineCompletionService.getInstance().isCompletionItemVisible();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.tabbyml.tabby4eclipse.commands.inlineCompletion;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;

import com.tabbyml.tabby4eclipse.Logger;
import com.tabbyml.tabby4eclipse.inlineCompletion.InlineCompletionService;

public class Previous extends AbstractHandler {
private Logger logger = new Logger("Commands.InlineCompletion.Next");

@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
logger.debug("Cycle inline completion choices: Previous.");
InlineCompletionService.getInstance().previous();
return null;
}

@Override
public boolean isEnabled() {
return InlineCompletionService.getInstance().isCompletionItemVisible();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.tabbyml.tabby4eclipse.commands.inlineCompletion;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;

import com.tabbyml.tabby4eclipse.Logger;
import com.tabbyml.tabby4eclipse.editor.EditorUtils;
import com.tabbyml.tabby4eclipse.inlineCompletion.InlineCompletionService;

public class Trigger extends AbstractHandler {
private Logger logger = new Logger("Commands.InlineCompletion.Trigger");

@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
logger.debug("Trigger inline completion manually.");
InlineCompletionService.getInstance().trigger(true);
return null;
}

@Override
public boolean isEnabled() {
return EditorUtils.getActiveTextEditor().isEditable();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,28 @@ public interface IInlineCompletionService {

/**
* Trigger an inline completion request at the current caret position of the
* active text editor.
*
* @param isManualTrigger whether to trigger manually or automatically
* active text editor.
*
* @param isManualTrigger Whether to trigger manually or automatically.
* Automatic trigger will only generate one choice, and
* manual trigger will generate multiple choices.
*/
public void trigger(boolean isManualTrigger);

/**
* Cycle to the next choice if it exists. If the current inline completion is
* auto-triggered, a manual trigger will be called to attempt to generate
* multiple choices.
*/
public void next();

/**
* Cycle to the previous choice if it exists. If the current inline completion
* is auto-triggered, a manual trigger will be called to attempt to generate
* multiple choices.
*/
public void previous();

/**
* Accept the current completion item ghost text.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ private static class LazyHolder {
public boolean isCompletionItemVisible() {
ITextEditor textEditor = EditorUtils.getActiveTextEditor();
ITextViewer textViewer = EditorUtils.getTextViewer(textEditor);
return current != null && current.request != null && current.request.textEditor == textEditor
boolean matched = current != null && current.request != null && current.request.textEditor == textEditor
&& current.response != null && textViewer != null && textViewer == renderer.getCurrentTextViewer()
&& renderer.getCurrentCompletionItem() != null
&& renderer.getCurrentCompletionItem() == current.response.getActiveCompletionItem();
logger.debug("isCompletionItemVisible: " + matched);
return matched;
}

@Override
Expand Down Expand Up @@ -113,6 +115,81 @@ public void trigger(boolean isManualTrigger) {
current = context;
}

@Override
public void next() {
cycle(1);
}

@Override
public void previous() {
cycle(-1);
}

private void cycle(int step) {
ITextEditor textEditor = EditorUtils.getActiveTextEditor();
ITextViewer textViewer = EditorUtils.getTextViewer(textEditor);

logger.info("Cycle inline completion choices, step: " + step);
if (current == null || current.request == null || current.response == null) {
return;
}
if (current.response.completionList.isIncomplete()) {
int index = current.response.getItemIndex();
int offset = EditorUtils.getCurrentOffsetInDocument(textEditor);
long modificationStamp = EditorUtils.getDocumentModificationStamp(textEditor);
InlineCompletionContext.Request request = new InlineCompletionContext.Request(textEditor, offset,
modificationStamp, true);
InlineCompletionParams params = request.toInlineCompletionParams();
if (params == null) {
return;
}
CompletableFuture<com.tabbyml.tabby4eclipse.lsp.protocol.InlineCompletionList> job = LanguageServerService
.getInstance().getServer().execute((server) -> {
ITextDocumentServiceExt textDocumentService = ((ILanguageServer) server)
.getTextDocumentServiceExt();
return textDocumentService.inlineCompletion(params);
});
job.thenAccept((completionList) -> {
if (completionList == null || request != current.request) {
return;
}
try {
InlineCompletionList list = request.convertInlineCompletionList(completionList);
int cycleIndex = calcCycleIndex(index, list.getItems().size(), step);
current.response = new InlineCompletionContext.Response(list, cycleIndex);
renderer.show(textViewer, current.response.getActiveCompletionItem());
EventParams eventParams = buildTelemetryEventParams(EventParams.Type.VIEW);
postTelemetryEvent(eventParams);
} catch (BadLocationException e) {
logger.error("Failed to show inline completion.", e);
}
});
InlineCompletionContext context = new InlineCompletionContext(request, job, current.response);
current = context;
} else {
int cycleIndex = calcCycleIndex(current.response.getItemIndex(),
current.response.completionList.getItems().size(), step);
current.response.setItemIndex(cycleIndex);
renderer.show(textViewer, current.response.getActiveCompletionItem());
EventParams eventParams = buildTelemetryEventParams(EventParams.Type.VIEW);
postTelemetryEvent(eventParams);
}
}

private int calcCycleIndex(int index, int listSize, int step) {
if (listSize <= 1) {
return index;
}
int cycleIndex = index + step;
while (cycleIndex >= listSize) {
cycleIndex -= listSize;
}
if (cycleIndex < 0) {
cycleIndex += listSize;
}
return cycleIndex;
}

@Override
public void accept() {
ITextEditor textEditor = EditorUtils.getActiveTextEditor();
Expand Down Expand Up @@ -258,12 +335,25 @@ public Response(InlineCompletionList completionList) {
this.itemIndex = 0;
}

public Response(InlineCompletionList completionList, int itemIndex) {
this.completionList = completionList;
this.itemIndex = itemIndex;
}

public InlineCompletionItem getActiveCompletionItem() {
if (itemIndex >= 0 && itemIndex < completionList.getItems().size()) {
return completionList.getItems().get(itemIndex);
}
return null;
}

public int getItemIndex() {
return itemIndex;
}

public void setItemIndex(int itemIndex) {
this.itemIndex = itemIndex;
}
}

private Request request;
Expand Down

0 comments on commit f0850a0

Please sign in to comment.