diff --git a/clients/tabby-agent/src/chat/inlineEdit.ts b/clients/tabby-agent/src/chat/inlineEdit.ts index 5194a0b21830..3438d76de832 100644 --- a/clients/tabby-agent/src/chat/inlineEdit.ts +++ b/clients/tabby-agent/src/chat/inlineEdit.ts @@ -1,4 +1,4 @@ -import type { Range, Location, Connection, CancellationToken } from "vscode-languageserver"; +import type { Connection, CancellationToken } from "vscode-languageserver"; import type { TextDocument } from "vscode-languageserver-textdocument"; import type { TextDocuments } from "../lsp/textDocuments"; import type { Feature } from "../feature"; @@ -20,19 +20,7 @@ import { } from "../protocol"; import cryptoRandomString from "crypto-random-string"; import { isEmptyRange } from "../utils/range"; -import { applyWorkspaceEdit, readResponseStream } from "./utils"; - -export type Edit = { - id: ChatEditToken; - location: Location; - languageId: string; - originalText: string; - editedRange: Range; - editedText: string; - comments: string; - buffer: string; - state: "editing" | "stopped" | "completed"; -}; +import { applyWorkspaceEdit, readResponseStream, Edit } from "./utils"; export class ChatEditProvider implements Feature { private lspConnection: Connection | undefined = undefined; diff --git a/clients/tabby-agent/src/chat/SmartApply.ts b/clients/tabby-agent/src/chat/smartApply.ts similarity index 87% rename from clients/tabby-agent/src/chat/SmartApply.ts rename to clients/tabby-agent/src/chat/smartApply.ts index 509ce22b3804..5947c9255e8c 100644 --- a/clients/tabby-agent/src/chat/SmartApply.ts +++ b/clients/tabby-agent/src/chat/smartApply.ts @@ -1,3 +1,4 @@ +import { TextDocument } from "vscode-languageserver-textdocument"; import { CancellationToken, Connection, @@ -12,18 +13,17 @@ import { ChatEditMutexError, ChatFeatureNotAvailableError, ServerCapabilities, - SmartApplyCodeRequest, - SmartApplyCodeParams, + SmartApplyRequest, + SmartApplyParams, } from "../protocol"; import { Configurations } from "../config"; import { TabbyApiClient } from "../http/tabbyApiClient"; import cryptoRandomString from "crypto-random-string"; import { getLogger } from "../logger"; -import { readResponseStream, showDocument } from "./utils"; -import { TextDocument } from "vscode-languageserver-textdocument"; -import { getSmartApplyRange } from "./SmartRange"; -import { Edit } from "./inlineEdit"; -const logger = getLogger("ChatEditProvider"); +import { readResponseStream, showDocument, Edit } from "./utils"; +import { getSmartApplyRange } from "./smartRange"; + +const logger = getLogger("SmartApplyFeature"); export class SmartApplyFeature implements Feature { private lspConnection: Connection | undefined = undefined; @@ -36,7 +36,7 @@ export class SmartApplyFeature implements Feature { initialize(connection: Connection): ServerCapabilities | Promise { this.lspConnection = connection; - connection.onRequest(SmartApplyCodeRequest.type, async (params, token) => { + connection.onRequest(SmartApplyRequest.type, async (params, token) => { return this.provideSmartApplyEdit(params, token); }); @@ -49,15 +49,15 @@ export class SmartApplyFeature implements Feature { //nothing } - async provideSmartApplyEdit(params: SmartApplyCodeParams, token: CancellationToken): Promise { - logger.info("Getting document"); + async provideSmartApplyEdit(params: SmartApplyParams, token: CancellationToken): Promise { + logger.debug("Getting document"); const document = this.documents.get(params.location.uri); if (!document) { - logger.info("Document not found, returning false"); + logger.debug("Document not found, returning false"); return false; } if (!this.lspConnection) { - logger.info("LSP connection lost."); + logger.debug("LSP connection lost."); return false; } @@ -69,21 +69,16 @@ export class SmartApplyFeature implements Feature { } as ChatEditMutexError; } this.mutexAbortController = new AbortController(); - logger.info("mutex abort status: " + (this.mutexAbortController === undefined)); + logger.debug("mutex abort status: " + (this.mutexAbortController === undefined)); token.onCancellationRequested(() => this.mutexAbortController?.abort()); - let applyRange = getSmartApplyRange(document, params.applyCode); + let applyRange = getSmartApplyRange(document, params.text); //if cannot find range, lets use backend LLMs if (!applyRange) { if (!this.tabbyApiClient.isChatApiAvailable) { return false; } - applyRange = await provideSmartApplyLineRange( - document, - params.applyCode, - this.tabbyApiClient, - this.configurations, - ); + applyRange = await provideSmartApplyLineRange(document, params.text, this.tabbyApiClient, this.configurations); } if (!applyRange) { @@ -114,7 +109,7 @@ export class SmartApplyFeature implements Feature { end: { line: applyRange.range.end.line + 1, character: 0 }, }, }, - params.applyCode, + params.text, applyRange.action === "insert" ? true : false, document, this.lspConnection, @@ -130,13 +125,13 @@ export class SmartApplyFeature implements Feature { logger.error("Error applying smart edit:", error); return false; } finally { - logger.info("Resetting mutex abort controller"); + logger.debug("Resetting mutex abort controller"); this.mutexAbortController = undefined; } } } -export async function provideSmartApplyLineRange( +async function provideSmartApplyLineRange( document: TextDocument, applyCodeBlock: string, tabbyApiClient: TabbyApiClient, @@ -159,7 +154,7 @@ export async function provideSmartApplyLineRange( .join("\n"); const config = configurations.getMergedConfig(); - const promptTemplate = config.chat.provideSmartApplyLineRange.promptTemplate; + const promptTemplate = config.chat.smartApplyLineRange.promptTemplate; const messages: { role: "user"; content: string }[] = [ { @@ -219,7 +214,7 @@ export async function provideSmartApplyLineRange( } } -export async function provideSmartApplyEditLLM( +async function provideSmartApplyEditLLM( location: Location, applyCode: string, insertMode: boolean, @@ -254,13 +249,13 @@ export async function provideSmartApplyEditLLM( }; const selectedDocumentText = documentText.substring(selection.start, selection.end); - logger.info("current selectedDoc: " + selectedDocumentText); + logger.debug("current selectedDoc: " + selectedDocumentText); if (selection.end - selection.start > config.chat.edit.documentMaxChars) { throw { name: "ChatEditDocumentTooLongError", message: "Document too long" } as ChatEditDocumentTooLongError; } - const promptTemplate = config.chat.provideSmartApply.promptTemplate; + const promptTemplate = config.chat.smartApply.promptTemplate; // Extract the selected text and the surrounding context let documentPrefix = documentText.substring(0, selection.start); diff --git a/clients/tabby-agent/src/chat/SmartRange.ts b/clients/tabby-agent/src/chat/smartRange.ts similarity index 92% rename from clients/tabby-agent/src/chat/SmartRange.ts rename to clients/tabby-agent/src/chat/smartRange.ts index 58adaffcb9d4..8471bf75b8b5 100644 --- a/clients/tabby-agent/src/chat/SmartRange.ts +++ b/clients/tabby-agent/src/chat/smartRange.ts @@ -18,7 +18,7 @@ export function getSmartApplyRange( return { range: applyRange.range, action: "replace" }; } -export function fuzzyApplyRange(document: TextDocument, snippet: string): { range: Range; score: number } | null { +function fuzzyApplyRange(document: TextDocument, snippet: string): { range: Range; score: number } | null { const lines = document.getText().split("\n"); const snippetLines = snippet.split("\n"); diff --git a/clients/tabby-agent/src/chat/utils.ts b/clients/tabby-agent/src/chat/utils.ts index 2aa8286102d9..4fedbbd4006c 100644 --- a/clients/tabby-agent/src/chat/utils.ts +++ b/clients/tabby-agent/src/chat/utils.ts @@ -1,12 +1,29 @@ //chat related utils functions import { Readable } from "stream"; -import { Edit } from "./inlineEdit"; -import { ShowDocumentParams, ShowDocumentRequest, WorkspaceEdit } from "vscode-languageserver-protocol"; -import * as Diff from "diff"; -import { isBlank } from "../utils/string"; +import { + Range, + Location, + ShowDocumentParams, + ShowDocumentRequest, + WorkspaceEdit, +} from "vscode-languageserver-protocol"; import { Connection } from "vscode-languageserver"; +import * as Diff from "diff"; import { ApplyWorkspaceEditParams, ApplyWorkspaceEditRequest } from "../protocol"; +import { isBlank } from "../utils/string"; + +export type Edit = { + id: string; + location: Location; + languageId: string; + originalText: string; + editedRange: Range; + editedText: string; + comments: string; + buffer: string; + state: "editing" | "stopped" | "completed"; +}; export async function readResponseStream( stream: Readable, diff --git a/clients/tabby-agent/src/config/default.ts b/clients/tabby-agent/src/config/default.ts index 1932de502e69..79d3758f632f 100644 --- a/clients/tabby-agent/src/config/default.ts +++ b/clients/tabby-agent/src/config/default.ts @@ -95,10 +95,10 @@ export const defaultConfigData: ConfigData = { responseMatcher: /(?<=(["'`]+)?\s*)(feat|fix|docs|refactor|style|test|build|ci|chore)(\(\S+\))?:.+(?=\s*\1)/gis.toString(), }, - provideSmartApplyLineRange: { + smartApplyLineRange: { promptTemplate: provideSmartApplyLineRangePrompt, }, - provideSmartApply: { + smartApply: { promptTemplate: generateSmartApplyPrompt, }, }, diff --git a/clients/tabby-agent/src/config/type.d.ts b/clients/tabby-agent/src/config/type.d.ts index 48cd448e74e9..c5028e85a7f4 100644 --- a/clients/tabby-agent/src/config/type.d.ts +++ b/clients/tabby-agent/src/config/type.d.ts @@ -99,10 +99,10 @@ export type ConfigData = { promptTemplate: string; responseMatcher: string; }; - provideSmartApplyLineRange: { + smartApplyLineRange: { promptTemplate: string; }; - provideSmartApply: { + smartApply: { promptTemplate: string; }; }; diff --git a/clients/tabby-agent/src/protocol.ts b/clients/tabby-agent/src/protocol.ts index 11b83caec788..9ac4b96912dd 100644 --- a/clients/tabby-agent/src/protocol.ts +++ b/clients/tabby-agent/src/protocol.ts @@ -527,34 +527,33 @@ export type ChatEditResolveCommand = LspCommand & { }; /** - * [Tabby] Chat Edit Request(↩️) + * [Tabby] Smart Apply Request(↩️) * - * This method is sent from the client to the server to edit the document content by user's command. + * This method is sent from the client to the server to smart apply the text to the target location. * The server will edit the document content using ApplyEdit(`workspace/applyEdit`) request, * which requires the client to have this capability. - * - method: `tabby/chat/edit` - * - params: {@link SmartApplyCodeParams} + * - method: `tabby/chat/smartApply` + * - params: {@link SmartApplyParams} * - result: boolean * - error: {@link ChatFeatureNotAvailableError} * | {@link ChatEditDocumentTooLongError} - * | {@link ChatEditCommandTooLongError} * | {@link ChatEditMutexError} */ -export namespace SmartApplyCodeRequest { - export const method = "tabby/chat/smartApply/apply"; +export namespace SmartApplyRequest { + export const method = "tabby/chat/smartApply"; export const messageDirection = MessageDirection.clientToServer; export const type = new ProtocolRequestType< - SmartApplyCodeParams, + SmartApplyParams, boolean, void, - ChatFeatureNotAvailableError | ChatEditDocumentTooLongError | ChatEditCommandTooLongError | ChatEditMutexError, + ChatFeatureNotAvailableError | ChatEditDocumentTooLongError | ChatEditMutexError, void >(method); } -export type SmartApplyCodeParams = { +export type SmartApplyParams = { location: Location; - applyCode: string; + text: string; }; /** diff --git a/clients/tabby-agent/src/server.ts b/clients/tabby-agent/src/server.ts index 755984faa00b..0acc20a01ffb 100644 --- a/clients/tabby-agent/src/server.ts +++ b/clients/tabby-agent/src/server.ts @@ -47,7 +47,7 @@ import { StatusProvider } from "./status"; import { CommandProvider } from "./command"; import { name as serverName, version as serverVersion } from "../package.json"; import "./utils/array"; -import { SmartApplyFeature } from "./chat/SmartApply"; +import { SmartApplyFeature } from "./chat/smartApply"; import { FileTracker } from "./codeSearch/fileTracker"; export class Server { diff --git a/clients/tabby-chat-panel/package.json b/clients/tabby-chat-panel/package.json index 2a8e6b54b04c..d07e9ea118dc 100644 --- a/clients/tabby-chat-panel/package.json +++ b/clients/tabby-chat-panel/package.json @@ -1,7 +1,7 @@ { "name": "tabby-chat-panel", "type": "module", - "version": "0.2.1", + "version": "0.2.2", "keywords": [], "sideEffects": false, "exports": { diff --git a/clients/vscode/src/chat/WebviewHelper.ts b/clients/vscode/src/chat/WebviewHelper.ts index 704e26542ec6..473114d9049a 100644 --- a/clients/vscode/src/chat/WebviewHelper.ts +++ b/clients/vscode/src/chat/WebviewHelper.ts @@ -27,7 +27,6 @@ import { GitProvider } from "../git/GitProvider"; import { createClient } from "./chatPanel"; import { ChatFeature } from "../lsp/ChatFeature"; import { isBrowser } from "../env"; -import { getLogger } from "../logger"; export class WebviewHelper { webview?: Webview; @@ -576,13 +575,14 @@ export class WebviewHelper { }; const smartApplyInEditor = async (editor: TextEditor, opts: { languageId: string; smart: boolean }) => { if (editor.document.languageId !== opts.languageId) { - getLogger().info("editor:", editor.document.languageId, "opts:", opts.languageId); + this.logger.debug("Editor's languageId:", editor.document.languageId, "opts.languageId:", opts.languageId); window.showInformationMessage("The active editor is not in the correct language. Did normal apply."); applyInEditor(editor); return; } - getLogger("Tabby").info("Smart apply in editor started.", content); + this.logger.info("Smart apply in editor started."); + this.logger.trace("Smart apply in editor with content:", { content }); await window.withProgress( { @@ -591,13 +591,11 @@ export class WebviewHelper { cancellable: true, }, async (progress, token) => { - progress.report({ increment: 0, message: "Analyzing code..." }); + progress.report({ increment: 0, message: "Applying smart edit..." }); try { - getLogger().info("getting provide edit command", content); - progress.report({ increment: 30, message: "Applying smart edit..." }); await this.chat?.provideSmartApplyEdit( { - applyCode: content, + text: content, location: { uri: editor.document.uri.toString(), range: { diff --git a/clients/vscode/src/extension.ts b/clients/vscode/src/extension.ts index 8f5c1cc4263a..1fdb1bfb9901 100644 --- a/clients/vscode/src/extension.ts +++ b/clients/vscode/src/extension.ts @@ -15,7 +15,6 @@ import { Commands } from "./Commands"; import { Status } from "tabby-agent"; import { CodeActions } from "./CodeActions"; import { isBrowser } from "./env"; -import { ChatPanelViewProvider } from "./chat/ChatPanelViewProvider"; const logger = getLogger(); let client: Client | undefined = undefined; @@ -85,8 +84,6 @@ export async function activate(context: ExtensionContext) { webviewOptions: { retainContextWhenHidden: true }, }), ); - // Create chat panel view - new ChatPanelViewProvider(context, client.agent, gitProvider, client.chat); await gitProvider.init(); await client.start(); diff --git a/clients/vscode/src/lsp/ChatFeature.ts b/clients/vscode/src/lsp/ChatFeature.ts index fb330bc4b81d..ce147eccb03f 100644 --- a/clients/vscode/src/lsp/ChatFeature.ts +++ b/clients/vscode/src/lsp/ChatFeature.ts @@ -15,8 +15,8 @@ import { ChatEditToken, ChatEditResolveRequest, ChatEditResolveParams, - SmartApplyCodeParams, - SmartApplyCodeRequest, + SmartApplyParams, + SmartApplyRequest, } from "tabby-agent"; export class ChatFeature extends EventEmitter implements DynamicFeature { @@ -111,12 +111,11 @@ export class ChatFeature extends EventEmitter implements DynamicFeature return this.client.sendRequest(ChatEditRequest.method, params, token); } - async provideSmartApplyEdit(params: SmartApplyCodeParams, token?: CancellationToken): Promise { + async provideSmartApplyEdit(params: SmartApplyParams, token?: CancellationToken): Promise { if (!this.isAvailable) { return null; } - const res = await this.client.sendRequest(SmartApplyCodeRequest.type, params, token); - return res; + return this.client.sendRequest(SmartApplyRequest.method, params, token); } async resolveEdit(params: ChatEditResolveParams): Promise { diff --git a/clients/vscode/src/lsp/WorkspaceFeature.ts b/clients/vscode/src/lsp/WorkspaceFeature.ts index 9ee028fbf6ce..f3598921b4d6 100644 --- a/clients/vscode/src/lsp/WorkspaceFeature.ts +++ b/clients/vscode/src/lsp/WorkspaceFeature.ts @@ -3,8 +3,8 @@ import { ApplyWorkspaceEditParams, ApplyWorkspaceEditRequest } from "tabby-agent import { BaseLanguageClient, FeatureState, StaticFeature, TextEdit } from "vscode-languageclient"; import { Disposable, Position, Range, TextDocument, TextEditorEdit, window, workspace } from "vscode"; import { diffLines } from "diff"; + export class WorkSpaceFeature extends EventEmitter implements StaticFeature { - private registration: string | undefined = undefined; private disposables: Disposable[] = []; constructor(private readonly client: BaseLanguageClient) { @@ -39,10 +39,6 @@ export class WorkSpaceFeature extends EventEmitter implements StaticFeature { this.disposables = []; } - get isAvailable(): boolean { - return !!this.registration; - } - private async handleApplyWorkspaceEdit(params: ApplyWorkspaceEditParams): Promise { const { edit, options } = params; const activeEditor = window.activeTextEditor; diff --git a/ee/tabby-ui/components/ui/codeblock.tsx b/ee/tabby-ui/components/ui/codeblock.tsx index c2102a2e4e5d..2831fc6f146a 100644 --- a/ee/tabby-ui/components/ui/codeblock.tsx +++ b/ee/tabby-ui/components/ui/codeblock.tsx @@ -145,7 +145,7 @@ const CodeBlock: FC = memo( variant="ghost" size="icon" className="text-xs hover:bg-[#3C382F] hover:text-[#F4F4F5] focus-visible:ring-1 focus-visible:ring-slate-700 focus-visible:ring-offset-0" - onClick={onApplyInEditor.bind(null, value, undefined)} + onClick={() => onApplyInEditor(value, undefined)} > Apply in Editor