Skip to content

Commit

Permalink
feat(chat): add symbol and file information retrieval methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Sma1lboy committed Dec 20, 2024
1 parent c78539d commit 09f7f30
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 2 deletions.
100 changes: 100 additions & 0 deletions clients/vscode/src/chat/WebviewHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
Location,
LocationLink,
workspace,
DocumentSymbol,
SymbolInformation,
} from "vscode";
import type {
ServerApi,
Expand All @@ -25,6 +27,9 @@ import type {
SymbolInfo,
FileLocation,
GitRepository,
SymbolAtInfo,
AtInputOpts,
FileAtInfo,
} from "tabby-chat-panel";
import { TABBY_CHAT_PANEL_API_VERSION } from "tabby-chat-panel";
import hashObject from "object-hash";
Expand All @@ -42,6 +47,10 @@ import {
vscodePositionToChatPanelPosition,
vscodeRangeToChatPanelPositionRange,
chatPanelLocationToVSCodeRange,
getAllowedSymbolKinds,
isDocumentSymbol,
vscodeSymbolToSymbolAtInfo,
uriToFileAtFileInfo,
} from "./utils";

export class WebviewHelper {
Expand Down Expand Up @@ -728,6 +737,97 @@ export class WebviewHelper {
}
return infoList;
},
provideSymbolAtInfo: async (opts?: AtInputOpts): Promise<SymbolAtInfo[] | null> => {
const maxResults = opts?.limit || 50;
const query = opts?.query?.toLowerCase();

const editor = window.activeTextEditor;
if (!editor) return null;
const document = editor.document;

// Try document symbols first
const documentSymbols = await commands.executeCommand<DocumentSymbol[] | SymbolInformation[]>(
"vscode.executeDocumentSymbolProvider",
document.uri,
);

let results: SymbolAtInfo[] = [];

if (documentSymbols && documentSymbols.length > 0) {
const processSymbol = (symbol: DocumentSymbol | SymbolInformation) => {
if (results.length >= maxResults) return;

const symbolName = symbol.name.toLowerCase();
if (query && !symbolName.includes(query)) return;

if (getAllowedSymbolKinds().includes(symbol.kind)) {
results.push(vscodeSymbolToSymbolAtInfo(symbol, document.uri, this.gitProvider));
}
if (isDocumentSymbol(symbol)) {
symbol.children.forEach(processSymbol);
}
};
documentSymbols.forEach(processSymbol);
}

// Try workspace symbols if no document symbols found
if (results.length === 0 && query) {
const workspaceSymbols = await commands.executeCommand<SymbolInformation[]>(
"vscode.executeWorkspaceSymbolProvider",
query,
);

if (workspaceSymbols) {
results = workspaceSymbols
.filter((symbol) => getAllowedSymbolKinds().includes(symbol.kind))
.slice(0, maxResults)
.map((symbol) => vscodeSymbolToSymbolAtInfo(symbol, symbol.location.uri, this.gitProvider));
}
}

return results.length > 0 ? results : null;
},

provideFileAtInfo: async (opts?: AtInputOpts): Promise<FileAtInfo[] | null> => {
const maxResults = opts?.limit || 50;
const query = opts?.query?.toLowerCase();

const globPattern = query ? `**/${query}*` : "**/*";
try {
const files = await workspace.findFiles(globPattern, null, maxResults);
return files.map((uri) => uriToFileAtFileInfo(uri, this.gitProvider));
} catch (error) {
this.logger.error("Failed to find files:", error);
return null;
}
},
getSymbolAtInfoContent: async (info: SymbolAtInfo): Promise<string | null> => {
try {
const uri = chatPanelFilepathToLocalUri(info.location.filepath, this.gitProvider);
if (!uri) return null;

const document = await workspace.openTextDocument(uri);
const range = chatPanelLocationToVSCodeRange(info.location.location);
if (!range) return null;

return document.getText(range);
} catch (error) {
this.logger.error("Failed to get symbol content:", error);
return null;
}
},
getFileAtInfoContent: async (info: FileAtInfo): Promise<string | null> => {
try {
const uri = chatPanelFilepathToLocalUri(info.filepath, this.gitProvider);
if (!uri) return null;

const document = await workspace.openTextDocument(uri);
return document.getText();
} catch (error) {
this.logger.error("Failed to get file content:", error);
return null;
}
},
});
}
}
4 changes: 4 additions & 0 deletions clients/vscode/src/chat/chatPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export function createClient(webview: Webview, api: ClientApiMethods): ServerApi
lookupSymbol: api.lookupSymbol,
openInEditor: api.openInEditor,
readWorkspaceGitRepositories: api.readWorkspaceGitRepositories,
provideSymbolAtInfo: api.provideSymbolAtInfo,
getSymbolAtInfoContent: api.getSymbolAtInfoContent,
provideFileAtInfo: api.provideFileAtInfo,
getFileAtInfoContent: api.getFileAtInfoContent,
},
});
}
69 changes: 67 additions & 2 deletions clients/vscode/src/chat/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import path from "path";
import { Position as VSCodePosition, Range as VSCodeRange, Uri, workspace } from "vscode";
import type { Filepath, Position as ChatPanelPosition, LineRange, PositionRange, Location } from "tabby-chat-panel";
import {
Position as VSCodePosition,
Range as VSCodeRange,
Uri,
workspace,
DocumentSymbol,
SymbolInformation,
SymbolKind,
} from "vscode";
import type {
Filepath,
Position as ChatPanelPosition,
LineRange,
PositionRange,
Location,
SymbolAtInfo,
FileAtInfo,
} from "tabby-chat-panel";
import type { GitProvider } from "../git/GitProvider";
import { getLogger } from "../logger";

Expand Down Expand Up @@ -100,3 +116,52 @@ export function chatPanelLocationToVSCodeRange(location: Location): VSCodeRange
logger.warn(`Invalid location params.`, location);
return null;
}

export function isDocumentSymbol(symbol: DocumentSymbol | SymbolInformation): symbol is DocumentSymbol {
return "children" in symbol;
}

// FIXME: All allow symbol kinds, could be change later
export function getAllowedSymbolKinds(): SymbolKind[] {
return [
SymbolKind.Class,
SymbolKind.Function,
SymbolKind.Method,
SymbolKind.Interface,
SymbolKind.Enum,
SymbolKind.Struct,
];
}

export function vscodeSymbolToSymbolAtInfo(
symbol: DocumentSymbol | SymbolInformation,
documentUri: Uri,
gitProvider: GitProvider,
): SymbolAtInfo {
if (isDocumentSymbol(symbol)) {
return {
atKind: "symbol",
name: symbol.name,
location: {
filepath: localUriToChatPanelFilepath(documentUri, gitProvider),
location: vscodeRangeToChatPanelPositionRange(symbol.range),
},
};
}
return {
atKind: "symbol",
name: symbol.name,
location: {
filepath: localUriToChatPanelFilepath(documentUri, gitProvider),
location: vscodeRangeToChatPanelPositionRange(symbol.location.range),
},
};
}

export function uriToFileAtFileInfo(uri: Uri, gitProvider: GitProvider): FileAtInfo {
return {
atKind: "file",
name: path.basename(uri.fsPath),
filepath: localUriToChatPanelFilepath(uri, gitProvider),
};
}

0 comments on commit 09f7f30

Please sign in to comment.