diff --git a/clients/tabby-agent/src/chat/index.ts b/clients/tabby-agent/src/chat/index.ts index bfd31c74fc27..b8539ed6291e 100644 --- a/clients/tabby-agent/src/chat/index.ts +++ b/clients/tabby-agent/src/chat/index.ts @@ -1,11 +1,12 @@ -import type { Connection } from "vscode-languageserver"; +import type { Connection, Disposable } from "vscode-languageserver"; import type { ServerCapabilities } from "../protocol"; import type { Feature } from "../feature"; import type { TabbyApiClient } from "../http/tabbyApiClient"; -import { RegistrationRequest, UnregistrationRequest } from "vscode-languageserver"; -import { ChatFeatureRegistration } from "../protocol"; +import { ChatFeatures } from "../protocol"; export class ChatFeature implements Feature { + private featureRegistration: Disposable | undefined = undefined; + constructor(private readonly tabbyApiClient: TabbyApiClient) {} initialize(): ServerCapabilities { @@ -20,24 +21,11 @@ export class ChatFeature implements Feature { } private async syncFeatureRegistration(connection: Connection) { - if (this.tabbyApiClient.isChatApiAvailable()) { - connection.sendRequest(RegistrationRequest.type, { - registrations: [ - { - id: ChatFeatureRegistration.type.method, - method: ChatFeatureRegistration.type.method, - }, - ], - }); + if (this.tabbyApiClient.isChatApiAvailable() && !this.featureRegistration) { + this.featureRegistration = await connection.client.register(ChatFeatures.type); } else { - connection.sendRequest(UnregistrationRequest.type, { - unregisterations: [ - { - id: ChatFeatureRegistration.type.method, - method: ChatFeatureRegistration.type.method, - }, - ], - }); + this.featureRegistration?.dispose(); + this.featureRegistration = undefined; } } } diff --git a/clients/tabby-agent/src/codeCompletion/index.ts b/clients/tabby-agent/src/codeCompletion/index.ts index 3c203ec913ff..d77c41182bb1 100644 --- a/clients/tabby-agent/src/codeCompletion/index.ts +++ b/clients/tabby-agent/src/codeCompletion/index.ts @@ -1,6 +1,7 @@ import type { Connection, CancellationToken, + Disposable, Position, Range, Location, @@ -8,6 +9,7 @@ import type { NotebookDocument, NotebookCell, CompletionParams, + CompletionOptions, InlineCompletionParams, TextDocumentPositionParams, } from "vscode-languageserver"; @@ -20,8 +22,7 @@ import type { TabbyApiClient } from "../http/tabbyApiClient"; import type { GitContextProvider } from "../git"; import type { RecentlyChangedCodeSearch } from "../codeSearch/recentlyChanged"; import { - RegistrationRequest, - UnregistrationRequest, + CompletionRequest as LspCompletionRequest, CompletionTriggerKind, InlineCompletionTriggerKind, CompletionItemKind, @@ -29,8 +30,6 @@ import { import { ClientCapabilities, ServerCapabilities, - TextDocumentCompletionFeatureRegistration, - TextDocumentInlineCompletionFeatureRegistration, CompletionList, CompletionItem as LspCompletionItem, InlineCompletionRequest, @@ -71,6 +70,10 @@ export class CompletionProvider implements Feature { private lspConnection: Connection | undefined = undefined; private clientCapabilities: ClientCapabilities | undefined = undefined; + private completionFeatureOptions: CompletionOptions | undefined = undefined; + private completionFeatureRegistration: Disposable | undefined = undefined; + private inlineCompletionFeatureRegistration: Disposable | undefined = undefined; + private mutexAbortController: AbortController | undefined = undefined; constructor( @@ -93,19 +96,29 @@ export class CompletionProvider implements Feature { connection.onCompletion(async (params, token) => { return this.provideCompletion(params, token); }); - serverCapabilities = { - ...serverCapabilities, - completionProvider: {}, + this.completionFeatureOptions = { + resolveProvider: false, + completionItem: { + labelDetailsSupport: true, + }, }; + if (!clientCapabilities.textDocument?.completion.dynamicRegistration) { + serverCapabilities = { + ...serverCapabilities, + completionProvider: this.completionFeatureOptions, + }; + } } if (clientCapabilities.textDocument?.inlineCompletion) { connection.onRequest(InlineCompletionRequest.type, async (params, token) => { return this.provideInlineCompletion(params, token); }); - serverCapabilities = { - ...serverCapabilities, - inlineCompletionProvider: true, - }; + if (!clientCapabilities.textDocument?.inlineCompletion.dynamicRegistration) { + serverCapabilities = { + ...serverCapabilities, + inlineCompletionProvider: true, + }; + } } connection.onNotification(TelemetryEventNotification.type, async (param) => { return this.postEvent(param); @@ -128,47 +141,26 @@ export class CompletionProvider implements Feature { private async syncFeatureRegistration(connection: Connection) { if (this.tabbyApiClient.isCodeCompletionApiAvailable()) { - if (this.clientCapabilities?.textDocument?.completion) { - connection.sendRequest(RegistrationRequest.type, { - registrations: [ - { - id: TextDocumentCompletionFeatureRegistration.type.method, - method: TextDocumentCompletionFeatureRegistration.type.method, - }, - ], - }); + if ( + this.clientCapabilities?.textDocument?.completion?.dynamicRegistration && + !this.completionFeatureRegistration + ) { + this.completionFeatureRegistration = await connection.client.register( + LspCompletionRequest.type, + this.completionFeatureOptions, + ); } - if (this.clientCapabilities?.textDocument?.inlineCompletion) { - connection.sendRequest(RegistrationRequest.type, { - registrations: [ - { - id: TextDocumentInlineCompletionFeatureRegistration.type.method, - method: TextDocumentInlineCompletionFeatureRegistration.type.method, - }, - ], - }); + if ( + this.clientCapabilities?.textDocument?.inlineCompletion?.dynamicRegistration && + !this.inlineCompletionFeatureRegistration + ) { + this.inlineCompletionFeatureRegistration = await connection.client.register(InlineCompletionRequest.type); } } else { - if (this.clientCapabilities?.textDocument?.completion) { - connection.sendRequest(UnregistrationRequest.type, { - unregisterations: [ - { - id: TextDocumentCompletionFeatureRegistration.type.method, - method: TextDocumentCompletionFeatureRegistration.type.method, - }, - ], - }); - } - if (this.clientCapabilities?.textDocument?.inlineCompletion) { - connection.sendRequest(UnregistrationRequest.type, { - unregisterations: [ - { - id: TextDocumentInlineCompletionFeatureRegistration.type.method, - method: TextDocumentInlineCompletionFeatureRegistration.type.method, - }, - ], - }); - } + this.completionFeatureRegistration?.dispose(); + this.completionFeatureRegistration = undefined; + this.inlineCompletionFeatureRegistration?.dispose(); + this.inlineCompletionFeatureRegistration = undefined; } } diff --git a/clients/tabby-agent/src/protocol.ts b/clients/tabby-agent/src/protocol.ts index 9ac4b96912dd..147fc85f234f 100644 --- a/clients/tabby-agent/src/protocol.ts +++ b/clients/tabby-agent/src/protocol.ts @@ -174,16 +174,8 @@ export type ServerCapabilities = LspServerCapabilities & { }; }; -export namespace TextDocumentCompletionFeatureRegistration { - export const type = new RegistrationType("textDocument/completion"); -} - -export namespace TextDocumentInlineCompletionFeatureRegistration { - export const type = new RegistrationType("textDocument/inlineCompletion"); -} - -export namespace ChatFeatureRegistration { - export const type = new RegistrationType("tabby/chat"); +export namespace ChatFeatures { + export const type = new RegistrationType("tabby/chat"); } /** diff --git a/clients/vscode/src/lsp/ChatFeature.ts b/clients/vscode/src/lsp/ChatFeature.ts index ce147eccb03f..68cf38aa7a43 100644 --- a/clients/vscode/src/lsp/ChatFeature.ts +++ b/clients/vscode/src/lsp/ChatFeature.ts @@ -2,8 +2,7 @@ import { EventEmitter } from "events"; import { Disposable, CancellationToken } from "vscode"; import { BaseLanguageClient, DynamicFeature, FeatureState, RegistrationData } from "vscode-languageclient"; import { - ServerCapabilities, - ChatFeatureRegistration, + ChatFeatures, GenerateCommitMessageRequest, GenerateCommitMessageParams, GenerateCommitMessageResult, @@ -27,7 +26,7 @@ export class ChatFeature extends EventEmitter implements DynamicFeature super(); } - readonly registrationType = ChatFeatureRegistration.type; + readonly registrationType = ChatFeatures.type; getState(): FeatureState { return { kind: "workspace", id: this.registrationType.method, registrations: this.isAvailable }; @@ -45,10 +44,8 @@ export class ChatFeature extends EventEmitter implements DynamicFeature // nothing } - initialize(capabilities: ServerCapabilities): void { - if (capabilities.tabby?.chat) { - this.register({ id: this.registrationType.method, registerOptions: {} }); - } + initialize(): void { + // nothing } register(data: RegistrationData): void {