Skip to content

Commit

Permalink
Merge branch 'main' into release-vsce-1.15
Browse files Browse the repository at this point in the history
  • Loading branch information
icycodes committed Dec 9, 2024
2 parents 91eea2d + 301c857 commit f56073f
Show file tree
Hide file tree
Showing 23 changed files with 593 additions and 156 deletions.
9 changes: 9 additions & 0 deletions .changes/v0.21.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## v0.21.1 (2024-12-09)

### ⚠️ Notice

* This is a patch release, please also check [the full release note](https://github.com/TabbyML/tabby/releases/tag/v0.21.0) for 0.21.

### 🧰 Fixed and Improvements

* Fixed Gitlab Context Provider.
9 changes: 4 additions & 5 deletions clients/tabby-agent/src/codeCompletion/contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,14 @@ export class CompletionContext {
*/
private handleAutoComplete(request: CompletionRequest): void {
if (!request.autoComplete?.completionItem) return;
// check if the completion item is the same as the curr segment
if (!request.autoComplete.currSeg || !request.autoComplete.completionItem.startsWith(request.autoComplete.currSeg))
return;

this.completionItem = request.autoComplete.completionItem;
this.currSeg = request.autoComplete.currSeg ?? "";
this.insertSeg = request.autoComplete.insertSeg ?? "";

// check if the completion item is the same as the insert segment
if (!this.completionItem.startsWith(this.insertSeg)) {
return;
}

const prefixText = request.text.slice(0, request.position);
const lastIndex = prefixText.lastIndexOf(this.currSeg);

Expand Down
29 changes: 14 additions & 15 deletions clients/tabby-agent/src/codeCompletion/solution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,22 @@ export class CompletionItem {
this.isBlank = isBlank(this.text);

// if with auto complete item, insert the completion item with the predicted text
let position = this.context.position;
this.processedText = this.fullText;

if (this.context.isWithCorrectAutoComplete()) {
this.processedText = this.context.insertSeg + this.fullText;
this.processedRange = {
start: this.context.insertPosition,
end: this.context.insertPosition,
};
} else {
this.processedText = this.fullText;
this.processedRange = {
start: this.context.currentLinePrefix.endsWith(this.replacePrefix)
? this.context.position - this.replacePrefix.length
: this.context.position,
end: this.context.currentLineSuffix.startsWith(this.replaceSuffix)
? this.context.position + this.replaceSuffix.length
: this.context.position,
};
position = this.context.insertPosition;
this.processedText = this.context.insertSeg + this.processedText;
}

this.processedRange = {
start: this.context.currentLinePrefix.endsWith(this.replacePrefix)
? position - this.replacePrefix.length
: position,
end: this.context.currentLineSuffix.startsWith(this.replaceSuffix)
? position + this.replaceSuffix.length
: position,
};
}

static createBlankItem(context: CompletionContext): CompletionItem {
Expand Down
12 changes: 11 additions & 1 deletion clients/tabby-chat-panel/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ export interface ServerApi {
updateTheme: (style: string, themeClass: string) => void
updateActiveSelection: (context: Context | null) => void
}

export interface SymbolInfo {
sourceFile: string
sourceLine: number
sourceCol: number
targetFile: string
targetLine: number
targetCol: number
}
export interface ClientApiMethods {
navigate: (context: Context, opts?: NavigateOpts) => void
refresh: () => Promise<void>
Expand All @@ -77,6 +84,8 @@ export interface ClientApiMethods {

onKeyboardEvent: (type: 'keydown' | 'keyup' | 'keypress', event: KeyboardEventInit) => void

// find symbol definition location by hint filepaths and keyword
onLookupSymbol?: (hintFilepaths: string[], keyword: string) => Promise<SymbolInfo | undefined>
}

export interface ClientApi extends ClientApiMethods {
Expand Down Expand Up @@ -119,6 +128,7 @@ export function createClient(target: HTMLIFrameElement, api: ClientApiMethods):
onLoaded: api.onLoaded,
onCopy: api.onCopy,
onKeyboardEvent: api.onKeyboardEvent,
onLookupSymbol: api.onLookupSymbol,
},
})
}
Expand Down
55 changes: 53 additions & 2 deletions clients/vscode/src/chat/WebviewHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {
Webview,
ColorThemeKind,
ProgressLocation,
commands,
LocationLink,
} from "vscode";
import type { ServerApi, ChatMessage, Context, NavigateOpts, OnLoadedParams } from "tabby-chat-panel";
import type { ServerApi, ChatMessage, Context, NavigateOpts, OnLoadedParams, SymbolInfo } from "tabby-chat-panel";
import { TABBY_CHAT_PANEL_API_VERSION } from "tabby-chat-panel";
import hashObject from "object-hash";
import * as semver from "semver";
Expand All @@ -20,7 +22,7 @@ import { GitProvider } from "../git/GitProvider";
import { createClient } from "./chatPanel";
import { Client as LspClient } from "../lsp/Client";
import { isBrowser } from "../env";
import { getFileContextFromSelection, showFileContext } from "./fileContext";
import { getFileContextFromSelection, showFileContext, openTextDocument } from "./fileContext";

export class WebviewHelper {
webview?: Webview;
Expand Down Expand Up @@ -384,6 +386,9 @@ export class WebviewHelper {
}

public createChatClient(webview: Webview) {
/*
utility functions for createClient
*/
const getIndentInfo = (document: TextDocument, selection: Selection) => {
// Determine the indentation for the content
// The calculation is based solely on the indentation of the first line
Expand Down Expand Up @@ -547,6 +552,52 @@ export class WebviewHelper {
this.logger.debug(`Dispatching keyboard event: ${type} ${JSON.stringify(event)}`);
this.webview?.postMessage({ action: "dispatchKeyboardEvent", type, event });
},
onLookupSymbol: async (hintFilepaths: string[], keyword: string): Promise<SymbolInfo | undefined> => {
const findSymbolInfo = async (filepaths: string[], keyword: string): Promise<SymbolInfo | undefined> => {
if (!keyword || !filepaths.length) {
this.logger.info("No keyword or filepaths provided");
return undefined;
}
try {
for (const filepath of filepaths) {
const document = await openTextDocument({ filePath: filepath }, this.gitProvider);
if (!document) {
this.logger.info(`File not found: ${filepath}`);
continue;
}
const content = document.getText();
let pos = 0;
while ((pos = content.indexOf(keyword, pos)) !== -1) {
const position = document.positionAt(pos);
const locations = await commands.executeCommand<LocationLink[]>(
"vscode.executeDefinitionProvider",
document.uri,
position,
);
if (locations && locations.length > 0) {
const location = locations[0];
if (location) {
return {
sourceFile: filepath,
sourceLine: position.line + 1,
sourceCol: position.character,
targetFile: location.targetUri.toString(true),
targetLine: location.targetRange.start.line + 1,
targetCol: location.targetRange.start.character,
};
}
}
pos += keyword.length;
}
}
} catch (error) {
this.logger.error("Error in findSymbolInfo:", error);
}
return undefined;
};

return await findSymbolInfo(hintFilepaths, keyword);
},
});
}
}
1 change: 1 addition & 0 deletions clients/vscode/src/chat/chatPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export function createClient(webview: Webview, api: ClientApiMethods): ServerApi
onLoaded: api.onLoaded,
onCopy: api.onCopy,
onKeyboardEvent: api.onKeyboardEvent,
onLookupSymbol: api.onLookupSymbol,
},
});
}
102 changes: 71 additions & 31 deletions clients/vscode/src/chat/fileContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { getLogger } from "../logger";

const logger = getLogger("FileContext");

export interface FilePathParams {
filePath: string;
gitRemoteUrl?: string;
}

export async function getFileContextFromSelection(
editor: TextEditor,
gitProvider: GitProvider,
Expand All @@ -19,7 +24,6 @@ export async function getFileContext(
gitProvider: GitProvider,
useSelection = false,
): Promise<FileContext | null> {
const uri = editor.document.uri;
const text = editor.document.getText(useSelection ? editor.selection : undefined);
if (!text || text.trim().length < 1) {
return null;
Expand All @@ -35,30 +39,14 @@ export async function getFileContext(
end: editor.document.lineCount,
};

const workspaceFolder =
workspace.getWorkspaceFolder(uri) ?? (editor.document.isUntitled ? workspace.workspaceFolders?.[0] : undefined);
const repo =
gitProvider.getRepository(uri) ?? (workspaceFolder ? gitProvider.getRepository(workspaceFolder.uri) : undefined);
const gitRemoteUrl = repo ? gitProvider.getDefaultRemoteUrl(repo) : undefined;
let filePath = uri.toString(true);
if (repo && gitRemoteUrl) {
const relativeFilePath = path.relative(repo.rootUri.toString(true), filePath);
if (!relativeFilePath.startsWith("..")) {
filePath = relativeFilePath;
}
} else if (workspaceFolder) {
const relativeFilePath = path.relative(workspaceFolder.uri.toString(true), filePath);
if (!relativeFilePath.startsWith("..")) {
filePath = relativeFilePath;
}
}
const filePathParams = await buildFilePathParams(editor.document.uri, gitProvider);

return {
kind: "file",
content,
range,
filepath: filePath,
git_url: gitRemoteUrl ?? "",
filepath: filePathParams.filePath,
git_url: filePathParams.gitRemoteUrl ?? "",
};
}

Expand All @@ -69,7 +57,13 @@ export async function showFileContext(fileContext: FileContext, gitProvider: Git
return;
}

const document = await openTextDocument(fileContext, gitProvider);
const document = await openTextDocument(
{
filePath: fileContext.filepath,
gitRemoteUrl: fileContext.git_url,
},
gitProvider,
);
if (!document) {
throw new Error(`File not found: ${fileContext.filepath}`);
}
Expand All @@ -87,43 +81,89 @@ export async function showFileContext(fileContext: FileContext, gitProvider: Git
editor.revealRange(new Range(start, end), TextEditorRevealType.InCenter);
}

async function openTextDocument(fileContext: FileContext, gitProvider: GitProvider): Promise<TextDocument | null> {
const { filepath: filePath, git_url: gitUrl } = fileContext;
export async function buildFilePathParams(uri: Uri, gitProvider: GitProvider): Promise<FilePathParams> {
const workspaceFolder =
workspace.getWorkspaceFolder(uri) ?? (uri.scheme === "untitled" ? workspace.workspaceFolders?.[0] : undefined);
const repo =
gitProvider.getRepository(uri) ?? (workspaceFolder ? gitProvider.getRepository(workspaceFolder.uri) : undefined);
const gitRemoteUrl = repo ? gitProvider.getDefaultRemoteUrl(repo) : undefined;
let filePath = uri.toString(true);
if (repo && gitRemoteUrl) {
const relativeFilePath = path.relative(repo.rootUri.toString(true), filePath);
if (!relativeFilePath.startsWith("..")) {
filePath = relativeFilePath;
}
} else if (workspaceFolder) {
const relativeFilePath = path.relative(workspaceFolder.uri.toString(true), filePath);
if (!relativeFilePath.startsWith("..")) {
filePath = relativeFilePath;
}
}
return {
filePath,
gitRemoteUrl,
};
}

export async function openTextDocument(
filePathParams: FilePathParams,
gitProvider: GitProvider,
): Promise<TextDocument | null> {
const { filePath, gitRemoteUrl } = filePathParams;

// Try parse as absolute path
try {
// try parse as absolute path
const absoluteFilepath = Uri.parse(filePath, true);
if (absoluteFilepath.scheme) {
return await workspace.openTextDocument(absoluteFilepath);
}
} catch (err) {
// Cannot open as absolute path, try to find file in git root
// ignore
}

if (gitUrl && gitUrl.trim().length > 0) {
const localGitRoot = gitProvider.findLocalRootUriByRemoteUrl(gitUrl);
// Try find file in provided git repository
if (gitRemoteUrl && gitRemoteUrl.trim().length > 0) {
const localGitRoot = gitProvider.findLocalRootUriByRemoteUrl(gitRemoteUrl);
if (localGitRoot) {
try {
const absoluteFilepath = Uri.joinPath(localGitRoot, filePath);
return await workspace.openTextDocument(absoluteFilepath);
} catch (err) {
// File not found in local git root, try to find file in workspace folders
// ignore
}
}
}

for (const root of workspace.workspaceFolders ?? []) {
// Try find file in workspace folder
const absoluteFilepath = Uri.joinPath(root.uri, filePath);
try {
return await workspace.openTextDocument(absoluteFilepath);
} catch (err) {
// File not found in workspace folder, try to use findFiles
// ignore
}

// Try find file in git repository of workspace folder
const localGitRoot = gitProvider.getRepository(root.uri)?.rootUri;
if (localGitRoot) {
try {
const absoluteFilepath = Uri.joinPath(localGitRoot, filePath);
return await workspace.openTextDocument(absoluteFilepath);
} catch (err) {
// ignore
}
}
}
logger.info("File not found in workspace folders, trying with findFiles...");

// Try find file in workspace folders using workspace.findFiles
logger.info("File not found in workspace folders, trying with findFiles...");
const files = await workspace.findFiles(filePath, undefined, 1);
if (files[0]) {
return workspace.openTextDocument(files[0]);
try {
return await workspace.openTextDocument(files[0]);
} catch (err) {
// ignore
}
}

return null;
Expand Down
3 changes: 2 additions & 1 deletion crates/tabby-git/src/serve_git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ fn resolve<'a>(
basename: relpath
.join(entry.name().expect("failed to resolve entry name"))
.display()
.to_string(),
.to_string()
.replace("\\", "/"),
}
})
.collect::<Vec<_>>(),
Expand Down
Loading

0 comments on commit f56073f

Please sign in to comment.