feat(vscode): add keybindings for accepting inline completions. (#297)

* feat(vscode): add keybindings for accepting inline completions.

* fix: set vscode keybinding profile to tabby-style.

* fix: update vscode keybindings documentation.

* docs: update vscode keybindings documentation.
sweep/improve-logging-information
Zhiming Ma 2023-07-14 14:54:24 +08:00 committed by GitHub
parent be5fe0d737
commit 1878644778
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 104 additions and 46 deletions

View File

@ -14,6 +14,5 @@
"no-throw-literal": "warn", "no-throw-literal": "warn",
"semi": "off" "semi": "off"
}, },
"ignorePatterns": ["out", "dist", "**/*.d.ts"], "ignorePatterns": ["out", "dist", "**/*.d.ts"]
"extends": ["prettier"]
} }

View File

@ -1,3 +1,3 @@
{ {
"printWidth": 120 "printWidth": 120
} }

View File

@ -9,10 +9,7 @@
"name": "Run Extension", "name": "Run Extension",
"type": "extensionHost", "type": "extensionHost",
"request": "launch", "request": "launch",
"args": [ "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--disable-extensions"],
"--extensionDevelopmentPath=${workspaceFolder}",
"--disable-extensions"
],
"outFiles": ["${workspaceFolder}/dist/**/*.js"], "outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "${defaultBuildTask}" "preLaunchTask": "${defaultBuildTask}"
}, },
@ -29,6 +26,6 @@
], ],
"outFiles": ["${workspaceFolder}/dist/**/*.js"], "outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "${defaultBuildTask}" "preLaunchTask": "${defaultBuildTask}"
}, }
] ]
} }

View File

@ -2,11 +2,13 @@
.vscode-test/** .vscode-test/**
.vscode-test-web/** .vscode-test-web/**
out/** out/**
node_modules/** **/node_modules/**
src/** src/**
.gitignore .gitignore
.gitattributes
.prettierrc.json
tsup.config.ts
.yarnrc .yarnrc
webpack.config.js
**/tsconfig.json **/tsconfig.json
**/.eslintrc.json **/.eslintrc.json
**/*.map **/*.map

View File

@ -2,21 +2,20 @@
## Basic Usage ## 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) ![Demo](https://tabbyml.github.io/tabby/img/demo.gif)
## More Actions ## Cycling Through Choices
Hover over the inline suggestion text to see the toolbar providing more actions.
![InlineCompletionToolbar](./toolbar.png)
### 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 + ]`.
### 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 | - |

View File

@ -4,8 +4,8 @@ Choose your preferred way to host your Tabby server, either through Tabby Cloud
## 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. 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. **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 ## Self-Hosting

View File

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:08f9f6cfa67831df25e5274878c9d6e64d193a89b8c3e267c3baea68b21ca043
size 156320

View File

@ -121,24 +121,61 @@
"default": true, "default": true,
"description": "Enable Tabby code completion or not." "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": { "tabby.usage.anonymousUsageTracking": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Disable anonymous usage tracking." "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": { "scripts": {
"prebuild": "cd ../tabby-agent && yarn build",
"build": "tsup --minify --treeshake smallest", "build": "tsup --minify --treeshake smallest",
"watch": "tsup --sourcemap --watch ./ --ignore-watch ./dist --watch ../tabby-agent/dist", "watch": "tsup --sourcemap --watch ./ --ignore-watch ./dist --watch ../tabby-agent/dist",
"dev": "code --extensionDevelopmentPath=$PWD --disable-extensions && yarn watch", "dev": "code --extensionDevelopmentPath=$PWD --disable-extensions && yarn watch",
"dev:browser": "vscode-test-web --extensionDevelopmentPath=$PWD --browserType=chromium --port=3000 && 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:prepackage": "yarn build",
"vscode:package": "vsce package", "vscode:package": "vsce package --no-dependencies",
"vscode:prepublish": "yarn build", "vscode:prepublish": "yarn build",
"vscode:publish": "vsce publish" "vscode:publish": "vsce publish --no-dependencies"
}, },
"devDependencies": { "devDependencies": {
"@types/glob": "^7.2.0", "@types/glob": "^7.2.0",
@ -153,7 +190,6 @@
"assert": "^2.0.0", "assert": "^2.0.0",
"esbuild-plugin-polyfill-node": "^0.3.0", "esbuild-plugin-polyfill-node": "^0.3.0",
"eslint": "^8.20.0", "eslint": "^8.20.0",
"eslint-config-prettier": "^8.8.0",
"glob": "^8.0.3", "glob": "^8.0.3",
"prettier": "^3.0.0", "prettier": "^3.0.0",
"tsup": "^7.1.0", "tsup": "^7.1.0",

View File

@ -102,7 +102,7 @@ export class TabbyCompletionProvider implements InlineCompletionItemProvider {
private hasSuffixParen(document: TextDocument, position: Position) { private hasSuffixParen(document: TextDocument, position: Position) {
const suffix = document.getText( 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; return ")]}".indexOf(suffix) > -1;
} }

View File

@ -84,11 +84,18 @@ const openTabbyAgentSettings: Command = {
}, },
() => { () => {
window.showWarningMessage("Tabby Agent config file not found.", { modal: true }); 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 = { const gettingStarted: Command = {
command: "tabby.gettingStarted", command: "tabby.gettingStarted",
callback: () => { callback: () => {
@ -138,13 +145,15 @@ const openAuthPage: Command = {
notifications.showInformationWhenAuthFailed(); notifications.showInformationWhenAuthFailed();
} }
} catch (error: any) { } catch (error: any) {
if (error.isCancelled) return; if (error.isCancelled) {
return;
}
console.debug("Error auth", { error }); console.debug("Error auth", { error });
notifications.showInformationWhenAuthFailed(); notifications.showInformationWhenAuthFailed();
} finally { } finally {
callbacks?.onAuthEnd?.(); 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 = () => export const tabbyCommands = () =>
[ [
toggleEnabled, toggleEnabled,
setApiEndpoint, setApiEndpoint,
openSettings, openSettings,
openTabbyAgentSettings, openTabbyAgentSettings,
openKeybindings,
gettingStarted, gettingStarted,
emitEvent, emitEvent,
openAuthPage, openAuthPage,
statusBarItemClicked, statusBarItemClicked,
acceptInlineCompletion,
acceptInlineCompletionNextWord,
acceptInlineCompletionNextLine,
].map((command) => commands.registerCommand(command.command, command.callback, command.thisArg)); ].map((command) => commands.registerCommand(command.command, command.callback, command.thisArg));

View File

@ -12,10 +12,7 @@ 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);
context.subscriptions.push( context.subscriptions.push(
languages.registerInlineCompletionItemProvider( languages.registerInlineCompletionItemProvider({ pattern: "**" }, new TabbyCompletionProvider()),
{ pattern: "**" },
new TabbyCompletionProvider()
),
tabbyStatusBarItem(), tabbyStatusBarItem(),
...tabbyCommands(), ...tabbyCommands(),
); );

View File

@ -55,7 +55,7 @@ function showInformationStartAuth(callbacks?: { onAuthStart?: () => void; onAuth
.showWarningMessage( .showWarningMessage(
"Tabby Server requires authorization. Continue to open authorization page in your browser.", "Tabby Server requires authorization. Continue to open authorization page in your browser.",
"Continue", "Continue",
"Settings" "Settings",
) )
.then((selection) => { .then((selection) => {
switch (selection) { switch (selection) {

View File

@ -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" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== 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: eslint-scope@^5.1.1:
version "5.1.1" version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"