Skip to content

Commit

Permalink
feat(vscode): support attach file context to inline edit
Browse files Browse the repository at this point in the history
  • Loading branch information
zhanba committed Jan 28, 2025
1 parent 8897506 commit 9a06bf4
Show file tree
Hide file tree
Showing 12 changed files with 548 additions and 112 deletions.
21 changes: 21 additions & 0 deletions clients/tabby-agent/src/chat/inlineEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import cryptoRandomString from "crypto-random-string";
import { isEmptyRange } from "../utils/range";
import { readResponseStream, Edit, applyWorkspaceEdit } from "./utils";
import { initMutexAbortController, mutexAbortController, resetMutexAbortController } from "./global";
import { readFile } from "fs-extra";
import { getLogger } from "../logger";

const logger = getLogger("ChatEditProvider");

export class ChatEditProvider implements Feature {
private lspConnection: Connection | undefined = undefined;
Expand Down Expand Up @@ -170,6 +174,20 @@ export class ChatEditProvider implements Feature {
}
}

// get file context
const fileContext =
(
await Promise.all(
(params.context ?? []).map(async (item) => {
const content = await readFile(item.path, "utf-8");
const fileContent = content.toString().substring(0, config.chat.edit.documentMaxChars);
return `filePath: "${item.path}" \n ${fileContent} \n`;
}),
)
).join("\n") ?? "";

logger.debug(`fileContext: ${fileContext}`);

const messages: { role: "user"; content: string }[] = [
{
role: "user",
Expand All @@ -189,13 +207,16 @@ export class ChatEditProvider implements Feature {
return userCommand;
case "{{languageId}}":
return document.languageId;
case "{{fileContext}}":
return fileContext;
default:
return "";
}
},
),
},
];
logger.debug(`messages: ${JSON.stringify(messages)}`);
const readableStream = await this.tabbyApiClient.fetchChatStream(
{
messages,
Expand Down
23 changes: 23 additions & 0 deletions clients/tabby-agent/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,31 @@ export type ChatEditParams = {
* use {@link ChatEditResolveRequest} to resolve it later.
*/
format: "previewChanges";

/**
* list of file contexts.
*/
context?: FileContext[];
};

/**
* Represents a file context.
* This type should only be used for sending context from client to server.
*/
export interface FileContext {
/**
* The filepath of the file.
* The path is relative to the workspace root.
*/
path: string;

/**
* The range of the selected content in the file.
* If the range is not provided, the whole file is considered.
*/
range?: Range;
}

export type ChatEditToken = string;

export type ChatFeatureNotAvailableError = {
Expand Down
2 changes: 2 additions & 0 deletions clients/vscode/.mocha.env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
process.env.NODE_ENV = "test";
process.env.IS_TEST = 1;
4 changes: 4 additions & 0 deletions clients/vscode/.mocharc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
spec: ["src/**/*.test.ts"],
require: ["ts-node/register", "./.mocha.env.js"],
};
6 changes: 5 additions & 1 deletion clients/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -454,10 +454,12 @@
"ovsx:publish-prerelease": "ovsx publish --no-dependencies --pre-release --pat $OVSX_PAT",
"package": "pnpm run vscode:package",
"publish": "pnpm run vscode:publish && pnpm run ovsx:publish",
"publish-prerelease": "pnpm run vscode:publish-prerelease && pnpm run ovsx:publish-prerelease"
"publish-prerelease": "pnpm run vscode:publish-prerelease && pnpm run ovsx:publish-prerelease",
"test": "mocha"
},
"devDependencies": {
"@types/dedent": "^0.7.2",
"@types/chai": "^4.3.5",
"@types/deep-equal": "^1.0.4",
"@types/diff": "^5.2.1",
"@types/fs-extra": "^11.0.1",
Expand All @@ -474,6 +476,7 @@
"@vscode/vsce": "^2.15.0",
"assert": "^2.0.0",
"debounce": "^2.2.0",
"chai": "^4.3.11",
"dedent": "^0.7.0",
"deep-equal": "^2.2.1",
"diff": "^5.2.0",
Expand All @@ -483,6 +486,7 @@
"eslint-config-prettier": "^9.0.0",
"fs-extra": "^11.1.1",
"get-installed-path": "^4.0.8",
"mocha": "^10.2.0",
"object-hash": "^3.0.0",
"ovsx": "^0.9.5",
"prettier": "^3.0.0",
Expand Down
5 changes: 0 additions & 5 deletions clients/vscode/src/chat/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,6 @@ export function localUriToListFileItem(uri: Uri, gitProvider: GitProvider): List
};
}

export function escapeGlobPattern(query: string): string {
// escape special glob characters: * ? [ ] { } ( ) ! @
return query.replace(/[*?[\]{}()!@]/g, "\\$&");
}

// Notebook cell uri conversion

function isJupyterNotebookFilepath(uri: Uri): boolean {
Expand Down
15 changes: 2 additions & 13 deletions clients/vscode/src/chat/webview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ import {
chatPanelLocationToVSCodeRange,
isValidForSyncActiveEditorSelection,
localUriToListFileItem,
escapeGlobPattern,
} from "./utils";
import { findFiles } from "../findFiles";
import { caseInsensitivePattern, findFiles } from "../findFiles";
import mainHtml from "./html/main.html";
import errorHtml from "./html/error.html";

Expand Down Expand Up @@ -519,17 +518,7 @@ export class ChatWebview {
}

try {
const caseInsensitivePattern = searchQuery
.split("")
.map((char) => {
if (char.toLowerCase() !== char.toUpperCase()) {
return `{${char.toLowerCase()},${char.toUpperCase()}}`;
}
return escapeGlobPattern(char);
})
.join("");

const globPattern = `**/${caseInsensitivePattern}*`;
const globPattern = caseInsensitivePattern(searchQuery);
this.logger.info(`Searching files with pattern: ${globPattern}, limit: ${maxResults}`);
const files = await findFiles(globPattern, { maxResults });
this.logger.info(`Found ${files.length} files.`);
Expand Down
19 changes: 19 additions & 0 deletions clients/vscode/src/findFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,22 @@ export async function findFiles(
});
}
}

export function escapeGlobPattern(query: string): string {
// escape special glob characters: * ? [ ] { } ( ) ! @
return query.replace(/[*?[\]{}()!@]/g, "\\$&");
}

export function caseInsensitivePattern(query: string) {
const caseInsensitivePattern = query
.split("")
.map((char) => {
if (char.toLowerCase() !== char.toUpperCase()) {
return `{${char.toLowerCase()},${char.toUpperCase()}}`;
}
return escapeGlobPattern(char);
})
.join("");

return `**/${caseInsensitivePattern}*`;
}
Loading

0 comments on commit 9a06bf4

Please sign in to comment.