feat(vscode): add manual trigger supporting. (#459)
* feat(vscode): add manual trigger supporting. * fix: update documents and fix minor bugs. * fix: lint.release-0.2
parent
61ade26545
commit
0aa6c5fcfb
|
|
@ -20,9 +20,11 @@ Try our online demo [here](https://tabby.tabbyml.com/playground).
|
||||||
|
|
||||||
## Get Started
|
## Get Started
|
||||||
|
|
||||||
If you have installed the Tabby VSCode extension, you can follow the built-in walkthrough guides to get started. You can also reopen walkthrough page anytime by using command `Tabby: Getting Started`.
|
Once you have installed the Tabby VSCode extension, you can easily get started by following the built-in walkthrough guides. You can access the walkthrough page at any time by using the command `Tabby: Getting Started`.
|
||||||
|
|
||||||
1. Setup the Tabby server: you can get a Tabby Cloud hosted server [here](https://app.tabbyml.com), or build your self-hosted Tabby server following [this guide](https://tabby.tabbyml.com/docs/installation).
|
1. **Setup the Tabby server**: You have two options to set up your Tabby server. You can either get a Tabby Cloud hosted server [here](https://app.tabbyml.com) or build your own self-hosted Tabby server following [this guide](https://tabby.tabbyml.com/docs/installation).
|
||||||
2. Use the command `Tabby: Specify API Endpoint of Tabby` to connect the extension to your Tabby server. If you are using a Tabby Cloud server endpoint, please follow the popup messages to complete authorization.
|
2. **Connect the extension to your Tabby server**: Use the command `Tabby: Specify API Endpoint of Tabby` to connect the extension to your Tabby server. If you are using a Tabby Cloud server endpoint, follow the instructions provided in the popup messages to complete the authorization process.
|
||||||
|
|
||||||
Once setup is complete, Tabby will provide inline suggestions automatically, and you can accept suggestions by just pressing the Tab key. You can hover on the inline suggestion text to find more useful actions such as partially accepting by word or by line.
|
Once the setup is complete, Tabby will automatically provide inline suggestions. You can accept the suggestions by simply pressing the `Tab` key. Hovering over the inline suggestion text will display additional useful actions, such as partially accepting suggestions by word or by line.
|
||||||
|
|
||||||
|
If you prefer to trigger code completion manually, you can select the manual trigger option in the settings. After that, use the shortcut `Alt + \` to trigger code completion. To access the settings page, use the command `Tabby: Open Settings`.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,10 @@ Tabby will show inline suggestions when you stop typing, and you can accept sugg
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Manual Trigger
|
||||||
|
|
||||||
|
If you select manual trigger in the [settings](command:tabby.openSettings), you can trigger code completion by pressing `Alt + \`.
|
||||||
|
|
||||||
## Cycling Through Choices
|
## Cycling Through Choices
|
||||||
|
|
||||||
When multiple choices are available, you can cycle through them by pressing `Alt + [` and `Alt + ]`.
|
When multiple choices are available, you can cycle through them by pressing `Alt + [` and `Alt + ]`.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Tabby Commands
|
||||||
|
|
||||||
|

|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:cefad8151a4e659cf21cf02cd1211eff8f965ede107642233badb5828904b5a5
|
oid sha256:90b67bc6280cfd0e4d73d61c2cad5bd22c634b864cb8dd941dfaeaddcaa03073
|
||||||
size 43420
|
size 68239
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Connect to Tabby Server
|
||||||
|
|
||||||
|
When you execute the command `Tabby: Specify API Endpoint of Tabby`, you will see a prompt asking you to enter the API endpoint of your Tabby server.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Once the connection is established, you will see the status bar showing the check mark.
|
||||||
|
|
||||||
|
If you are using a Tabby Cloud server endpoint, please follow the popup messages to complete authorization.
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
"repository": "https://github.com/TabbyML/tabby",
|
"repository": "https://github.com/TabbyML/tabby",
|
||||||
"bugs": "https://github.com/TabbyML/tabby/issues",
|
"bugs": "https://github.com/TabbyML/tabby/issues",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"version": "0.5.0",
|
"version": "0.6.0-dev",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"ai",
|
"ai",
|
||||||
"autocomplete",
|
"autocomplete",
|
||||||
|
|
@ -35,8 +35,12 @@
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"command": "tabby.toggleEnabled",
|
"command": "tabby.toggleInlineCompletionTriggerMode",
|
||||||
"title": "Tabby: Toggle Code Suggestion On/Off"
|
"title": "Tabby: Toggle Code Completion Trigger Mode (Automatic/Manual)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "tabby.inlineCompletion.trigger",
|
||||||
|
"title": "Tabby: Trigger Code Completion Manually"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "tabby.setApiEndpoint",
|
"command": "tabby.setApiEndpoint",
|
||||||
|
|
@ -57,6 +61,10 @@
|
||||||
],
|
],
|
||||||
"menus": {
|
"menus": {
|
||||||
"commandPalette": [
|
"commandPalette": [
|
||||||
|
{
|
||||||
|
"command": "tabby.inlineCompletion.trigger",
|
||||||
|
"when": "config.tabby.inlineCompletion.triggerMode === 'manual' && !editorHasSelection && !inlineSuggestionsVisible"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "tabby.openTabbyAgentSettings",
|
"command": "tabby.openTabbyAgentSettings",
|
||||||
"when": "!isWeb"
|
"when": "!isWeb"
|
||||||
|
|
@ -80,16 +88,15 @@
|
||||||
{
|
{
|
||||||
"id": "connectToTabbyServer",
|
"id": "connectToTabbyServer",
|
||||||
"title": "Connect to Tabby Server",
|
"title": "Connect to Tabby Server",
|
||||||
"description": "Once your Tabby server is ready, specify the server API endpoint here. If you are using a Tabby Cloud endpoint, please follow the popup messages to complete authorization. \n[Specify API Endpoint](command:tabby.setApiEndpoint)",
|
"description": "Once your Tabby server is ready, specify the server API endpoint here. \n[Specify API Endpoint](command:tabby.setApiEndpoint)",
|
||||||
"media": {
|
"media": {
|
||||||
"image": "assets/walkthroughs/setApiEndpoint.png",
|
"markdown": "assets/walkthroughs/setApiEndpoint.md"
|
||||||
"altText": "Tabby: Specify API Endpoint of Tabby"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "codeCompletion",
|
"id": "codeCompletion",
|
||||||
"title": "Code Completion",
|
"title": "Code Completion",
|
||||||
"description": "Tabby provides inline suggestions automatically, and you can accept suggestions by just pressing the Tab key. Follow this guide to find more useful actions.",
|
"description": "Tabby provides inline suggestions automatically by default, and you can accept suggestions by just pressing the Tab key.",
|
||||||
"media": {
|
"media": {
|
||||||
"markdown": "assets/walkthroughs/codeCompletion.md"
|
"markdown": "assets/walkthroughs/codeCompletion.md"
|
||||||
}
|
}
|
||||||
|
|
@ -99,8 +106,7 @@
|
||||||
"title": "Commands",
|
"title": "Commands",
|
||||||
"description": "Type `>Tabby:` in command palette to list all Tabby commands. \n[Tabby commands](command:workbench.action.quickOpen?%5B%22%3ETabby%3A%22%5D)",
|
"description": "Type `>Tabby:` in command palette to list all Tabby commands. \n[Tabby commands](command:workbench.action.quickOpen?%5B%22%3ETabby%3A%22%5D)",
|
||||||
"media": {
|
"media": {
|
||||||
"image": "assets/walkthroughs/commands.png",
|
"markdown": "assets/walkthroughs/commands.md"
|
||||||
"altText": "Tabby Commands"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -116,10 +122,18 @@
|
||||||
"patternErrorMessage": "Please enter a validate http or https URL.",
|
"patternErrorMessage": "Please enter a validate http or https URL.",
|
||||||
"markdownDescription": "Specify API Endpoint of Tabby. \nIf leave empty, server endpoint in [Tabby Agent Settings](command:tabby.openTabbyAgentSettings) will be used."
|
"markdownDescription": "Specify API Endpoint of Tabby. \nIf leave empty, server endpoint in [Tabby Agent Settings](command:tabby.openTabbyAgentSettings) will be used."
|
||||||
},
|
},
|
||||||
"tabby.codeCompletion": {
|
"tabby.inlineCompletion.triggerMode": {
|
||||||
"type": "boolean",
|
"type": "string",
|
||||||
"default": true,
|
"enum": [
|
||||||
"description": "Enable Tabby code completion or not."
|
"automatic",
|
||||||
|
"manual"
|
||||||
|
],
|
||||||
|
"default": "automatic",
|
||||||
|
"description": "Select the code completion trigger mode.",
|
||||||
|
"enumDescriptions": [
|
||||||
|
"Automatic trigger when you stop typing",
|
||||||
|
"Manual trigger by pressing `Alt + \\`"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"tabby.keybindings": {
|
"tabby.keybindings": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -128,7 +142,7 @@
|
||||||
"tabby-style"
|
"tabby-style"
|
||||||
],
|
],
|
||||||
"default": "vscode-style",
|
"default": "vscode-style",
|
||||||
"markdownDescription": "Select keybinding profile to accept current inline completion. \n | | Next Line | Full Completion | Next Word | \n |:---:|:---:|:---:|:---:| \n | _vscode-style_ | - | Tab | Ctrl + RightArrow | \n | _tabby-style_ <br/> _(experimental)_ | Tab | Ctrl + Tab | Ctrl + RightArrow | \n"
|
"markdownDescription": "Select the keybinding profile to accept shown inline completion. \n | | Next Line | Full Completion | Next Word | \n |:---:|:---:|:---:|:---:| \n | _vscode-style_ | - | Tab | Ctrl + RightArrow | \n | _tabby-style_ <br/> _(experimental)_ | Tab | Ctrl + Tab | Ctrl + RightArrow | \n"
|
||||||
},
|
},
|
||||||
"tabby.usage.anonymousUsageTracking": {
|
"tabby.usage.anonymousUsageTracking": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|
@ -138,30 +152,35 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"keybindings": [
|
"keybindings": [
|
||||||
|
{
|
||||||
|
"key": "alt+\\",
|
||||||
|
"command": "tabby.inlineCompletion.trigger",
|
||||||
|
"when": "config.tabby.inlineCompletion.triggerMode === 'manual' && editorTextFocus && !editorHasSelection && !inlineSuggestionsVisible"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "tabby.inlineCompletion.accept",
|
"command": "tabby.inlineCompletion.accept",
|
||||||
"key": "tab",
|
"key": "tab",
|
||||||
"when": "config.tabby.codeCompletion && config.tabby.keybindings === 'vscode-style' && inlineSuggestionVisible && !editorReadonly && !editorTabMovesFocus && inlineSuggestionHasIndentationLessThanTabSize"
|
"when": "config.tabby.keybindings === 'vscode-style' && inlineSuggestionVisible && !editorReadonly && !suggestWidgetVisible && !editorHoverFocused && !editorTabMovesFocus && inlineSuggestionHasIndentationLessThanTabSize"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "tabby.inlineCompletion.acceptNextWord",
|
"command": "tabby.inlineCompletion.acceptNextWord",
|
||||||
"key": "ctrl+right",
|
"key": "ctrl+right",
|
||||||
"when": "config.tabby.codeCompletion && config.tabby.keybindings === 'vscode-style' && inlineSuggestionVisible && !editorReadonly"
|
"when": "config.tabby.keybindings === 'vscode-style' && inlineSuggestionVisible && !editorReadonly && !suggestWidgetVisible"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "tabby.inlineCompletion.accept",
|
"command": "tabby.inlineCompletion.accept",
|
||||||
"key": "ctrl+tab",
|
"key": "ctrl+tab",
|
||||||
"when": "config.tabby.codeCompletion && config.tabby.keybindings === 'tabby-style' && inlineSuggestionVisible && !editorReadonly"
|
"when": "config.tabby.keybindings === 'tabby-style' && inlineSuggestionVisible && !editorReadonly && !suggestWidgetVisible"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "tabby.inlineCompletion.acceptNextWord",
|
"command": "tabby.inlineCompletion.acceptNextWord",
|
||||||
"key": "ctrl+right",
|
"key": "ctrl+right",
|
||||||
"when": "config.tabby.codeCompletion && config.tabby.keybindings === 'tabby-style' && inlineSuggestionVisible && !editorReadonly"
|
"when": "config.tabby.keybindings === 'tabby-style' && inlineSuggestionVisible && !editorReadonly && !suggestWidgetVisible"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "tabby.inlineCompletion.acceptNextLine",
|
"command": "tabby.inlineCompletion.acceptNextLine",
|
||||||
"key": "tab",
|
"key": "tab",
|
||||||
"when": "config.tabby.codeCompletion && config.tabby.keybindings === 'tabby-style' && inlineSuggestionVisible && !editorReadonly && !editorTabMovesFocus"
|
"when": "config.tabby.keybindings === 'tabby-style' && inlineSuggestionVisible && !editorReadonly && !suggestWidgetVisible && !editorHoverFocused && !editorTabMovesFocus"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,17 @@ import {
|
||||||
TextDocument,
|
TextDocument,
|
||||||
workspace,
|
workspace,
|
||||||
} from "vscode";
|
} from "vscode";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
import { CompletionResponse } from "tabby-agent";
|
import { CompletionResponse } from "tabby-agent";
|
||||||
import { agent } from "./agent";
|
import { agent } from "./agent";
|
||||||
import { notifications } from "./notifications";
|
|
||||||
|
|
||||||
export class TabbyCompletionProvider implements InlineCompletionItemProvider {
|
export class TabbyCompletionProvider extends EventEmitter implements InlineCompletionItemProvider {
|
||||||
// User Settings
|
private triggerMode: "automatic" | "manual" | "disabled" = "automatic";
|
||||||
private enabled: boolean = true;
|
private onGoingRequestAbortController: AbortController | null = null;
|
||||||
|
private loading: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
this.updateConfiguration();
|
this.updateConfiguration();
|
||||||
workspace.onDidChangeConfiguration((event) => {
|
workspace.onDidChangeConfiguration((event) => {
|
||||||
if (event.affectsConfiguration("tabby") || event.affectsConfiguration("editor.inlineSuggest")) {
|
if (event.affectsConfiguration("tabby") || event.affectsConfiguration("editor.inlineSuggest")) {
|
||||||
|
|
@ -26,27 +28,37 @@ export class TabbyCompletionProvider implements InlineCompletionItemProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTriggerMode(): "automatic" | "manual" | "disabled" {
|
||||||
|
return this.triggerMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isLoading(): boolean {
|
||||||
|
return this.loading;
|
||||||
|
}
|
||||||
|
|
||||||
public async provideInlineCompletionItems(
|
public async provideInlineCompletionItems(
|
||||||
document: TextDocument,
|
document: TextDocument,
|
||||||
position: Position,
|
position: Position,
|
||||||
context: InlineCompletionContext,
|
context: InlineCompletionContext,
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
): Promise<InlineCompletionItem[]> {
|
): Promise<InlineCompletionItem[] | null> {
|
||||||
const emptyResponse = Promise.resolve([] as InlineCompletionItem[]);
|
if (context.triggerKind === InlineCompletionTriggerKind.Automatic && this.triggerMode === "manual") {
|
||||||
if (!this.enabled) {
|
return null;
|
||||||
console.debug("Extension not enabled, skipping.");
|
}
|
||||||
return emptyResponse;
|
|
||||||
|
if (context.triggerKind === InlineCompletionTriggerKind.Invoke && this.triggerMode === "automatic") {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if autocomplete widget is visible
|
// Check if autocomplete widget is visible
|
||||||
if (context.selectedCompletionInfo !== undefined) {
|
if (context.selectedCompletionInfo !== undefined) {
|
||||||
console.debug("Autocomplete widget is visible, skipping.");
|
console.debug("Autocomplete widget is visible, skipping.");
|
||||||
return emptyResponse;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token?.isCancellationRequested) {
|
if (token?.isCancellationRequested) {
|
||||||
console.debug("Cancellation was requested.");
|
console.debug("Cancellation was requested.");
|
||||||
return emptyResponse;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const replaceRange = this.calculateReplaceRange(document, position);
|
const replaceRange = this.calculateReplaceRange(document, position);
|
||||||
|
|
@ -60,33 +72,40 @@ export class TabbyCompletionProvider implements InlineCompletionItemProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
|
this.onGoingRequestAbortController = abortController;
|
||||||
token?.onCancellationRequested(() => {
|
token?.onCancellationRequested(() => {
|
||||||
console.debug("Cancellation requested.");
|
console.debug("Cancellation requested.");
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
});
|
});
|
||||||
|
|
||||||
const completion = await agent()
|
try {
|
||||||
.provideCompletions(request, { signal: abortController.signal })
|
this.loading = true;
|
||||||
.catch((_) => {
|
this.emit("loadingStatusUpdated");
|
||||||
return null;
|
const result = await agent().provideCompletions(request, { signal: abortController.signal });
|
||||||
});
|
this.loading = false;
|
||||||
|
this.emit("loadingStatusUpdated");
|
||||||
|
return this.toInlineCompletions(result, replaceRange);
|
||||||
|
} catch (error: any) {
|
||||||
|
if (this.onGoingRequestAbortController === abortController) {
|
||||||
|
// the request was not replaced by a new request, set loading to false safely
|
||||||
|
this.loading = false;
|
||||||
|
this.emit("loadingStatusUpdated");
|
||||||
|
}
|
||||||
|
if (error.name !== "AbortError") {
|
||||||
|
console.debug("Error when providing completions", { error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const completions = this.toInlineCompletions(completion, replaceRange);
|
return null;
|
||||||
return Promise.resolve(completions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateConfiguration() {
|
private updateConfiguration() {
|
||||||
const configuration = workspace.getConfiguration("tabby");
|
if (!workspace.getConfiguration("editor").get("inlineSuggest.enabled", true)) {
|
||||||
this.enabled = configuration.get("codeCompletion", true);
|
this.triggerMode = "disabled";
|
||||||
this.checkInlineCompletionEnabled();
|
this.emit("triggerModeUpdated");
|
||||||
}
|
} else {
|
||||||
|
this.triggerMode = workspace.getConfiguration("tabby").get("inlineCompletion.triggerMode", "automatic");
|
||||||
private checkInlineCompletionEnabled() {
|
this.emit("triggerModeUpdated");
|
||||||
const configuration = workspace.getConfiguration("editor.inlineSuggest");
|
|
||||||
const inlineSuggestEnabled = configuration.get("enabled", true);
|
|
||||||
if (this.enabled && !inlineSuggestEnabled) {
|
|
||||||
console.debug("Tabby code completion is enabled but inline suggest is disabled.");
|
|
||||||
notifications.showInformationWhenInlineSuggestDisabled();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,316 @@
|
||||||
|
import { StatusBarAlignment, ThemeColor, window } from "vscode";
|
||||||
|
import { createMachine, interpret } from "@xstate/fsm";
|
||||||
|
import { agent } from "./agent";
|
||||||
|
import { notifications } from "./notifications";
|
||||||
|
import { TabbyCompletionProvider } from "./TabbyCompletionProvider";
|
||||||
|
|
||||||
|
const label = "Tabby";
|
||||||
|
const iconLoading = "$(loading~spin)";
|
||||||
|
const iconAutomatic = "$(check)";
|
||||||
|
const iconManual = "$(chevron-right)";
|
||||||
|
const iconDisabled = "$(x)";
|
||||||
|
const iconDisconnected = "$(plug)";
|
||||||
|
const iconUnauthorized = "$(key)";
|
||||||
|
const iconIssueExist = "$(warning)";
|
||||||
|
const colorNormal = new ThemeColor("statusBar.foreground");
|
||||||
|
const colorWarning = new ThemeColor("statusBarItem.warningForeground");
|
||||||
|
const backgroundColorNormal = new ThemeColor("statusBar.background");
|
||||||
|
const backgroundColorWarning = new ThemeColor("statusBarItem.warningBackground");
|
||||||
|
|
||||||
|
export class TabbyStatusBarItem {
|
||||||
|
private item = window.createStatusBarItem(StatusBarAlignment.Right);
|
||||||
|
private completionProvider: TabbyCompletionProvider;
|
||||||
|
|
||||||
|
private transitionsForCompletionProviderStatus = [
|
||||||
|
{
|
||||||
|
target: "automatic",
|
||||||
|
cond: () => this.completionProvider.getTriggerMode() === "automatic",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "manual",
|
||||||
|
cond: () => this.completionProvider.getTriggerMode() === "manual" && !this.completionProvider.isLoading(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "loading",
|
||||||
|
cond: () => this.completionProvider.getTriggerMode() === "manual" && this.completionProvider.isLoading(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "disabled",
|
||||||
|
cond: () => this.completionProvider.getTriggerMode() === "disabled",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
private fsm = createMachine({
|
||||||
|
id: "statusBarItem",
|
||||||
|
initial: "initializing",
|
||||||
|
states: {
|
||||||
|
initializing: {
|
||||||
|
on: {
|
||||||
|
ready: this.transitionsForCompletionProviderStatus,
|
||||||
|
disconnected: "disconnected",
|
||||||
|
unauthorized: "unauthorized",
|
||||||
|
issuesExist: "issuesExist",
|
||||||
|
},
|
||||||
|
entry: () => this.toInitializing(),
|
||||||
|
},
|
||||||
|
automatic: {
|
||||||
|
on: {
|
||||||
|
completionStatusChanged: this.transitionsForCompletionProviderStatus,
|
||||||
|
ready: this.transitionsForCompletionProviderStatus,
|
||||||
|
disconnected: "disconnected",
|
||||||
|
unauthorized: "unauthorized",
|
||||||
|
issuesExist: "issuesExist",
|
||||||
|
},
|
||||||
|
entry: () => this.toAutomatic(),
|
||||||
|
},
|
||||||
|
manual: {
|
||||||
|
on: {
|
||||||
|
completionStatusChanged: this.transitionsForCompletionProviderStatus,
|
||||||
|
ready: this.transitionsForCompletionProviderStatus,
|
||||||
|
disconnected: "disconnected",
|
||||||
|
unauthorized: "unauthorized",
|
||||||
|
issuesExist: "issuesExist",
|
||||||
|
},
|
||||||
|
entry: () => this.toManual(),
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
on: {
|
||||||
|
completionStatusChanged: this.transitionsForCompletionProviderStatus,
|
||||||
|
ready: this.transitionsForCompletionProviderStatus,
|
||||||
|
disconnected: "disconnected",
|
||||||
|
unauthorized: "unauthorized",
|
||||||
|
issuesExist: "issuesExist",
|
||||||
|
},
|
||||||
|
entry: () => this.toLoading(),
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
on: {
|
||||||
|
completionStatusChanged: this.transitionsForCompletionProviderStatus,
|
||||||
|
ready: this.transitionsForCompletionProviderStatus,
|
||||||
|
disconnected: "disconnected",
|
||||||
|
unauthorized: "unauthorized",
|
||||||
|
issuesExist: "issuesExist",
|
||||||
|
},
|
||||||
|
entry: () => this.toDisabled(),
|
||||||
|
},
|
||||||
|
disconnected: {
|
||||||
|
on: {
|
||||||
|
ready: this.transitionsForCompletionProviderStatus,
|
||||||
|
unauthorized: "unauthorized",
|
||||||
|
issuesExist: "issuesExist",
|
||||||
|
},
|
||||||
|
entry: () => this.toDisconnected(),
|
||||||
|
},
|
||||||
|
unauthorized: {
|
||||||
|
on: {
|
||||||
|
ready: this.transitionsForCompletionProviderStatus,
|
||||||
|
disconnected: "disconnected",
|
||||||
|
issuesExist: "issuesExist",
|
||||||
|
authStart: "unauthorizedAndAuthInProgress",
|
||||||
|
},
|
||||||
|
entry: () => this.toUnauthorized(),
|
||||||
|
},
|
||||||
|
unauthorizedAndAuthInProgress: {
|
||||||
|
on: {
|
||||||
|
ready: this.transitionsForCompletionProviderStatus,
|
||||||
|
disconnected: "disconnected",
|
||||||
|
issuesExist: "issuesExist",
|
||||||
|
authEnd: "unauthorized", // if auth succeeds, we will get `ready` before `authEnd` event
|
||||||
|
},
|
||||||
|
entry: () => this.toUnauthorizedAndAuthInProgress(),
|
||||||
|
},
|
||||||
|
issuesExist: {
|
||||||
|
on: {
|
||||||
|
ready: this.transitionsForCompletionProviderStatus,
|
||||||
|
disconnected: "disconnected",
|
||||||
|
unauthorized: "unauthorized",
|
||||||
|
},
|
||||||
|
entry: () => this.toIssuesExist(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
private fsmService = interpret(this.fsm);
|
||||||
|
|
||||||
|
constructor(completionProvider: TabbyCompletionProvider) {
|
||||||
|
this.completionProvider = completionProvider;
|
||||||
|
this.fsmService.start();
|
||||||
|
this.fsmService.send(agent().getStatus());
|
||||||
|
this.fsmService.send("completionStatusChanged");
|
||||||
|
this.item.show();
|
||||||
|
|
||||||
|
this.completionProvider.on("triggerModeUpdated", () => {
|
||||||
|
this.fsmService.send("completionStatusChanged");
|
||||||
|
});
|
||||||
|
this.completionProvider.on("loadingStatusUpdated", () => {
|
||||||
|
this.fsmService.send("completionStatusChanged");
|
||||||
|
});
|
||||||
|
agent().on("statusChanged", (event) => {
|
||||||
|
console.debug("Tabby agent statusChanged", { event });
|
||||||
|
this.fsmService.send(event.status);
|
||||||
|
});
|
||||||
|
|
||||||
|
agent().on("authRequired", (event) => {
|
||||||
|
console.debug("Tabby agent authRequired", { event });
|
||||||
|
notifications.showInformationStartAuth({
|
||||||
|
onAuthStart: () => {
|
||||||
|
this.fsmService.send("authStart");
|
||||||
|
},
|
||||||
|
onAuthEnd: () => {
|
||||||
|
this.fsmService.send("authEnd");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
agent().on("newIssue", (event) => {
|
||||||
|
console.debug("Tabby agent newIssue", { event });
|
||||||
|
if (event.issue.name === "slowCompletionResponseTime") {
|
||||||
|
notifications.showInformationWhenSlowCompletionResponseTime();
|
||||||
|
} else if (event.issue.name === "highCompletionTimeoutRate") {
|
||||||
|
notifications.showInformationWhenHighCompletionTimeoutRate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public register() {
|
||||||
|
return this.item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private toInitializing() {
|
||||||
|
this.item.color = colorNormal;
|
||||||
|
this.item.backgroundColor = backgroundColorNormal;
|
||||||
|
this.item.text = `${iconLoading} ${label}`;
|
||||||
|
this.item.tooltip = "Tabby is initializing.";
|
||||||
|
this.item.command = {
|
||||||
|
title: "",
|
||||||
|
command: "tabby.applyCallback",
|
||||||
|
arguments: [() => notifications.showInformationWhenInitializing()],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private toAutomatic() {
|
||||||
|
this.item.color = colorNormal;
|
||||||
|
this.item.backgroundColor = backgroundColorNormal;
|
||||||
|
this.item.text = `${iconAutomatic} ${label}`;
|
||||||
|
this.item.tooltip = "Tabby automatic code completion is enabled.";
|
||||||
|
this.item.command = {
|
||||||
|
title: "",
|
||||||
|
command: "tabby.applyCallback",
|
||||||
|
arguments: [() => notifications.showInformationWhenAutomaticTrigger()],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private toManual() {
|
||||||
|
this.item.color = colorNormal;
|
||||||
|
this.item.backgroundColor = backgroundColorNormal;
|
||||||
|
this.item.text = `${iconManual} ${label}`;
|
||||||
|
this.item.tooltip = "Tabby is standing by, click or press `Alt + \\` to trigger code completion.";
|
||||||
|
this.item.command = {
|
||||||
|
title: "",
|
||||||
|
command: "tabby.applyCallback",
|
||||||
|
arguments: [() => notifications.showInformationWhenManualTrigger()],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private toLoading() {
|
||||||
|
this.item.color = colorNormal;
|
||||||
|
this.item.backgroundColor = backgroundColorNormal;
|
||||||
|
this.item.text = `${iconLoading} ${label}`;
|
||||||
|
this.item.tooltip = "Tabby is generating code completions.";
|
||||||
|
this.item.command = {
|
||||||
|
title: "",
|
||||||
|
command: "tabby.applyCallback",
|
||||||
|
arguments: [() => notifications.showInformationWhenManualTriggerLoading()],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private toDisabled() {
|
||||||
|
this.item.color = colorWarning;
|
||||||
|
this.item.backgroundColor = backgroundColorWarning;
|
||||||
|
this.item.text = `${iconDisabled} ${label}`;
|
||||||
|
this.item.tooltip = "Tabby is disabled. Click to check settings.";
|
||||||
|
this.item.command = {
|
||||||
|
title: "",
|
||||||
|
command: "tabby.applyCallback",
|
||||||
|
arguments: [() => notifications.showInformationWhenInlineSuggestDisabled()],
|
||||||
|
};
|
||||||
|
|
||||||
|
console.debug("Tabby code completion is enabled but inline suggest is disabled.");
|
||||||
|
notifications.showInformationWhenInlineSuggestDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
private toDisconnected() {
|
||||||
|
this.item.color = colorWarning;
|
||||||
|
this.item.backgroundColor = backgroundColorWarning;
|
||||||
|
this.item.text = `${iconDisconnected} ${label}`;
|
||||||
|
this.item.tooltip = "Cannot connect to Tabby Server. Click to open settings.";
|
||||||
|
this.item.command = {
|
||||||
|
title: "",
|
||||||
|
command: "tabby.applyCallback",
|
||||||
|
arguments: [() => notifications.showInformationWhenDisconnected()],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private toUnauthorized() {
|
||||||
|
this.item.color = colorWarning;
|
||||||
|
this.item.backgroundColor = backgroundColorWarning;
|
||||||
|
this.item.text = `${iconUnauthorized} ${label}`;
|
||||||
|
this.item.tooltip = "Tabby Server requires authorization. Click to continue.";
|
||||||
|
this.item.command = {
|
||||||
|
title: "",
|
||||||
|
command: "tabby.applyCallback",
|
||||||
|
arguments: [
|
||||||
|
() =>
|
||||||
|
notifications.showInformationStartAuth({
|
||||||
|
onAuthStart: () => {
|
||||||
|
this.fsmService.send("authStart");
|
||||||
|
},
|
||||||
|
onAuthEnd: () => {
|
||||||
|
this.fsmService.send("authEnd");
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private toUnauthorizedAndAuthInProgress() {
|
||||||
|
this.item.color = colorWarning;
|
||||||
|
this.item.backgroundColor = backgroundColorWarning;
|
||||||
|
this.item.text = `${iconUnauthorized} ${label}`;
|
||||||
|
this.item.tooltip = "Waiting for authorization.";
|
||||||
|
this.item.command = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private toIssuesExist() {
|
||||||
|
this.item.color = colorWarning;
|
||||||
|
this.item.backgroundColor = backgroundColorWarning;
|
||||||
|
this.item.text = `${iconIssueExist} ${label}`;
|
||||||
|
switch (agent().getIssues()[0]?.name) {
|
||||||
|
case "slowCompletionResponseTime":
|
||||||
|
this.item.tooltip = "Completion requests appear to take too much time.";
|
||||||
|
break;
|
||||||
|
case "highCompletionTimeoutRate":
|
||||||
|
this.item.tooltip = "Most completion requests timed out.";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.item.tooltip = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.item.command = {
|
||||||
|
title: "",
|
||||||
|
command: "tabby.applyCallback",
|
||||||
|
arguments: [
|
||||||
|
() => {
|
||||||
|
switch (agent().getIssues()[0]?.name) {
|
||||||
|
case "slowCompletionResponseTime":
|
||||||
|
notifications.showInformationWhenSlowCompletionResponseTime();
|
||||||
|
break;
|
||||||
|
case "highCompletionTimeoutRate":
|
||||||
|
notifications.showInformationWhenHighCompletionTimeoutRate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,13 +20,20 @@ type Command = {
|
||||||
thisArg?: any;
|
thisArg?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleEnabled: Command = {
|
const toggleInlineCompletionTriggerMode: Command = {
|
||||||
command: "tabby.toggleEnabled",
|
command: "tabby.toggleInlineCompletionTriggerMode",
|
||||||
callback: () => {
|
callback: (value: "automatic" | "manual" | undefined) => {
|
||||||
const configuration = workspace.getConfiguration("tabby");
|
const configuration = workspace.getConfiguration("tabby");
|
||||||
const enabled = configuration.get("codeCompletion", true);
|
let target = value;
|
||||||
console.debug(`Toggle Enabled: ${enabled} -> ${!enabled}.`);
|
if (!target) {
|
||||||
configuration.update("codeCompletion", !enabled, configTarget, false);
|
const current = configuration.get("inlineCompletion.triggerMode", "automatic");
|
||||||
|
if (current === "automatic") {
|
||||||
|
target = "manual";
|
||||||
|
} else {
|
||||||
|
target = "automatic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configuration.update("inlineCompletion.triggerMode", target, configTarget, false);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -154,42 +161,17 @@ const openAuthPage: Command = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusBarItemClicked: Command = {
|
const applyCallback: Command = {
|
||||||
command: "tabby.statusBarItemClicked",
|
command: "tabby.applyCallback",
|
||||||
callback: (status) => {
|
callback: (callback) => {
|
||||||
switch (status) {
|
callback?.();
|
||||||
case "loading":
|
},
|
||||||
notifications.showInformationWhenLoading();
|
};
|
||||||
break;
|
|
||||||
case "ready":
|
const triggerInlineCompletion: Command = {
|
||||||
notifications.showInformationWhenReady();
|
command: "tabby.inlineCompletion.trigger",
|
||||||
break;
|
callback: () => {
|
||||||
case "disconnected":
|
commands.executeCommand("editor.action.inlineSuggest.trigger");
|
||||||
notifications.showInformationWhenDisconnected();
|
|
||||||
break;
|
|
||||||
case "unauthorized":
|
|
||||||
notifications.showInformationStartAuth();
|
|
||||||
break;
|
|
||||||
case "issuesExist":
|
|
||||||
switch (agent().getIssues()[0]?.name) {
|
|
||||||
case "slowCompletionResponseTime":
|
|
||||||
notifications.showInformationWhenSlowCompletionResponseTime();
|
|
||||||
break;
|
|
||||||
case "highCompletionTimeoutRate":
|
|
||||||
notifications.showInformationWhenHighCompletionTimeoutRate();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "disabled":
|
|
||||||
const enabled = workspace.getConfiguration("tabby").get("codeCompletion", true);
|
|
||||||
const inlineSuggestEnabled = workspace.getConfiguration("editor").get("inlineSuggest.enabled", true);
|
|
||||||
if (enabled && !inlineSuggestEnabled) {
|
|
||||||
notifications.showInformationWhenInlineSuggestDisabled();
|
|
||||||
} else {
|
|
||||||
notifications.showInformationWhenDisabled();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -218,7 +200,7 @@ const acceptInlineCompletionNextLine: Command = {
|
||||||
|
|
||||||
export const tabbyCommands = () =>
|
export const tabbyCommands = () =>
|
||||||
[
|
[
|
||||||
toggleEnabled,
|
toggleInlineCompletionTriggerMode,
|
||||||
setApiEndpoint,
|
setApiEndpoint,
|
||||||
openSettings,
|
openSettings,
|
||||||
openTabbyAgentSettings,
|
openTabbyAgentSettings,
|
||||||
|
|
@ -226,7 +208,8 @@ export const tabbyCommands = () =>
|
||||||
gettingStarted,
|
gettingStarted,
|
||||||
emitEvent,
|
emitEvent,
|
||||||
openAuthPage,
|
openAuthPage,
|
||||||
statusBarItemClicked,
|
applyCallback,
|
||||||
|
triggerInlineCompletion,
|
||||||
acceptInlineCompletion,
|
acceptInlineCompletion,
|
||||||
acceptInlineCompletionNextWord,
|
acceptInlineCompletionNextWord,
|
||||||
acceptInlineCompletionNextLine,
|
acceptInlineCompletionNextLine,
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,18 @@ import { ExtensionContext, languages } from "vscode";
|
||||||
import { createAgentInstance } from "./agent";
|
import { createAgentInstance } from "./agent";
|
||||||
import { tabbyCommands } from "./commands";
|
import { tabbyCommands } from "./commands";
|
||||||
import { TabbyCompletionProvider } from "./TabbyCompletionProvider";
|
import { TabbyCompletionProvider } from "./TabbyCompletionProvider";
|
||||||
import { tabbyStatusBarItem } from "./statusBarItem";
|
import { TabbyStatusBarItem } from "./TabbyStatusBarItem";
|
||||||
|
|
||||||
// this method is called when your extension is activated
|
// this method is called when your extension is activated
|
||||||
// your extension is activated the very first time the command is executed
|
// your extension is activated the very first time the command is executed
|
||||||
export async function activate(context: ExtensionContext) {
|
export async function activate(context: ExtensionContext) {
|
||||||
console.debug("Activating Tabby extension", new Date());
|
console.debug("Activating Tabby extension", new Date());
|
||||||
await createAgentInstance(context);
|
await createAgentInstance(context);
|
||||||
|
const completionProvider = new TabbyCompletionProvider();
|
||||||
|
const statusBarItem = new TabbyStatusBarItem(completionProvider);
|
||||||
context.subscriptions.push(
|
context.subscriptions.push(
|
||||||
languages.registerInlineCompletionItemProvider({ pattern: "**" }, new TabbyCompletionProvider()),
|
languages.registerInlineCompletionItemProvider({ pattern: "**" }, completionProvider),
|
||||||
tabbyStatusBarItem(),
|
statusBarItem.register(),
|
||||||
...tabbyCommands(),
|
...tabbyCommands(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { commands, window, workspace, env, ConfigurationTarget, Uri } from "vscode";
|
import { commands, window, workspace, env, ConfigurationTarget, Uri } from "vscode";
|
||||||
import { agent } from "./agent";
|
import { agent } from "./agent";
|
||||||
|
|
||||||
function showInformationWhenLoading() {
|
function showInformationWhenInitializing() {
|
||||||
window.showInformationMessage("Tabby is initializing.", "Settings").then((selection) => {
|
window.showInformationMessage("Tabby is initializing.", "Settings").then((selection) => {
|
||||||
switch (selection) {
|
switch (selection) {
|
||||||
case "Settings":
|
case "Settings":
|
||||||
|
|
@ -11,13 +11,17 @@ function showInformationWhenLoading() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showInformationWhenDisabled() {
|
function showInformationWhenAutomaticTrigger() {
|
||||||
window
|
window
|
||||||
.showInformationMessage("Tabby code completion is disabled. Enable it?", "Enable", "Settings")
|
.showInformationMessage(
|
||||||
|
"Tabby automatic code completion is enabled. Switch to manual trigger mode?",
|
||||||
|
"Manual Mode",
|
||||||
|
"Settings",
|
||||||
|
)
|
||||||
.then((selection) => {
|
.then((selection) => {
|
||||||
switch (selection) {
|
switch (selection) {
|
||||||
case "Enable":
|
case "Manual Mode":
|
||||||
commands.executeCommand("tabby.toggleEnabled");
|
commands.executeCommand("tabby.toggleInlineCompletionTriggerMode", "manual");
|
||||||
break;
|
break;
|
||||||
case "Settings":
|
case "Settings":
|
||||||
commands.executeCommand("tabby.openSettings");
|
commands.executeCommand("tabby.openSettings");
|
||||||
|
|
@ -26,13 +30,21 @@ function showInformationWhenDisabled() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showInformationWhenReady() {
|
function showInformationWhenManualTrigger() {
|
||||||
window
|
window
|
||||||
.showInformationMessage("Tabby is providing code suggestions for you. Disable it?", "Disable", "Settings")
|
.showInformationMessage(
|
||||||
|
"Tabby is standing by. Trigger code completion manually?",
|
||||||
|
"Trigger",
|
||||||
|
"Automatic Mode",
|
||||||
|
"Settings",
|
||||||
|
)
|
||||||
.then((selection) => {
|
.then((selection) => {
|
||||||
switch (selection) {
|
switch (selection) {
|
||||||
case "Disable":
|
case "Trigger":
|
||||||
commands.executeCommand("tabby.toggleEnabled");
|
commands.executeCommand("editor.action.inlineSuggest.trigger");
|
||||||
|
break;
|
||||||
|
case "Automatic Mode":
|
||||||
|
commands.executeCommand("tabby.toggleInlineCompletionTriggerMode", "automatic");
|
||||||
break;
|
break;
|
||||||
case "Settings":
|
case "Settings":
|
||||||
commands.executeCommand("tabby.openSettings");
|
commands.executeCommand("tabby.openSettings");
|
||||||
|
|
@ -41,6 +53,37 @@ function showInformationWhenReady() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showInformationWhenManualTriggerLoading() {
|
||||||
|
window.showInformationMessage("Tabby is generating code completions.", "Settings").then((selection) => {
|
||||||
|
switch (selection) {
|
||||||
|
case "Settings":
|
||||||
|
commands.executeCommand("tabby.openSettings");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showInformationWhenInlineSuggestDisabled() {
|
||||||
|
window
|
||||||
|
.showWarningMessage(
|
||||||
|
"Tabby's suggestion is not showing because inline suggestion is disabled. Please enable it first.",
|
||||||
|
"Enable",
|
||||||
|
"Settings",
|
||||||
|
)
|
||||||
|
.then((selection) => {
|
||||||
|
switch (selection) {
|
||||||
|
case "Enable":
|
||||||
|
const configuration = workspace.getConfiguration("editor");
|
||||||
|
console.debug(`Set editor.inlineSuggest.enabled: true.`);
|
||||||
|
configuration.update("inlineSuggest.enabled", true, ConfigurationTarget.Global, false);
|
||||||
|
break;
|
||||||
|
case "Settings":
|
||||||
|
commands.executeCommand("workbench.action.openSettings", "@id:editor.inlineSuggest.enabled");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function showInformationWhenDisconnected() {
|
function showInformationWhenDisconnected() {
|
||||||
window
|
window
|
||||||
.showInformationMessage("Cannot connect to Tabby Server. Please check settings.", "Settings")
|
.showInformationMessage("Cannot connect to Tabby Server. Please check settings.", "Settings")
|
||||||
|
|
@ -89,27 +132,6 @@ function showInformationWhenAuthFailed() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showInformationWhenInlineSuggestDisabled() {
|
|
||||||
window
|
|
||||||
.showWarningMessage(
|
|
||||||
"Tabby's suggestion is not showing because inline suggestion is disabled. Please enable it first.",
|
|
||||||
"Enable",
|
|
||||||
"Settings",
|
|
||||||
)
|
|
||||||
.then((selection) => {
|
|
||||||
switch (selection) {
|
|
||||||
case "Enable":
|
|
||||||
const configuration = workspace.getConfiguration("editor");
|
|
||||||
console.debug(`Set editor.inlineSuggest.enabled: true.`);
|
|
||||||
configuration.update("inlineSuggest.enabled", true, ConfigurationTarget.Global, false);
|
|
||||||
break;
|
|
||||||
case "Settings":
|
|
||||||
commands.executeCommand("workbench.action.openSettings", "@id:editor.inlineSuggest.enabled");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHelpMessageForCompletionResponseTimeIssue() {
|
function getHelpMessageForCompletionResponseTimeIssue() {
|
||||||
let helpMessageForRunningLargeModelOnCPU = "";
|
let helpMessageForRunningLargeModelOnCPU = "";
|
||||||
const serverHealthState = agent().getServerHealthState();
|
const serverHealthState = agent().getServerHealthState();
|
||||||
|
|
@ -218,15 +240,16 @@ function showInformationWhenHighCompletionTimeoutRate(modal: boolean = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const notifications = {
|
export const notifications = {
|
||||||
showInformationWhenLoading,
|
showInformationWhenInitializing,
|
||||||
showInformationWhenDisabled,
|
showInformationWhenAutomaticTrigger,
|
||||||
showInformationWhenReady,
|
showInformationWhenManualTrigger,
|
||||||
|
showInformationWhenManualTriggerLoading,
|
||||||
|
showInformationWhenInlineSuggestDisabled,
|
||||||
showInformationWhenDisconnected,
|
showInformationWhenDisconnected,
|
||||||
showInformationStartAuth,
|
showInformationStartAuth,
|
||||||
showInformationAuthSuccess,
|
showInformationAuthSuccess,
|
||||||
showInformationWhenStartAuthButAlreadyAuthorized,
|
showInformationWhenStartAuthButAlreadyAuthorized,
|
||||||
showInformationWhenAuthFailed,
|
showInformationWhenAuthFailed,
|
||||||
showInformationWhenInlineSuggestDisabled,
|
|
||||||
showInformationWhenSlowCompletionResponseTime,
|
showInformationWhenSlowCompletionResponseTime,
|
||||||
showInformationWhenHighCompletionTimeoutRate,
|
showInformationWhenHighCompletionTimeoutRate,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
import { StatusBarAlignment, ThemeColor, window, workspace } from "vscode";
|
|
||||||
import { createMachine, interpret } from "@xstate/fsm";
|
|
||||||
import { agent } from "./agent";
|
|
||||||
import { notifications } from "./notifications";
|
|
||||||
|
|
||||||
const label = "Tabby";
|
|
||||||
const iconLoading = "$(loading~spin)";
|
|
||||||
const iconReady = "$(check)";
|
|
||||||
const iconDisconnected = "$(plug)";
|
|
||||||
const iconUnauthorized = "$(key)";
|
|
||||||
const iconIssueExist = "$(warning)";
|
|
||||||
const iconDisabled = "$(x)";
|
|
||||||
const colorNormal = new ThemeColor("statusBar.foreground");
|
|
||||||
const colorWarning = new ThemeColor("statusBarItem.warningForeground");
|
|
||||||
const backgroundColorNormal = new ThemeColor("statusBar.background");
|
|
||||||
const backgroundColorWarning = new ThemeColor("statusBarItem.warningBackground");
|
|
||||||
|
|
||||||
const item = window.createStatusBarItem(StatusBarAlignment.Right);
|
|
||||||
const fsm = createMachine({
|
|
||||||
id: "statusBarItem",
|
|
||||||
initial: "loading",
|
|
||||||
states: {
|
|
||||||
loading: {
|
|
||||||
on: {
|
|
||||||
ready: "ready",
|
|
||||||
disconnected: "disconnected",
|
|
||||||
unauthorized: "unauthorized",
|
|
||||||
issuesExist: "issuesExist",
|
|
||||||
disabled: "disabled",
|
|
||||||
},
|
|
||||||
entry: () => toLoading(),
|
|
||||||
},
|
|
||||||
ready: {
|
|
||||||
on: {
|
|
||||||
loading: "loading",
|
|
||||||
disconnected: "disconnected",
|
|
||||||
unauthorized: "unauthorized",
|
|
||||||
issuesExist: "issuesExist",
|
|
||||||
disabled: "disabled",
|
|
||||||
},
|
|
||||||
entry: () => toReady(),
|
|
||||||
},
|
|
||||||
disconnected: {
|
|
||||||
on: {
|
|
||||||
loading: "loading",
|
|
||||||
ready: "ready",
|
|
||||||
unauthorized: "unauthorized",
|
|
||||||
issuesExist: "issuesExist",
|
|
||||||
disabled: "disabled",
|
|
||||||
},
|
|
||||||
entry: () => toDisconnected(),
|
|
||||||
},
|
|
||||||
unauthorized: {
|
|
||||||
on: {
|
|
||||||
ready: "ready",
|
|
||||||
disconnected: "disconnected",
|
|
||||||
disabled: "disabled",
|
|
||||||
issuesExist: "issuesExist",
|
|
||||||
authStart: "unauthorizedAndAuthInProgress",
|
|
||||||
},
|
|
||||||
entry: () => toUnauthorized(),
|
|
||||||
},
|
|
||||||
unauthorizedAndAuthInProgress: {
|
|
||||||
on: {
|
|
||||||
ready: "ready",
|
|
||||||
disconnected: "disconnected",
|
|
||||||
issuesExist: "issuesExist",
|
|
||||||
disabled: "disabled",
|
|
||||||
authEnd: "unauthorized", // if auth succeeds, we will get `ready` before `authEnd` event
|
|
||||||
},
|
|
||||||
entry: () => toUnauthorizedAndAuthInProgress(),
|
|
||||||
},
|
|
||||||
issuesExist: {
|
|
||||||
on: {
|
|
||||||
loading: "loading",
|
|
||||||
ready: "ready",
|
|
||||||
disconnected: "disconnected",
|
|
||||||
unauthorized: "unauthorized",
|
|
||||||
disabled: "disabled",
|
|
||||||
},
|
|
||||||
entry: () => toIssuesExist(),
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
on: {
|
|
||||||
loading: "loading",
|
|
||||||
ready: "ready",
|
|
||||||
disconnected: "disconnected",
|
|
||||||
unauthorized: "unauthorized",
|
|
||||||
issuesExist: "issuesExist",
|
|
||||||
},
|
|
||||||
entry: () => toDisabled(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const fsmService = interpret(fsm);
|
|
||||||
|
|
||||||
function toLoading() {
|
|
||||||
item.color = colorNormal;
|
|
||||||
item.backgroundColor = backgroundColorNormal;
|
|
||||||
item.text = `${iconLoading} ${label}`;
|
|
||||||
item.tooltip = "Tabby is initializing.";
|
|
||||||
item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["loading"] };
|
|
||||||
}
|
|
||||||
|
|
||||||
function toReady() {
|
|
||||||
item.color = colorNormal;
|
|
||||||
item.backgroundColor = backgroundColorNormal;
|
|
||||||
item.text = `${iconReady} ${label}`;
|
|
||||||
item.tooltip = "Tabby is providing code suggestions for you.";
|
|
||||||
item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["ready"] };
|
|
||||||
}
|
|
||||||
|
|
||||||
function toDisconnected() {
|
|
||||||
item.color = colorWarning;
|
|
||||||
item.backgroundColor = backgroundColorWarning;
|
|
||||||
item.text = `${iconDisconnected} ${label}`;
|
|
||||||
item.tooltip = "Cannot connect to Tabby Server. Click to open settings.";
|
|
||||||
item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["disconnected"] };
|
|
||||||
}
|
|
||||||
|
|
||||||
function toUnauthorized() {
|
|
||||||
item.color = colorWarning;
|
|
||||||
item.backgroundColor = backgroundColorWarning;
|
|
||||||
item.text = `${iconUnauthorized} ${label}`;
|
|
||||||
item.tooltip = "Tabby Server requires authorization. Click to continue.";
|
|
||||||
item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["unauthorized"] };
|
|
||||||
}
|
|
||||||
|
|
||||||
function toUnauthorizedAndAuthInProgress() {
|
|
||||||
item.color = colorWarning;
|
|
||||||
item.backgroundColor = backgroundColorWarning;
|
|
||||||
item.text = `${iconUnauthorized} ${label}`;
|
|
||||||
item.tooltip = "Waiting for authorization.";
|
|
||||||
item.command = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toIssuesExist() {
|
|
||||||
item.color = colorWarning;
|
|
||||||
item.backgroundColor = backgroundColorWarning;
|
|
||||||
item.text = `${iconIssueExist} ${label}`;
|
|
||||||
switch (agent().getIssues()[0]?.name) {
|
|
||||||
case "slowCompletionResponseTime":
|
|
||||||
item.tooltip = "Completion requests appear to take too much time.";
|
|
||||||
break;
|
|
||||||
case "highCompletionTimeoutRate":
|
|
||||||
item.tooltip = "Most completion requests timed out.";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
item.tooltip = "";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["issuesExist"] };
|
|
||||||
}
|
|
||||||
|
|
||||||
function toDisabled() {
|
|
||||||
item.color = colorWarning;
|
|
||||||
item.backgroundColor = backgroundColorWarning;
|
|
||||||
item.text = `${iconDisabled} ${label}`;
|
|
||||||
item.tooltip = "Tabby is disabled.";
|
|
||||||
item.command = { title: "", command: "tabby.statusBarItemClicked", arguments: ["disabled"] };
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateStatusBarItem() {
|
|
||||||
const enabled = workspace.getConfiguration("tabby").get("codeCompletion", true);
|
|
||||||
const inlineSuggestEnabled = workspace.getConfiguration("editor").get("inlineSuggest.enabled", true);
|
|
||||||
if (!enabled || !inlineSuggestEnabled) {
|
|
||||||
fsmService.send("disabled");
|
|
||||||
} else {
|
|
||||||
const status = agent().getStatus();
|
|
||||||
switch (status) {
|
|
||||||
case "notInitialized":
|
|
||||||
fsmService.send("loading");
|
|
||||||
break;
|
|
||||||
case "ready":
|
|
||||||
case "disconnected":
|
|
||||||
case "unauthorized":
|
|
||||||
case "issuesExist":
|
|
||||||
fsmService.send(status);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const tabbyStatusBarItem = () => {
|
|
||||||
fsmService.start();
|
|
||||||
updateStatusBarItem();
|
|
||||||
|
|
||||||
workspace.onDidChangeConfiguration((event) => {
|
|
||||||
if (event.affectsConfiguration("tabby") || event.affectsConfiguration("editor.inlineSuggest")) {
|
|
||||||
updateStatusBarItem();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
agent().on("statusChanged", updateStatusBarItem);
|
|
||||||
|
|
||||||
agent().on("authRequired", () => {
|
|
||||||
notifications.showInformationStartAuth({
|
|
||||||
onAuthStart: () => {
|
|
||||||
fsmService.send("authStart");
|
|
||||||
},
|
|
||||||
onAuthEnd: () => {
|
|
||||||
fsmService.send("authEnd");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
agent().on("newIssue", (event) => {
|
|
||||||
if (event.issue.name === "slowCompletionResponseTime") {
|
|
||||||
notifications.showInformationWhenSlowCompletionResponseTime();
|
|
||||||
} else if (event.issue.name === "highCompletionTimeoutRate") {
|
|
||||||
notifications.showInformationWhenHighCompletionTimeoutRate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
item.show();
|
|
||||||
return item;
|
|
||||||
};
|
|
||||||
Loading…
Reference in New Issue