Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vscode): adding clickble symbol render in chat panel #3420

Merged
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4d75874
feat: add onRenderLsp event handler for rendering language server pro…
Sma1lboy Nov 14, 2024
d23e21e
Merge branch 'main' into feat-adding-clickble-keywords-chat-panel
Sma1lboy Nov 16, 2024
9bc7372
feat: Add onRenderLsp event handler for rendering language server pro…
Sma1lboy Nov 16, 2024
06e3c86
chore(vscode): update targetFile path to use workspace relative path
Sma1lboy Nov 16, 2024
8916a6a
refactor: Update code to use KeywordInfo type for onRenderLsp event h…
Sma1lboy Nov 16, 2024
227cdea
Merge branch 'main' into feat-adding-clickble-keywords-chat-panel
Sma1lboy Nov 30, 2024
96ea160
feat: add onNavigateSymbol method to ClientApi interface
Sma1lboy Nov 30, 2024
5da9abe
feat: add onNavigateSymbol method to ClientApi interface
Sma1lboy Nov 30, 2024
c3d0709
feat: add onNavigateSymbol method to ClientApi interface
Sma1lboy Nov 30, 2024
db8e89d
feat: add onHoverSymbol method to ClientApi interface
Sma1lboy Nov 30, 2024
6719a43
feat: add onHoverSymbol and findSymbolInfo methods to WebviewHelper
Sma1lboy Nov 30, 2024
e63de18
feat: add onHoverSymbol and findSymbolInfo methods to WebviewHelper
Sma1lboy Nov 30, 2024
94337e5
fix: update onNavigateSymbol parameter name in ClientApi interface
Sma1lboy Nov 30, 2024
9de4f31
fix: update onNavigateSymbol parameter name in ClientApi interface
Sma1lboy Nov 30, 2024
f06da60
feat: Add support for onNavigateSymbol and onHoverSymbol in ChatPage
Sma1lboy Nov 30, 2024
8c4f3e6
chore: remove unused type
Sma1lboy Nov 30, 2024
96f9a5a
feat: Update ClientApi interface to make onNavigateSymbol optional
Sma1lboy Dec 3, 2024
245a11c
feat: Add support for onHoverSymbol in ChatPage
Sma1lboy Dec 3, 2024
f3ed10d
[autofix.ci] apply automated fixes
autofix-ci[bot] Dec 3, 2024
f147583
feat: Add activeSelection prop to MessageMarkdown and update imports
Sma1lboy Dec 4, 2024
5d3d8b3
feat: Rename parameter in onNavigateSymbol to hintFilepaths for clarity
Sma1lboy Dec 4, 2024
6ba498c
feat: Implement CodeElement component for rendering code in Markdown
Sma1lboy Dec 4, 2024
b45e256
refactor: Remove onNavigateToContext prop from MessageMarkdown and re…
Sma1lboy Dec 4, 2024
a6fcbf5
feat: Rename onNavigateSymbol to onLookupSymbol and update its signat…
Sma1lboy Dec 5, 2024
e4d0428
feat: Rename onNavigateSymbol to onLookupSymbol and refactor symbol l…
Sma1lboy Dec 5, 2024
9be446b
feat: Rename onNavigateSymbol to onLookupSymbol and update related lo…
Sma1lboy Dec 5, 2024
2059ad7
[autofix.ci] apply automated fixes
autofix-ci[bot] Dec 5, 2024
0992aad
update: render symbol
liangfung Dec 6, 2024
9253ce6
Merge branch 'main' into feat-adding-clickble-keywords-chat-panel
wsxiaoys Dec 8, 2024
02bd159
Update icons.tsx
wsxiaoys Dec 8, 2024
b16344b
[autofix.ci] apply automated fixes
autofix-ci[bot] Dec 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 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 @@ -76,7 +83,11 @@ export interface ClientApiMethods {
onCopy: (content: string) => void

onKeyboardEvent: (type: 'keydown' | 'keyup' | 'keypress', event: KeyboardEventInit) => void
// navigate to lsp definition by symbol
onNavigateSymbol: (filepaths: string[], keyword: string) => void
Sma1lboy marked this conversation as resolved.
Show resolved Hide resolved

// on hover symbol return symbol info if exist
onHoverSymbol: (filepaths: string[], keyword: string) => Promise<SymbolInfo | undefined>
}

export interface ClientApi extends ClientApiMethods {
Expand Down Expand Up @@ -119,6 +130,8 @@ export function createClient(target: HTMLIFrameElement, api: ClientApiMethods):
onLoaded: api.onLoaded,
onCopy: api.onCopy,
onKeyboardEvent: api.onKeyboardEvent,
onNavigateSymbol: api.onNavigateSymbol,
onHoverSymbol: api.onHoverSymbol,
},
})
}
Expand Down
98 changes: 97 additions & 1 deletion clients/vscode/src/chat/WebviewHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ import {
Webview,
ColorThemeKind,
ProgressLocation,
commands,
LocationLink,
workspace,
Range,
Position,
TextEditorRevealType,
} 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 @@ -22,6 +28,7 @@ import { createClient } from "./chatPanel";
import { ChatFeature } from "../lsp/ChatFeature";
import { isBrowser } from "../env";
import { getFileContextFromSelection, showFileContext } from "./fileContext";
import path from "path";

export class WebviewHelper {
webview?: Webview;
Expand Down Expand Up @@ -394,6 +401,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 @@ -424,6 +434,61 @@ export class WebviewHelper {
});
};

const findSymbolInfo = async (filepaths: string[], keyword: string): Promise<SymbolInfo | undefined> => {
Sma1lboy marked this conversation as resolved.
Show resolved Hide resolved
if (!keyword || !filepaths.length) {
this.logger.info("No keyword or filepaths provided");
return undefined;
}

try {
const workspaceRoot = workspace.workspaceFolders?.[0];
if (!workspaceRoot) {
this.logger.error("No workspace folder found");
return undefined;
}
const rootPath = workspaceRoot.uri;

for (const filepath of filepaths) {
const normalizedPath = filepath.startsWith("/") ? filepath.slice(1) : filepath;
const fullPath = path.join(rootPath.path, normalizedPath);
const fileUri = Uri.file(fullPath);
const document = await workspace.openTextDocument(fileUri);

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",
fileUri,
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.fsPath,
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 createClient(webview, {
navigate: async (context: Context, opts?: NavigateOpts) => {
if (opts?.openInEditor) {
Expand Down Expand Up @@ -557,6 +622,37 @@ export class WebviewHelper {
this.logger.debug(`Dispatching keyboard event: ${type} ${JSON.stringify(event)}`);
this.webview?.postMessage({ action: "dispatchKeyboardEvent", type, event });
},
onNavigateSymbol: async (filepath: string[], keyword: string) => {
const symbolInfo = await findSymbolInfo(filepath, keyword);

if (symbolInfo) {
try {
const targetUri = Uri.file(symbolInfo.targetFile);
const targetDocument = await workspace.openTextDocument(targetUri);
const editor = await window.showTextDocument(targetDocument);
editor.revealRange(
new Range(
new Position(symbolInfo.targetLine - 1, symbolInfo.targetCol),
new Position(symbolInfo.targetLine - 1, symbolInfo.targetCol),
),
TextEditorRevealType.InCenter,
);

this.logger.info(`Navigated to symbol: ${keyword}`);
} catch (error) {
this.logger.error("Error navigating to symbol:", error);
}
} else {
this.logger.info(`Keyword "${keyword}" not found in provided filepaths`);
}
},
onHoverSymbol: async (filepaths: string[], keyword: string): Promise<SymbolInfo | undefined> => {
const symbolInfo = await findSymbolInfo(filepaths, keyword);
if (!symbolInfo) {
this.logger.info(`Keyword "${keyword}" not found in provided filepaths`);
}
return symbolInfo;
},
});
}
}
2 changes: 2 additions & 0 deletions clients/vscode/src/chat/chatPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export function createClient(webview: Webview, api: ClientApiMethods): ServerApi
onLoaded: api.onLoaded,
onCopy: api.onCopy,
onKeyboardEvent: api.onKeyboardEvent,
onNavigateSymbol: api.onNavigateSymbol,
onHoverSymbol: api.onHoverSymbol,
},
});
}
15 changes: 15 additions & 0 deletions ee/tabby-ui/app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ export default function ChatPage() {
// server feature support check
const [supportsOnApplyInEditorV2, setSupportsOnApplyInEditorV2] =
useState(false)
const [supportsOnNavigateSymbol, setSupportsOnNavigateSymbol] =
useState(false)
const [supportsOnHoverSymbol, setSupportsOnHoverSymbol] = useState(false)

const sendMessage = (message: ChatMessage) => {
if (chatRef.current) {
Expand Down Expand Up @@ -236,6 +239,10 @@ export default function ChatPage() {
server
?.hasCapability('onApplyInEditorV2')
.then(setSupportsOnApplyInEditorV2)
server
?.hasCapability('onNavigateSymbol')
.then(setSupportsOnNavigateSymbol)
server?.hasCapability('onHoverSymbol').then(setSupportsOnHoverSymbol)
}

checkCapabilities()
Expand Down Expand Up @@ -388,6 +395,14 @@ export default function ChatPage() {
: server?.onApplyInEditor)
}
supportsOnApplyInEditorV2={supportsOnApplyInEditorV2}
onNavigateSymbol={
isInEditor &&
(supportsOnNavigateSymbol ? server?.onNavigateSymbol : undefined)
}
onHoverSymbol={
isInEditor &&
(supportsOnHoverSymbol ? server?.onHoverSymbol : undefined)
}
/>
</ErrorBoundary>
)
Expand Down
6 changes: 5 additions & 1 deletion ee/tabby-ui/app/files/components/chat-side-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ export const ChatSideBar: React.FC<ChatSideBarProps> = ({
onApplyInEditor(_content) {},
onLoaded() {},
onCopy(_content) {},
onKeyboardEvent() {}
onKeyboardEvent() {},
onNavigateSymbol(_filepath, _keywords) {},
async onHoverSymbol(_filepath, _keyword) {
return undefined
}
})

const getPrompt = ({ action }: QuickActionEventPayload) => {
Expand Down
21 changes: 20 additions & 1 deletion ee/tabby-ui/components/chat/chat.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import React, { RefObject } from 'react'
import { compact, findIndex, isEqual, some, uniqWith } from 'lodash-es'
import type { Context, FileContext, NavigateOpts } from 'tabby-chat-panel'
import type {
Context,
FileContext,
NavigateOpts,
SymbolInfo
} from 'tabby-chat-panel'

import { ERROR_CODE_NOT_FOUND } from '@/lib/constants'
import {
Expand Down Expand Up @@ -45,6 +50,11 @@ type ChatContextValue = {
onApplyInEditor?:
| ((content: string) => void)
| ((content: string, opts?: { languageId: string; smart: boolean }) => void)
onNavigateSymbol?: (filepaths: string[], keyword: string) => void
onHoverSymbol?: (
filepaths: string[],
keyword: string
) => Promise<SymbolInfo | undefined>
relevantContext: Context[]
activeSelection: Context | null
removeRelevantContext: (index: number) => void
Expand Down Expand Up @@ -83,6 +93,11 @@ interface ChatProps extends React.ComponentProps<'div'> {
onApplyInEditor?:
| ((content: string) => void)
| ((content: string, opts?: { languageId: string; smart: boolean }) => void)
onNavigateSymbol?: (filepaths: string[], keyword: string) => void
onHoverSymbol?: (
filepaths: string[],
keyword: string
) => Promise<SymbolInfo | undefined>
chatInputRef: RefObject<HTMLTextAreaElement>
supportsOnApplyInEditorV2: boolean
}
Expand All @@ -104,6 +119,8 @@ function ChatRenderer(
onCopyContent,
onSubmitMessage,
onApplyInEditor,
onNavigateSymbol,
onHoverSymbol,
chatInputRef,
supportsOnApplyInEditorV2
}: ChatProps,
Expand Down Expand Up @@ -529,6 +546,8 @@ function ChatRenderer(
container,
onCopyContent,
onApplyInEditor,
onNavigateSymbol,
onHoverSymbol,
relevantContext,
removeRelevantContext,
chatInputRef,
Expand Down
5 changes: 5 additions & 0 deletions ee/tabby-ui/components/chat/question-answer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ function AssistantMessageCard(props: AssistantMessageCardProps) {
onNavigateToContext,
onApplyInEditor,
onCopyContent,
onNavigateSymbol,
onHoverSymbol,
supportsOnApplyInEditorV2
} = React.useContext(ChatContext)
const [relevantCodeHighlightIndex, setRelevantCodeHighlightIndex] =
Expand Down Expand Up @@ -398,6 +400,9 @@ function AssistantMessageCard(props: AssistantMessageCardProps) {
onCodeCitationMouseEnter={onCodeCitationMouseEnter}
onCodeCitationMouseLeave={onCodeCitationMouseLeave}
canWrapLongLines={!isLoading}
onNavigateSymbol={onNavigateSymbol}
onHoverSymbol={onHoverSymbol}
onNavigateToContext={onNavigateToContext}
supportsOnApplyInEditorV2={supportsOnApplyInEditorV2}
/>
{!!message.error && <ErrorMessageBlock error={message.error} />}
Expand Down
Loading