diff --git a/clients/eclipse/feature/feature.xml b/clients/eclipse/feature/feature.xml
index a7d93cc17569..cb0cd824006f 100644
--- a/clients/eclipse/feature/feature.xml
+++ b/clients/eclipse/feature/feature.xml
@@ -2,7 +2,7 @@
@@ -19,6 +19,6 @@
+ version="0.0.2.26"/>
diff --git a/clients/eclipse/plugin/META-INF/MANIFEST.MF b/clients/eclipse/plugin/META-INF/MANIFEST.MF
index 606601c995ec..7230fab47635 100644
--- a/clients/eclipse/plugin/META-INF/MANIFEST.MF
+++ b/clients/eclipse/plugin/META-INF/MANIFEST.MF
@@ -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.25
+Bundle-Version: 0.0.2.26
Bundle-Activator: com.tabbyml.tabby4eclipse.Activator
Bundle-Vendor: com.tabbyml
Require-Bundle: org.eclipse.ui,
diff --git a/clients/eclipse/plugin/plugin.xml b/clients/eclipse/plugin/plugin.xml
index eb2061be7bb2..5cd00f7d994d 100644
--- a/clients/eclipse/plugin/plugin.xml
+++ b/clients/eclipse/plugin/plugin.xml
@@ -130,6 +130,16 @@
name="Accept Inline Completion"
id="com.tabbyml.tabby4eclipse.commands.inlineCompletion.accept">
+
+
+
+
+
+
+
+
@@ -241,6 +259,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/Accept.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/Accept.java
index 490db7a512e4..109e19954d6a 100644
--- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/Accept.java
+++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/Accept.java
@@ -5,6 +5,7 @@
import org.eclipse.core.commands.ExecutionException;
import com.tabbyml.tabby4eclipse.Logger;
+import com.tabbyml.tabby4eclipse.inlineCompletion.IInlineCompletionService.AcceptType;
import com.tabbyml.tabby4eclipse.inlineCompletion.InlineCompletionService;
public class Accept extends AbstractHandler {
@@ -13,7 +14,7 @@ public class Accept extends AbstractHandler {
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
logger.debug("Accept the current inline completion.");
- InlineCompletionService.getInstance().accept();
+ InlineCompletionService.getInstance().accept(AcceptType.FULL_COMPLETION);
return null;
}
diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/AcceptNextLine.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/AcceptNextLine.java
new file mode 100644
index 000000000000..b013ad0771a0
--- /dev/null
+++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/AcceptNextLine.java
@@ -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.inlineCompletion.IInlineCompletionService.AcceptType;
+import com.tabbyml.tabby4eclipse.inlineCompletion.InlineCompletionService;
+
+public class AcceptNextLine extends AbstractHandler {
+ private Logger logger = new Logger("Commands.InlineCompletion.AcceptNextLine");
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ logger.debug("Accept next line of the current inline completion.");
+ InlineCompletionService.getInstance().accept(AcceptType.NEXT_LINE);
+ return null;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return InlineCompletionService.getInstance().isCompletionItemVisible();
+ }
+
+}
diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/AcceptNextWord.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/AcceptNextWord.java
new file mode 100644
index 000000000000..4ddb31d2666f
--- /dev/null
+++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/AcceptNextWord.java
@@ -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.inlineCompletion.IInlineCompletionService.AcceptType;
+import com.tabbyml.tabby4eclipse.inlineCompletion.InlineCompletionService;
+
+public class AcceptNextWord extends AbstractHandler {
+ private Logger logger = new Logger("Commands.InlineCompletion.AcceptNextLine");
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ logger.debug("Accept next word of the current inline completion.");
+ InlineCompletionService.getInstance().accept(AcceptType.NEXT_WORD);
+ return null;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return InlineCompletionService.getInstance().isCompletionItemVisible();
+ }
+
+}
diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/EditorUtils.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/EditorUtils.java
index b7447f4dc853..8d7a9be0b6a8 100644
--- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/EditorUtils.java
+++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/EditorUtils.java
@@ -17,6 +17,7 @@
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.texteditor.ITextEditor;
import com.tabbyml.tabby4eclipse.chat.ChatMessage.FileContext;
@@ -67,6 +68,10 @@ public static Display getDisplay(ITextEditor textEditor) {
return getStyledTextWidget(textEditor).getDisplay();
}
+ public static IContextService getContextService(ITextEditor textEditor) {
+ return textEditor.getSite().getService(IContextService.class);
+ }
+
public static void asyncExec(Runnable runnable) {
PlatformUI.getWorkbench().getDisplay().asyncExec(runnable);
}
@@ -127,7 +132,7 @@ public static int getCurrentOffsetInDocument(ITextEditor textEditor) throws Ille
throw new IllegalStateException("Failed to get current offset in document.");
});
}
-
+
public static String getSelectedText() {
ITextEditor editor = getActiveTextEditor();
if (editor != null) {
@@ -135,7 +140,7 @@ public static String getSelectedText() {
}
return null;
}
-
+
public static String getSelectedText(ITextEditor textEditor) {
ISelection selection = textEditor.getSelectionProvider().getSelection();
if (selection instanceof ITextSelection textSelection) {
diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/IInlineCompletionService.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/IInlineCompletionService.java
index 2cb098d573cc..cc192ea29138 100644
--- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/IInlineCompletionService.java
+++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/IInlineCompletionService.java
@@ -42,10 +42,16 @@ public interface IInlineCompletionService {
*/
public void previous();
+ enum AcceptType {
+ FULL_COMPLETION, NEXT_LINE, NEXT_WORD,
+ }
+
/**
* Accept the current completion item ghost text.
+ *
+ * @param type the type of completion to accept.
*/
- public void accept();
+ public void accept(AcceptType type);
/**
* Dismiss the current completion item ghost text.
diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionService.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionService.java
index 2c900bac7855..012659dcc7e6 100644
--- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionService.java
+++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionService.java
@@ -3,6 +3,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
@@ -12,6 +14,8 @@
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.eclipse.ui.contexts.IContextActivation;
+import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.texteditor.ITextEditor;
import com.tabbyml.tabby4eclipse.Logger;
@@ -34,9 +38,12 @@ private static class LazyHolder {
private static final IInlineCompletionService INSTANCE = new InlineCompletionService();
}
+ private static final String INLINE_COMPLETION_VISIBLE_CONTEXT_ID = "com.tabbyml.tabby4eclipse.inlineCompletionVisible";
+
private Logger logger = new Logger("InlineCompletionService");
private InlineCompletionRenderer renderer = new InlineCompletionRenderer();
private InlineCompletionContext current;
+ private IContextActivation inlineCompletionVisibleContext;
@Override
public boolean isCompletionItemVisible() {
@@ -76,6 +83,7 @@ public void trigger(boolean isManualTrigger) {
logger.info("Provide inline completion for TextEditor " + textEditor.getTitle() + " at offset " + offset
+ " with modification stamp " + modificationStamp);
renderer.hide();
+ deactivateInlineCompletionVisibleContext(textEditor);
if (current != null) {
if (current.job != null && !current.job.isDone()) {
logger.info("Cancel the current job due to new request.");
@@ -105,6 +113,7 @@ public void trigger(boolean isManualTrigger) {
InlineCompletionList list = request.convertInlineCompletionList(completionList);
current.response = new InlineCompletionContext.Response(list);
renderer.show(textViewer, current.response.getActiveCompletionItem());
+ activateInlineCompletionVisibleContext(textEditor);
EventParams eventParams = buildTelemetryEventParams(EventParams.Type.VIEW);
postTelemetryEvent(eventParams);
} catch (BadLocationException e) {
@@ -191,7 +200,7 @@ private int calcCycleIndex(int index, int listSize, int step) {
}
@Override
- public void accept() {
+ public void accept(AcceptType acceptType) {
ITextEditor textEditor = EditorUtils.getActiveTextEditor();
logger.info("Accept inline completion in TextEditor " + textEditor.toString());
if (current == null || current.request == null || current.response == null) {
@@ -199,23 +208,47 @@ public void accept() {
}
int offset = current.request.offset;
InlineCompletionItem item = current.response.getActiveCompletionItem();
- EventParams eventParams = buildTelemetryEventParams(EventParams.Type.SELECT);
+ EventParams eventParams = buildTelemetryEventParams(EventParams.Type.SELECT,
+ acceptType == AcceptType.FULL_COMPLETION ? null : "line");
renderer.hide();
+ deactivateInlineCompletionVisibleContext(textEditor);
current = null;
int prefixReplaceLength = item.getReplaceRange().getPrefixLength();
int suffixReplaceLength = item.getReplaceRange().getSuffixLength();
String text = item.getInsertText().substring(prefixReplaceLength);
- if (text.isEmpty()) {
+ String textToInsert;
+
+ if (acceptType == AcceptType.NEXT_WORD) {
+ Pattern pattern = Pattern.compile("\\w+|\\W+");
+ Matcher matcher = pattern.matcher(text);
+ if (matcher.find()) {
+ textToInsert = matcher.group();
+ } else {
+ textToInsert = text;
+ }
+ } else if (acceptType == AcceptType.NEXT_LINE) {
+ List lines = List.of(text.split("\n"));
+ String line = lines.get(0);
+ if (text.isEmpty() && lines.size() > 1) {
+ line += "\n";
+ line += lines.get(1);
+ }
+ textToInsert = line;
+ } else {
+ textToInsert = text;
+ }
+
+ if (textToInsert.isEmpty()) {
return;
}
IDocument document = EditorUtils.getDocument(textEditor);
EditorUtils.syncExec(textEditor, () -> {
try {
- document.replace(offset, suffixReplaceLength, text);
- ITextSelection selection = new TextSelection(offset + text.length(), 0);
+ document.replace(offset, suffixReplaceLength, textToInsert);
+ ITextSelection selection = new TextSelection(offset + textToInsert.length(), 0);
textEditor.getSelectionProvider().setSelection(selection);
postTelemetryEvent(eventParams);
} catch (BadLocationException e) {
@@ -230,6 +263,8 @@ public void dismiss() {
logger.info("Dismiss inline completion.");
EventParams eventParams = buildTelemetryEventParams(EventParams.Type.DISMISS);
renderer.hide();
+ ITextEditor textEditor = EditorUtils.getActiveTextEditor();
+ deactivateInlineCompletionVisibleContext(textEditor);
postTelemetryEvent(eventParams);
}
if (current != null) {
@@ -269,6 +304,22 @@ private void postTelemetryEvent(EventParams params) {
}
}
+ private void activateInlineCompletionVisibleContext(ITextEditor editor) {
+ IContextService contextService = EditorUtils.getContextService(editor);
+ if (contextService != null) {
+ logger.debug("Activating inline completion visible context.");
+ inlineCompletionVisibleContext = contextService.activateContext(INLINE_COMPLETION_VISIBLE_CONTEXT_ID);
+ }
+ }
+
+ private void deactivateInlineCompletionVisibleContext(ITextEditor editor) {
+ IContextService contextService = EditorUtils.getContextService(editor);
+ if (contextService != null && inlineCompletionVisibleContext != null) {
+ logger.debug("Deactivating inline completion visible context.");
+ contextService.deactivateContext(inlineCompletionVisibleContext);
+ }
+ }
+
private class InlineCompletionContext {
private static class Request {
private Logger logger = new Logger("InlineCompletionContext.Request");