Skip to content

Commit

Permalink
feat: add onNavigateSymbol method to ClientApi interface
Browse files Browse the repository at this point in the history
  • Loading branch information
Sma1lboy committed Nov 30, 2024
1 parent 5da9abe commit c3d0709
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 88 deletions.
2 changes: 1 addition & 1 deletion ee/tabby-ui/app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ export default function ChatPage() {
}
supportsOnApplyInEditorV2={supportsOnApplyInEditorV2}
// TODO: adding capability check for onRenderLsp
onRenderLsp={isInEditor && server?.onRenderLsp}
onNavigateSymbol={isInEditor && server?.onNavigateSymbol}
/>
</ErrorBoundary>
)
Expand Down
3 changes: 2 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,8 @@ export const ChatSideBar: React.FC<ChatSideBarProps> = ({
onApplyInEditor(_content) {},
onLoaded() {},
onCopy(_content) {},
onKeyboardEvent() {}
onKeyboardEvent() {},
onNavigateSymbol(_filepath, _keywords) {}
})

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

import { ERROR_CODE_NOT_FOUND } from '@/lib/constants'
import {
Expand Down Expand Up @@ -50,10 +45,7 @@ type ChatContextValue = {
onApplyInEditor?:
| ((content: string) => void)
| ((content: string, opts?: { languageId: string; smart: boolean }) => void)
onRenderLsp?: (
filepaths: string[],
keywords: string[]
) => Promise<Record<string, KeywordInfo>>
onNavigateSymbol?: (filepaths: string[], keywords: string) => void
relevantContext: Context[]
activeSelection: Context | null
removeRelevantContext: (index: number) => void
Expand Down Expand Up @@ -92,10 +84,7 @@ interface ChatProps extends React.ComponentProps<'div'> {
onApplyInEditor?:
| ((content: string) => void)
| ((content: string, opts?: { languageId: string; smart: boolean }) => void)
onRenderLsp?: (
filepaths: string[],
keywords: string[]
) => Promise<Record<string, KeywordInfo>>
onNavigateSymbol?: (filepaths: string[], keywords: string) => void
chatInputRef: RefObject<HTMLTextAreaElement>
supportsOnApplyInEditorV2: boolean
}
Expand All @@ -117,7 +106,7 @@ function ChatRenderer(
onCopyContent,
onSubmitMessage,
onApplyInEditor,
onRenderLsp,
onNavigateSymbol,
chatInputRef,
supportsOnApplyInEditorV2
}: ChatProps,
Expand Down Expand Up @@ -543,7 +532,7 @@ function ChatRenderer(
container,
onCopyContent,
onApplyInEditor,
onRenderLsp,
onNavigateSymbol,
relevantContext,
removeRelevantContext,
chatInputRef,
Expand Down
4 changes: 2 additions & 2 deletions ee/tabby-ui/components/chat/question-answer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ function AssistantMessageCard(props: AssistantMessageCardProps) {
onNavigateToContext,
onApplyInEditor,
onCopyContent,
onRenderLsp,
onNavigateSymbol,
supportsOnApplyInEditorV2
} = React.useContext(ChatContext)
const [relevantCodeHighlightIndex, setRelevantCodeHighlightIndex] =
Expand Down Expand Up @@ -399,7 +399,7 @@ function AssistantMessageCard(props: AssistantMessageCardProps) {
onCodeCitationMouseEnter={onCodeCitationMouseEnter}
onCodeCitationMouseLeave={onCodeCitationMouseLeave}
canWrapLongLines={!isLoading}
onRenderLsp={onRenderLsp}
onNavigateSymbol={onNavigateSymbol}
onNavigateToContext={onNavigateToContext}
supportsOnApplyInEditorV2={supportsOnApplyInEditorV2}
/>
Expand Down
98 changes: 30 additions & 68 deletions ee/tabby-ui/components/message-markdown/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import {
createContext,
ReactNode,
useContext,
useEffect,
useMemo,
useState
} from 'react'
import { createContext, ReactNode, useContext, useMemo, useState } from 'react'
import Image from 'next/image'
import defaultFavicon from '@/assets/default-favicon.png'
import DOMPurify from 'dompurify'
Expand All @@ -28,7 +21,7 @@ import { MemoizedReactMarkdown } from '@/components/markdown'

import './style.css'

import { Context, KeywordInfo, NavigateOpts } from 'tabby-chat-panel/index'
import { Context, NavigateOpts, SymbolInfo } from 'tabby-chat-panel/index'

import {
MARKDOWN_CITATION_REGEX,
Expand Down Expand Up @@ -79,10 +72,7 @@ export interface MessageMarkdownProps {
content: string,
opts?: { languageId: string; smart: boolean }
) => void
onRenderLsp?: (
filepaths: string[],
keywords: string[]
) => Promise<Record<string, KeywordInfo>>
onNavigateSymbol?: (filepaths: string[], keywords: string) => void
onNavigateToContext?:
| ((context: Context, opts?: NavigateOpts) => void)
| undefined
Expand All @@ -109,8 +99,8 @@ type MessageMarkdownContextValue = {
contextInfo: ContextInfo | undefined
fetchingContextInfo: boolean
canWrapLongLines: boolean
keywordMap: KeywordMapType
onNavigateToContext?: (context: Context, opts?: NavigateOpts) => void
onNavigateSymbol?: (filepaths: string[], keywords: string) => void
supportsOnApplyInEditorV2: boolean
}

Expand All @@ -129,7 +119,7 @@ export function MessageMarkdown({
fetchingContextInfo,
className,
canWrapLongLines,
onRenderLsp,
onNavigateSymbol,
onNavigateToContext,
supportsOnApplyInEditorV2,
...rest
Expand Down Expand Up @@ -196,27 +186,6 @@ export function MessageMarkdown({
return elements
}

const [keywordMap, setKeywordMap] = useState<KeywordMapType>({})
// rendering keywords
useEffect(() => {
if (message && canWrapLongLines && onRenderLsp) {
setKeywordMap({}) // TODO: remove htis
const inlineCodeRegex = /`([^`]+)`/g
const matches = message.match(inlineCodeRegex)
if (matches) {
const inlineCodes = Array.from(
new Set(matches.map(match => match.replace(/`/g, '').trim()))
)
onRenderLsp(
attachmentCode ? attachmentCode?.map(code => code.filepath) : [],
inlineCodes
).then(res => {
setKeywordMap(res)
})
}
}
}, [message, canWrapLongLines, onRenderLsp, attachmentCode])

return (
<MessageMarkdownContext.Provider
value={{
Expand All @@ -228,9 +197,9 @@ export function MessageMarkdown({
contextInfo,
fetchingContextInfo: !!fetchingContextInfo,
canWrapLongLines: !!canWrapLongLines,
keywordMap,
onNavigateToContext,
supportsOnApplyInEditorV2
supportsOnApplyInEditorV2,
onNavigateSymbol
}}
>
<MemoizedReactMarkdown
Expand Down Expand Up @@ -269,12 +238,13 @@ export function MessageMarkdown({
return <li>{children}</li>
},
code({ node, inline, className, children, ...props }) {
const { keywordMap, onNavigateToContext, canWrapLongLines } =
// eslint-disable-next-line react-hooks/rules-of-hooks
useContext(MessageMarkdownContext)
// eslint-disable-next-line react-hooks/rules-of-hooks
const { onNavigateSymbol, canWrapLongLines } = useContext(
MessageMarkdownContext
)

if (children.length) {
if (children[0] == '▍') {
if (children[0] === '▍') {
return (
<span className="mt-1 animate-pulse cursor-default"></span>
)
Expand All @@ -294,39 +264,31 @@ export function MessageMarkdown({
)
}

const info = keywordMap[keyword]
const isClickable = Boolean(onNavigateSymbol && canWrapLongLines)

const isClickable = Boolean(
info && onNavigateToContext && canWrapLongLines
)
const handleClick = () => {
if (!isClickable) return
if (onNavigateSymbol) {
onNavigateSymbol(
attachmentCode
? attachmentCode.map(code => code.filepath)
: [],
keyword
)
}
}

return (
<code
className={cn(
className,
isClickable
? 'hover:bg-muted/50 cursor-pointer transition-colors'
? 'cursor-pointer transition-colors hover:bg-muted/50'
: ''
)}
onClick={() => {
if (!isClickable) return
if (onNavigateToContext) {
onNavigateToContext(
{
filepath: info.targetFile,
content: '',
git_url: '',
kind: 'file',
range: {
start: info.targetLine,
end: info.targetLine + 1
}
},
{ openInEditor: true }
)
}
}}
title={info ? `${info.targetFile}:${info.targetLine}` : ''}
onClick={handleClick}
// TODO(Sma1lboy): use onHover to lazy load this part
title={isClickable ? '' : ''}
{...props}
>
{children}
Expand Down Expand Up @@ -588,8 +550,8 @@ export function SiteFavicon({
)
}

interface KeywordMapType {
[key: string]: KeywordInfo
interface SymbolMapType {
[key: string]: SymbolInfo
}

function IssueStateBadge({ closed }: { closed: boolean }) {
Expand Down

0 comments on commit c3d0709

Please sign in to comment.