tabby/clients/vscode/src/TabbyCompletionProvider.ts

121 lines
4.0 KiB
TypeScript
Raw Normal View History

2023-03-28 07:53:57 +00:00
import {
CancellationToken,
InlineCompletionContext,
InlineCompletionItem,
InlineCompletionItemProvider,
InlineCompletionList,
InlineCompletionTriggerKind,
2023-03-28 07:53:57 +00:00
Position,
ProviderResult,
Range,
TextDocument,
workspace,
} from "vscode";
import { CompletionResponse, CancelablePromise } from "tabby-agent";
import { agent } from "./agent";
import { notifications } from "./notifications";
2023-03-28 07:53:57 +00:00
export class TabbyCompletionProvider implements InlineCompletionItemProvider {
private pendingCompletion: CancelablePromise<CompletionResponse> | null = null;
2023-03-28 07:53:57 +00:00
// User Settings
private enabled: boolean = true;
2023-03-28 07:53:57 +00:00
constructor() {
this.updateConfiguration();
workspace.onDidChangeConfiguration((event) => {
if (event.affectsConfiguration("tabby") || event.affectsConfiguration("editor.inlineSuggest")) {
2023-03-28 07:53:57 +00:00
this.updateConfiguration();
}
});
}
//@ts-ignore because ASYNC and PROMISE
//prettier-ignore
public async provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult<InlineCompletionItem[] | InlineCompletionList> {
const emptyResponse = Promise.resolve([] as InlineCompletionItem[]);
if (!this.enabled) {
console.debug("Extension not enabled, skipping.");
return emptyResponse;
}
// Check if autocomplete widget is visible
if (context.selectedCompletionInfo !== undefined) {
console.debug("Autocomplete widget is visible, skipping.");
return emptyResponse;
}
const replaceRange = this.calculateReplaceRange(document, position);
if (this.pendingCompletion) {
this.pendingCompletion.cancel();
}
const request = {
filepath: document.uri.fsPath,
language: document.languageId, // https://code.visualstudio.com/docs/languages/identifiers
text: document.getText(),
position: document.offsetAt(position),
manually: context.triggerKind === InlineCompletionTriggerKind.Invoke,
};
this.pendingCompletion = agent().provideCompletions(request);
const completion = await this.pendingCompletion.catch((e: Error) => {
return null;
});
this.pendingCompletion = null;
2023-03-28 07:53:57 +00:00
const completions = this.toInlineCompletions(completion, replaceRange);
2023-03-28 07:53:57 +00:00
return Promise.resolve(completions);
}
private updateConfiguration() {
const configuration = workspace.getConfiguration("tabby");
this.enabled = configuration.get("codeCompletion", true);
this.checkInlineCompletionEnabled();
}
private checkInlineCompletionEnabled() {
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();
}
2023-03-28 07:53:57 +00:00
}
private toInlineCompletions(tabbyCompletion: CompletionResponse | null, range: Range): InlineCompletionItem[] {
2023-03-28 07:53:57 +00:00
return (
tabbyCompletion?.choices?.map((choice: any) => {
let event = {
type: "select",
completion_id: tabbyCompletion.id,
choice_index: choice.index,
};
return new InlineCompletionItem(choice.text, range, {
title: "",
command: "tabby.emitEvent",
arguments: [event],
});
}) || []
2023-03-28 07:53:57 +00:00
);
}
private hasSuffixParen(document: TextDocument, position: Position) {
const suffix = document.getText(
new Range(position.line, position.character, position.line, position.character + 1),
2023-03-28 07:53:57 +00:00
);
return ")]}".indexOf(suffix) > -1;
}
// FIXME: move replace range calculation to tabby-agent
private calculateReplaceRange(document: TextDocument, position: Position): Range {
const hasSuffixParen = this.hasSuffixParen(document, position);
if (hasSuffixParen) {
return new Range(position.line, position.character, position.line, position.character + 1);
} else {
return new Range(position, position);
}
}
2023-03-28 07:53:57 +00:00
}