From 8cdf6a5f410005210a50b2129f33f63bdeb275c6 Mon Sep 17 00:00:00 2001 From: Meng Zhang Date: Wed, 21 Aug 2024 04:19:40 -0700 Subject: [PATCH] feat(vscode): use CommandPalette to display tabby menu (#2917) * feat(vscode): use CommandPalette to display tabby menu * update * [autofix.ci] apply automated fixes * Apply suggestions from code review * update * Update clients/vscode/src/Commands.ts Co-authored-by: Zhiming Ma * chore(vscode): display issues help message in CommandPalette * update label * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Zhiming Ma --- clients/vscode/src/CommandPalette.ts | 135 +++++++++++++++++++++++++++ clients/vscode/src/Commands.ts | 28 +++--- clients/vscode/src/StatusBarItem.ts | 97 +------------------ clients/vscode/src/extension.ts | 1 + clients/vscode/src/logger.ts | 4 + 5 files changed, 159 insertions(+), 106 deletions(-) create mode 100644 clients/vscode/src/CommandPalette.ts diff --git a/clients/vscode/src/CommandPalette.ts b/clients/vscode/src/CommandPalette.ts new file mode 100644 index 00000000000..3eb78a9a0e7 --- /dev/null +++ b/clients/vscode/src/CommandPalette.ts @@ -0,0 +1,135 @@ +import { commands, QuickPick, QuickPickItem, QuickPickItemKind, ThemeIcon, window } from "vscode"; +import { State as LanguageClientState } from "vscode-languageclient"; +import { Client } from "./lsp/Client"; +import { Config } from "./Config"; +import { Issues } from "./Issues"; + +export default class CommandPalette { + quickPick: QuickPick; + + constructor( + private readonly client: Client, + private readonly config: Config, + private readonly issues: Issues, + ) { + this.quickPick = window.createQuickPick(); + this.quickPick.title = "Tabby Command Palette"; + + let items: CommandPaletteItem[] = [this.itemForStatus()]; + + if (this.client.chat.isAvailable) { + items.push({ + label: "Chat", + command: "tabby.chatView.focus", + iconPath: new ThemeIcon("comment"), + }); + } + + items = items.concat([ + { + label: "", + kind: QuickPickItemKind.Separator, + }, + { + label: this.config.inlineCompletionTriggerMode === "manual" ? "Enable Completions" : "Disable Completions", + command: "tabby.toggleInlineCompletionTriggerMode", + }, + { + label: "", + kind: QuickPickItemKind.Separator, + }, + { + label: "Set Credentials", + command: "tabby.setApiToken", + iconPath: new ThemeIcon("key"), + }, + { + label: "Settings...", + command: "tabby.openSettings", + iconPath: new ThemeIcon("extensions-manage"), + }, + { + label: "Agent Settings...", + command: "tabby.openTabbyAgentSettings", + iconPath: new ThemeIcon("console"), + }, + { + label: "Show Logs...", + command: "tabby.outputPanel.focus", + }, + { + label: "", + kind: QuickPickItemKind.Separator, + }, + { + label: "Help", + command: "tabby.openOnlineHelp", + iconPath: new ThemeIcon("question"), + }, + ]); + + this.quickPick.items = items; + this.quickPick.onDidAccept(this.onDidAccept, this); + this.quickPick.show(); + } + + onDidAccept() { + this.quickPick.hide(); + const item = this.quickPick.activeItems[0]; + if (item?.command) { + if (typeof item.command === "function") { + item.command(); + } else { + commands.executeCommand(item.command); + } + } + } + + private itemForStatus(): CommandPaletteItem { + const lspState = this.client.languageClient.state; + const agentStatus = this.client.agent.status; + const item: CommandPaletteItem = { + label: "Status", + iconPath: new ThemeIcon("warning"), + }; + if (lspState === LanguageClientState.Starting || agentStatus === "notInitialized") { + item.label = "Starting..."; + item.iconPath = new ThemeIcon("sync"); + } else if (lspState === LanguageClientState.Stopped || agentStatus === "finalized") { + item.label = "Disabled"; + item.iconPath = new ThemeIcon("circle-slash"); + } else if (agentStatus === "disconnected" || this.issues.first === "connectionFailed") { + item.label = "Disconnected"; + item.description = "Cannot connect to Tabby Server"; + item.command = "tabby.openSettings"; + } else if (agentStatus === "unauthorized") { + item.label = "Unauthorized"; + item.description = "Your credentials are invalid"; + item.command = "tabby.setApiToken"; + } else if (this.issues.length > 0) { + switch (this.issues.first) { + case "highCompletionTimeoutRate": + item.label = "Timeout"; + item.description = "Most completion requests timed out."; + break; + case "slowCompletionResponseTime": + item.label = "Slow Response"; + item.description = "Completion requests appear to take too much time."; + break; + } + item.command = () => this.issues.showHelpMessage(); + } else if (agentStatus === "ready") { + item.label = "Ready"; + item.iconPath = new ThemeIcon("check"); + item.command = "tabby.outputPanel.focus"; + } + + return item; + } +} + +interface CommandPaletteItem extends QuickPickItem { + command?: string | CallbackCommand; +} + +type CallbackCommand = () => void; diff --git a/clients/vscode/src/Commands.ts b/clients/vscode/src/Commands.ts index bd3fb539a55..01db9794a33 100644 --- a/clients/vscode/src/Commands.ts +++ b/clients/vscode/src/Commands.ts @@ -25,6 +25,9 @@ import { ContextVariables } from "./ContextVariables"; import { InlineCompletionProvider } from "./InlineCompletionProvider"; import { ChatViewProvider } from "./chat/ChatViewProvider"; import { GitProvider, Repository } from "./git/GitProvider"; +import CommandPalette from "./CommandPalette"; +import { showOutputPanel } from "./logger"; +import { Issues } from "./Issues"; export class Commands { private chatEditCancellationTokenSource: CancellationTokenSource | null = null; @@ -33,6 +36,7 @@ export class Commands { private readonly context: ExtensionContext, private readonly client: Client, private readonly config: Config, + private readonly issues: Issues, private readonly contextVariables: ContextVariables, private readonly inlineCompletionProvider: InlineCompletionProvider, private readonly chatViewProvider: ChatViewProvider, @@ -165,25 +169,20 @@ export class Commands { window .showQuickPick([ { - label: "Online Documentation", + label: "Website", iconPath: new ThemeIcon("book"), alwaysShow: true, - }, - { - label: "Model Registry", - description: "Explore more recommend models from Tabby's model registry", - iconPath: new ThemeIcon("library"), - alwaysShow: true, + description: "Visit Tabby's website to learn more about features and use cases", }, { label: "Tabby Slack Community", - description: "Join Tabby's Slack community to get help or feed back", + description: "Join Tabby's Slack community to get help or share feedback", iconPath: new ThemeIcon("comment-discussion"), alwaysShow: true, }, { label: "Tabby GitHub Repository", - description: "View the source code for Tabby, and open issues", + description: "Open issues for bugs or feature requests", iconPath: new ThemeIcon("github"), alwaysShow: true, }, @@ -191,12 +190,9 @@ export class Commands { .then((selection) => { if (selection) { switch (selection.label) { - case "Online Documentation": + case "Website": env.openExternal(Uri.parse("https://tabby.tabbyml.com/")); break; - case "Model Registry": - env.openExternal(Uri.parse("https://tabby.tabbyml.com/docs/models/")); - break; case "Tabby Slack Community": env.openExternal(Uri.parse("https://links.tabbyml.com/join-slack-extensions/")); break; @@ -213,6 +209,12 @@ export class Commands { gettingStarted: () => { commands.executeCommand("workbench.action.openWalkthrough", "TabbyML.vscode-tabby#gettingStarted"); }, + "commandPalette.trigger": () => { + new CommandPalette(this.client, this.config, this.issues); + }, + "outputPanel.focus": () => { + showOutputPanel(); + }, "inlineCompletion.trigger": () => { commands.executeCommand("editor.action.inlineSuggest.trigger"); }, diff --git a/clients/vscode/src/StatusBarItem.ts b/clients/vscode/src/StatusBarItem.ts index 72ead8b620f..3003c881707 100644 --- a/clients/vscode/src/StatusBarItem.ts +++ b/clients/vscode/src/StatusBarItem.ts @@ -39,6 +39,10 @@ export class StatusBarItem { ) { this.updateStatus(); this.item.show(); + this.item.command = { + title: "Show Tabby Command Palette", + command: "tabby.commandPalette.trigger", + }; this.context.subscriptions.push(this.item); this.client.languageClient.onDidChangeState(() => this.updateStatus()); @@ -100,11 +104,6 @@ export class StatusBarItem { this.item.backgroundColor = backgroundColorNormal; this.item.text = `${iconLoading} ${label}`; this.item.tooltip = "Tabby is initializing."; - this.item.command = { - title: "", - command: "tabby.applyCallback", - arguments: [() => this.showInformationWhenInitializing()], - }; } private toAutomatic() { @@ -116,11 +115,6 @@ export class StatusBarItem { 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: [() => this.showInformationWhenAutomaticTrigger()], - }; } private toManual() { @@ -132,11 +126,6 @@ export class StatusBarItem { 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: [() => this.showInformationWhenManualTrigger()], - }; } private toDisabled() { @@ -148,11 +137,6 @@ export class StatusBarItem { 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: [() => this.showInformationWhenInlineSuggestDisabled()], - }; this.showInformationWhenInlineSuggestDisabled(); } @@ -165,11 +149,6 @@ export class StatusBarItem { 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: [() => this.showInformationWhenLoading()], - }; } private toDisconnected() { @@ -181,11 +160,6 @@ export class StatusBarItem { 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: [() => this.issues.showHelpMessage("connectionFailed")], - }; } private toUnauthorized() { @@ -197,11 +171,6 @@ export class StatusBarItem { this.item.backgroundColor = backgroundColorWarning; this.item.text = `${iconUnauthorized} ${label}`; this.item.tooltip = "Tabby Server requires authorization. Please set your personal token."; - this.item.command = { - title: "", - command: "tabby.applyCallback", - arguments: [() => this.showInformationWhenUnauthorized()], - }; this.showInformationWhenUnauthorized(); } @@ -224,64 +193,6 @@ export class StatusBarItem { this.item.tooltip = ""; break; } - this.item.command = { - title: "", - command: "tabby.applyCallback", - arguments: [() => this.issues.showHelpMessage()], - }; - } - - private showInformationWhenInitializing() { - window.showInformationMessage("Tabby is initializing.", "Settings").then((selection) => { - switch (selection) { - case "Settings": - commands.executeCommand("tabby.openSettings"); - break; - } - }); - } - - private showInformationWhenAutomaticTrigger() { - window.showInformationMessage("Tabby automatic code completion is enabled.", "Settings").then((selection) => { - switch (selection) { - case "Settings": - commands.executeCommand("tabby.openSettings"); - break; - } - }); - } - - private showInformationWhenManualTrigger() { - window - .showInformationMessage( - "Tabby is standing by. Trigger code completion manually?", - "Trigger", - "Automatic Mode", - "Settings", - ) - .then((selection) => { - switch (selection) { - case "Trigger": - commands.executeCommand("editor.action.inlineSuggest.trigger"); - break; - case "Automatic Mode": - commands.executeCommand("tabby.toggleInlineCompletionTriggerMode", "automatic"); - break; - case "Settings": - commands.executeCommand("tabby.openSettings"); - break; - } - }); - } - - private showInformationWhenLoading() { - window.showInformationMessage("Tabby is generating code completions.", "Settings").then((selection) => { - switch (selection) { - case "Settings": - commands.executeCommand("tabby.openSettings"); - break; - } - }); } private showInformationWhenInlineSuggestDisabled() { diff --git a/clients/vscode/src/extension.ts b/clients/vscode/src/extension.ts index 343634639d1..b2df2a8f910 100644 --- a/clients/vscode/src/extension.ts +++ b/clients/vscode/src/extension.ts @@ -75,6 +75,7 @@ export async function activate(context: ExtensionContext) { context, client, config, + issues, contextVariables, inlineCompletionProvider, chatViewProvider, diff --git a/clients/vscode/src/logger.ts b/clients/vscode/src/logger.ts index 50842b862c9..58cb1e856b6 100644 --- a/clients/vscode/src/logger.ts +++ b/clients/vscode/src/logger.ts @@ -41,3 +41,7 @@ export function getLogger(tag = "Tabby"): LogOutputChannel { }, }); } + +export function showOutputPanel(): void { + outputChannel.show(); +}