diff --git a/clients/eclipse/feature/feature.xml b/clients/eclipse/feature/feature.xml index 3ba483e25eb5..1306a58484ff 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.20"/> diff --git a/clients/eclipse/plugin/META-INF/MANIFEST.MF b/clients/eclipse/plugin/META-INF/MANIFEST.MF index 1e8cfeee804f..fd94d25580a2 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.1.19 +Bundle-Version: 0.0.2.20 Bundle-Activator: com.tabbyml.tabby4eclipse.Activator Bundle-Vendor: com.tabbyml Require-Bundle: org.eclipse.ui, 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 9b2c5411e3ea..490db7a512e4 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 @@ -8,7 +8,7 @@ import com.tabbyml.tabby4eclipse.inlineCompletion.InlineCompletionService; public class Accept extends AbstractHandler { - private Logger logger = new Logger("commands.inlineCompletion.Accept"); + private Logger logger = new Logger("Commands.InlineCompletion.Accept"); @Override public Object execute(ExecutionEvent event) throws ExecutionException { diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/Dismiss.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/Dismiss.java index dbe16f0dc12d..fdacc5199a18 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/Dismiss.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/commands/inlineCompletion/Dismiss.java @@ -8,7 +8,7 @@ import com.tabbyml.tabby4eclipse.inlineCompletion.InlineCompletionService; public class Dismiss extends AbstractHandler { - private Logger logger = new Logger("commands.inlineCompletion.Dismiss"); + private Logger logger = new Logger("Commands.InlineCompletion.Dismiss"); @Override public Object execute(ExecutionEvent event) throws ExecutionException { diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/WorkbenchPartListener.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/WorkbenchPartListener.java index ab985a55fec1..c137ed206b6e 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/WorkbenchPartListener.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/editor/WorkbenchPartListener.java @@ -28,7 +28,7 @@ private static class LazyHolder { private static final WorkbenchPartListener INSTANCE = new WorkbenchPartListener(); } - private Logger logger = new Logger("EditorListener"); + private Logger logger = new Logger("WorkbenchPartListener"); private List editors = new ArrayList<>(); public void init() { diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionItem.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionItem.java index db9202c3387d..31610be70a3d 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionItem.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionItem.java @@ -1,5 +1,7 @@ package com.tabbyml.tabby4eclipse.inlineCompletion; +import com.tabbyml.tabby4eclipse.lsp.protocol.CompletionEventId; + public class InlineCompletionItem { public static class ReplaceRange { @@ -22,10 +24,12 @@ public int getSuffixLength() { private String insertText; private ReplaceRange replaceRange; + private CompletionEventId eventId; - public InlineCompletionItem(String insertText, ReplaceRange replaceRange) { + public InlineCompletionItem(String insertText, ReplaceRange replaceRange, CompletionEventId eventId) { this.insertText = insertText; this.replaceRange = replaceRange; + this.eventId = eventId; } public String getInsertText() { @@ -35,4 +39,8 @@ public String getInsertText() { public ReplaceRange getReplaceRange() { return replaceRange; } + + public CompletionEventId getEventId() { + return eventId; + } } diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionRenderer.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionRenderer.java index 1a156949162a..3602f94d9b5d 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionRenderer.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionRenderer.java @@ -33,6 +33,8 @@ public class InlineCompletionRenderer { private Map painters = new HashMap<>(); private ITextViewer currentTextViewer = null; private InlineCompletionItem currentCompletionItem = null; + private String currentViewId = null; + private Long currentDisplayAt = null; public void show(ITextViewer viewer, InlineCompletionItem item) { if (currentTextViewer != null) { @@ -41,14 +43,24 @@ public void show(ITextViewer viewer, InlineCompletionItem item) { currentTextViewer = viewer; currentCompletionItem = item; getPainter(viewer).update(item); + + currentDisplayAt = System.currentTimeMillis(); + + String completionId = "no-cmpl-id"; + if (item.getEventId() != null && item.getEventId().getCompletionId()!= null) { + completionId = item.getEventId().getCompletionId().replace("cmpl-", ""); + } + currentViewId = String.format("view-%s-at-%d", completionId, currentDisplayAt); } public void hide() { if (currentTextViewer != null) { getPainter(currentTextViewer).update(null); currentTextViewer = null; - currentCompletionItem = null; } + currentCompletionItem = null; + currentViewId = null; + currentDisplayAt = null; } public ITextViewer getCurrentTextViewer() { @@ -58,6 +70,17 @@ public ITextViewer getCurrentTextViewer() { public InlineCompletionItem getCurrentCompletionItem() { return currentCompletionItem; } + + public String getCurrentViewId() { + return currentViewId; + } + + public Long getCurrentDisplayedTime() { + if (currentDisplayAt != null) { + return System.currentTimeMillis() - currentDisplayAt; + } + return null; + } private InlineCompletionItemPainter getPainter(ITextViewer viewer) { InlineCompletionItemPainter painter = painters.get(viewer); 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 d519968709e5..82993fc1b264 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionService.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/inlineCompletion/InlineCompletionService.java @@ -17,7 +17,10 @@ import com.tabbyml.tabby4eclipse.Logger; import com.tabbyml.tabby4eclipse.editor.EditorUtils; import com.tabbyml.tabby4eclipse.lsp.LanguageServerService; +import com.tabbyml.tabby4eclipse.lsp.protocol.CompletionEventId; +import com.tabbyml.tabby4eclipse.lsp.protocol.EventParams; import com.tabbyml.tabby4eclipse.lsp.protocol.ILanguageServer; +import com.tabbyml.tabby4eclipse.lsp.protocol.ITelemetryService; import com.tabbyml.tabby4eclipse.lsp.protocol.ITextDocumentServiceExt; import com.tabbyml.tabby4eclipse.lsp.protocol.InlineCompletionParams; @@ -63,7 +66,7 @@ public void trigger() { ITextEditor textEditor = EditorUtils.getActiveTextEditor(); int offset = EditorUtils.getCurrentOffsetInDocument(textEditor); long modificationStamp = EditorUtils.getDocumentModificationStamp(textEditor); - logger.info("Provide inline completion for TextEditor " + textEditor.toString() + " at offset " + offset + logger.info("Provide inline completion for TextEditor " + textEditor.getTitle() + " at offset " + offset + " with modification stamp " + modificationStamp); renderer.hide(); if (current != null) { @@ -95,6 +98,8 @@ public void trigger() { InlineCompletionList list = request.convertInlineCompletionList(completionList); current.response = new InlineCompletionContext.Response(list); 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); } @@ -112,7 +117,8 @@ public void accept() { } int offset = current.request.offset; InlineCompletionItem item = current.response.getActiveCompletionItem(); - + EventParams eventParams = buildTelemetryEventParams(EventParams.Type.SELECT); + renderer.hide(); current = null; @@ -129,6 +135,7 @@ public void accept() { document.replace(offset, suffixReplaceLength, text); ITextSelection selection = new TextSelection(offset + text.length(), 0); textEditor.getSelectionProvider().setSelection(selection); + postTelemetryEvent(eventParams); } catch (BadLocationException e) { logger.error("Failed to accept inline completion.", e); } @@ -137,8 +144,12 @@ public void accept() { @Override public void dismiss() { - logger.info("Dismiss inline completion."); - renderer.hide(); + if (renderer.getCurrentCompletionItem() != null) { + logger.info("Dismiss inline completion."); + EventParams eventParams = buildTelemetryEventParams(EventParams.Type.DISMISS); + renderer.hide(); + postTelemetryEvent(eventParams); + } if (current != null) { if (current.job != null && !current.job.isDone()) { logger.info("Cancel the current job due to dismissed."); @@ -148,6 +159,35 @@ public void dismiss() { } } + private EventParams buildTelemetryEventParams(String type) { + return buildTelemetryEventParams(type, null); + } + + private EventParams buildTelemetryEventParams(String type, String selectKind) { + InlineCompletionItem item = this.renderer.getCurrentCompletionItem(); + if (item != null && item == current.response.getActiveCompletionItem()) { + EventParams params = new EventParams(); + params.setType(type); + params.setSelectKind(selectKind); + params.setCompletionEventId(item.getEventId()); + params.setViewId(this.renderer.getCurrentViewId()); + params.setElapsed(this.renderer.getCurrentDisplayedTime()); + return params; + } + return null; + } + + private void postTelemetryEvent(EventParams params) { + if (params != null) { + LanguageServerService.getInstance().getServer().execute((server) -> { + ITelemetryService telemetryService = ((ILanguageServer) server) + .getTelemetryService(); + telemetryService.event(params); + return null; + }); + } + } + private class InlineCompletionContext { private static class Request { private Logger logger = new Logger("InlineCompletionContext.Request"); @@ -193,7 +233,11 @@ public InlineCompletionList convertInlineCompletionList( int suffixReplaceLength = LSPEclipseUtils.toOffset(item.getRange().getEnd(), document) - offset; InlineCompletionItem.ReplaceRange replaceRange = new InlineCompletionItem.ReplaceRange( prefixReplaceLength, suffixReplaceLength); - items.add(new InlineCompletionItem(insertText, replaceRange)); + CompletionEventId eventId = null; + if (item.getData() != null) { + eventId = item.getData().getEventId(); + } + items.add(new InlineCompletionItem(insertText, replaceRange, eventId)); logger.debug("Converted InlineCompletionItem " + i + ": " + insertText + "\n replace range: " + replaceRange.getPrefixLength() + ", " + replaceRange.getSuffixLength()); } diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/EventParams.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/EventParams.java new file mode 100644 index 000000000000..170b2b102be1 --- /dev/null +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/EventParams.java @@ -0,0 +1,62 @@ +package com.tabbyml.tabby4eclipse.lsp.protocol; + +public class EventParams { + private String type; + private String selectKind; + private CompletionEventId eventId; + private String viewId; + private Long elapsed; + + public EventParams() { + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSelectKind() { + return selectKind; + } + + public void setSelectKind(String selectKind) { + this.selectKind = selectKind; + } + + public CompletionEventId getCompletionEventId() { + return eventId; + } + + public void setCompletionEventId(CompletionEventId completionEventId) { + this.eventId = completionEventId; + } + + public String getViewId() { + return viewId; + } + + public void setViewId(String viewId) { + this.viewId = viewId; + } + + public Long getElapsed() { + return elapsed; + } + + public void setElapsed(Long elapsed) { + this.elapsed = elapsed; + } + + public static class Type { + public static final String VIEW = "view"; + public static final String SELECT = "select"; + public static final String DISMISS = "dismiss"; + } + + public static class SelectKind { + public static final String LINE = "line"; + } +} diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ILanguageServer.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ILanguageServer.java index bf8184c93fe3..f34236e14738 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ILanguageServer.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ILanguageServer.java @@ -9,4 +9,7 @@ public interface ILanguageServer extends LanguageServer { @JsonDelegate IStatusService getStatusService(); + + @JsonDelegate + ITelemetryService getTelemetryService(); } diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ITelemetryService.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ITelemetryService.java new file mode 100644 index 000000000000..c1bcff3a06d6 --- /dev/null +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/ITelemetryService.java @@ -0,0 +1,10 @@ +package com.tabbyml.tabby4eclipse.lsp.protocol; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +@JsonSegment("tabby/telemetry") +public interface ITelemetryService { + @JsonNotification + void event(EventParams params); +} diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusInfo.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusInfo.java index 0f496c8149a4..7c790dffa91e 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusInfo.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/lsp/protocol/StatusInfo.java @@ -11,7 +11,7 @@ public class StatusInfo { private Command command; public StatusInfo() { - this.status = Status.NOT_INITIALIZED; + this.status = Status.DISCONNECTED; } public String getStatus() { @@ -47,8 +47,6 @@ public void setCommand(Command command) { } public static class Status { - public static final String NOT_INITIALIZED = "notInitialized"; - public static final String FINALIZED = "finalized"; public static final String CONNECTING = "connecting"; public static final String UNAUTHORIZED = "unauthorized"; public static final String DISCONNECTED = "disconnected"; diff --git a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusbarContribution.java b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusbarContribution.java index 4d34e8ff220d..f3d7da9c69b7 100644 --- a/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusbarContribution.java +++ b/clients/eclipse/plugin/src/com/tabbyml/tabby4eclipse/statusbar/StatusbarContribution.java @@ -60,9 +60,6 @@ private void updateLabel(CLabel label) { label.setToolTipText("Tabby: " + statusInfo.getStatus()); } switch (statusInfo.getStatus()) { - case StatusInfo.Status.NOT_INITIALIZED: - label.setImage(Images.getIcon(Images.ICON_LOADING)); - break; case StatusInfo.Status.CONNECTING: label.setImage(Images.getIcon(Images.ICON_LOADING)); break; @@ -117,9 +114,6 @@ public void widgetSelected(SelectionEvent e) { }); } switch (statusInfo.getStatus()) { - case StatusInfo.Status.NOT_INITIALIZED: - statusItem.setImage(Images.getIcon(Images.ICON_LOADING)); - break; case StatusInfo.Status.CONNECTING: statusItem.setImage(Images.getIcon(Images.ICON_LOADING)); break;