diff --git a/clients/vscode/.eslintrc.json b/clients/vscode/.eslintrc.json index cb74060..5dfecab 100644 --- a/clients/vscode/.eslintrc.json +++ b/clients/vscode/.eslintrc.json @@ -14,6 +14,5 @@ "no-throw-literal": "warn", "semi": "off" }, - "ignorePatterns": ["out", "dist", "**/*.d.ts"], - "extends": ["prettier"] + "ignorePatterns": ["out", "dist", "**/*.d.ts"] } diff --git a/clients/vscode/.prettierrc.json b/clients/vscode/.prettierrc.json index c42ca6e..963354f 100644 --- a/clients/vscode/.prettierrc.json +++ b/clients/vscode/.prettierrc.json @@ -1,3 +1,3 @@ -{ - "printWidth": 120 +{ + "printWidth": 120 } diff --git a/clients/vscode/.vscode/launch.json b/clients/vscode/.vscode/launch.json index 606591f..a6821e6 100644 --- a/clients/vscode/.vscode/launch.json +++ b/clients/vscode/.vscode/launch.json @@ -9,10 +9,7 @@ "name": "Run Extension", "type": "extensionHost", "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "--disable-extensions" - ], + "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--disable-extensions"], "outFiles": ["${workspaceFolder}/dist/**/*.js"], "preLaunchTask": "${defaultBuildTask}" }, @@ -29,6 +26,6 @@ ], "outFiles": ["${workspaceFolder}/dist/**/*.js"], "preLaunchTask": "${defaultBuildTask}" - }, + } ] } diff --git a/clients/vscode/.vscodeignore b/clients/vscode/.vscodeignore index fe3b4ed..a1c30ae 100644 --- a/clients/vscode/.vscodeignore +++ b/clients/vscode/.vscodeignore @@ -2,11 +2,13 @@ .vscode-test/** .vscode-test-web/** out/** -node_modules/** +**/node_modules/** src/** .gitignore +.gitattributes +.prettierrc.json +tsup.config.ts .yarnrc -webpack.config.js **/tsconfig.json **/.eslintrc.json **/*.map diff --git a/clients/vscode/assets/walkthroughs/codeCompletion.md b/clients/vscode/assets/walkthroughs/codeCompletion.md index c636579..18e4c2b 100644 --- a/clients/vscode/assets/walkthroughs/codeCompletion.md +++ b/clients/vscode/assets/walkthroughs/codeCompletion.md @@ -2,21 +2,20 @@ ## Basic Usage -Tabby will show inline suggestions when you stop typing. You can press the `Tab` key to accept the current suggestion. +Tabby will show inline suggestions when you stop typing. +You can accept press `Tab` to accept by line, `Ctrl +Tab` key to accept full suggestion, or `Ctrl + RightArrow` to accept single word. ![Demo](https://tabbyml.github.io/tabby/img/demo.gif) -## More Actions - -Hover over the inline suggestion text to see the toolbar providing more actions. - -![InlineCompletionToolbar](./toolbar.png) - -### Cycling Through Choices +## Cycling Through Choices When multiple choices are available, you can cycle through them by pressing `Alt + [` and `Alt + ]`. -### Partially Accept by Word or by Line +## Keybindings -If you want to partially accept a code suggestion, you can accept it by word or by line. Press `Ctrl + RightArrow` to accept next word. Click `...` button on the toolbar, and you will find the option to accept the suggestion by line. - +You can select a keybinding profile in the [settings](command:tabby.openSettings), or customize your own [keybindings](command:tabby.openKeybindings). + +| | Accept Full Completion | Accept Next Word | Accept Next Line | +| -------------: | :--------------------: | :---------------: | :--------------: | +| _tabby-style_ | Ctrl + Tab | Ctrl + RightArrow | Tab | +| _vscode-style_ | Tab | Ctrl + RightArrow | - | diff --git a/clients/vscode/assets/walkthroughs/server.md b/clients/vscode/assets/walkthroughs/server.md index 0019104..c91a8f8 100644 --- a/clients/vscode/assets/walkthroughs/server.md +++ b/clients/vscode/assets/walkthroughs/server.md @@ -4,8 +4,8 @@ Choose your preferred way to host your Tabby server, either through Tabby Cloud ## Tabby Cloud -You can get a Tabby Cloud account [here](https://app.tabbyml.com). Once you create or join a workspace, you will receive a URL for your Tabby server endpoint. -*Note*: Tabby Cloud is currently in beta. Join our [Slack community](https://join.slack.com/t/tabbycommunity/shared_invite/zt-1xeiddizp-bciR2RtFTaJ37RBxr8VxpA) and ask in Tabby Cloud channel to get a beta invite. +You can get a Tabby Cloud account [here](https://app.tabbyml.com). Once you create or join a workspace, you will receive a URL for your Tabby server endpoint. +**Note**: Tabby Cloud is currently in beta. Join our [Slack community](https://join.slack.com/t/tabbycommunity/shared_invite/zt-1xeiddizp-bciR2RtFTaJ37RBxr8VxpA) and ask in Tabby Cloud channel to get a beta invite. ## Self-Hosting diff --git a/clients/vscode/assets/walkthroughs/toolbar.png b/clients/vscode/assets/walkthroughs/toolbar.png deleted file mode 100644 index 2cb51fd..0000000 --- a/clients/vscode/assets/walkthroughs/toolbar.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:08f9f6cfa67831df25e5274878c9d6e64d193a89b8c3e267c3baea68b21ca043 -size 156320 diff --git a/clients/vscode/package.json b/clients/vscode/package.json index 5698902..f060da4 100644 --- a/clients/vscode/package.json +++ b/clients/vscode/package.json @@ -121,24 +121,61 @@ "default": true, "description": "Enable Tabby code completion or not." }, + "tabby.keybindings": { + "type": "string", + "enum": [ + "tabby-style", + "vscode-style" + ], + "default": "tabby-style", + "markdownDescription": "Select keybinding profile to accept current inline completion. \n | | Accept Full Completion | Accept Next Word | Accept Next Line | \n |---:|:--:|:--:|:--:| \n | _tabby-style_ | Ctrl + Tab | Ctrl + RightArrow | Tab | \n | _vscode-style_ | Tab | Ctrl + RightArrow | - | \n " + }, "tabby.usage.anonymousUsageTracking": { "type": "boolean", "default": false, "description": "Disable anonymous usage tracking." } } - } + }, + "keybindings": [ + { + "command": "tabby.inlineCompletion.accept", + "key": "tab", + "when": "config.tabby.codeCompletion && config.tabby.keybindings === 'vscode-style' && inlineSuggestionVisible && !editorReadonly && !editorTabMovesFocus && inlineSuggestionHasIndentationLessThanTabSize" + }, + { + "command": "tabby.inlineCompletion.acceptNextWord", + "key": "ctrl+right", + "when": "config.tabby.codeCompletion && config.tabby.keybindings === 'vscode-style' && inlineSuggestionVisible && !editorReadonly" + }, + { + "command": "tabby.inlineCompletion.accept", + "key": "ctrl+tab", + "when": "config.tabby.codeCompletion && config.tabby.keybindings === 'tabby-style' && inlineSuggestionVisible && !editorReadonly" + }, + { + "command": "tabby.inlineCompletion.acceptNextWord", + "key": "ctrl+right", + "when": "config.tabby.codeCompletion && config.tabby.keybindings === 'tabby-style' && inlineSuggestionVisible && !editorReadonly" + }, + { + "command": "tabby.inlineCompletion.acceptNextLine", + "key": "tab", + "when": "config.tabby.codeCompletion && config.tabby.keybindings === 'tabby-style' && inlineSuggestionVisible && !editorReadonly && !editorTabMovesFocus" + } + ] }, "scripts": { + "prebuild": "cd ../tabby-agent && yarn build", "build": "tsup --minify --treeshake smallest", "watch": "tsup --sourcemap --watch ./ --ignore-watch ./dist --watch ../tabby-agent/dist", "dev": "code --extensionDevelopmentPath=$PWD --disable-extensions && yarn watch", "dev:browser": "vscode-test-web --extensionDevelopmentPath=$PWD --browserType=chromium --port=3000 && yarn watch", - "lint": "eslint . --fix", + "lint": "prettier --write .", "vscode:prepackage": "yarn build", - "vscode:package": "vsce package", + "vscode:package": "vsce package --no-dependencies", "vscode:prepublish": "yarn build", - "vscode:publish": "vsce publish" + "vscode:publish": "vsce publish --no-dependencies" }, "devDependencies": { "@types/glob": "^7.2.0", @@ -153,7 +190,6 @@ "assert": "^2.0.0", "esbuild-plugin-polyfill-node": "^0.3.0", "eslint": "^8.20.0", - "eslint-config-prettier": "^8.8.0", "glob": "^8.0.3", "prettier": "^3.0.0", "tsup": "^7.1.0", diff --git a/clients/vscode/src/TabbyCompletionProvider.ts b/clients/vscode/src/TabbyCompletionProvider.ts index d91d4ac..c4742f8 100644 --- a/clients/vscode/src/TabbyCompletionProvider.ts +++ b/clients/vscode/src/TabbyCompletionProvider.ts @@ -102,7 +102,7 @@ export class TabbyCompletionProvider implements InlineCompletionItemProvider { private hasSuffixParen(document: TextDocument, position: Position) { const suffix = document.getText( - new Range(position.line, position.character, position.line, position.character + 1) + new Range(position.line, position.character, position.line, position.character + 1), ); return ")]}".indexOf(suffix) > -1; } diff --git a/clients/vscode/src/commands.ts b/clients/vscode/src/commands.ts index 0ebe3f4..1960a5c 100644 --- a/clients/vscode/src/commands.ts +++ b/clients/vscode/src/commands.ts @@ -84,11 +84,18 @@ const openTabbyAgentSettings: Command = { }, () => { window.showWarningMessage("Tabby Agent config file not found.", { modal: true }); - } + }, ); }, }; +const openKeybindings: Command = { + command: "tabby.openKeybindings", + callback: () => { + commands.executeCommand("workbench.action.openGlobalKeybindings", "tabby.inlineCompletion"); + }, +}; + const gettingStarted: Command = { command: "tabby.gettingStarted", callback: () => { @@ -138,13 +145,15 @@ const openAuthPage: Command = { notifications.showInformationWhenAuthFailed(); } } catch (error: any) { - if (error.isCancelled) return; + if (error.isCancelled) { + return; + } console.debug("Error auth", { error }); notifications.showInformationWhenAuthFailed(); } finally { callbacks?.onAuthEnd?.(); } - } + }, ); }, }; @@ -172,14 +181,41 @@ const statusBarItemClicked: Command = { }, }; +const acceptInlineCompletion: Command = { + command: "tabby.inlineCompletion.accept", + callback: () => { + commands.executeCommand("editor.action.inlineSuggest.commit"); + }, +}; + +const acceptInlineCompletionNextWord: Command = { + command: "tabby.inlineCompletion.acceptNextWord", + callback: () => { + // FIXME: sent event when partially accept? + commands.executeCommand("editor.action.inlineSuggest.acceptNextWord"); + }, +}; + +const acceptInlineCompletionNextLine: Command = { + command: "tabby.inlineCompletion.acceptNextLine", + callback: () => { + // FIXME: sent event when partially accept? + commands.executeCommand("editor.action.inlineSuggest.acceptNextLine"); + }, +}; + export const tabbyCommands = () => [ toggleEnabled, setApiEndpoint, openSettings, openTabbyAgentSettings, + openKeybindings, gettingStarted, emitEvent, openAuthPage, statusBarItemClicked, + acceptInlineCompletion, + acceptInlineCompletionNextWord, + acceptInlineCompletionNextLine, ].map((command) => commands.registerCommand(command.command, command.callback, command.thisArg)); diff --git a/clients/vscode/src/extension.ts b/clients/vscode/src/extension.ts index 9bc8030..7c8cc62 100644 --- a/clients/vscode/src/extension.ts +++ b/clients/vscode/src/extension.ts @@ -12,10 +12,7 @@ export async function activate(context: ExtensionContext) { console.debug("Activating Tabby extension", new Date()); await createAgentInstance(context); context.subscriptions.push( - languages.registerInlineCompletionItemProvider( - { pattern: "**" }, - new TabbyCompletionProvider() - ), + languages.registerInlineCompletionItemProvider({ pattern: "**" }, new TabbyCompletionProvider()), tabbyStatusBarItem(), ...tabbyCommands(), ); diff --git a/clients/vscode/src/notifications.ts b/clients/vscode/src/notifications.ts index da405c6..e039219 100644 --- a/clients/vscode/src/notifications.ts +++ b/clients/vscode/src/notifications.ts @@ -55,7 +55,7 @@ function showInformationStartAuth(callbacks?: { onAuthStart?: () => void; onAuth .showWarningMessage( "Tabby Server requires authorization. Continue to open authorization page in your browser.", "Continue", - "Settings" + "Settings", ) .then((selection) => { switch (selection) { diff --git a/yarn.lock b/yarn.lock index 3466a13..9324d2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1481,11 +1481,6 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -eslint-config-prettier@^8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" - integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== - eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"