diff --git a/clients/vscode/package.json b/clients/vscode/package.json index 2688c7a..11c59e6 100644 --- a/clients/vscode/package.json +++ b/clients/vscode/package.json @@ -53,12 +53,15 @@ "tabby.serverUrl": { "type": "string", "default": "http://127.0.0.1:5000", + "pattern": "^https?:\\/\\/[^\\s]+$", + "patternErrorMessage": "Please enter a validate http or https URL.", "markdownDescription": "Specifies the url of [Tabby Server](https://github.com/TabbyML/tabby)." }, "tabby.suggestionDelay": { "type": "number", "default": 150, - "markdownDescription": "Specifies the delay after which the request is sent to the tabby(ms)." + "minimum": 0, + "description": "Specifies the delay in milliseconds after which the request is sent to the tabby." } } } @@ -99,6 +102,7 @@ "webpack-cli": "^4.10.0" }, "dependencies": { + "@sapphire/duration": "^1.1.0", "assert": "^2.0.0", "axios": "^1.3.4", "events": "^3.3.0", diff --git a/clients/vscode/src/Commands.ts b/clients/vscode/src/Commands.ts index 61050d3..c504d74 100644 --- a/clients/vscode/src/Commands.ts +++ b/clients/vscode/src/Commands.ts @@ -1,6 +1,16 @@ -import { ConfigurationTarget, workspace, window, commands } from "vscode"; +import { + ConfigurationTarget, + InputBoxValidationSeverity, + QuickPickItem, + QuickPickItemKind, + workspace, + window, + commands, +} from "vscode"; +import { Duration } from "@sapphire/duration"; import { ChoiceEvent, ApiError } from "./generated"; import { TabbyClient } from "./TabbyClient"; +import { strict as assert } from "node:assert"; const target = ConfigurationTarget.Global; @@ -24,26 +34,60 @@ const setSuggestionDelay: Command = { command: "tabby.setSuggestionDelay", callback: () => { const configuration = workspace.getConfiguration("tabby"); - window - .showInputBox({ - prompt: "Enter the suggestion delay in ms", - value: configuration.get("suggestionDelay", "150"), - }) - .then((delay) => { - if (delay) { - if (Number.parseInt(delay) !== null) { - console.debug("Set suggestion delay: ", Number.parseInt(delay)); - configuration.update( - "suggestionDelay", - Number.parseInt(delay), - target, - false - ); - } else { - console.debug("Set suggestion delay error. Wrong input."); - } + const current = configuration.get("suggestionDelay", 150); + const items = { + "Immediately": 0, // ms + "Default": 150, + "Slowly": 1000, + }; + const createQuickPickItem = (value: number): QuickPickItem => { + const tags: string[] = []; + if (value == current) { + tags.push("Current"); + } + Object.entries(items).forEach(([k, v]) => { + if (v == value) { + tags.push(k); } }); + return { + label: value % 1000 == 0 ? `${value / 1000}s` : `${value}ms`, + description: tags.join(" "), + alwaysShow: true, + }; + }; + const buildQuickPickList = (input: string = "") => { + const list: QuickPickItem[] = []; + const customized = new Duration(input).offset || Number.parseInt(input); + if (customized >= 0) { + list.push(createQuickPickItem(customized)); + } + if (current != customized) { + list.push(createQuickPickItem(current)); + } + list.push({ + label: "", + kind: QuickPickItemKind.Separator, + }); + Object.values(items) + .filter((item) => item != current && item != customized) + .forEach((item) => list.push(createQuickPickItem(item))); + return list; + }; + const quickPick = window.createQuickPick(); + quickPick.placeholder = "Enter the delay after which the completion request is sent"; + quickPick.matchOnDescription = true; + quickPick.items = buildQuickPickList(); + quickPick.onDidChangeValue((input: string) => { + quickPick.items = buildQuickPickList(input); + }); + quickPick.onDidAccept(() => { + quickPick.hide(); + const delay = new Duration(quickPick.selectedItems[0].label).offset; + console.debug("Set suggestion delay: ", delay); + configuration.update("suggestionDelay", delay, target, false); + }); + quickPick.show(); }, }; @@ -55,6 +99,18 @@ const setServerUrl: Command = { .showInputBox({ prompt: "Enter the URL of your Tabby Server", value: configuration.get("serverUrl", ""), + validateInput: (input: string) => { + try { + let url = new URL(input); + assert(url.protocol == "http:" || url.protocol == "https:"); + } catch (_) { + return { + message: "Please enter a validate http or https URL.", + severity: InputBoxValidationSeverity.Error, + }; + } + return null; + }, }) .then((url) => { if (url) { @@ -77,12 +133,15 @@ const emitEvent: Command = { command: "tabby.emitEvent", callback: (event: ChoiceEvent) => { console.debug("Emit Event: ", event); - tabbyClient.api.default.eventsV1EventsPost(event).then(() => { - tabbyClient.changeStatus("ready"); - }).catch((err: ApiError) => { - console.error(err); - tabbyClient.changeStatus("disconnected"); - }); + tabbyClient.api.default + .eventsV1EventsPost(event) + .then(() => { + tabbyClient.changeStatus("ready"); + }) + .catch((err: ApiError) => { + console.error(err); + tabbyClient.changeStatus("disconnected"); + }); }, }; diff --git a/clients/vscode/yarn.lock b/clients/vscode/yarn.lock index 97b3964..f7c48fa 100644 --- a/clients/vscode/yarn.lock +++ b/clients/vscode/yarn.lock @@ -134,6 +134,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@sapphire/duration@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@sapphire/duration/-/duration-1.1.0.tgz#941d93585c6dd434d4d3416c28d2eb20d936db19" + integrity sha512-ATb2pWPLcSgG7bzvT6MglUcDexFSufr2FLXUmhipWGFtZbvDhkopGBIuHyzoGy7LZvL8UY5T6pRLNdFv5pl/Lg== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.npmmirror.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"