Skip to content

Commit

Permalink
refactor: Update code to use KeywordInfo type for onRenderLsp event h…
Browse files Browse the repository at this point in the history
…andler
  • Loading branch information
Sma1lboy committed Nov 16, 2024
1 parent 06e3c86 commit 8916a6a
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 24 deletions.
18 changes: 9 additions & 9 deletions 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 KeywordInfo {
sourceFile: string
sourceLine: number
sourceChar: number
targetFile: string
targetLine: number
targetChar: number
}
export interface ClientApi {
navigate: (context: Context, opts?: NavigateOpts) => void
refresh: () => Promise<void>
Expand All @@ -71,14 +78,7 @@ export interface ClientApi {
onKeyboardEvent: (type: 'keydown' | 'keyup' | 'keypress', event: KeyboardEventInit) => void
onRenderLsp: (filepaths: string[], keywords: string[]) => Promise<Record<
string,
{
sourceFile: string
sourceLine: number
sourceChar: number
targetFile: string
targetLine: number
targetChar: number
}
KeywordInfo
>>
}

Expand Down
17 changes: 14 additions & 3 deletions 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,
KeywordInfo,
NavigateOpts
} from 'tabby-chat-panel'

import { ERROR_CODE_NOT_FOUND } from '@/lib/constants'
import {
Expand Down Expand Up @@ -46,7 +51,10 @@ type ChatContextValue = {
content: string,
opts?: { languageId: string; smart: boolean }
) => void
onRenderLsp?: (filepaths: string[], keywords: string[]) => void
onRenderLsp?: (
filepaths: string[],
keywords: string[]
) => Promise<Record<string, KeywordInfo>>
relevantContext: Context[]
activeSelection: Context | null
removeRelevantContext: (index: number) => void
Expand Down Expand Up @@ -85,7 +93,10 @@ interface ChatProps extends React.ComponentProps<'div'> {
content: string,
opts?: { languageId: string; smart: boolean }
) => void
onRenderLsp?: (filepaths: string[], keywords: string[]) => void
onRenderLsp?: (
filepaths: string[],
keywords: string[]
) => Promise<Record<string, KeywordInfo>>
chatInputRef: RefObject<HTMLTextAreaElement>
}

Expand Down
1 change: 1 addition & 0 deletions ee/tabby-ui/components/chat/question-answer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ function AssistantMessageCard(props: AssistantMessageCardProps) {
onCodeCitationMouseLeave={onCodeCitationMouseLeave}
canWrapLongLines={!isLoading}
onRenderLsp={onRenderLsp}
onNavigateToContext={onNavigateToContext}
/>
{!!message.error && <ErrorMessageBlock error={message.error} />}
</>
Expand Down
90 changes: 78 additions & 12 deletions ee/tabby-ui/components/message-markdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { MemoizedReactMarkdown } from '@/components/markdown'

import './style.css'

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

import {
MARKDOWN_CITATION_REGEX,
MARKDOWN_SOURCE_REGEX
Expand Down Expand Up @@ -75,7 +77,13 @@ export interface MessageMarkdownProps {
content: string,
opts?: { languageId: string; smart: boolean }
) => void
onRenderLsp?: (filepaths: string[], keywords: string[]) => void
onRenderLsp?: (
filepaths: string[],
keywords: string[]
) => Promise<Record<string, KeywordInfo>>
onNavigateToContext?:
| ((context: Context, opts?: NavigateOpts) => void)
| undefined
onCodeCitationClick?: (code: MessageAttachmentCode) => void
onCodeCitationMouseEnter?: (index: number) => void
onCodeCitationMouseLeave?: (index: number) => void
Expand All @@ -98,6 +106,8 @@ type MessageMarkdownContextValue = {
contextInfo: ContextInfo | undefined
fetchingContextInfo: boolean
canWrapLongLines: boolean
keywordMap: KeywordMapType
onNavigateToContext?: (context: Context, opts?: NavigateOpts) => void
}

const MessageMarkdownContext = createContext<MessageMarkdownContextValue>(
Expand All @@ -116,6 +126,7 @@ export function MessageMarkdown({
className,
canWrapLongLines,
onRenderLsp,
onNavigateToContext,
...rest
}: MessageMarkdownProps) {
const messageAttachments: MessageAttachments = useMemo(() => {
Expand Down Expand Up @@ -180,21 +191,26 @@ export function MessageMarkdown({
return elements
}

const [keywordMap, setKeywordMap] = useState<KeywordMapType>({})
// rendering keywords
useEffect(() => {
if (message && canWrapLongLines && onRenderLsp) {
// eslint-disable-next-line no-console
console.log('docs', attachmentDocs?.map(doc => doc.link).join(','))
// eslint-disable-next-line no-console
console.log('code', attachmentCode?.map(code => code.filepath).join(','))
setKeywordMap({}) // TODO: remove htis
const inlineCodeRegex = /`([^`]+)`/g
const matches = message.match(inlineCodeRegex)
if (matches) {
const inlineCodes = matches.map(match => match.replace(/`/g, '').trim())
onRenderLsp(inlineCodes, [attachmentCode ? attachmentCode?.map(code => code.filepath) : ...[]])
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])
}, [message, canWrapLongLines, onRenderLsp, attachmentCode])

return (
<MessageMarkdownContext.Provider
Expand All @@ -206,7 +222,9 @@ export function MessageMarkdown({
onCodeCitationMouseLeave: rest.onCodeCitationMouseLeave,
contextInfo,
fetchingContextInfo: !!fetchingContextInfo,
canWrapLongLines: !!canWrapLongLines
canWrapLongLines: !!canWrapLongLines,
keywordMap,
onNavigateToContext
}}
>
<MemoizedReactMarkdown
Expand Down Expand Up @@ -237,7 +255,6 @@ export function MessageMarkdown({
if (typeof childrenItem === 'string') {
return processMessagePlaceholder(childrenItem)
}

return <span key={index}>{childrenItem}</span>
})}
</li>
Expand All @@ -246,21 +263,66 @@ 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)

if (children.length) {
if (children[0] == '▍') {
return (
<span className="mt-1 animate-pulse cursor-default"></span>
)
}

children[0] = (children[0] as string).replace('`▍`', '▍')
}

const match = /language-(\w+)/.exec(className || '')

if (inline) {
const keyword = children[0]?.toString()
if (!keyword) {
return (
<code className={className} {...props}>
{children}
</code>
)
}

const info = keywordMap[keyword]

const isClickable = Boolean(
info && onNavigateToContext && canWrapLongLines
)

return (
<code className={className} {...props}>
<code
className={cn(
className,
isClickable
? 'hover:bg-muted/50 cursor-pointer transition-colors'
: ''
)}
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}` : ''}
{...props}
>
{children}
</code>
)
Expand Down Expand Up @@ -504,3 +566,7 @@ export function SiteFavicon({
</div>
)
}

interface KeywordMapType {
[key: string]: KeywordInfo
}

0 comments on commit 8916a6a

Please sign in to comment.